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

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


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

.github/FUNDING.yml+1-1
......@@ -1 +1 @@
1github: [andrewrk]
1github: [ziglang]
CMakeLists.txt+12
......@@ -53,6 +53,8 @@ set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not com
5353set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
5454set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation")
5555set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp")
56set(ZIG_WORKAROUND_4799 off CACHE BOOL "workaround for https://github.com/ziglang/zig/issues/4799")
57set(ZIG_WORKAROUND_POLLY_SO off CACHE STRING "workaround for https://github.com/ziglang/zig/issues/4799")
5658set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available")
5759
5860if(CCACHE_PROGRAM AND ZIG_USE_CCACHE)
......@@ -88,6 +90,11 @@ if(APPLE AND ZIG_STATIC)
8890 list(APPEND LLVM_LIBRARIES "${ZLIB}")
8991endif()
9092
93if(APPLE AND ZIG_WORKAROUND_4799)
94 # eg: ${CMAKE_PREFIX_PATH} could be /usr/local/opt/llvm/
95 list(APPEND LLVM_LIBRARIES "-Wl,${CMAKE_PREFIX_PATH}/lib/libPolly.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyPPCG.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyISL.a")
96endif()
97
9198set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp")
9299
93100# Handle multi-config builds and place each into a common lib. The VS generator
......@@ -288,6 +295,7 @@ set(ZIG_SOURCES
288295 "${CMAKE_SOURCE_DIR}/src/target.cpp"
289296 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
290297 "${CMAKE_SOURCE_DIR}/src/util.cpp"
298 "${CMAKE_SOURCE_DIR}/src/softfloat_ext.cpp"
291299 "${ZIG_SOURCES_MEM_PROFILE}"
292300)
293301set(OPTIMIZED_C_SOURCES
......@@ -396,11 +404,15 @@ add_library(zig_cpp STATIC ${ZIG_CPP_SOURCES})
396404set_target_properties(zig_cpp PROPERTIES
397405 COMPILE_FLAGS ${EXE_CFLAGS}
398406)
407
399408target_link_libraries(zig_cpp LINK_PUBLIC
400409 ${CLANG_LIBRARIES}
401410 ${LLD_LIBRARIES}
402411 ${LLVM_LIBRARIES}
403412)
413if(ZIG_WORKAROUND_POLLY_SO)
414 target_link_libraries(zig_cpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}")
415endif()
404416
405417add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES})
406418set_target_properties(opt_c_util PROPERTIES
CONTRIBUTING.md+5
......@@ -152,6 +152,11 @@ The relevant tests for this feature are:
152152 same, and that the program exits cleanly. This kind of test coverage is preferred, when
153153 possible, because it makes sure that the resulting Zig code is actually viable.
154154
155 * `test/stage1/behavior/translate_c_macros.zig` - each test case consists of a Zig test
156 which checks that the relevant macros in `test/stage1/behavior/translate_c_macros.h`.
157 have the correct values. Macros have to be tested separately since they are expanded by
158 Clang in `run_translated_c` tests.
159
155160 * `test/translate_c.zig` - each test case is C code, with a list of expected strings which
156161 must be found in the resulting Zig code. This kind of test is more precise in what it
157162 measures, but does not provide test coverage of whether the resulting Zig code is valid.
README.md+6-2
......@@ -51,6 +51,8 @@ cmake ..
5151make install
5252```
5353
54Need help? [Troubleshooting Build Issues](https://github.com/ziglang/zig/wiki/Troubleshooting-Build-Issues)
55
5456##### MacOS
5557
5658```
......@@ -64,9 +66,11 @@ make install
6466
6567You will now run into this issue:
6668[homebrew and llvm 10 packages in apt.llvm.org are broken with undefined reference to getPollyPluginInfo](https://github.com/ziglang/zig/issues/4799)
69or
70[error: unable to create target: 'Unable to find target for this triple (no targets are registered)'](https://github.com/ziglang/zig/issues/5055),
71in which case try `-DZIG_WORKAROUND_4799=ON`
6772
68Please help upstream LLVM and Homebrew solve this issue, there is nothing Zig
69can do about it. See that issue for a workaround you can do in the meantime.
73Hopefully this will be fixed upstream with LLVM 10.0.1.
7074
7175##### Windows
7276
build.zig+43-27
......@@ -34,26 +34,12 @@ pub fn build(b: *Builder) !void {
3434
3535 const test_step = b.step("test", "Run all the tests");
3636
37 const config_h_text = if (b.option(
38 []const u8,
39 "config_h",
40 "Path to the generated config.h",
41 )) |config_h_path|
42 try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes)
43 else
44 try findAndReadConfigH(b);
45
4637 var test_stage2 = b.addTest("src-self-hosted/test.zig");
4738 test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness
4839 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
4940
5041 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
5142
52 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
53 exe.setBuildMode(mode);
54 test_step.dependOn(&exe.step);
55 b.default_step.dependOn(&exe.step);
56
5743 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
5844 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
5945 const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
......@@ -63,17 +49,44 @@ pub fn build(b: *Builder) !void {
6349
6450 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
6551 const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false;
66 if (enable_llvm) {
67 var ctx = parseConfigH(b, config_h_text);
68 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
52 const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
6953
70 try configureStage2(b, exe, ctx);
71 }
7254 if (!only_install_lib_files) {
73 exe.install();
55 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
56 exe.setBuildMode(mode);
57 test_step.dependOn(&exe.step);
58 b.default_step.dependOn(&exe.step);
59
60 if (enable_llvm) {
61 const config_h_text = if (config_h_path_option) |config_h_path|
62 try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes)
63 else
64 try findAndReadConfigH(b);
65
66 var ctx = parseConfigH(b, config_h_text);
67 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
68
69 try configureStage2(b, exe, ctx);
70 }
71 if (!only_install_lib_files) {
72 exe.install();
73 }
74 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
75 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
76 if (link_libc) exe.linkLibC();
77
78 exe.addBuildOption(bool, "enable_tracy", tracy != null);
79 if (tracy) |tracy_path| {
80 const client_cpp = fs.path.join(
81 b.allocator,
82 &[_][]const u8{ tracy_path, "TracyClient.cpp" },
83 ) catch unreachable;
84 exe.addIncludeDir(tracy_path);
85 exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" });
86 exe.linkSystemLibraryName("c++");
87 exe.linkLibC();
88 }
7489 }
75 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
76 if (link_libc) exe.linkLibC();
7790
7891 b.installDirectory(InstallDirectoryOptions{
7992 .source_dir = "lib",
......@@ -126,7 +139,10 @@ pub fn build(b: *Builder) !void {
126139 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
127140 test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
128141 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
129 test_step.dependOn(tests.addCliTests(b, test_filter, modes));
142 const test_cli = tests.addCliTests(b, test_filter, modes);
143 const test_cli_step = b.step("test-cli", "Run zig cli tests");
144 test_cli_step.dependOn(test_cli);
145 test_step.dependOn(test_cli);
130146 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
131147 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
132148 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
......@@ -137,7 +153,7 @@ pub fn build(b: *Builder) !void {
137153 test_step.dependOn(docs_step);
138154}
139155
140fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
156fn dependOnLib(b: *Builder, lib_exe_obj: anytype, dep: LibraryDep) void {
141157 for (dep.libdirs.items) |lib_dir| {
142158 lib_exe_obj.addLibPath(lib_dir);
143159 }
......@@ -177,7 +193,7 @@ fn fileExists(filename: []const u8) !bool {
177193 return true;
178194}
179195
180fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
196fn addCppLib(b: *Builder, lib_exe_obj: anytype, cmake_binary_dir: []const u8, lib_name: []const u8) void {
181197 lib_exe_obj.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
182198 cmake_binary_dir,
183199 "zig_cpp",
......@@ -259,7 +275,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
259275 return result;
260276}
261277
262fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
278fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
263279 exe.addIncludeDir("src");
264280 exe.addIncludeDir(ctx.cmake_binary_dir);
265281 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
......@@ -324,7 +340,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
324340fn addCxxKnownPath(
325341 b: *Builder,
326342 ctx: Context,
327 exe: var,
343 exe: anytype,
328344 objname: []const u8,
329345 errtxt: ?[]const u8,
330346) !void {
ci/azure/linux_script+5-1
......@@ -12,7 +12,7 @@ sudo apt-get update -q
1212
1313sudo apt-get remove -y llvm-*
1414sudo rm -rf /usr/local/*
15sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build
15sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build tidy
1616
1717QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552"
1818wget https://ziglang.org/deps/$QEMUBASE.tar.xz
......@@ -51,6 +51,10 @@ cd build
5151cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja
5252ninja install
5353./zig build test -Denable-qemu -Denable-wasmtime
54
55# look for HTML errors
56tidy -qe ../zig-cache/langref.html
57
5458VERSION="$(./zig version)"
5559
5660if [ "${BUILD_REASON}" != "PullRequest" ]; then
ci/azure/pipelines.yml+13-5
......@@ -40,12 +40,20 @@ jobs:
4040 timeoutInMinutes: 360
4141
4242 steps:
43 - powershell: |
44 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-06-02/msys2-base-x86_64-20200602.sfx.exe", "sfx.exe")
45 .\sfx.exe -y -o\
46 del sfx.exe
47 displayName: Download/Extract/Install MSYS2
4348 - script: |
44 git clone https://github.com/msys2/msys2-ci-base.git %CD:~0,2%\msys64
45 %CD:~0,2%\msys64\usr\bin\rm -rf %CD:~0,2%\msys64\.git
46 set PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem
47 %CD:~0,2%\msys64\usr\bin\pacman --noconfirm -Syyuu
48 displayName: Install and Update MSYS2
49 @REM install updated filesystem package first without dependency checking
50 @REM because of: https://github.com/msys2/MSYS2-packages/issues/2021
51 %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Sydd filesystem"
52 displayName: Workaround filesystem dash MSYS2 dependency issue
53 - script: |
54 %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
55 %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
56 displayName: Update MSYS2
4957 - task: DownloadSecureFile@1
5058 inputs:
5159 secureFile: s3cfg
ci/azure/windows_msvc_install+1-1
......@@ -4,7 +4,7 @@ set -x
44set -e
55
66pacman -Su --needed --noconfirm
7pacman -S --needed --noconfirm wget p7zip python3-pip
7pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
88pip install s3cmd
99wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz"
1010tar xf llvm+clang+lld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz
doc/docgen.zig+7-6
......@@ -212,7 +212,7 @@ const Tokenizer = struct {
212212 }
213213};
214214
215fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: var) anyerror {
215fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror {
216216 const loc = tokenizer.getTokenLocation(token);
217217 const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 };
218218 warn("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args);
......@@ -392,7 +392,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
392392 .n = header_stack_size,
393393 },
394394 });
395 if (try urls.put(urlized, tag_token)) |entry| {
395 if (try urls.fetchPut(urlized, tag_token)) |entry| {
396396 parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {};
397397 parseError(tokenizer, entry.value, "other tag here", .{}) catch {};
398398 return error.ParseError;
......@@ -634,7 +634,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
634634 return buf.toOwnedSlice();
635635}
636636
637fn writeEscaped(out: var, input: []const u8) !void {
637fn writeEscaped(out: anytype, input: []const u8) !void {
638638 for (input) |c| {
639639 try switch (c) {
640640 '&' => out.writeAll("&amp;"),
......@@ -765,7 +765,7 @@ fn isType(name: []const u8) bool {
765765 return false;
766766}
767767
768fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void {
768fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: Token, raw_src: []const u8) !void {
769769 const src = mem.trim(u8, raw_src, " \n");
770770 try out.writeAll("<code class=\"zig\">");
771771 var tokenizer = std.zig.Tokenizer.init(src);
......@@ -825,6 +825,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
825825 .Keyword_volatile,
826826 .Keyword_allowzero,
827827 .Keyword_while,
828 .Keyword_anytype,
828829 => {
829830 try out.writeAll("<span class=\"tok-kw\">");
830831 try writeEscaped(out, src[token.loc.start..token.loc.end]);
......@@ -977,12 +978,12 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
977978 try out.writeAll("</code>");
978979}
979980
980fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void {
981fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: anytype, source_token: Token) !void {
981982 const raw_src = docgen_tokenizer.buffer[source_token.start..source_token.end];
982983 return tokenizeAndPrintRaw(docgen_tokenizer, out, source_token, raw_src);
983984}
984985
985fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {
986fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8) !void {
986987 var code_progress_index: usize = 0;
987988
988989 var env_map = try process.getEnvMap(allocator);
doc/langref.html.in+304-145
......@@ -97,7 +97,7 @@
9797 margin: auto;
9898 }
9999
100 #index {
100 #toc {
101101 padding: 0 1em;
102102 }
103103
......@@ -105,7 +105,7 @@
105105 #main-wrapper {
106106 flex-direction: row;
107107 }
108 #contents-wrapper, #index {
108 #contents-wrapper, #toc {
109109 overflow: auto;
110110 }
111111 }
......@@ -181,7 +181,7 @@
181181 </head>
182182 <body>
183183 <div id="main-wrapper">
184 <div id="index">
184 <div id="toc">
185185 <a href="https://ziglang.org/documentation/0.1.1/">0.1.1</a> |
186186 <a href="https://ziglang.org/documentation/0.2.0/">0.2.0</a> |
187187 <a href="https://ziglang.org/documentation/0.3.0/">0.3.0</a> |
......@@ -189,7 +189,7 @@
189189 <a href="https://ziglang.org/documentation/0.5.0/">0.5.0</a> |
190190 <a href="https://ziglang.org/documentation/0.6.0/">0.6.0</a> |
191191 master
192 <h1>Index</h1>
192 <h1>Contents</h1>
193193 {#nav#}
194194 </div>
195195 <div id="contents-wrapper"><div id="contents">
......@@ -218,6 +218,8 @@
218218 </p>
219219 <p>
220220 The code samples in this document are compiled and tested as part of the main test suite of Zig.
221 </p>
222 <p>
221223 This HTML document depends on no external files, so you can use it offline.
222224 </p>
223225 <p>
......@@ -231,26 +233,113 @@
231233const std = @import("std");
232234
233235pub fn main() !void {
234 const stdout = std.io.getStdOut().outStream();
236 const stdout = std.io.getStdOut().writer();
235237 try stdout.print("Hello, {}!\n", .{"world"});
236238}
237239 {#code_end#}
238240 <p>
239 Usually you don't want to write to stdout. You want to write to stderr. And you
240 don't care if it fails. It's more like a <em>warning message</em> that you want
241 to emit. For that you can use a simpler API:
241 The Zig code sample above demonstrates one way to create a program that will output <code>Hello, world!</code>.
242242 </p>
243 {#code_begin|exe|hello#}
244const warn = @import("std").debug.warn;
243 <p>
244 The code sample shows the contents of a file named <code>hello.zig</code>. Files storing Zig
245 source code are {#link|UTF-8 encoded|Source Encoding#} text files. The files storing
246 Zig source code are usually named with the <code>.zig</code> extension.
247 </p>
248 <p>
249 Following the <code>hello.zig</code> Zig code sample, the {#link|Zig Build System#} is used
250 to build an executable program from the <code>hello.zig</code> source code. Then, the
251 <code>hello</code> program is executed showing its output <code>Hello, world!</code>. The
252 lines beginning with <code>$</code> represent command line prompts and a command.
253 Everything else is program output.
254 </p>
255 <p>
256 The code sample begins by adding Zig's Standard Library to the build using the {#link|@import#} builtin function.
257 The {#syntax#}@import("std"){#endsyntax#} function call creates a structure to represent the Standard Library.
258 The code then makes a {#link|top-level declaration|Global Variables#} of a
259 {#link|constant identifier|Assignment#}, named <code>std</code>, for easy access to
260 <a href="https://github.com/ziglang/zig/wiki/FAQ#where-is-the-documentation-for-the-zig-standard-library">Zig's standard library</a>.
261 </p>
262 <p>
263 Next, a {#link|public function|Functions#}, {#syntax#}pub fn{#endsyntax#}, named <code>main</code>
264 is declared. The <code>main</code> function is necessary because it tells the Zig compiler where the start of
265 the program exists. Programs designed to be executed will need a {#syntax#}pub fn main{#endsyntax#} function.
266 For more advanced use cases, Zig offers other features to inform the compiler where the start of
267 the program exists. Libraries, on the other hand, do not need a <code>main</code> function because
268 library code is usually called by other programs.
269 </p>
270 <p>
271 A function is a block of any number of statements and expressions that, as a whole, perform a task.
272 Functions may or may not return data after they are done performing their task. If a function
273 cannot perform its task, it might return an error. Zig makes all of this explicit.
274 </p>
275 <p>
276 In the <code>hello.zig</code> code sample, the <code>main</code> function is declared
277 with the {#syntax#}!void{#endsyntax#} return type. This return type is known as an {#link|Error Union Type#}.
278 This syntax tells the Zig compiler that the function will either return an
279 error or a value. An error union type combines an {#link|Error Set Type#} and a {#link|Primitive Type|Primitive Types#}.
280 The full form of an error union type is
281 <code>&lt;error set type&gt;</code>{#syntax#}!{#endsyntax#}<code>&lt;primitive type&gt;</code>. In the code
282 sample, the error set type is not explicitly written on the left side of the {#syntax#}!{#endsyntax#} operator.
283 When written this way, the error set type is a special kind of error union type that has an
284 {#link|inferred error set type|Inferred Error Sets#}. The {#syntax#}void{#endsyntax#} after the {#syntax#}!{#endsyntax#} operator
285 tells the compiler that the function will not return a value under normal circumstances (i.e. no errors occur).
286 </p>
287 <p>
288 Note to experienced programmers: Zig also has the boolean {#link|operator|Operators#} {#syntax#}!a{#endsyntax#}
289 where {#syntax#}a{#endsyntax#} is a value of type {#syntax#}bool{#endsyntax#}. Error union types contain the
290 name of the type in the syntax: {#syntax#}!{#endsyntax#}<code>&lt;primitive type&gt;</code>.
291 </p>
292 <p>
293 In Zig, a function's block of statements and expressions are surrounded by <code>{</code> and
294 <code>}</code> curly-braces. Inside of the <code>main</code> function are expressions that perform
295 the task of outputting <code>Hello, world!</code> to standard output.
296 </p>
297 <p>
298 First, a constant identifier, <code>stdout</code>, is initialized to represent standard output's
299 writer. Then, the program tries to print the <code>Hello, world!</code>
300 message to standard output.
301 </p>
302 <p>
303 Functions sometimes need information to perform their task. In Zig, information is passed
304 to functions between open <code>(</code> and close <code>)</code> parenthesis placed after
305 the function's name. This information is also known as arguments. When there are
306 multiple arguments passed to a function, they are separated by commas <code>,</code>.
307 </p>
308 <p>
309 The two arguments passed to the <code>stdout.print()</code> function, <code>"Hello, {}!\n"</code>
310 and <code>.{"world"}</code>, are evaluated at {#link|compile-time|comptime#}. The code sample is
311 purposely written to show how to perform {#link|string|String Literals and Character Literals#}
312 substitution in the <code>print</code> function. The curly-braces inside of the first argument
313 are substituted with the compile-time known value inside of the second argument
314 (known as an {#link|anonymous struct literal|Anonymous Struct Literals#}). The <code>\n</code>
315 inside of the double-quotes of the first argument is the {#link|escape sequence|Escape Sequences#} for the
316 newline character. The {#link|try#} expression evaluates the result of <code>stdout.print</code>.
317 If the result is an error, then the {#syntax#}try{#endsyntax#} expression will return from
318 <code>main</code> with the error. Otherwise, the program will continue. In this case, there are no
319 more statements or expressions left to execute in the <code>main</code> function, so the program exits.
320 </p>
321 <p>
322 In Zig, the standard output writer's <code>print</code> function is allowed to fail because
323 it is actually a function defined as part of a generic Writer. Consider a generic Writer that
324 represents writing data to a file. When the disk is full, a write to the file will fail.
325 However, we typically do not expect writing text to the standard output to fail. To avoid having
326 to handle the failure case of printing to standard output, you can use alternate functions: the
327 <code>std.log</code> function for proper logging or the <code>std.debug.print</code> function.
328 This documentation will use the latter option to print to standard error (stderr) and silently return
329 on failure. The next code sample, <code>hello_again.zig</code> demonstrates the use of
330 <code>std.debug.print</code>.
331 </p>
332 {#code_begin|exe|hello_again#}
333const print = @import("std").debug.print;
245334
246335pub fn main() void {
247 warn("Hello, world!\n", .{});
336 print("Hello, world!\n", .{});
248337}
249338 {#code_end#}
250339 <p>
251 Note that you can leave off the {#syntax#}!{#endsyntax#} from the return type because {#syntax#}warn{#endsyntax#} cannot fail.
340 Note that you can leave off the {#syntax#}!{#endsyntax#} from the return type because <code>std.debug.print</code> cannot fail.
252341 </p>
253 {#see_also|Values|@import|Errors|Root Source File#}
342 {#see_also|Values|@import|Errors|Root Source File|Source Encoding#}
254343 {#header_close#}
255344 {#header_open|Comments#}
256345 {#code_begin|test|comments#}
......@@ -303,11 +392,23 @@ const Timestamp = struct {
303392 in the middle of an expression, or just before a non-doc comment.
304393 </p>
305394 {#header_close#}
395 {#header_open|Top-Level Doc Comments#}
396 <p>User documentation that doesn't belong to whatever
397 immediately follows it, like package-level documentation, goes
398 in top-level doc comments. A top-level doc comment is one that
399 begins with two slashes and an exclamation point:
400 {#syntax#}//!{#endsyntax#}.</p>
401 {#code_begin|syntax|tldoc_comments#}
402//! This module provides functions for retrieving the current date and
403//! time with varying degrees of precision and accuracy. It does not
404//! depend on libc, but will use functions from it if available.
405 {#code_end#}
406 {#header_close#}
306407 {#header_close#}
307408 {#header_open|Values#}
308409 {#code_begin|exe|values#}
309410// Top-level declarations are order-independent:
310const warn = std.debug.warn;
411const print = std.debug.print;
311412const std = @import("std");
312413const os = std.os;
313414const assert = std.debug.assert;
......@@ -315,14 +416,14 @@ const assert = std.debug.assert;
315416pub fn main() void {
316417 // integers
317418 const one_plus_one: i32 = 1 + 1;
318 warn("1 + 1 = {}\n", .{one_plus_one});
419 print("1 + 1 = {}\n", .{one_plus_one});
319420
320421 // floats
321422 const seven_div_three: f32 = 7.0 / 3.0;
322 warn("7.0 / 3.0 = {}\n", .{seven_div_three});
423 print("7.0 / 3.0 = {}\n", .{seven_div_three});
323424
324425 // boolean
325 warn("{}\n{}\n{}\n", .{
426 print("{}\n{}\n{}\n", .{
326427 true and false,
327428 true or false,
328429 !true,
......@@ -332,7 +433,7 @@ pub fn main() void {
332433 var optional_value: ?[]const u8 = null;
333434 assert(optional_value == null);
334435
335 warn("\noptional 1\ntype: {}\nvalue: {}\n", .{
436 print("\noptional 1\ntype: {}\nvalue: {}\n", .{
336437 @typeName(@TypeOf(optional_value)),
337438 optional_value,
338439 });
......@@ -340,7 +441,7 @@ pub fn main() void {
340441 optional_value = "hi";
341442 assert(optional_value != null);
342443
343 warn("\noptional 2\ntype: {}\nvalue: {}\n", .{
444 print("\noptional 2\ntype: {}\nvalue: {}\n", .{
344445 @typeName(@TypeOf(optional_value)),
345446 optional_value,
346447 });
......@@ -348,14 +449,14 @@ pub fn main() void {
348449 // error union
349450 var number_or_error: anyerror!i32 = error.ArgNotFound;
350451
351 warn("\nerror union 1\ntype: {}\nvalue: {}\n", .{
452 print("\nerror union 1\ntype: {}\nvalue: {}\n", .{
352453 @typeName(@TypeOf(number_or_error)),
353454 number_or_error,
354455 });
355456
356457 number_or_error = 1234;
357458
358 warn("\nerror union 2\ntype: {}\nvalue: {}\n", .{
459 print("\nerror union 2\ntype: {}\nvalue: {}\n", .{
359460 @typeName(@TypeOf(number_or_error)),
360461 number_or_error,
361462 });
......@@ -994,15 +1095,15 @@ export fn foo_optimized(x: f64) f64 {
9941095 which operates in strict mode.</p>
9951096 {#code_begin|exe|float_mode#}
9961097 {#code_link_object|foo#}
997const warn = @import("std").debug.warn;
1098const print = @import("std").debug.print;
9981099
9991100extern fn foo_strict(x: f64) f64;
10001101extern fn foo_optimized(x: f64) f64;
10011102
10021103pub fn main() void {
10031104 const x = 0.001;
1004 warn("optimized = {}\n", .{foo_optimized(x)});
1005 warn("strict = {}\n", .{foo_strict(x)});
1105 print("optimized = {}\n", .{foo_optimized(x)});
1106 print("strict = {}\n", .{foo_strict(x)});
10061107}
10071108 {#code_end#}
10081109 {#see_also|@setFloatMode|Division by Zero#}
......@@ -1786,7 +1887,7 @@ test "fully anonymous list literal" {
17861887 dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});
17871888}
17881889
1789fn dump(args: var) void {
1890fn dump(args: anytype) void {
17901891 assert(args.@"0" == 1234);
17911892 assert(args.@"1" == 12.34);
17921893 assert(args.@"2");
......@@ -1849,7 +1950,7 @@ test "null terminated array" {
18491950
18501951 {#header_open|Vectors#}
18511952 <p>
1852 A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
1953 A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
18531954 in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin function {#link|@Type#},
18541955 or using the shorthand as {#syntax#}std.meta.Vector{#endsyntax#}.
18551956 </p>
......@@ -2668,9 +2769,9 @@ const std = @import("std");
26682769
26692770pub fn main() void {
26702771 const Foo = struct {};
2671 std.debug.warn("variable: {}\n", .{@typeName(Foo)});
2672 std.debug.warn("anonymous: {}\n", .{@typeName(struct {})});
2673 std.debug.warn("function: {}\n", .{@typeName(List(i32))});
2772 std.debug.print("variable: {}\n", .{@typeName(Foo)});
2773 std.debug.print("anonymous: {}\n", .{@typeName(struct {})});
2774 std.debug.print("function: {}\n", .{@typeName(List(i32))});
26742775}
26752776
26762777fn List(comptime T: type) type {
......@@ -2718,7 +2819,7 @@ test "fully anonymous struct" {
27182819 });
27192820}
27202821
2721fn dump(args: var) void {
2822fn dump(args: anytype) void {
27222823 assert(args.int == 1234);
27232824 assert(args.float == 12.34);
27242825 assert(args.b);
......@@ -3862,6 +3963,48 @@ test "if error union" {
38623963 unreachable;
38633964 }
38643965}
3966
3967test "if error union with optional" {
3968 // If expressions test for errors before unwrapping optionals.
3969 // The |optional_value| capture's type is ?u32.
3970
3971 const a: anyerror!?u32 = 0;
3972 if (a) |optional_value| {
3973 assert(optional_value.? == 0);
3974 } else |err| {
3975 unreachable;
3976 }
3977
3978 const b: anyerror!?u32 = null;
3979 if (b) |optional_value| {
3980 assert(optional_value == null);
3981 } else |err| {
3982 unreachable;
3983 }
3984
3985 const c: anyerror!?u32 = error.BadValue;
3986 if (c) |optional_value| {
3987 unreachable;
3988 } else |err| {
3989 assert(err == error.BadValue);
3990 }
3991
3992 // Access the value by reference by using a pointer capture each time.
3993 var d: anyerror!?u32 = 3;
3994 if (d) |*optional_value| {
3995 if (optional_value.*) |*value| {
3996 value.* = 9;
3997 }
3998 } else |err| {
3999 unreachable;
4000 }
4001
4002 if (d) |optional_value| {
4003 assert(optional_value.? == 9);
4004 } else |err| {
4005 unreachable;
4006 }
4007}
38654008 {#code_end#}
38664009 {#see_also|Optionals|Errors#}
38674010 {#header_close#}
......@@ -3869,7 +4012,7 @@ test "if error union" {
38694012 {#code_begin|test|defer#}
38704013const std = @import("std");
38714014const assert = std.debug.assert;
3872const warn = std.debug.warn;
4015const print = std.debug.print;
38734016
38744017// defer will execute an expression at the end of the current scope.
38754018fn deferExample() usize {
......@@ -3892,18 +4035,18 @@ test "defer basics" {
38924035// If multiple defer statements are specified, they will be executed in
38934036// the reverse order they were run.
38944037fn deferUnwindExample() void {
3895 warn("\n", .{});
4038 print("\n", .{});
38964039
38974040 defer {
3898 warn("1 ", .{});
4041 print("1 ", .{});
38994042 }
39004043 defer {
3901 warn("2 ", .{});
4044 print("2 ", .{});
39024045 }
39034046 if (false) {
39044047 // defers are not run if they are never executed.
39054048 defer {
3906 warn("3 ", .{});
4049 print("3 ", .{});
39074050 }
39084051 }
39094052}
......@@ -3918,15 +4061,15 @@ test "defer unwinding" {
39184061// This is especially useful in allowing a function to clean up properly
39194062// on error, and replaces goto error handling tactics as seen in c.
39204063fn deferErrorExample(is_error: bool) !void {
3921 warn("\nstart of function\n", .{});
4064 print("\nstart of function\n", .{});
39224065
39234066 // This will always be executed on exit
39244067 defer {
3925 warn("end of function\n", .{});
4068 print("end of function\n", .{});
39264069 }
39274070
39284071 errdefer {
3929 warn("encountered an error!\n", .{});
4072 print("encountered an error!\n", .{});
39304073 }
39314074
39324075 if (is_error) {
......@@ -4140,14 +4283,14 @@ test "pass struct to function" {
41404283 {#header_close#}
41414284 {#header_open|Function Parameter Type Inference#}
41424285 <p>
4143 Function parameters can be declared with {#syntax#}var{#endsyntax#} in place of the type.
4286 Function parameters can be declared with {#syntax#}anytype{#endsyntax#} in place of the type.
41444287 In this case the parameter types will be inferred when the function is called.
41454288 Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.
41464289 </p>
41474290 {#code_begin|test#}
41484291const assert = @import("std").debug.assert;
41494292
4150fn addFortyTwo(x: var) @TypeOf(x) {
4293fn addFortyTwo(x: anytype) @TypeOf(x) {
41514294 return x + 42;
41524295}
41534296
......@@ -5364,11 +5507,11 @@ const std = @import("std");
53645507const assert = std.debug.assert;
53655508
53665509test "turn HashMap into a set with void" {
5367 var map = std.HashMap(i32, void, hash_i32, eql_i32).init(std.testing.allocator);
5510 var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
53685511 defer map.deinit();
53695512
5370 _ = try map.put(1, {});
5371 _ = try map.put(2, {});
5513 try map.put(1, {});
5514 try map.put(2, {});
53725515
53735516 assert(map.contains(2));
53745517 assert(!map.contains(3));
......@@ -5376,14 +5519,6 @@ test "turn HashMap into a set with void" {
53765519 _ = map.remove(2);
53775520 assert(!map.contains(2));
53785521}
5379
5380fn hash_i32(x: i32) u32 {
5381 return @bitCast(u32, x);
5382}
5383
5384fn eql_i32(a: i32, b: i32) bool {
5385 return a == b;
5386}
53875522 {#code_end#}
53885523 <p>Note that this is different from using a dummy value for the hash map value.
53895524 By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and
......@@ -5925,13 +6060,13 @@ const Node = struct {
59256060 Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig.
59266061 </p>
59276062 {#code_begin|exe|printf#}
5928const warn = @import("std").debug.warn;
6063const print = @import("std").debug.print;
59296064
59306065const a_number: i32 = 1234;
59316066const a_string = "foobar";
59326067
59336068pub fn main() void {
5934 warn("here is a string: '{}' here is a number: {}\n", .{a_string, a_number});
6069 print("here is a string: '{}' here is a number: {}\n", .{a_string, a_number});
59356070}
59366071 {#code_end#}
59376072
......@@ -5941,7 +6076,7 @@ pub fn main() void {
59416076
59426077 {#code_begin|syntax#}
59436078/// Calls print and then flushes the buffer.
5944pub fn printf(self: *OutStream, comptime format: []const u8, args: var) anyerror!void {
6079pub fn printf(self: *OutStream, comptime format: []const u8, args: anytype) anyerror!void {
59456080 const State = enum {
59466081 Start,
59476082 OpenBrace,
......@@ -6027,7 +6162,7 @@ pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void {
60276162 on the type:
60286163 </p>
60296164 {#code_begin|syntax#}
6030pub fn printValue(self: *OutStream, value: var) !void {
6165pub fn printValue(self: *OutStream, value: anytype) !void {
60316166 switch (@typeInfo(@TypeOf(value))) {
60326167 .Int => {
60336168 return self.printInt(T, value);
......@@ -6045,13 +6180,13 @@ pub fn printValue(self: *OutStream, value: var) !void {
60456180 And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}?
60466181 </p>
60476182 {#code_begin|test_err|Unused arguments#}
6048const warn = @import("std").debug.warn;
6183const print = @import("std").debug.print;
60496184
60506185const a_number: i32 = 1234;
60516186const a_string = "foobar";
60526187
60536188test "printf too many arguments" {
6054 warn("here is a string: '{}' here is a number: {}\n", .{
6189 print("here is a string: '{}' here is a number: {}\n", .{
60556190 a_string,
60566191 a_number,
60576192 a_number,
......@@ -6066,14 +6201,14 @@ test "printf too many arguments" {
60666201 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
60676202 </p>
60686203 {#code_begin|exe|printf#}
6069const warn = @import("std").debug.warn;
6204const print = @import("std").debug.print;
60706205
60716206const a_number: i32 = 1234;
60726207const a_string = "foobar";
60736208const fmt = "here is a string: '{}' here is a number: {}\n";
60746209
60756210pub fn main() void {
6076 warn(fmt, .{a_string, a_number});
6211 print(fmt, .{a_string, a_number});
60776212}
60786213 {#code_end#}
60796214 <p>
......@@ -6511,7 +6646,7 @@ pub fn main() void {
65116646
65126647fn amainWrap() void {
65136648 amain() catch |e| {
6514 std.debug.warn("{}\n", .{e});
6649 std.debug.print("{}\n", .{e});
65156650 if (@errorReturnTrace()) |trace| {
65166651 std.debug.dumpStackTrace(trace.*);
65176652 }
......@@ -6541,8 +6676,8 @@ fn amain() !void {
65416676 const download_text = try await download_frame;
65426677 defer allocator.free(download_text);
65436678
6544 std.debug.warn("download_text: {}\n", .{download_text});
6545 std.debug.warn("file_text: {}\n", .{file_text});
6679 std.debug.print("download_text: {}\n", .{download_text});
6680 std.debug.print("file_text: {}\n", .{file_text});
65466681}
65476682
65486683var global_download_frame: anyframe = undefined;
......@@ -6552,7 +6687,7 @@ fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
65526687 suspend {
65536688 global_download_frame = @frame();
65546689 }
6555 std.debug.warn("fetchUrl returning\n", .{});
6690 std.debug.print("fetchUrl returning\n", .{});
65566691 return result;
65576692}
65586693
......@@ -6563,7 +6698,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
65636698 suspend {
65646699 global_file_frame = @frame();
65656700 }
6566 std.debug.warn("readFile returning\n", .{});
6701 std.debug.print("readFile returning\n", .{});
65676702 return result;
65686703}
65696704 {#code_end#}
......@@ -6581,7 +6716,7 @@ pub fn main() void {
65816716
65826717fn amainWrap() void {
65836718 amain() catch |e| {
6584 std.debug.warn("{}\n", .{e});
6719 std.debug.print("{}\n", .{e});
65856720 if (@errorReturnTrace()) |trace| {
65866721 std.debug.dumpStackTrace(trace.*);
65876722 }
......@@ -6611,21 +6746,21 @@ fn amain() !void {
66116746 const download_text = try await download_frame;
66126747 defer allocator.free(download_text);
66136748
6614 std.debug.warn("download_text: {}\n", .{download_text});
6615 std.debug.warn("file_text: {}\n", .{file_text});
6749 std.debug.print("download_text: {}\n", .{download_text});
6750 std.debug.print("file_text: {}\n", .{file_text});
66166751}
66176752
66186753fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
66196754 const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents");
66206755 errdefer allocator.free(result);
6621 std.debug.warn("fetchUrl returning\n", .{});
6756 std.debug.print("fetchUrl returning\n", .{});
66226757 return result;
66236758}
66246759
66256760fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
66266761 const result = try std.mem.dupe(allocator, u8, "this is the file contents");
66276762 errdefer allocator.free(result);
6628 std.debug.warn("readFile returning\n", .{});
6763 std.debug.print("readFile returning\n", .{});
66296764 return result;
66306765}
66316766 {#code_end#}
......@@ -6653,7 +6788,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
66536788 </p>
66546789 {#header_close#}
66556790 {#header_open|@alignCast#}
6656 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}</pre>
6791 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
66576792 <p>
66586793 {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#},
66596794 {#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#}
......@@ -6690,7 +6825,7 @@ comptime {
66906825 {#header_close#}
66916826
66926827 {#header_open|@asyncCall#}
6693 <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
6828 <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: anytype) anyframe->T{#endsyntax#}</pre>
66946829 <p>
66956830 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
66966831 which may or may not be an {#link|async function|Async Functions#}.
......@@ -6717,7 +6852,7 @@ test "async fn pointer in a struct field" {
67176852 };
67186853 var foo = Foo{ .bar = func };
67196854 var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
6720 const f = @asyncCall(&bytes, {}, foo.bar, &data);
6855 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
67216856 assert(data == 2);
67226857 resume f;
67236858 assert(data == 4);
......@@ -6778,7 +6913,7 @@ fn func(y: *i32) void {
67786913 </p>
67796914 {#header_close#}
67806915 {#header_open|@bitCast#}
6781 <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
6916 <pre>{#syntax#}@bitCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
67826917 <p>
67836918 Converts a value of one type to another type.
67846919 </p>
......@@ -6899,7 +7034,7 @@ fn func(y: *i32) void {
68997034 {#header_close#}
69007035
69017036 {#header_open|@call#}
6902 <pre>{#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}</pre>
7037 <pre>{#syntax#}@call(options: std.builtin.CallOptions, function: anytype, args: anytype) anytype{#endsyntax#}</pre>
69037038 <p>
69047039 Calls a function, in the same way that invoking an expression with parentheses does:
69057040 </p>
......@@ -7121,7 +7256,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
71217256 compile-time executing code.
71227257 </p>
71237258 {#code_begin|test_err|found compile log statement#}
7124const warn = @import("std").debug.warn;
7259const print = @import("std").debug.print;
71257260
71267261const num1 = blk: {
71277262 var val1: i32 = 99;
......@@ -7133,7 +7268,7 @@ const num1 = blk: {
71337268test "main" {
71347269 @compileLog("comptime in main");
71357270
7136 warn("Runtime in main, num1 = {}.\n", .{num1});
7271 print("Runtime in main, num1 = {}.\n", .{num1});
71377272}
71387273 {#code_end#}
71397274 <p>
......@@ -7145,7 +7280,7 @@ test "main" {
71457280 program compiles successfully and the generated executable prints:
71467281 </p>
71477282 {#code_begin|test#}
7148const warn = @import("std").debug.warn;
7283const print = @import("std").debug.print;
71497284
71507285const num1 = blk: {
71517286 var val1: i32 = 99;
......@@ -7154,7 +7289,7 @@ const num1 = blk: {
71547289};
71557290
71567291test "main" {
7157 warn("Runtime in main, num1 = {}.\n", .{num1});
7292 print("Runtime in main, num1 = {}.\n", .{num1});
71587293}
71597294 {#code_end#}
71607295 {#header_close#}
......@@ -7246,7 +7381,7 @@ test "main" {
72467381 {#header_close#}
72477382
72487383 {#header_open|@enumToInt#}
7249 <pre>{#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}</pre>
7384 <pre>{#syntax#}@enumToInt(enum_or_tagged_union: anytype) anytype{#endsyntax#}</pre>
72507385 <p>
72517386 Converts an enumeration value into its integer tag type. When a tagged union is passed,
72527387 the tag value is used as the enumeration value.
......@@ -7281,7 +7416,7 @@ test "main" {
72817416 {#header_close#}
72827417
72837418 {#header_open|@errorToInt#}
7284 <pre>{#syntax#}@errorToInt(err: var) std.meta.IntType(false, @sizeOf(anyerror) * 8){#endsyntax#}</pre>
7419 <pre>{#syntax#}@errorToInt(err: anytype) std.meta.IntType(false, @sizeOf(anyerror) * 8){#endsyntax#}</pre>
72857420 <p>
72867421 Supports the following types:
72877422 </p>
......@@ -7301,7 +7436,7 @@ test "main" {
73017436 {#header_close#}
73027437
73037438 {#header_open|@errSetCast#}
7304 <pre>{#syntax#}@errSetCast(comptime T: DestType, value: var) DestType{#endsyntax#}</pre>
7439 <pre>{#syntax#}@errSetCast(comptime T: DestType, value: anytype) DestType{#endsyntax#}</pre>
73057440 <p>
73067441 Converts an error value from one error set to another error set. Attempting to convert an error
73077442 which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.
......@@ -7309,7 +7444,7 @@ test "main" {
73097444 {#header_close#}
73107445
73117446 {#header_open|@export#}
7312 <pre>{#syntax#}@export(target: var, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre>
7447 <pre>{#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre>
73137448 <p>
73147449 Creates a symbol in the output object file.
73157450 </p>
......@@ -7354,7 +7489,7 @@ export fn @"A function name that is a complete sentence."() void {}
73547489 {#header_close#}
73557490
73567491 {#header_open|@field#}
7357 <pre>{#syntax#}@field(lhs: var, comptime field_name: []const u8) (field){#endsyntax#}</pre>
7492 <pre>{#syntax#}@field(lhs: anytype, comptime field_name: []const u8) (field){#endsyntax#}</pre>
73587493 <p>Performs field access by a compile-time string.
73597494 </p>
73607495 {#code_begin|test#}
......@@ -7388,7 +7523,7 @@ test "field access by string" {
73887523 {#header_close#}
73897524
73907525 {#header_open|@floatCast#}
7391 <pre>{#syntax#}@floatCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
7526 <pre>{#syntax#}@floatCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
73927527 <p>
73937528 Convert from one float type to another. This cast is safe, but may cause the
73947529 numeric value to lose precision.
......@@ -7396,7 +7531,7 @@ test "field access by string" {
73967531 {#header_close#}
73977532
73987533 {#header_open|@floatToInt#}
7399 <pre>{#syntax#}@floatToInt(comptime DestType: type, float: var) DestType{#endsyntax#}</pre>
7534 <pre>{#syntax#}@floatToInt(comptime DestType: type, float: anytype) DestType{#endsyntax#}</pre>
74007535 <p>
74017536 Converts the integer part of a floating point number to the destination type.
74027537 </p>
......@@ -7422,7 +7557,7 @@ test "field access by string" {
74227557 {#header_close#}
74237558
74247559 {#header_open|@Frame#}
7425 <pre>{#syntax#}@Frame(func: var) type{#endsyntax#}</pre>
7560 <pre>{#syntax#}@Frame(func: anytype) type{#endsyntax#}</pre>
74267561 <p>
74277562 This function returns the frame type of a function. This works for {#link|Async Functions#}
74287563 as well as any function without a specific calling convention.
......@@ -7531,7 +7666,7 @@ test "@hasDecl" {
75317666 source file than the one they are declared in.
75327667 </p>
75337668 <p>
7534 {#syntax#}path{#endsyntax#} can be a relative or absolute path, or it can be the name of a package.
7669 {#syntax#}path{#endsyntax#} can be a relative path or it can be the name of a package.
75357670 If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#}
75367671 function call.
75377672 </p>
......@@ -7548,7 +7683,7 @@ test "@hasDecl" {
75487683 {#header_close#}
75497684
75507685 {#header_open|@intCast#}
7551 <pre>{#syntax#}@intCast(comptime DestType: type, int: var) DestType{#endsyntax#}</pre>
7686 <pre>{#syntax#}@intCast(comptime DestType: type, int: anytype) DestType{#endsyntax#}</pre>
75527687 <p>
75537688 Converts an integer to another integer while keeping the same numerical value.
75547689 Attempting to convert a number which is out of range of the destination type results in
......@@ -7589,7 +7724,7 @@ test "@hasDecl" {
75897724 {#header_close#}
75907725
75917726 {#header_open|@intToFloat#}
7592 <pre>{#syntax#}@intToFloat(comptime DestType: type, int: var) DestType{#endsyntax#}</pre>
7727 <pre>{#syntax#}@intToFloat(comptime DestType: type, int: anytype) DestType{#endsyntax#}</pre>
75937728 <p>
75947729 Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe.
75957730 </p>
......@@ -7740,7 +7875,7 @@ test "@wasmMemoryGrow" {
77407875 {#header_close#}
77417876
77427877 {#header_open|@ptrCast#}
7743 <pre>{#syntax#}@ptrCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
7878 <pre>{#syntax#}@ptrCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
77447879 <p>
77457880 Converts a pointer of one type to a pointer of another type.
77467881 </p>
......@@ -7751,7 +7886,7 @@ test "@wasmMemoryGrow" {
77517886 {#header_close#}
77527887
77537888 {#header_open|@ptrToInt#}
7754 <pre>{#syntax#}@ptrToInt(value: var) usize{#endsyntax#}</pre>
7889 <pre>{#syntax#}@ptrToInt(value: anytype) usize{#endsyntax#}</pre>
77557890 <p>
77567891 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer. {#syntax#}value{#endsyntax#} can be one of these types:
77577892 </p>
......@@ -8009,7 +8144,7 @@ test "@setRuntimeSafety" {
80098144 {#header_close#}
80108145
80118146 {#header_open|@splat#}
8012 <pre>{#syntax#}@splat(comptime len: u32, scalar: var) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}</pre>
8147 <pre>{#syntax#}@splat(comptime len: u32, scalar: anytype) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}</pre>
80138148 <p>
80148149 Produces a vector of length {#syntax#}len{#endsyntax#} where each element is the value
80158150 {#syntax#}scalar{#endsyntax#}:
......@@ -8031,9 +8166,31 @@ test "vector @splat" {
80318166 </p>
80328167 {#see_also|Vectors|@shuffle#}
80338168 {#header_close#}
8169 {#header_open|@src#}
8170 <pre>{#syntax#}@src() std.builtin.SourceLocation{#endsyntax#}</pre>
8171 <p>
8172 Returns a {#syntax#}SourceLocation{#endsyntax#} struct representing the function's name and location in the source code. This must be called in a function.
8173 </p>
8174 {#code_begin|test#}
8175const std = @import("std");
8176const expect = std.testing.expect;
80348177
8178test "@src" {
8179 doTheTest();
8180}
8181
8182fn doTheTest() void {
8183 const src = @src();
8184
8185 expect(src.line == 9);
8186 expect(src.column == 17);
8187 expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
8188 expect(std.mem.endsWith(u8, src.file, "test.zig"));
8189}
8190 {#code_end#}
8191 {#header_close#}
80358192 {#header_open|@sqrt#}
8036 <pre>{#syntax#}@sqrt(value: var) @TypeOf(value){#endsyntax#}</pre>
8193 <pre>{#syntax#}@sqrt(value: anytype) @TypeOf(value){#endsyntax#}</pre>
80378194 <p>
80388195 Performs the square root of a floating point number. Uses a dedicated hardware instruction
80398196 when available.
......@@ -8044,7 +8201,7 @@ test "vector @splat" {
80448201 </p>
80458202 {#header_close#}
80468203 {#header_open|@sin#}
8047 <pre>{#syntax#}@sin(value: var) @TypeOf(value){#endsyntax#}</pre>
8204 <pre>{#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}</pre>
80488205 <p>
80498206 Sine trigometric function on a floating point number. Uses a dedicated hardware instruction
80508207 when available.
......@@ -8055,7 +8212,7 @@ test "vector @splat" {
80558212 </p>
80568213 {#header_close#}
80578214 {#header_open|@cos#}
8058 <pre>{#syntax#}@cos(value: var) @TypeOf(value){#endsyntax#}</pre>
8215 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>
80598216 <p>
80608217 Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction
80618218 when available.
......@@ -8066,7 +8223,7 @@ test "vector @splat" {
80668223 </p>
80678224 {#header_close#}
80688225 {#header_open|@exp#}
8069 <pre>{#syntax#}@exp(value: var) @TypeOf(value){#endsyntax#}</pre>
8226 <pre>{#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}</pre>
80708227 <p>
80718228 Base-e exponential function on a floating point number. Uses a dedicated hardware instruction
80728229 when available.
......@@ -8077,7 +8234,7 @@ test "vector @splat" {
80778234 </p>
80788235 {#header_close#}
80798236 {#header_open|@exp2#}
8080 <pre>{#syntax#}@exp2(value: var) @TypeOf(value){#endsyntax#}</pre>
8237 <pre>{#syntax#}@exp2(value: anytype) @TypeOf(value){#endsyntax#}</pre>
80818238 <p>
80828239 Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction
80838240 when available.
......@@ -8088,7 +8245,7 @@ test "vector @splat" {
80888245 </p>
80898246 {#header_close#}
80908247 {#header_open|@log#}
8091 <pre>{#syntax#}@log(value: var) @TypeOf(value){#endsyntax#}</pre>
8248 <pre>{#syntax#}@log(value: anytype) @TypeOf(value){#endsyntax#}</pre>
80928249 <p>
80938250 Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction
80948251 when available.
......@@ -8099,7 +8256,7 @@ test "vector @splat" {
80998256 </p>
81008257 {#header_close#}
81018258 {#header_open|@log2#}
8102 <pre>{#syntax#}@log2(value: var) @TypeOf(value){#endsyntax#}</pre>
8259 <pre>{#syntax#}@log2(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81038260 <p>
81048261 Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction
81058262 when available.
......@@ -8110,7 +8267,7 @@ test "vector @splat" {
81108267 </p>
81118268 {#header_close#}
81128269 {#header_open|@log10#}
8113 <pre>{#syntax#}@log10(value: var) @TypeOf(value){#endsyntax#}</pre>
8270 <pre>{#syntax#}@log10(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81148271 <p>
81158272 Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction
81168273 when available.
......@@ -8121,7 +8278,7 @@ test "vector @splat" {
81218278 </p>
81228279 {#header_close#}
81238280 {#header_open|@fabs#}
8124 <pre>{#syntax#}@fabs(value: var) @TypeOf(value){#endsyntax#}</pre>
8281 <pre>{#syntax#}@fabs(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81258282 <p>
81268283 Returns the absolute value of a floating point number. Uses a dedicated hardware instruction
81278284 when available.
......@@ -8132,7 +8289,7 @@ test "vector @splat" {
81328289 </p>
81338290 {#header_close#}
81348291 {#header_open|@floor#}
8135 <pre>{#syntax#}@floor(value: var) @TypeOf(value){#endsyntax#}</pre>
8292 <pre>{#syntax#}@floor(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81368293 <p>
81378294 Returns the largest integral value not greater than the given floating point number.
81388295 Uses a dedicated hardware instruction when available.
......@@ -8143,7 +8300,7 @@ test "vector @splat" {
81438300 </p>
81448301 {#header_close#}
81458302 {#header_open|@ceil#}
8146 <pre>{#syntax#}@ceil(value: var) @TypeOf(value){#endsyntax#}</pre>
8303 <pre>{#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81478304 <p>
81488305 Returns the largest integral value not less than the given floating point number.
81498306 Uses a dedicated hardware instruction when available.
......@@ -8154,7 +8311,7 @@ test "vector @splat" {
81548311 </p>
81558312 {#header_close#}
81568313 {#header_open|@trunc#}
8157 <pre>{#syntax#}@trunc(value: var) @TypeOf(value){#endsyntax#}</pre>
8314 <pre>{#syntax#}@trunc(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81588315 <p>
81598316 Rounds the given floating point number to an integer, towards zero.
81608317 Uses a dedicated hardware instruction when available.
......@@ -8165,7 +8322,7 @@ test "vector @splat" {
81658322 </p>
81668323 {#header_close#}
81678324 {#header_open|@round#}
8168 <pre>{#syntax#}@round(value: var) @TypeOf(value){#endsyntax#}</pre>
8325 <pre>{#syntax#}@round(value: anytype) @TypeOf(value){#endsyntax#}</pre>
81698326 <p>
81708327 Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction
81718328 when available.
......@@ -8186,7 +8343,7 @@ test "vector @splat" {
81868343 {#header_close#}
81878344
81888345 {#header_open|@tagName#}
8189 <pre>{#syntax#}@tagName(value: var) []const u8{#endsyntax#}</pre>
8346 <pre>{#syntax#}@tagName(value: anytype) []const u8{#endsyntax#}</pre>
81908347 <p>
81918348 Converts an enum value or union value to a slice of bytes representing the name.</p><p>If the enum is non-exhaustive and the tag value does not map to a name, it invokes safety-checked {#link|Undefined Behavior#}.
81928349 </p>
......@@ -8205,7 +8362,7 @@ test "vector @splat" {
82058362 {#header_open|@This#}
82068363 <pre>{#syntax#}@This() type{#endsyntax#}</pre>
82078364 <p>
8208 Returns the innermost struct or union that this function call is inside.
8365 Returns the innermost struct, enum, or union that this function call is inside.
82098366 This can be useful for an anonymous struct that needs to refer to itself:
82108367 </p>
82118368 {#code_begin|test#}
......@@ -8237,7 +8394,7 @@ fn List(comptime T: type) type {
82378394 {#header_close#}
82388395
82398396 {#header_open|@truncate#}
8240 <pre>{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}</pre>
8397 <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
82418398 <p>
82428399 This function truncates bits from an integer type, resulting in a smaller
82438400 or same-sized integer type.
......@@ -8380,6 +8537,7 @@ fn foo(comptime T: type, ptr: *T) T {
83808537 {#header_close#}
83818538
83828539 {#header_open|Opaque Types#}
8540 <p>
83838541 {#syntax#}@Type(.Opaque){#endsyntax#} creates a new type with an unknown (but non-zero) size and alignment.
83848542 </p>
83858543 <p>
......@@ -8555,7 +8713,7 @@ const std = @import("std");
85558713pub fn main() void {
85568714 var value: i32 = -1;
85578715 var unsigned = @intCast(u32, value);
8558 std.debug.warn("value: {}\n", .{unsigned});
8716 std.debug.print("value: {}\n", .{unsigned});
85598717}
85608718 {#code_end#}
85618719 <p>
......@@ -8577,7 +8735,7 @@ const std = @import("std");
85778735pub fn main() void {
85788736 var spartan_count: u16 = 300;
85798737 const byte = @intCast(u8, spartan_count);
8580 std.debug.warn("value: {}\n", .{byte});
8738 std.debug.print("value: {}\n", .{byte});
85818739}
85828740 {#code_end#}
85838741 <p>
......@@ -8611,7 +8769,7 @@ const std = @import("std");
86118769pub fn main() void {
86128770 var byte: u8 = 255;
86138771 byte += 1;
8614 std.debug.warn("value: {}\n", .{byte});
8772 std.debug.print("value: {}\n", .{byte});
86158773}
86168774 {#code_end#}
86178775 {#header_close#}
......@@ -8629,16 +8787,16 @@ pub fn main() void {
86298787 <p>Example of catching an overflow for addition:</p>
86308788 {#code_begin|exe_err#}
86318789const math = @import("std").math;
8632const warn = @import("std").debug.warn;
8790const print = @import("std").debug.print;
86338791pub fn main() !void {
86348792 var byte: u8 = 255;
86358793
86368794 byte = if (math.add(u8, byte, 1)) |result| result else |err| {
8637 warn("unable to add one: {}\n", .{@errorName(err)});
8795 print("unable to add one: {}\n", .{@errorName(err)});
86388796 return err;
86398797 };
86408798
8641 warn("result: {}\n", .{byte});
8799 print("result: {}\n", .{byte});
86428800}
86438801 {#code_end#}
86448802 {#header_close#}
......@@ -8657,15 +8815,15 @@ pub fn main() !void {
86578815 Example of {#link|@addWithOverflow#}:
86588816 </p>
86598817 {#code_begin|exe#}
8660const warn = @import("std").debug.warn;
8818const print = @import("std").debug.print;
86618819pub fn main() void {
86628820 var byte: u8 = 255;
86638821
86648822 var result: u8 = undefined;
86658823 if (@addWithOverflow(u8, byte, 10, &result)) {
8666 warn("overflowed result: {}\n", .{result});
8824 print("overflowed result: {}\n", .{result});
86678825 } else {
8668 warn("result: {}\n", .{result});
8826 print("result: {}\n", .{result});
86698827 }
86708828}
86718829 {#code_end#}
......@@ -8710,7 +8868,7 @@ const std = @import("std");
87108868pub fn main() void {
87118869 var x: u8 = 0b01010101;
87128870 var y = @shlExact(x, 2);
8713 std.debug.warn("value: {}\n", .{y});
8871 std.debug.print("value: {}\n", .{y});
87148872}
87158873 {#code_end#}
87168874 {#header_close#}
......@@ -8728,7 +8886,7 @@ const std = @import("std");
87288886pub fn main() void {
87298887 var x: u8 = 0b10101010;
87308888 var y = @shrExact(x, 2);
8731 std.debug.warn("value: {}\n", .{y});
8889 std.debug.print("value: {}\n", .{y});
87328890}
87338891 {#code_end#}
87348892 {#header_close#}
......@@ -8749,7 +8907,7 @@ pub fn main() void {
87498907 var a: u32 = 1;
87508908 var b: u32 = 0;
87518909 var c = a / b;
8752 std.debug.warn("value: {}\n", .{c});
8910 std.debug.print("value: {}\n", .{c});
87538911}
87548912 {#code_end#}
87558913 {#header_close#}
......@@ -8770,7 +8928,7 @@ pub fn main() void {
87708928 var a: u32 = 10;
87718929 var b: u32 = 0;
87728930 var c = a % b;
8773 std.debug.warn("value: {}\n", .{c});
8931 std.debug.print("value: {}\n", .{c});
87748932}
87758933 {#code_end#}
87768934 {#header_close#}
......@@ -8791,7 +8949,7 @@ pub fn main() void {
87918949 var a: u32 = 10;
87928950 var b: u32 = 3;
87938951 var c = @divExact(a, b);
8794 std.debug.warn("value: {}\n", .{c});
8952 std.debug.print("value: {}\n", .{c});
87958953}
87968954 {#code_end#}
87978955 {#header_close#}
......@@ -8810,20 +8968,20 @@ const std = @import("std");
88108968pub fn main() void {
88118969 var optional_number: ?i32 = null;
88128970 var number = optional_number.?;
8813 std.debug.warn("value: {}\n", .{number});
8971 std.debug.print("value: {}\n", .{number});
88148972}
88158973 {#code_end#}
88168974 <p>One way to avoid this crash is to test for null instead of assuming non-null, with
88178975 the {#syntax#}if{#endsyntax#} expression:</p>
88188976 {#code_begin|exe|test#}
8819const warn = @import("std").debug.warn;
8977const print = @import("std").debug.print;
88208978pub fn main() void {
88218979 const optional_number: ?i32 = null;
88228980
88238981 if (optional_number) |number| {
8824 warn("got number: {}\n", .{number});
8982 print("got number: {}\n", .{number});
88258983 } else {
8826 warn("it's null\n", .{});
8984 print("it's null\n", .{});
88278985 }
88288986}
88298987 {#code_end#}
......@@ -8846,7 +9004,7 @@ const std = @import("std");
88469004
88479005pub fn main() void {
88489006 const number = getNumberOrFail() catch unreachable;
8849 std.debug.warn("value: {}\n", .{number});
9007 std.debug.print("value: {}\n", .{number});
88509008}
88519009
88529010fn getNumberOrFail() !i32 {
......@@ -8856,15 +9014,15 @@ fn getNumberOrFail() !i32 {
88569014 <p>One way to avoid this crash is to test for an error instead of assuming a successful result, with
88579015 the {#syntax#}if{#endsyntax#} expression:</p>
88589016 {#code_begin|exe#}
8859const warn = @import("std").debug.warn;
9017const print = @import("std").debug.print;
88609018
88619019pub fn main() void {
88629020 const result = getNumberOrFail();
88639021
88649022 if (result) |number| {
8865 warn("got number: {}\n", .{number});
9023 print("got number: {}\n", .{number});
88669024 } else |err| {
8867 warn("got error: {}\n", .{@errorName(err)});
9025 print("got error: {}\n", .{@errorName(err)});
88689026 }
88699027}
88709028
......@@ -8891,7 +9049,7 @@ pub fn main() void {
88919049 var err = error.AnError;
88929050 var number = @errorToInt(err) + 500;
88939051 var invalid_err = @intToError(number);
8894 std.debug.warn("value: {}\n", .{number});
9052 std.debug.print("value: {}\n", .{number});
88959053}
88969054 {#code_end#}
88979055 {#header_close#}
......@@ -8921,7 +9079,7 @@ const Foo = enum {
89219079pub fn main() void {
89229080 var a: u2 = 3;
89239081 var b = @intToEnum(Foo, a);
8924 std.debug.warn("value: {}\n", .{@tagName(b)});
9082 std.debug.print("value: {}\n", .{@tagName(b)});
89259083}
89269084 {#code_end#}
89279085 {#header_close#}
......@@ -8958,7 +9116,7 @@ pub fn main() void {
89589116}
89599117fn foo(set1: Set1) void {
89609118 const x = @errSetCast(Set2, set1);
8961 std.debug.warn("value: {}\n", .{x});
9119 std.debug.print("value: {}\n", .{x});
89629120}
89639121 {#code_end#}
89649122 {#header_close#}
......@@ -9015,7 +9173,7 @@ pub fn main() void {
90159173
90169174fn bar(f: *Foo) void {
90179175 f.float = 12.34;
9018 std.debug.warn("value: {}\n", .{f.float});
9176 std.debug.print("value: {}\n", .{f.float});
90199177}
90209178 {#code_end#}
90219179 <p>
......@@ -9039,7 +9197,7 @@ pub fn main() void {
90399197
90409198fn bar(f: *Foo) void {
90419199 f.* = Foo{ .float = 12.34 };
9042 std.debug.warn("value: {}\n", .{f.float});
9200 std.debug.print("value: {}\n", .{f.float});
90439201}
90449202 {#code_end#}
90459203 <p>
......@@ -9058,7 +9216,7 @@ pub fn main() void {
90589216 var f = Foo{ .int = 42 };
90599217 f = Foo{ .float = undefined };
90609218 bar(&f);
9061 std.debug.warn("value: {}\n", .{f.float});
9219 std.debug.print("value: {}\n", .{f.float});
90629220}
90639221
90649222fn bar(f: *Foo) void {
......@@ -9178,7 +9336,7 @@ pub fn main() !void {
91789336 const allocator = &arena.allocator;
91799337
91809338 const ptr = try allocator.create(i32);
9181 std.debug.warn("ptr={*}\n", .{ptr});
9339 std.debug.print("ptr={*}\n", .{ptr});
91829340}
91839341 {#code_end#}
91849342 When using this kind of allocator, there is no need to free anything manually. Everything
......@@ -9712,7 +9870,7 @@ pub fn main() !void {
97129870 defer std.process.argsFree(std.heap.page_allocator, args);
97139871
97149872 for (args) |arg, i| {
9715 std.debug.warn("{}: {}\n", .{i, arg});
9873 std.debug.print("{}: {}\n", .{i, arg});
97169874 }
97179875}
97189876 {#code_end#}
......@@ -9734,12 +9892,12 @@ pub fn main() !void {
97349892 try preopens.populate();
97359893
97369894 for (preopens.asSlice()) |preopen, i| {
9737 std.debug.warn("{}: {}\n", .{ i, preopen });
9895 std.debug.print("{}: {}\n", .{ i, preopen });
97389896 }
97399897}
97409898 {#code_end#}
97419899 <pre><code>$ wasmtime --dir=. preopens.wasm
97420: { .fd = 3, .Dir = '.' }
99000: Preopen{ .fd = 3, .type = PreopenType{ .Dir = '.' } }
97439901</code></pre>
97449902 {#header_close#}
97459903 {#header_close#}
......@@ -10158,7 +10316,7 @@ TopLevelDecl
1015810316 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
1015910317 / KEYWORD_usingnamespace Expr SEMICOLON
1016010318
10161FnProto &lt;- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
10319FnProto &lt;- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
1016210320
1016310321VarDecl &lt;- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
1016410322
......@@ -10330,7 +10488,7 @@ LinkSection &lt;- KEYWORD_linksection LPAREN Expr RPAREN
1033010488ParamDecl &lt;- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
1033110489
1033210490ParamType
10333 &lt;- KEYWORD_var
10491 &lt;- KEYWORD_anytype
1033410492 / DOT3
1033510493 / TypeExpr
1033610494
......@@ -10568,6 +10726,7 @@ KEYWORD_align &lt;- 'align' end_of_word
1056810726KEYWORD_allowzero &lt;- 'allowzero' end_of_word
1056910727KEYWORD_and &lt;- 'and' end_of_word
1057010728KEYWORD_anyframe &lt;- 'anyframe' end_of_word
10729KEYWORD_anytype &lt;- 'anytype' end_of_word
1057110730KEYWORD_asm &lt;- 'asm' end_of_word
1057210731KEYWORD_async &lt;- 'async' end_of_word
1057310732KEYWORD_await &lt;- 'await' end_of_word
......@@ -10613,14 +10772,14 @@ KEYWORD_var &lt;- 'var' end_of_word
1061310772KEYWORD_volatile &lt;- 'volatile' end_of_word
1061410773KEYWORD_while &lt;- 'while' end_of_word
1061510774
10616keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_asm
10617 / KEYWORD_async / KEYWORD_await / KEYWORD_break
10775keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyframe / KEYWORD_anytype
10776 / KEYWORD_allowzero / KEYWORD_asm / KEYWORD_async / KEYWORD_await / KEYWORD_break
1061810777 / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
1061910778 / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer
1062010779 / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false
1062110780 / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline
1062210781 / KEYWORD_noalias / KEYWORD_null / KEYWORD_or
10623 / KEYWORD_orelse / KEYWORD_packed / KEYWORD_anyframe / KEYWORD_pub
10782 / KEYWORD_orelse / KEYWORD_packed / KEYWORD_pub
1062410783 / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
1062510784 / KEYWORD_struct / KEYWORD_suspend
1062610785 / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try
lib/std/array_list.zig+108-15
......@@ -53,7 +53,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
5353 /// Deprecated: use `items` field directly.
5454 /// Return contents as a slice. Only valid while the list
5555 /// doesn't change size.
56 pub fn span(self: var) @TypeOf(self.items) {
56 pub fn span(self: anytype) @TypeOf(self.items) {
5757 return self.items;
5858 }
5959
......@@ -162,19 +162,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
162162 mem.copy(T, self.items[oldlen..], items);
163163 }
164164
165 /// Same as `append` except it returns the number of bytes written, which is always the same
166 /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
167 /// This function may be called only when `T` is `u8`.
168 fn appendWrite(self: *Self, m: []const u8) !usize {
169 try self.appendSlice(m);
170 return m.len;
171 }
165 pub usingnamespace if (T != u8) struct {} else struct {
166 pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
172167
173 /// Initializes an OutStream which will append to the list.
174 /// This function may be called only when `T` is `u8`.
175 pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
176 return .{ .context = self };
177 }
168 /// Initializes a Writer which will append to the list.
169 pub fn writer(self: *Self) Writer {
170 return .{ .context = self };
171 }
172
173 /// Deprecated: use `writer`
174 pub const outStream = writer;
175
176 /// Same as `append` except it returns the number of bytes written, which is always the same
177 /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.
178 fn appendWrite(self: *Self, m: []const u8) !usize {
179 try self.appendSlice(m);
180 return m.len;
181 }
182 };
178183
179184 /// Append a value to the list `n` times.
180185 /// Allocates more memory as necessary.
......@@ -205,6 +210,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
205210 self.capacity = new_len;
206211 }
207212
213 /// Reduce length to `new_len`.
214 /// Invalidates element pointers.
215 /// Keeps capacity the same.
216 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
217 assert(new_len <= self.items.len);
218 self.items.len = new_len;
219 }
220
208221 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
209222 var better_capacity = self.capacity;
210223 if (better_capacity >= new_capacity) return;
......@@ -214,7 +227,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
214227 if (better_capacity >= new_capacity) break;
215228 }
216229
217 const new_memory = try self.allocator.realloc(self.allocatedSlice(), better_capacity);
230 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
218231 self.items.ptr = new_memory.ptr;
219232 self.capacity = new_memory.len;
220233 }
......@@ -244,6 +257,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
244257 return &self.items[self.items.len - 1];
245258 }
246259
260 /// Resize the array, adding `n` new elements, which have `undefined` values.
261 /// The return value is an array pointing to the newly allocated elements.
262 pub fn addManyAsArray(self: *Self, comptime n: usize) !*[n]T {
263 const prev_len = self.items.len;
264 try self.resize(self.items.len + n);
265 return self.items[prev_len..][0..n];
266 }
267
268 /// Resize the array, adding `n` new elements, which have `undefined` values.
269 /// The return value is an array pointing to the newly allocated elements.
270 /// Asserts that there is already space for the new item without allocating more.
271 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
272 assert(self.items.len + n <= self.capacity);
273 const prev_len = self.items.len;
274 self.items.len += n;
275 return self.items[prev_len..][0..n];
276 }
277
247278 /// Remove and return the last element from the list.
248279 /// Asserts the list has at least one item.
249280 pub fn pop(self: *Self) T {
......@@ -427,6 +458,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
427458 self.capacity = new_len;
428459 }
429460
461 /// Reduce length to `new_len`.
462 /// Invalidates element pointers.
463 /// Keeps capacity the same.
464 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
465 assert(new_len <= self.items.len);
466 self.items.len = new_len;
467 }
468
430469 pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
431470 var better_capacity = self.capacity;
432471 if (better_capacity >= new_capacity) return;
......@@ -436,7 +475,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
436475 if (better_capacity >= new_capacity) break;
437476 }
438477
439 const new_memory = try allocator.realloc(self.allocatedSlice(), better_capacity);
478 const new_memory = try allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
440479 self.items.ptr = new_memory.ptr;
441480 self.capacity = new_memory.len;
442481 }
......@@ -467,6 +506,24 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
467506 return &self.items[self.items.len - 1];
468507 }
469508
509 /// Resize the array, adding `n` new elements, which have `undefined` values.
510 /// The return value is an array pointing to the newly allocated elements.
511 pub fn addManyAsArray(self: *Self, allocator: *Allocator, comptime n: usize) !*[n]T {
512 const prev_len = self.items.len;
513 try self.resize(allocator, self.items.len + n);
514 return self.items[prev_len..][0..n];
515 }
516
517 /// Resize the array, adding `n` new elements, which have `undefined` values.
518 /// The return value is an array pointing to the newly allocated elements.
519 /// Asserts that there is already space for the new item without allocating more.
520 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
521 assert(self.items.len + n <= self.capacity);
522 const prev_len = self.items.len;
523 self.items.len += n;
524 return self.items[prev_len..][0..n];
525 }
526
470527 /// Remove and return the last element from the list.
471528 /// Asserts the list has at least one item.
472529 /// This operation does not invalidate any element pointers.
......@@ -694,3 +751,39 @@ test "std.ArrayList.shrink still sets length on error.OutOfMemory" {
694751 list.shrink(1);
695752 testing.expect(list.items.len == 1);
696753}
754
755test "std.ArrayList.writer" {
756 var list = ArrayList(u8).init(std.testing.allocator);
757 defer list.deinit();
758
759 const writer = list.writer();
760 try writer.writeAll("a");
761 try writer.writeAll("bc");
762 try writer.writeAll("d");
763 try writer.writeAll("efg");
764 testing.expectEqualSlices(u8, list.items, "abcdefg");
765}
766
767test "addManyAsArray" {
768 const a = std.testing.allocator;
769 {
770 var list = ArrayList(u8).init(a);
771 defer list.deinit();
772
773 (try list.addManyAsArray(4)).* = "aoeu".*;
774 try list.ensureCapacity(8);
775 list.addManyAsArrayAssumeCapacity(4).* = "asdf".*;
776
777 testing.expectEqualSlices(u8, list.items, "aoeuasdf");
778 }
779 {
780 var list = ArrayListUnmanaged(u8){};
781 defer list.deinit(a);
782
783 (try list.addManyAsArray(a, 4)).* = "aoeu".*;
784 try list.ensureCapacity(a, 8);
785 list.addManyAsArrayAssumeCapacity(4).* = "asdf".*;
786
787 testing.expectEqualSlices(u8, list.items, "aoeuasdf");
788 }
789}
lib/std/array_list_sentineled.zig+2-2
......@@ -69,7 +69,7 @@ pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {
6969 }
7070
7171 /// Only works when `T` is `u8`.
72 pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Self {
72 pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: anytype) !Self {
7373 const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {
7474 error.Overflow => return error.OutOfMemory,
7575 };
......@@ -82,7 +82,7 @@ pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {
8282 self.list.deinit();
8383 }
8484
85 pub fn span(self: var) @TypeOf(self.list.items[0..:sentinel]) {
85 pub fn span(self: anytype) @TypeOf(self.list.items[0..:sentinel]) {
8686 return self.list.items[0..self.len() :sentinel];
8787 }
8888
lib/std/atomic/queue.zig+2-2
......@@ -123,10 +123,10 @@ pub fn Queue(comptime T: type) type {
123123 /// Dumps the contents of the queue to `stream`.
124124 /// Up to 4 elements from the head are dumped and the tail of the queue is
125125 /// dumped as well.
126 pub fn dumpToStream(self: *Self, stream: var) !void {
126 pub fn dumpToStream(self: *Self, stream: anytype) !void {
127127 const S = struct {
128128 fn dumpRecursive(
129 s: var,
129 s: anytype,
130130 optional_node: ?*Node,
131131 indent: usize,
132132 comptime depth: comptime_int,
lib/std/buf_map.zig+8-9
......@@ -33,10 +33,10 @@ pub const BufMap = struct {
3333 pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void {
3434 const get_or_put = try self.hash_map.getOrPut(key);
3535 if (get_or_put.found_existing) {
36 self.free(get_or_put.kv.key);
37 get_or_put.kv.key = key;
36 self.free(get_or_put.entry.key);
37 get_or_put.entry.key = key;
3838 }
39 get_or_put.kv.value = value;
39 get_or_put.entry.value = value;
4040 }
4141
4242 /// `key` and `value` are copied into the BufMap.
......@@ -45,19 +45,18 @@ pub const BufMap = struct {
4545 errdefer self.free(value_copy);
4646 const get_or_put = try self.hash_map.getOrPut(key);
4747 if (get_or_put.found_existing) {
48 self.free(get_or_put.kv.value);
48 self.free(get_or_put.entry.value);
4949 } else {
50 get_or_put.kv.key = self.copy(key) catch |err| {
50 get_or_put.entry.key = self.copy(key) catch |err| {
5151 _ = self.hash_map.remove(key);
5252 return err;
5353 };
5454 }
55 get_or_put.kv.value = value_copy;
55 get_or_put.entry.value = value_copy;
5656 }
5757
5858 pub fn get(self: BufMap, key: []const u8) ?[]const u8 {
59 const entry = self.hash_map.get(key) orelse return null;
60 return entry.value;
59 return self.hash_map.get(key);
6160 }
6261
6362 pub fn delete(self: *BufMap, key: []const u8) void {
......@@ -79,7 +78,7 @@ pub const BufMap = struct {
7978 }
8079
8180 fn copy(self: BufMap, value: []const u8) ![]u8 {
82 return mem.dupe(self.hash_map.allocator, u8, value);
81 return self.hash_map.allocator.dupe(u8, value);
8382 }
8483};
8584
lib/std/buf_set.zig+3-5
......@@ -14,14 +14,12 @@ pub const BufSet = struct {
1414 return self;
1515 }
1616
17 pub fn deinit(self: *const BufSet) void {
18 var it = self.hash_map.iterator();
19 while (true) {
20 const entry = it.next() orelse break;
17 pub fn deinit(self: *BufSet) void {
18 for (self.hash_map.items()) |entry| {
2119 self.free(entry.key);
2220 }
23
2421 self.hash_map.deinit();
22 self.* = undefined;
2523 }
2624
2725 pub fn put(self: *BufSet, key: []const u8) !void {
lib/std/build.zig+24-16
......@@ -286,7 +286,7 @@ pub const Builder = struct {
286286 }
287287
288288 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
289 return mem.dupe(self.allocator, u8, bytes) catch unreachable;
289 return self.allocator.dupe(u8, bytes) catch unreachable;
290290 }
291291
292292 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
......@@ -312,7 +312,7 @@ pub const Builder = struct {
312312 return write_file_step;
313313 }
314314
315 pub fn addLog(self: *Builder, comptime format: []const u8, args: var) *LogStep {
315 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
316316 const data = self.fmt(format, args);
317317 const log_step = self.allocator.create(LogStep) catch unreachable;
318318 log_step.* = LogStep.init(self, data);
......@@ -422,12 +422,12 @@ pub const Builder = struct {
422422 .type_id = type_id,
423423 .description = description,
424424 };
425 if ((self.available_options_map.put(name, available_option) catch unreachable) != null) {
425 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
426426 panic("Option '{}' declared twice", .{name});
427427 }
428428 self.available_options_list.append(available_option) catch unreachable;
429429
430 const entry = self.user_input_options.get(name) orelse return null;
430 const entry = self.user_input_options.getEntry(name) orelse return null;
431431 entry.value.used = true;
432432 switch (type_id) {
433433 TypeId.Bool => switch (entry.value.value) {
......@@ -512,7 +512,7 @@ pub const Builder = struct {
512512 if (self.release_mode != null) {
513513 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
514514 }
515 const description = self.fmt("create a release build ({})", .{@tagName(mode)});
515 const description = self.fmt("Create a release build ({})", .{@tagName(mode)});
516516 self.is_release = self.option(bool, "release", description) orelse false;
517517 self.release_mode = if (self.is_release) mode else builtin.Mode.Debug;
518518 }
......@@ -522,9 +522,9 @@ pub const Builder = struct {
522522 pub fn standardReleaseOptions(self: *Builder) builtin.Mode {
523523 if (self.release_mode) |mode| return mode;
524524
525 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false;
526 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false;
527 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false;
525 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
526 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
527 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
528528
529529 const mode = if (release_safe and !release_fast and !release_small)
530530 builtin.Mode.ReleaseSafe
......@@ -555,7 +555,7 @@ pub const Builder = struct {
555555 const triple = self.option(
556556 []const u8,
557557 "target",
558 "The CPU architecture, OS, and ABI to build for.",
558 "The CPU architecture, OS, and ABI to build for",
559559 ) orelse return args.default_target;
560560
561561 // TODO add cpu and features as part of the target triple
......@@ -634,7 +634,7 @@ pub const Builder = struct {
634634 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
635635 const gop = try self.user_input_options.getOrPut(name);
636636 if (!gop.found_existing) {
637 gop.kv.value = UserInputOption{
637 gop.entry.value = UserInputOption{
638638 .name = name,
639639 .value = UserValue{ .Scalar = value },
640640 .used = false,
......@@ -643,7 +643,7 @@ pub const Builder = struct {
643643 }
644644
645645 // option already exists
646 switch (gop.kv.value.value) {
646 switch (gop.entry.value.value) {
647647 UserValue.Scalar => |s| {
648648 // turn it into a list
649649 var list = ArrayList([]const u8).init(self.allocator);
......@@ -675,7 +675,7 @@ pub const Builder = struct {
675675 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
676676 const gop = try self.user_input_options.getOrPut(name);
677677 if (!gop.found_existing) {
678 gop.kv.value = UserInputOption{
678 gop.entry.value = UserInputOption{
679679 .name = name,
680680 .value = UserValue{ .Flag = {} },
681681 .used = false,
......@@ -684,7 +684,7 @@ pub const Builder = struct {
684684 }
685685
686686 // option already exists
687 switch (gop.kv.value.value) {
687 switch (gop.entry.value.value) {
688688 UserValue.Scalar => |s| {
689689 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s });
690690 return true;
......@@ -883,7 +883,7 @@ pub const Builder = struct {
883883 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
884884 }
885885
886 pub fn fmt(self: *Builder, comptime format: []const u8, args: var) []u8 {
886 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
887887 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
888888 }
889889
......@@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct {
19051905 builder.allocator,
19061906 &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },
19071907 );
1908 try fs.cwd().writeFile(build_options_file, self.build_options_contents.span());
1908 const path_from_root = builder.pathFromRoot(build_options_file);
1909 try fs.cwd().writeFile(path_from_root, self.build_options_contents.span());
19091910 try zig_args.append("--pkg-begin");
19101911 try zig_args.append("build_options");
1911 try zig_args.append(builder.pathFromRoot(build_options_file));
1912 try zig_args.append(path_from_root);
19121913 try zig_args.append("--pkg-end");
19131914 }
19141915
......@@ -2558,3 +2559,10 @@ pub const InstalledFile = struct {
25582559 dir: InstallDir,
25592560 path: []const u8,
25602561};
2562
2563test "" {
2564 // The only purpose of this test is to get all these untested functions
2565 // to be referenced to avoid regression so it is okay to skip some targets.
2566 if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64)
2567 std.meta.refAllDecls(@This());
2568}
lib/std/build/emit_raw.zig+5-1
......@@ -126,7 +126,7 @@ const BinaryElfOutput = struct {
126126 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
127127 }
128128
129 fn sectionValidForOutput(shdr: var) bool {
129 fn sectionValidForOutput(shdr: anytype) bool {
130130 return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and
131131 ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
132132 }
......@@ -215,3 +215,7 @@ pub const InstallRawStep = struct {
215215 try emitRaw(builder.allocator, full_src_path, full_dest_path);
216216 }
217217};
218
219test "" {
220 std.meta.refAllDecls(InstallRawStep);
221}
lib/std/builtin.zig+31-14
......@@ -131,6 +131,15 @@ pub const CallingConvention = enum {
131131 AAPCSVFP,
132132};
133133
134/// This data structure is used by the Zig language code generation and
135/// therefore must be kept in sync with the compiler implementation.
136pub const SourceLocation = struct {
137 file: [:0]const u8,
138 fn_name: [:0]const u8,
139 line: u32,
140 column: u32,
141};
142
134143pub const TypeId = @TagType(TypeInfo);
135144
136145/// This data structure is used by the Zig language code generation and
......@@ -157,7 +166,7 @@ pub const TypeInfo = union(enum) {
157166 Fn: Fn,
158167 BoundFn: Fn,
159168 Opaque: void,
160 Frame: void,
169 Frame: Frame,
161170 AnyFrame: AnyFrame,
162171 Vector: Vector,
163172 EnumLiteral: void,
......@@ -189,7 +198,7 @@ pub const TypeInfo = union(enum) {
189198 /// The type of the sentinel is the element type of the pointer, which is
190199 /// the value of the `child` field in this struct. However there is no way
191200 /// to refer to that type here, so we use `var`.
192 sentinel: var,
201 sentinel: anytype,
193202
194203 /// This data structure is used by the Zig language code generation and
195204 /// therefore must be kept in sync with the compiler implementation.
......@@ -211,7 +220,7 @@ pub const TypeInfo = union(enum) {
211220 /// The type of the sentinel is the element type of the array, which is
212221 /// the value of the `child` field in this struct. However there is no way
213222 /// to refer to that type here, so we use `var`.
214 sentinel: var,
223 sentinel: anytype,
215224 };
216225
217226 /// This data structure is used by the Zig language code generation and
......@@ -228,15 +237,16 @@ pub const TypeInfo = union(enum) {
228237 name: []const u8,
229238 offset: ?comptime_int,
230239 field_type: type,
231 default_value: var,
240 default_value: anytype,
232241 };
233242
234243 /// This data structure is used by the Zig language code generation and
235244 /// therefore must be kept in sync with the compiler implementation.
236245 pub const Struct = struct {
237246 layout: ContainerLayout,
238 fields: []StructField,
239 decls: []Declaration,
247 fields: []const StructField,
248 decls: []const Declaration,
249 is_tuple: bool,
240250 };
241251
242252 /// This data structure is used by the Zig language code generation and
......@@ -256,12 +266,13 @@ pub const TypeInfo = union(enum) {
256266 /// therefore must be kept in sync with the compiler implementation.
257267 pub const Error = struct {
258268 name: []const u8,
269 /// This field is ignored when using @Type().
259270 value: comptime_int,
260271 };
261272
262273 /// This data structure is used by the Zig language code generation and
263274 /// therefore must be kept in sync with the compiler implementation.
264 pub const ErrorSet = ?[]Error;
275 pub const ErrorSet = ?[]const Error;
265276
266277 /// This data structure is used by the Zig language code generation and
267278 /// therefore must be kept in sync with the compiler implementation.
......@@ -275,8 +286,8 @@ pub const TypeInfo = union(enum) {
275286 pub const Enum = struct {
276287 layout: ContainerLayout,
277288 tag_type: type,
278 fields: []EnumField,
279 decls: []Declaration,
289 fields: []const EnumField,
290 decls: []const Declaration,
280291 is_exhaustive: bool,
281292 };
282293
......@@ -293,8 +304,8 @@ pub const TypeInfo = union(enum) {
293304 pub const Union = struct {
294305 layout: ContainerLayout,
295306 tag_type: ?type,
296 fields: []UnionField,
297 decls: []Declaration,
307 fields: []const UnionField,
308 decls: []const Declaration,
298309 };
299310
300311 /// This data structure is used by the Zig language code generation and
......@@ -312,7 +323,13 @@ pub const TypeInfo = union(enum) {
312323 is_generic: bool,
313324 is_var_args: bool,
314325 return_type: ?type,
315 args: []FnArg,
326 args: []const FnArg,
327 };
328
329 /// This data structure is used by the Zig language code generation and
330 /// therefore must be kept in sync with the compiler implementation.
331 pub const Frame = struct {
332 function: anytype,
316333 };
317334
318335 /// This data structure is used by the Zig language code generation and
......@@ -352,7 +369,7 @@ pub const TypeInfo = union(enum) {
352369 is_export: bool,
353370 lib_name: ?[]const u8,
354371 return_type: type,
355 arg_names: [][]const u8,
372 arg_names: []const []const u8,
356373
357374 /// This data structure is used by the Zig language code generation and
358375 /// therefore must be kept in sync with the compiler implementation.
......@@ -436,7 +453,7 @@ pub const Version = struct {
436453 self: Version,
437454 comptime fmt: []const u8,
438455 options: std.fmt.FormatOptions,
439 out_stream: var,
456 out_stream: anytype,
440457 ) !void {
441458 if (fmt.len == 0) {
442459 if (self.patch == 0) {
lib/std/c.zig+15-2
......@@ -27,7 +27,7 @@ pub usingnamespace switch (std.Target.current.os.tag) {
2727 else => struct {},
2828};
2929
30pub fn getErrno(rc: var) u16 {
30pub fn getErrno(rc: anytype) u16 {
3131 if (rc == -1) {
3232 return @intCast(u16, _errno().*);
3333 } else {
......@@ -73,7 +73,6 @@ pub extern "c" fn abort() noreturn;
7373pub extern "c" fn exit(code: c_int) noreturn;
7474pub extern "c" fn isatty(fd: fd_t) c_int;
7575pub extern "c" fn close(fd: fd_t) c_int;
76pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int;
7776pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t;
7877pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int;
7978pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int;
......@@ -102,6 +101,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
102101pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
103102pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
104103pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
104pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int;
105105pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
106106pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;
107107pub extern "c" fn chdir(path: [*:0]const u8) c_int;
......@@ -115,9 +115,11 @@ pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias b
115115pub usingnamespace switch (builtin.os.tag) {
116116 .macosx, .ios, .watchos, .tvos => struct {
117117 pub const realpath = @"realpath$DARWIN_EXTSN";
118 pub const fstatat = @"fstatat$INODE64";
118119 },
119120 else => struct {
120121 pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
122 pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int;
121123 },
122124};
123125
......@@ -231,6 +233,17 @@ pub extern "c" fn setuid(uid: c_uint) c_int;
231233
232234pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
233235pub extern "c" fn malloc(usize) ?*c_void;
236
237pub usingnamespace switch (builtin.os.tag) {
238 .linux, .freebsd, .kfreebsd, .netbsd, .openbsd => struct {
239 pub extern "c" fn malloc_usable_size(?*const c_void) usize;
240 },
241 .macosx, .ios, .watchos, .tvos => struct {
242 pub extern "c" fn malloc_size(?*const c_void) usize;
243 },
244 else => struct {},
245};
246
234247pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
235248pub extern "c" fn free(*c_void) void;
236249pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
lib/std/c/ast.zig+7-7
......@@ -64,7 +64,7 @@ pub const Error = union(enum) {
6464 NothingDeclared: SimpleError("declaration doesn't declare anything"),
6565 QualifierIgnored: SingleTokenError("qualifier '{}' ignored"),
6666
67 pub fn render(self: *const Error, tree: *Tree, stream: var) !void {
67 pub fn render(self: *const Error, tree: *Tree, stream: anytype) !void {
6868 switch (self.*) {
6969 .InvalidToken => |*x| return x.render(tree, stream),
7070 .ExpectedToken => |*x| return x.render(tree, stream),
......@@ -114,7 +114,7 @@ pub const Error = union(enum) {
114114 token: TokenIndex,
115115 expected_id: @TagType(Token.Id),
116116
117 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {
117 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
118118 const found_token = tree.tokens.at(self.token);
119119 if (found_token.id == .Invalid) {
120120 return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
......@@ -129,7 +129,7 @@ pub const Error = union(enum) {
129129 token: TokenIndex,
130130 type_spec: *Node.TypeSpec,
131131
132 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {
132 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
133133 try stream.write("invalid type specifier '");
134134 try type_spec.spec.print(tree, stream);
135135 const token_name = tree.tokens.at(self.token).id.symbol();
......@@ -141,7 +141,7 @@ pub const Error = union(enum) {
141141 kw: TokenIndex,
142142 name: TokenIndex,
143143
144 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {
144 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
145145 return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) });
146146 }
147147 };
......@@ -150,7 +150,7 @@ pub const Error = union(enum) {
150150 return struct {
151151 token: TokenIndex,
152152
153 pub fn render(self: *const @This(), tree: *Tree, stream: var) !void {
153 pub fn render(self: *const @This(), tree: *Tree, stream: anytype) !void {
154154 const actual_token = tree.tokens.at(self.token);
155155 return stream.print(msg, .{actual_token.id.symbol()});
156156 }
......@@ -163,7 +163,7 @@ pub const Error = union(enum) {
163163
164164 token: TokenIndex,
165165
166 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {
166 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: anytype) !void {
167167 return stream.write(msg);
168168 }
169169 };
......@@ -317,7 +317,7 @@ pub const Node = struct {
317317 sym_type: *Type,
318318 },
319319
320 pub fn print(self: *@This(), self: *const @This(), tree: *Tree, stream: var) !void {
320 pub fn print(self: *@This(), self: *const @This(), tree: *Tree, stream: anytype) !void {
321321 switch (self.spec) {
322322 .None => unreachable,
323323 .Void => |index| try stream.write(tree.slice(index)),
lib/std/c/darwin.zig+1
......@@ -16,6 +16,7 @@ pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noa
1616
1717pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;
1818pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;
19pub extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int;
1920
2021pub extern "c" fn mach_absolute_time() u64;
2122pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
lib/std/c/tokenizer.zig+50-50
......@@ -278,62 +278,62 @@ pub const Token = struct {
278278
279279 // TODO extensions
280280 pub const keywords = std.ComptimeStringMap(Id, .{
281 .{"auto", .Keyword_auto},
282 .{"break", .Keyword_break},
283 .{"case", .Keyword_case},
284 .{"char", .Keyword_char},
285 .{"const", .Keyword_const},
286 .{"continue", .Keyword_continue},
287 .{"default", .Keyword_default},
288 .{"do", .Keyword_do},
289 .{"double", .Keyword_double},
290 .{"else", .Keyword_else},
291 .{"enum", .Keyword_enum},
292 .{"extern", .Keyword_extern},
293 .{"float", .Keyword_float},
294 .{"for", .Keyword_for},
295 .{"goto", .Keyword_goto},
296 .{"if", .Keyword_if},
297 .{"int", .Keyword_int},
298 .{"long", .Keyword_long},
299 .{"register", .Keyword_register},
300 .{"return", .Keyword_return},
301 .{"short", .Keyword_short},
302 .{"signed", .Keyword_signed},
303 .{"sizeof", .Keyword_sizeof},
304 .{"static", .Keyword_static},
305 .{"struct", .Keyword_struct},
306 .{"switch", .Keyword_switch},
307 .{"typedef", .Keyword_typedef},
308 .{"union", .Keyword_union},
309 .{"unsigned", .Keyword_unsigned},
310 .{"void", .Keyword_void},
311 .{"volatile", .Keyword_volatile},
312 .{"while", .Keyword_while},
281 .{ "auto", .Keyword_auto },
282 .{ "break", .Keyword_break },
283 .{ "case", .Keyword_case },
284 .{ "char", .Keyword_char },
285 .{ "const", .Keyword_const },
286 .{ "continue", .Keyword_continue },
287 .{ "default", .Keyword_default },
288 .{ "do", .Keyword_do },
289 .{ "double", .Keyword_double },
290 .{ "else", .Keyword_else },
291 .{ "enum", .Keyword_enum },
292 .{ "extern", .Keyword_extern },
293 .{ "float", .Keyword_float },
294 .{ "for", .Keyword_for },
295 .{ "goto", .Keyword_goto },
296 .{ "if", .Keyword_if },
297 .{ "int", .Keyword_int },
298 .{ "long", .Keyword_long },
299 .{ "register", .Keyword_register },
300 .{ "return", .Keyword_return },
301 .{ "short", .Keyword_short },
302 .{ "signed", .Keyword_signed },
303 .{ "sizeof", .Keyword_sizeof },
304 .{ "static", .Keyword_static },
305 .{ "struct", .Keyword_struct },
306 .{ "switch", .Keyword_switch },
307 .{ "typedef", .Keyword_typedef },
308 .{ "union", .Keyword_union },
309 .{ "unsigned", .Keyword_unsigned },
310 .{ "void", .Keyword_void },
311 .{ "volatile", .Keyword_volatile },
312 .{ "while", .Keyword_while },
313313
314314 // ISO C99
315 .{"_Bool", .Keyword_bool},
316 .{"_Complex", .Keyword_complex},
317 .{"_Imaginary", .Keyword_imaginary},
318 .{"inline", .Keyword_inline},
319 .{"restrict", .Keyword_restrict},
315 .{ "_Bool", .Keyword_bool },
316 .{ "_Complex", .Keyword_complex },
317 .{ "_Imaginary", .Keyword_imaginary },
318 .{ "inline", .Keyword_inline },
319 .{ "restrict", .Keyword_restrict },
320320
321321 // ISO C11
322 .{"_Alignas", .Keyword_alignas},
323 .{"_Alignof", .Keyword_alignof},
324 .{"_Atomic", .Keyword_atomic},
325 .{"_Generic", .Keyword_generic},
326 .{"_Noreturn", .Keyword_noreturn},
327 .{"_Static_assert", .Keyword_static_assert},
328 .{"_Thread_local", .Keyword_thread_local},
322 .{ "_Alignas", .Keyword_alignas },
323 .{ "_Alignof", .Keyword_alignof },
324 .{ "_Atomic", .Keyword_atomic },
325 .{ "_Generic", .Keyword_generic },
326 .{ "_Noreturn", .Keyword_noreturn },
327 .{ "_Static_assert", .Keyword_static_assert },
328 .{ "_Thread_local", .Keyword_thread_local },
329329
330330 // Preprocessor directives
331 .{"include", .Keyword_include},
332 .{"define", .Keyword_define},
333 .{"ifdef", .Keyword_ifdef},
334 .{"ifndef", .Keyword_ifndef},
335 .{"error", .Keyword_error},
336 .{"pragma", .Keyword_pragma},
331 .{ "include", .Keyword_include },
332 .{ "define", .Keyword_define },
333 .{ "ifdef", .Keyword_ifdef },
334 .{ "ifndef", .Keyword_ifndef },
335 .{ "error", .Keyword_error },
336 .{ "pragma", .Keyword_pragma },
337337 });
338338
339339 // TODO do this in the preprocessor
lib/std/cache_hash.zig+2-2
......@@ -70,7 +70,7 @@ pub const CacheHash = struct {
7070
7171 /// Convert the input value into bytes and record it as a dependency of the
7272 /// process being cached
73 pub fn add(self: *CacheHash, val: var) void {
73 pub fn add(self: *CacheHash, val: anytype) void {
7474 assert(self.manifest_file == null);
7575
7676 const valPtr = switch (@typeInfo(@TypeOf(val))) {
......@@ -207,7 +207,7 @@ pub const CacheHash = struct {
207207 }
208208
209209 if (cache_hash_file.path == null) {
210 cache_hash_file.path = try mem.dupe(self.allocator, u8, file_path);
210 cache_hash_file.path = try self.allocator.dupe(u8, file_path);
211211 }
212212
213213 const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {
lib/std/comptime_string_map.zig+3-3
......@@ -8,7 +8,7 @@ const mem = std.mem;
88/// `kvs` expects a list literal containing list literals or an array/slice of structs
99/// where `.@"0"` is the `[]const u8` key and `.@"1"` is the associated value of type `V`.
1010/// TODO: https://github.com/ziglang/zig/issues/4335
11pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type {
11pub fn ComptimeStringMap(comptime V: type, comptime kvs: anytype) type {
1212 const precomputed = comptime blk: {
1313 @setEvalBranchQuota(2000);
1414 const KV = struct {
......@@ -126,7 +126,7 @@ test "ComptimeStringMap slice of structs" {
126126 testMap(map);
127127}
128128
129fn testMap(comptime map: var) void {
129fn testMap(comptime map: anytype) void {
130130 std.testing.expectEqual(TestEnum.A, map.get("have").?);
131131 std.testing.expectEqual(TestEnum.B, map.get("nothing").?);
132132 std.testing.expect(null == map.get("missing"));
......@@ -165,7 +165,7 @@ test "ComptimeStringMap void value type, list literal of list literals" {
165165 testSet(map);
166166}
167167
168fn testSet(comptime map: var) void {
168fn testSet(comptime map: anytype) void {
169169 std.testing.expectEqual({}, map.get("have").?);
170170 std.testing.expectEqual({}, map.get("nothing").?);
171171 std.testing.expect(null == map.get("missing"));
lib/std/crypto/benchmark.zig+6-18
......@@ -29,7 +29,7 @@ const hashes = [_]Crypto{
2929 Crypto{ .ty = crypto.Blake3, .name = "blake3" },
3030};
3131
32pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 {
32pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 {
3333 var h = Hash.init();
3434
3535 var block: [Hash.digest_length]u8 = undefined;
......@@ -56,7 +56,7 @@ const macs = [_]Crypto{
5656 Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },
5757};
5858
59pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
59pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
6060 std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
6161
6262 var in: [1 * MiB]u8 = undefined;
......@@ -81,7 +81,7 @@ pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
8181
8282const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
8383
84pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 {
84pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_count: comptime_int) !u64 {
8585 std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
8686
8787 var in: [DhKeyExchange.minimum_key_length]u8 = undefined;
......@@ -123,15 +123,6 @@ fn mode(comptime x: comptime_int) comptime_int {
123123 return if (builtin.mode == .Debug) x / 64 else x;
124124}
125125
126// TODO(#1358): Replace with builtin formatted padding when available.
127fn printPad(stdout: var, s: []const u8) !void {
128 var i: usize = 0;
129 while (i < 12 - s.len) : (i += 1) {
130 try stdout.print(" ", .{});
131 }
132 try stdout.print("{}", .{s});
133}
134
135126pub fn main() !void {
136127 const stdout = std.io.getStdOut().outStream();
137128
......@@ -175,24 +166,21 @@ pub fn main() !void {
175166 inline for (hashes) |H| {
176167 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
177168 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
178 try printPad(stdout, H.name);
179 try stdout.print(": {} MiB/s\n", .{throughput / (1 * MiB)});
169 try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
180170 }
181171 }
182172
183173 inline for (macs) |M| {
184174 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
185175 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
186 try printPad(stdout, M.name);
187 try stdout.print(": {} MiB/s\n", .{throughput / (1 * MiB)});
176 try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
188177 }
189178 }
190179
191180 inline for (exchanges) |E| {
192181 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
193182 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
194 try printPad(stdout, E.name);
195 try stdout.print(": {} exchanges/s\n", .{throughput});
183 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
196184 }
197185 }
198186}
lib/std/crypto/test.zig+1-1
......@@ -4,7 +4,7 @@ const mem = std.mem;
44const fmt = std.fmt;
55
66// Hash using the specified hasher `H` asserting `expected == H(input)`.
7pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) void {
7pub fn assertEqualHash(comptime Hasher: anytype, comptime expected: []const u8, input: []const u8) void {
88 var h: [expected.len / 2]u8 = undefined;
99 Hasher.hash(input, h[0..]);
1010
lib/std/debug.zig+30-40
......@@ -50,33 +50,21 @@ pub const LineInfo = struct {
5050 }
5151};
5252
53/// Tries to write to stderr, unbuffered, and ignores any error returned.
54/// Does not append a newline.
55var stderr_file: File = undefined;
56var stderr_file_writer: File.Writer = undefined;
57
58var stderr_stream: ?*File.OutStream = null;
5953var stderr_mutex = std.Mutex.init();
6054
61pub fn warn(comptime fmt: []const u8, args: var) void {
55/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
56/// "printf debugging".
57pub const warn = print;
58
59/// Print to stderr, unbuffered, and silently returning on failure. Intended
60/// for use in "printf debugging." Use `std.log` functions for proper logging.
61pub fn print(comptime fmt: []const u8, args: anytype) void {
6262 const held = stderr_mutex.acquire();
6363 defer held.release();
64 const stderr = getStderrStream();
64 const stderr = io.getStdErr().writer();
6565 nosuspend stderr.print(fmt, args) catch return;
6666}
6767
68pub fn getStderrStream() *File.OutStream {
69 if (stderr_stream) |st| {
70 return st;
71 } else {
72 stderr_file = io.getStdErr();
73 stderr_file_writer = stderr_file.outStream();
74 const st = &stderr_file_writer;
75 stderr_stream = st;
76 return st;
77 }
78}
79
8068pub fn getStderrMutex() *std.Mutex {
8169 return &stderr_mutex;
8270}
......@@ -99,6 +87,7 @@ pub fn detectTTYConfig() TTY.Config {
9987 if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| {
10088 return .escape_codes;
10189 } else |_| {
90 const stderr_file = io.getStdErr();
10291 if (stderr_file.supportsAnsiEscapeCodes()) {
10392 return .escape_codes;
10493 } else if (builtin.os.tag == .windows and stderr_file.isTty()) {
......@@ -113,7 +102,7 @@ pub fn detectTTYConfig() TTY.Config {
113102/// TODO multithreaded awareness
114103pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
115104 nosuspend {
116 const stderr = getStderrStream();
105 const stderr = io.getStdErr().writer();
117106 if (builtin.strip_debug_info) {
118107 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
119108 return;
......@@ -134,7 +123,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
134123/// TODO multithreaded awareness
135124pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
136125 nosuspend {
137 const stderr = getStderrStream();
126 const stderr = io.getStdErr().writer();
138127 if (builtin.strip_debug_info) {
139128 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
140129 return;
......@@ -204,7 +193,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace
204193/// TODO multithreaded awareness
205194pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {
206195 nosuspend {
207 const stderr = getStderrStream();
196 const stderr = io.getStdErr().writer();
208197 if (builtin.strip_debug_info) {
209198 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
210199 return;
......@@ -234,7 +223,7 @@ pub fn assert(ok: bool) void {
234223 if (!ok) unreachable; // assertion failure
235224}
236225
237pub fn panic(comptime format: []const u8, args: var) noreturn {
226pub fn panic(comptime format: []const u8, args: anytype) noreturn {
238227 @setCold(true);
239228 // TODO: remove conditional once wasi / LLVM defines __builtin_return_address
240229 const first_trace_addr = if (builtin.os.tag == .wasi) null else @returnAddress();
......@@ -252,7 +241,7 @@ var panic_mutex = std.Mutex.init();
252241/// This is used to catch and handle panics triggered by the panic handler.
253242threadlocal var panic_stage: usize = 0;
254243
255pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {
244pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: anytype) noreturn {
256245 @setCold(true);
257246
258247 if (enable_segfault_handler) {
......@@ -272,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
272261 const held = panic_mutex.acquire();
273262 defer held.release();
274263
275 const stderr = getStderrStream();
264 const stderr = io.getStdErr().writer();
276265 stderr.print(format ++ "\n", args) catch os.abort();
277266 if (trace) |t| {
278267 dumpStackTrace(t.*);
......@@ -297,7 +286,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
297286 // A panic happened while trying to print a previous panic message,
298287 // we're still holding the mutex but that's fine as we're going to
299288 // call abort()
300 const stderr = getStderrStream();
289 const stderr = io.getStdErr().writer();
301290 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
302291 },
303292 else => {
......@@ -317,7 +306,7 @@ const RESET = "\x1b[0m";
317306
318307pub fn writeStackTrace(
319308 stack_trace: builtin.StackTrace,
320 out_stream: var,
309 out_stream: anytype,
321310 allocator: *mem.Allocator,
322311 debug_info: *DebugInfo,
323312 tty_config: TTY.Config,
......@@ -395,7 +384,7 @@ pub const StackIterator = struct {
395384};
396385
397386pub fn writeCurrentStackTrace(
398 out_stream: var,
387 out_stream: anytype,
399388 debug_info: *DebugInfo,
400389 tty_config: TTY.Config,
401390 start_addr: ?usize,
......@@ -410,7 +399,7 @@ pub fn writeCurrentStackTrace(
410399}
411400
412401pub fn writeCurrentStackTraceWindows(
413 out_stream: var,
402 out_stream: anytype,
414403 debug_info: *DebugInfo,
415404 tty_config: TTY.Config,
416405 start_addr: ?usize,
......@@ -446,7 +435,7 @@ pub const TTY = struct {
446435 // TODO give this a payload of file handle
447436 windows_api,
448437
449 fn setColor(conf: Config, out_stream: var, color: Color) void {
438 fn setColor(conf: Config, out_stream: anytype, color: Color) void {
450439 nosuspend switch (conf) {
451440 .no_color => return,
452441 .escape_codes => switch (color) {
......@@ -458,6 +447,7 @@ pub const TTY = struct {
458447 .Reset => out_stream.writeAll(RESET) catch return,
459448 },
460449 .windows_api => if (builtin.os.tag == .windows) {
450 const stderr_file = io.getStdErr();
461451 const S = struct {
462452 var attrs: windows.WORD = undefined;
463453 var init_attrs = false;
......@@ -565,7 +555,7 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach
565555}
566556
567557/// TODO resources https://github.com/ziglang/zig/issues/4353
568pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
558pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
569559 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
570560 error.MissingDebugInfo, error.InvalidDebugInfo => {
571561 return printLineInfo(
......@@ -596,13 +586,13 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us
596586}
597587
598588fn printLineInfo(
599 out_stream: var,
589 out_stream: anytype,
600590 line_info: ?LineInfo,
601591 address: usize,
602592 symbol_name: []const u8,
603593 compile_unit_name: []const u8,
604594 tty_config: TTY.Config,
605 comptime printLineFromFile: var,
595 comptime printLineFromFile: anytype,
606596) !void {
607597 nosuspend {
608598 tty_config.setColor(out_stream, .White);
......@@ -830,7 +820,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
830820 }
831821}
832822
833fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
823fn readSparseBitVector(stream: anytype, allocator: *mem.Allocator) ![]usize {
834824 const num_words = try stream.readIntLittle(u32);
835825 var word_i: usize = 0;
836826 var list = ArrayList(usize).init(allocator);
......@@ -1014,7 +1004,7 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI
10141004 };
10151005}
10161006
1017fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
1007fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void {
10181008 // Need this to always block even in async I/O mode, because this could potentially
10191009 // be called from e.g. the event loop code crashing.
10201010 var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking });
......@@ -1142,7 +1132,7 @@ pub const DebugInfo = struct {
11421132 const seg_end = seg_start + segment_cmd.vmsize;
11431133
11441134 if (rebased_address >= seg_start and rebased_address < seg_end) {
1145 if (self.address_map.getValue(base_address)) |obj_di| {
1135 if (self.address_map.get(base_address)) |obj_di| {
11461136 return obj_di;
11471137 }
11481138
......@@ -1214,7 +1204,7 @@ pub const DebugInfo = struct {
12141204 const seg_end = seg_start + info.SizeOfImage;
12151205
12161206 if (address >= seg_start and address < seg_end) {
1217 if (self.address_map.getValue(seg_start)) |obj_di| {
1207 if (self.address_map.get(seg_start)) |obj_di| {
12181208 return obj_di;
12191209 }
12201210
......@@ -1288,7 +1278,7 @@ pub const DebugInfo = struct {
12881278 else => return error.MissingDebugInfo,
12891279 }
12901280
1291 if (self.address_map.getValue(ctx.base_address)) |obj_di| {
1281 if (self.address_map.get(ctx.base_address)) |obj_di| {
12921282 return obj_di;
12931283 }
12941284
......@@ -1451,7 +1441,7 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) {
14511441 const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]);
14521442
14531443 // Check if its debug infos are already in the cache
1454 var o_file_di = self.ofiles.getValue(o_file_path) orelse
1444 var o_file_di = self.ofiles.get(o_file_path) orelse
14551445 (self.loadOFile(o_file_path) catch |err| switch (err) {
14561446 error.FileNotFound,
14571447 error.MissingDebugInfo,
lib/std/debug/leb128.zig+243-109
......@@ -1,171 +1,211 @@
11const std = @import("std");
22const testing = std.testing;
33
4pub fn readULEB128(comptime T: type, in_stream: var) !T {
5 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));
4/// Read a single unsigned LEB128 value from the given reader as type T,
5/// or error.Overflow if the value cannot fit.
6pub fn readULEB128(comptime T: type, reader: anytype) !T {
7 const U = if (T.bit_count < 8) u8 else T;
8 const ShiftT = std.math.Log2Int(U);
69
7 var result: T = 0;
8 var shift: usize = 0;
10 const max_group = (U.bit_count + 6) / 7;
911
10 while (true) {
11 const byte = try in_stream.readByte();
12
13 if (shift > T.bit_count)
14 return error.Overflow;
15
16 var operand: T = undefined;
17 if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
18 return error.Overflow;
12 var value = @as(U, 0);
13 var group = @as(ShiftT, 0);
1914
20 result |= operand;
15 while (group < max_group) : (group += 1) {
16 const byte = try reader.readByte();
17 var temp = @as(U, byte & 0x7f);
2118
22 if ((byte & 0x80) == 0)
23 return result;
19 if (@shlWithOverflow(U, temp, group * 7, &temp)) return error.Overflow;
2420
25 shift += 7;
21 value |= temp;
22 if (byte & 0x80 == 0) break;
23 } else {
24 return error.Overflow;
2625 }
27}
28
29pub fn readULEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
30 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));
3126
32 var result: T = 0;
33 var shift: usize = 0;
34 var i: usize = 0;
35
36 while (true) : (i += 1) {
37 const byte = ptr.*[i];
38
39 if (shift > T.bit_count)
40 return error.Overflow;
27 // only applies in the case that we extended to u8
28 if (U != T) {
29 if (value > std.math.maxInt(T)) return error.Overflow;
30 }
4131
42 var operand: T = undefined;
43 if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
44 return error.Overflow;
32 return @truncate(T, value);
33}
4534
46 result |= operand;
35/// Write a single unsigned integer as unsigned LEB128 to the given writer.
36pub fn writeULEB128(writer: anytype, uint_value: anytype) !void {
37 const T = @TypeOf(uint_value);
38 const U = if (T.bit_count < 8) u8 else T;
39 var value = @intCast(U, uint_value);
4740
48 if ((byte & 0x80) == 0) {
49 ptr.* += i + 1;
50 return result;
41 while (true) {
42 const byte = @truncate(u8, value & 0x7f);
43 value >>= 7;
44 if (value == 0) {
45 try writer.writeByte(byte);
46 break;
47 } else {
48 try writer.writeByte(byte | 0x80);
5149 }
52
53 shift += 7;
5450 }
5551}
5652
57pub fn readILEB128(comptime T: type, in_stream: var) !T {
58 const UT = std.meta.Int(false, T.bit_count);
59 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));
53/// Read a single unsinged integer from the given memory as type T.
54/// The provided slice reference will be updated to point to the byte after the last byte read.
55pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {
56 var buf = std.io.fixedBufferStream(ptr.*);
57 const value = try readULEB128(T, buf.reader());
58 ptr.*.ptr += buf.pos;
59 return value;
60}
6061
61 var result: UT = 0;
62 var shift: usize = 0;
62/// Write a single unsigned LEB128 integer to the given memory as unsigned LEB128,
63/// returning the number of bytes written.
64pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {
65 const T = @TypeOf(uint_value);
66 const max_group = (T.bit_count + 6) / 7;
67 var buf = std.io.fixedBufferStream(ptr);
68 try writeULEB128(buf.writer(), uint_value);
69 return buf.pos;
70}
6371
64 while (true) {
65 const byte: u8 = try in_stream.readByte();
72/// Read a single signed LEB128 value from the given reader as type T,
73/// or error.Overflow if the value cannot fit.
74pub fn readILEB128(comptime T: type, reader: anytype) !T {
75 const S = if (T.bit_count < 8) i8 else T;
76 const U = std.meta.Int(false, S.bit_count);
77 const ShiftU = std.math.Log2Int(U);
6678
67 if (shift > T.bit_count)
68 return error.Overflow;
79 const max_group = (U.bit_count + 6) / 7;
6980
70 var operand: UT = undefined;
71 if (@shlWithOverflow(UT, @as(UT, byte & 0x7f), @intCast(ShiftT, shift), &operand)) {
72 if (byte != 0x7f)
73 return error.Overflow;
74 }
81 var value = @as(U, 0);
82 var group = @as(ShiftU, 0);
7583
76 result |= operand;
84 while (group < max_group) : (group += 1) {
85 const byte = try reader.readByte();
86 var temp = @as(U, byte & 0x7f);
7787
78 shift += 7;
88 const shift = group * 7;
89 if (@shlWithOverflow(U, temp, shift, &temp)) {
90 // Overflow is ok so long as the sign bit is set and this is the last byte
91 if (byte & 0x80 != 0) return error.Overflow;
92 if (@bitCast(S, temp) >= 0) return error.Overflow;
7993
80 if ((byte & 0x80) == 0) {
81 if (shift < T.bit_count and (byte & 0x40) != 0) {
82 result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);
94 // and all the overflowed bits are 1
95 const remaining_shift = @intCast(u3, U.bit_count - @as(u16, shift));
96 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
97 if (remaining_bits != -1) return error.Overflow;
98 }
99
100 value |= temp;
101 if (byte & 0x80 == 0) {
102 const needs_sign_ext = group + 1 < max_group;
103 if (byte & 0x40 != 0 and needs_sign_ext) {
104 const ones = @as(S, -1);
105 value |= @bitCast(U, ones) << (shift + 7);
83106 }
84 return @bitCast(T, result);
107 break;
85108 }
109 } else {
110 return error.Overflow;
86111 }
87}
88112
89pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
90 const UT = std.meta.Int(false, T.bit_count);
91 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));
113 const result = @bitCast(S, value);
114 // Only applies if we extended to i8
115 if (S != T) {
116 if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow;
117 }
92118
93 var result: UT = 0;
94 var shift: usize = 0;
95 var i: usize = 0;
119 return @truncate(T, result);
120}
96121
97 while (true) : (i += 1) {
98 const byte = ptr.*[i];
122/// Write a single signed integer as signed LEB128 to the given writer.
123pub fn writeILEB128(writer: anytype, int_value: anytype) !void {
124 const T = @TypeOf(int_value);
125 const S = if (T.bit_count < 8) i8 else T;
126 const U = std.meta.Int(false, S.bit_count);
99127
100 if (shift > T.bit_count)
101 return error.Overflow;
128 var value = @intCast(S, int_value);
102129
103 var operand: UT = undefined;
104 if (@shlWithOverflow(UT, @as(UT, byte & 0x7f), @intCast(ShiftT, shift), &operand)) {
105 if (byte != 0x7f)
106 return error.Overflow;
130 while (true) {
131 const uvalue = @bitCast(U, value);
132 const byte = @truncate(u8, uvalue);
133 value >>= 6;
134 if (value == -1 or value == 0) {
135 try writer.writeByte(byte & 0x7F);
136 break;
137 } else {
138 value >>= 1;
139 try writer.writeByte(byte | 0x80);
107140 }
141 }
142}
108143
109 result |= operand;
110
111 shift += 7;
144/// Read a single singed LEB128 integer from the given memory as type T.
145/// The provided slice reference will be updated to point to the byte after the last byte read.
146pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T {
147 var buf = std.io.fixedBufferStream(ptr.*);
148 const value = try readILEB128(T, buf.reader());
149 ptr.*.ptr += buf.pos;
150 return value;
151}
112152
113 if ((byte & 0x80) == 0) {
114 if (shift < T.bit_count and (byte & 0x40) != 0) {
115 result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);
116 }
117 ptr.* += i + 1;
118 return @bitCast(T, result);
119 }
120 }
153/// Write a single signed LEB128 integer to the given memory as unsigned LEB128,
154/// returning the number of bytes written.
155pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
156 const T = @TypeOf(int_value);
157 var buf = std.io.fixedBufferStream(ptr);
158 try writeILEB128(buf.writer(), int_value);
159 return buf.pos;
121160}
122161
162// tests
123163fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
124 var in_stream = std.io.fixedBufferStream(encoded);
125 return try readILEB128(T, in_stream.inStream());
164 var reader = std.io.fixedBufferStream(encoded);
165 return try readILEB128(T, reader.reader());
126166}
127167
128168fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
129 var in_stream = std.io.fixedBufferStream(encoded);
130 return try readULEB128(T, in_stream.inStream());
169 var reader = std.io.fixedBufferStream(encoded);
170 return try readULEB128(T, reader.reader());
131171}
132172
133173fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
134 var in_stream = std.io.fixedBufferStream(encoded);
135 const v1 = readILEB128(T, in_stream.inStream());
136 var in_ptr = encoded.ptr;
137 const v2 = readILEB128Mem(T, &in_ptr);
174 var reader = std.io.fixedBufferStream(encoded);
175 const v1 = try readILEB128(T, reader.reader());
176 var in_ptr = encoded;
177 const v2 = try readILEB128Mem(T, &in_ptr);
138178 testing.expectEqual(v1, v2);
139179 return v1;
140180}
141181
142182fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
143 var in_stream = std.io.fixedBufferStream(encoded);
144 const v1 = readULEB128(T, in_stream.inStream());
145 var in_ptr = encoded.ptr;
146 const v2 = readULEB128Mem(T, &in_ptr);
183 var reader = std.io.fixedBufferStream(encoded);
184 const v1 = try readULEB128(T, reader.reader());
185 var in_ptr = encoded;
186 const v2 = try readULEB128Mem(T, &in_ptr);
147187 testing.expectEqual(v1, v2);
148188 return v1;
149189}
150190
151fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {
152 var in_stream = std.io.fixedBufferStream(encoded);
153 var in_ptr = encoded.ptr;
191fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void {
192 var reader = std.io.fixedBufferStream(encoded);
193 var in_ptr = encoded;
154194 var i: usize = 0;
155195 while (i < N) : (i += 1) {
156 const v1 = readILEB128(T, in_stream.inStream());
157 const v2 = readILEB128Mem(T, &in_ptr);
196 const v1 = try readILEB128(T, reader.reader());
197 const v2 = try readILEB128Mem(T, &in_ptr);
158198 testing.expectEqual(v1, v2);
159199 }
160200}
161201
162fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {
163 var in_stream = std.io.fixedBufferStream(encoded);
164 var in_ptr = encoded.ptr;
202fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void {
203 var reader = std.io.fixedBufferStream(encoded);
204 var in_ptr = encoded;
165205 var i: usize = 0;
166206 while (i < N) : (i += 1) {
167 const v1 = readULEB128(T, in_stream.inStream());
168 const v2 = readULEB128Mem(T, &in_ptr);
207 const v1 = try readULEB128(T, reader.reader());
208 const v2 = try readULEB128Mem(T, &in_ptr);
169209 testing.expectEqual(v1, v2);
170210 }
171211}
......@@ -212,7 +252,7 @@ test "deserialize signed LEB128" {
212252 testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80);
213253
214254 // Decode sequence of SLEB128 values
215 test_read_ileb128_seq(i64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");
255 try test_read_ileb128_seq(i64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");
216256}
217257
218258test "deserialize unsigned LEB128" {
......@@ -252,5 +292,99 @@ test "deserialize unsigned LEB128" {
252292 testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80);
253293
254294 // Decode sequence of ULEB128 values
255 test_read_uleb128_seq(u64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");
295 try test_read_uleb128_seq(u64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");
296}
297
298fn test_write_leb128(value: anytype) !void {
299 const T = @TypeOf(value);
300
301 const writeStream = if (T.is_signed) writeILEB128 else writeULEB128;
302 const writeMem = if (T.is_signed) writeILEB128Mem else writeULEB128Mem;
303 const readStream = if (T.is_signed) readILEB128 else readULEB128;
304 const readMem = if (T.is_signed) readILEB128Mem else readULEB128Mem;
305
306 // decode to a larger bit size too, to ensure sign extension
307 // is working as expected
308 const larger_type_bits = ((T.bit_count + 8) / 8) * 8;
309 const B = std.meta.Int(T.is_signed, larger_type_bits);
310
311 const bytes_needed = bn: {
312 const S = std.meta.Int(T.is_signed, @sizeOf(T) * 8);
313 if (T.bit_count <= 7) break :bn @as(u16, 1);
314
315 const unused_bits = if (value < 0) @clz(T, ~value) else @clz(T, value);
316 const used_bits: u16 = (T.bit_count - unused_bits) + @boolToInt(T.is_signed);
317 if (used_bits <= 7) break :bn @as(u16, 1);
318 break :bn ((used_bits + 6) / 7);
319 };
320
321 const max_groups = if (T.bit_count == 0) 1 else (T.bit_count + 6) / 7;
322
323 var buf: [max_groups]u8 = undefined;
324 var fbs = std.io.fixedBufferStream(&buf);
325
326 // stream write
327 try writeStream(fbs.writer(), value);
328 const w1_pos = fbs.pos;
329 testing.expect(w1_pos == bytes_needed);
330
331 // stream read
332 fbs.pos = 0;
333 const sr = try readStream(T, fbs.reader());
334 testing.expect(fbs.pos == w1_pos);
335 testing.expect(sr == value);
336
337 // bigger type stream read
338 fbs.pos = 0;
339 const bsr = try readStream(B, fbs.reader());
340 testing.expect(fbs.pos == w1_pos);
341 testing.expect(bsr == value);
342
343 // mem write
344 const w2_pos = try writeMem(&buf, value);
345 testing.expect(w2_pos == w1_pos);
346
347 // mem read
348 var buf_ref: []u8 = buf[0..];
349 const mr = try readMem(T, &buf_ref);
350 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);
351 testing.expect(mr == value);
352
353 // bigger type mem read
354 buf_ref = buf[0..];
355 const bmr = try readMem(T, &buf_ref);
356 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);
357 testing.expect(bmr == value);
358}
359
360test "serialize unsigned LEB128" {
361 const max_bits = 18;
362
363 comptime var t = 0;
364 inline while (t <= max_bits) : (t += 1) {
365 const T = std.meta.Int(false, t);
366 const min = std.math.minInt(T);
367 const max = std.math.maxInt(T);
368 var i = @as(std.meta.Int(false, T.bit_count + 1), min);
369
370 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
371 }
372}
373
374test "serialize signed LEB128" {
375 // explicitly test i0 because starting `t` at 0
376 // will break the while loop
377 try test_write_leb128(@as(i0, 0));
378
379 const max_bits = 18;
380
381 comptime var t = 1;
382 inline while (t <= max_bits) : (t += 1) {
383 const T = std.meta.Int(true, t);
384 const min = std.math.minInt(T);
385 const max = std.math.maxInt(T);
386 var i = @as(std.meta.Int(true, T.bit_count + 1), min);
387
388 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
389 }
256390}
lib/std/dwarf.zig+10-10
......@@ -236,7 +236,7 @@ const LineNumberProgram = struct {
236236 }
237237};
238238
239fn readUnitLength(in_stream: var, endian: builtin.Endian, is_64: *bool) !u64 {
239fn readUnitLength(in_stream: anytype, endian: builtin.Endian, is_64: *bool) !u64 {
240240 const first_32_bits = try in_stream.readInt(u32, endian);
241241 is_64.* = (first_32_bits == 0xffffffff);
242242 if (is_64.*) {
......@@ -249,7 +249,7 @@ fn readUnitLength(in_stream: var, endian: builtin.Endian, is_64: *bool) !u64 {
249249}
250250
251251// TODO the nosuspends here are workarounds
252fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
252fn readAllocBytes(allocator: *mem.Allocator, in_stream: anytype, size: usize) ![]u8 {
253253 const buf = try allocator.alloc(u8, size);
254254 errdefer allocator.free(buf);
255255 if ((try nosuspend in_stream.read(buf)) < size) return error.EndOfFile;
......@@ -257,25 +257,25 @@ fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8
257257}
258258
259259// TODO the nosuspends here are workarounds
260fn readAddress(in_stream: var, endian: builtin.Endian, is_64: bool) !u64 {
260fn readAddress(in_stream: anytype, endian: builtin.Endian, is_64: bool) !u64 {
261261 return nosuspend if (is_64)
262262 try in_stream.readInt(u64, endian)
263263 else
264264 @as(u64, try in_stream.readInt(u32, endian));
265265}
266266
267fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
267fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: anytype, size: usize) !FormValue {
268268 const buf = try readAllocBytes(allocator, in_stream, size);
269269 return FormValue{ .Block = buf };
270270}
271271
272272// TODO the nosuspends here are workarounds
273fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, endian: builtin.Endian, size: usize) !FormValue {
273fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: anytype, endian: builtin.Endian, size: usize) !FormValue {
274274 const block_len = try nosuspend in_stream.readVarInt(usize, endian, size);
275275 return parseFormValueBlockLen(allocator, in_stream, block_len);
276276}
277277
278fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, endian: builtin.Endian, comptime size: i32) !FormValue {
278fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: anytype, signed: bool, endian: builtin.Endian, comptime size: i32) !FormValue {
279279 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
280280 // `nosuspend` should be removed from all the function calls once it is fixed.
281281 return FormValue{
......@@ -302,7 +302,7 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo
302302}
303303
304304// TODO the nosuspends here are workarounds
305fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, endian: builtin.Endian, size: i32) !FormValue {
305fn parseFormValueRef(allocator: *mem.Allocator, in_stream: anytype, endian: builtin.Endian, size: i32) !FormValue {
306306 return FormValue{
307307 .Ref = switch (size) {
308308 1 => try nosuspend in_stream.readInt(u8, endian),
......@@ -316,7 +316,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, endian: builtin.
316316}
317317
318318// TODO the nosuspends here are workarounds
319fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endian: builtin.Endian, is_64: bool) anyerror!FormValue {
319fn parseFormValue(allocator: *mem.Allocator, in_stream: anytype, form_id: u64, endian: builtin.Endian, is_64: bool) anyerror!FormValue {
320320 return switch (form_id) {
321321 FORM_addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },
322322 FORM_block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
......@@ -359,7 +359,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endia
359359 const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, endian, is_64));
360360 var frame = try allocator.create(F);
361361 defer allocator.destroy(frame);
362 return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, endian, is_64);
362 return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
363363 },
364364 else => error.InvalidDebugInfo,
365365 };
......@@ -670,7 +670,7 @@ pub const DwarfInfo = struct {
670670 }
671671 }
672672
673 fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
673 fn parseDie(di: *DwarfInfo, in_stream: anytype, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
674674 const abbrev_code = try leb.readULEB128(u64, in_stream);
675675 if (abbrev_code == 0) return null;
676676 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
lib/std/elf.zig+3-2
......@@ -517,7 +517,7 @@ pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
517517 return hdrs;
518518}
519519
520pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
520pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
521521 if (is_64) {
522522 if (need_bswap) {
523523 return @byteSwap(@TypeOf(int_64), int_64);
......@@ -529,7 +529,7 @@ pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_
529529 }
530530}
531531
532pub fn int32(need_bswap: bool, int_32: var, comptime Int64: var) Int64 {
532pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {
533533 if (need_bswap) {
534534 return @byteSwap(@TypeOf(int_32), int_32);
535535 } else {
......@@ -551,6 +551,7 @@ fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
551551 error.InputOutput => return error.FileSystem,
552552 error.Unexpected => return error.Unexpected,
553553 error.WouldBlock => return error.Unexpected,
554 error.AccessDenied => return error.Unexpected,
554555 };
555556 if (len == 0) return error.UnexpectedEndOfFile;
556557 i += len;
lib/std/event/group.zig+1-1
......@@ -65,7 +65,7 @@ pub fn Group(comptime ReturnType: type) type {
6565 /// allocated by the group and freed by `wait`.
6666 /// `func` must be async and have return type `ReturnType`.
6767 /// Thread-safe.
68 pub fn call(self: *Self, comptime func: var, args: var) error{OutOfMemory}!void {
68 pub fn call(self: *Self, comptime func: anytype, args: anytype) error{OutOfMemory}!void {
6969 var frame = try self.allocator.create(@TypeOf(@call(.{ .modifier = .async_kw }, func, args)));
7070 errdefer self.allocator.destroy(frame);
7171 const node = try self.allocator.create(AllocStack.Node);
lib/std/fmt.zig+266-219
......@@ -64,21 +64,22 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
6464/// - `e`: output floating point value in scientific notation
6565/// - `d`: output numeric value in decimal notation
6666/// - `b`: output integer value in binary notation
67/// - `o`: output integer value in octal notation
6768/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
6869/// - `*`: output the address of the value instead of the value itself.
6970///
7071/// If a formatted user type contains a function of the type
7172/// ```
72/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void
73/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void
7374/// ```
7475/// with `?` being the type formatted, this function will be called instead of the default implementation.
7576/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
7677///
7778/// A user type may be a `struct`, `vector`, `union` or `enum` type.
7879pub fn format(
79 out_stream: var,
80 writer: anytype,
8081 comptime fmt: []const u8,
81 args: var,
82 args: anytype,
8283) !void {
8384 const ArgSetType = u32;
8485 if (@typeInfo(@TypeOf(args)) != .Struct) {
......@@ -136,7 +137,7 @@ pub fn format(
136137 .Start => switch (c) {
137138 '{' => {
138139 if (start_index < i) {
139 try out_stream.writeAll(fmt[start_index..i]);
140 try writer.writeAll(fmt[start_index..i]);
140141 }
141142
142143 start_index = i;
......@@ -148,7 +149,7 @@ pub fn format(
148149 },
149150 '}' => {
150151 if (start_index < i) {
151 try out_stream.writeAll(fmt[start_index..i]);
152 try writer.writeAll(fmt[start_index..i]);
152153 }
153154 state = .CloseBrace;
154155 },
......@@ -183,7 +184,7 @@ pub fn format(
183184 args[arg_to_print],
184185 fmt[0..0],
185186 options,
186 out_stream,
187 writer,
187188 default_max_depth,
188189 );
189190
......@@ -214,7 +215,7 @@ pub fn format(
214215 args[arg_to_print],
215216 fmt[specifier_start..i],
216217 options,
217 out_stream,
218 writer,
218219 default_max_depth,
219220 );
220221 state = .Start;
......@@ -259,7 +260,7 @@ pub fn format(
259260 args[arg_to_print],
260261 fmt[specifier_start..specifier_end],
261262 options,
262 out_stream,
263 writer,
263264 default_max_depth,
264265 );
265266 state = .Start;
......@@ -285,7 +286,7 @@ pub fn format(
285286 args[arg_to_print],
286287 fmt[specifier_start..specifier_end],
287288 options,
288 out_stream,
289 writer,
289290 default_max_depth,
290291 );
291292 state = .Start;
......@@ -306,148 +307,149 @@ pub fn format(
306307 }
307308 }
308309 if (start_index < fmt.len) {
309 try out_stream.writeAll(fmt[start_index..]);
310 try writer.writeAll(fmt[start_index..]);
310311 }
311312}
312313
313314pub fn formatType(
314 value: var,
315 value: anytype,
315316 comptime fmt: []const u8,
316317 options: FormatOptions,
317 out_stream: var,
318 writer: anytype,
318319 max_depth: usize,
319) @TypeOf(out_stream).Error!void {
320) @TypeOf(writer).Error!void {
320321 if (comptime std.mem.eql(u8, fmt, "*")) {
321 try out_stream.writeAll(@typeName(@TypeOf(value).Child));
322 try out_stream.writeAll("@");
323 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream);
322 try writer.writeAll(@typeName(@TypeOf(value).Child));
323 try writer.writeAll("@");
324 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
324325 return;
325326 }
326327
327328 const T = @TypeOf(value);
328329 if (comptime std.meta.trait.hasFn("format")(T)) {
329 return try value.format(fmt, options, out_stream);
330 return try value.format(fmt, options, writer);
330331 }
331332
332333 switch (@typeInfo(T)) {
333334 .ComptimeInt, .Int, .ComptimeFloat, .Float => {
334 return formatValue(value, fmt, options, out_stream);
335 return formatValue(value, fmt, options, writer);
335336 },
336337 .Void => {
337 return formatBuf("void", options, out_stream);
338 return formatBuf("void", options, writer);
338339 },
339340 .Bool => {
340 return formatBuf(if (value) "true" else "false", options, out_stream);
341 return formatBuf(if (value) "true" else "false", options, writer);
341342 },
342343 .Optional => {
343344 if (value) |payload| {
344 return formatType(payload, fmt, options, out_stream, max_depth);
345 return formatType(payload, fmt, options, writer, max_depth);
345346 } else {
346 return formatBuf("null", options, out_stream);
347 return formatBuf("null", options, writer);
347348 }
348349 },
349350 .ErrorUnion => {
350351 if (value) |payload| {
351 return formatType(payload, fmt, options, out_stream, max_depth);
352 return formatType(payload, fmt, options, writer, max_depth);
352353 } else |err| {
353 return formatType(err, fmt, options, out_stream, max_depth);
354 return formatType(err, fmt, options, writer, max_depth);
354355 }
355356 },
356357 .ErrorSet => {
357 try out_stream.writeAll("error.");
358 return out_stream.writeAll(@errorName(value));
358 try writer.writeAll("error.");
359 return writer.writeAll(@errorName(value));
359360 },
360361 .Enum => |enumInfo| {
361 try out_stream.writeAll(@typeName(T));
362 try writer.writeAll(@typeName(T));
362363 if (enumInfo.is_exhaustive) {
363 try out_stream.writeAll(".");
364 try out_stream.writeAll(@tagName(value));
364 try writer.writeAll(".");
365 try writer.writeAll(@tagName(value));
365366 return;
366367 }
367368
368369 // Use @tagName only if value is one of known fields
370 @setEvalBranchQuota(3 * enumInfo.fields.len);
369371 inline for (enumInfo.fields) |enumField| {
370372 if (@enumToInt(value) == enumField.value) {
371 try out_stream.writeAll(".");
372 try out_stream.writeAll(@tagName(value));
373 try writer.writeAll(".");
374 try writer.writeAll(@tagName(value));
373375 return;
374376 }
375377 }
376378
377 try out_stream.writeAll("(");
378 try formatType(@enumToInt(value), fmt, options, out_stream, max_depth);
379 try out_stream.writeAll(")");
379 try writer.writeAll("(");
380 try formatType(@enumToInt(value), fmt, options, writer, max_depth);
381 try writer.writeAll(")");
380382 },
381383 .Union => {
382 try out_stream.writeAll(@typeName(T));
384 try writer.writeAll(@typeName(T));
383385 if (max_depth == 0) {
384 return out_stream.writeAll("{ ... }");
386 return writer.writeAll("{ ... }");
385387 }
386388 const info = @typeInfo(T).Union;
387389 if (info.tag_type) |UnionTagType| {
388 try out_stream.writeAll("{ .");
389 try out_stream.writeAll(@tagName(@as(UnionTagType, value)));
390 try out_stream.writeAll(" = ");
390 try writer.writeAll("{ .");
391 try writer.writeAll(@tagName(@as(UnionTagType, value)));
392 try writer.writeAll(" = ");
391393 inline for (info.fields) |u_field| {
392394 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
393 try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1);
395 try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
394396 }
395397 }
396 try out_stream.writeAll(" }");
398 try writer.writeAll(" }");
397399 } else {
398 try format(out_stream, "@{x}", .{@ptrToInt(&value)});
400 try format(writer, "@{x}", .{@ptrToInt(&value)});
399401 }
400402 },
401403 .Struct => |StructT| {
402 try out_stream.writeAll(@typeName(T));
404 try writer.writeAll(@typeName(T));
403405 if (max_depth == 0) {
404 return out_stream.writeAll("{ ... }");
406 return writer.writeAll("{ ... }");
405407 }
406 try out_stream.writeAll("{");
408 try writer.writeAll("{");
407409 inline for (StructT.fields) |f, i| {
408410 if (i == 0) {
409 try out_stream.writeAll(" .");
411 try writer.writeAll(" .");
410412 } else {
411 try out_stream.writeAll(", .");
413 try writer.writeAll(", .");
412414 }
413 try out_stream.writeAll(f.name);
414 try out_stream.writeAll(" = ");
415 try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1);
415 try writer.writeAll(f.name);
416 try writer.writeAll(" = ");
417 try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1);
416418 }
417 try out_stream.writeAll(" }");
419 try writer.writeAll(" }");
418420 },
419421 .Pointer => |ptr_info| switch (ptr_info.size) {
420422 .One => switch (@typeInfo(ptr_info.child)) {
421423 .Array => |info| {
422424 if (info.child == u8) {
423 return formatText(value, fmt, options, out_stream);
425 return formatText(value, fmt, options, writer);
424426 }
425 return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
427 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
426428 },
427429 .Enum, .Union, .Struct => {
428 return formatType(value.*, fmt, options, out_stream, max_depth);
430 return formatType(value.*, fmt, options, writer, max_depth);
429431 },
430 else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
432 else => return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
431433 },
432434 .Many, .C => {
433435 if (ptr_info.sentinel) |sentinel| {
434 return formatType(mem.span(value), fmt, options, out_stream, max_depth);
436 return formatType(mem.span(value), fmt, options, writer, max_depth);
435437 }
436438 if (ptr_info.child == u8) {
437439 if (fmt.len > 0 and fmt[0] == 's') {
438 return formatText(mem.span(value), fmt, options, out_stream);
440 return formatText(mem.span(value), fmt, options, writer);
439441 }
440442 }
441 return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
443 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
442444 },
443445 .Slice => {
444446 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
445 return formatText(value, fmt, options, out_stream);
447 return formatText(value, fmt, options, writer);
446448 }
447449 if (ptr_info.child == u8) {
448 return formatText(value, fmt, options, out_stream);
450 return formatText(value, fmt, options, writer);
449451 }
450 return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
452 return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
451453 },
452454 },
453455 .Array => |info| {
......@@ -462,58 +464,58 @@ pub fn formatType(
462464 .sentinel = null,
463465 },
464466 });
465 return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth);
467 return formatType(@as(Slice, &value), fmt, options, writer, max_depth);
466468 },
467469 .Vector => {
468470 const len = @typeInfo(T).Vector.len;
469 try out_stream.writeAll("{ ");
471 try writer.writeAll("{ ");
470472 var i: usize = 0;
471473 while (i < len) : (i += 1) {
472 try formatValue(value[i], fmt, options, out_stream);
474 try formatValue(value[i], fmt, options, writer);
473475 if (i < len - 1) {
474 try out_stream.writeAll(", ");
476 try writer.writeAll(", ");
475477 }
476478 }
477 try out_stream.writeAll(" }");
479 try writer.writeAll(" }");
478480 },
479481 .Fn => {
480 return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
482 return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
481483 },
482 .Type => return out_stream.writeAll(@typeName(T)),
484 .Type => return writer.writeAll(@typeName(T)),
483485 .EnumLiteral => {
484486 const buffer = [_]u8{'.'} ++ @tagName(value);
485 return formatType(buffer, fmt, options, out_stream, max_depth);
487 return formatType(buffer, fmt, options, writer, max_depth);
486488 },
487489 else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
488490 }
489491}
490492
491493fn formatValue(
492 value: var,
494 value: anytype,
493495 comptime fmt: []const u8,
494496 options: FormatOptions,
495 out_stream: var,
497 writer: anytype,
496498) !void {
497499 if (comptime std.mem.eql(u8, fmt, "B")) {
498 return formatBytes(value, options, 1000, out_stream);
500 return formatBytes(value, options, 1000, writer);
499501 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {
500 return formatBytes(value, options, 1024, out_stream);
502 return formatBytes(value, options, 1024, writer);
501503 }
502504
503505 const T = @TypeOf(value);
504506 switch (@typeInfo(T)) {
505 .Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, out_stream),
506 .Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream),
507 .Bool => return formatBuf(if (value) "true" else "false", options, out_stream),
507 .Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, writer),
508 .Int, .ComptimeInt => return formatIntValue(value, fmt, options, writer),
509 .Bool => return formatBuf(if (value) "true" else "false", options, writer),
508510 else => comptime unreachable,
509511 }
510512}
511513
512514pub fn formatIntValue(
513 value: var,
515 value: anytype,
514516 comptime fmt: []const u8,
515517 options: FormatOptions,
516 out_stream: var,
518 writer: anytype,
517519) !void {
518520 comptime var radix = 10;
519521 comptime var uppercase = false;
......@@ -529,7 +531,7 @@ pub fn formatIntValue(
529531 uppercase = false;
530532 } else if (comptime std.mem.eql(u8, fmt, "c")) {
531533 if (@TypeOf(int_value).bit_count <= 8) {
532 return formatAsciiChar(@as(u8, int_value), options, out_stream);
534 return formatAsciiChar(@as(u8, int_value), options, writer);
533535 } else {
534536 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
535537 }
......@@ -542,23 +544,26 @@ pub fn formatIntValue(
542544 } else if (comptime std.mem.eql(u8, fmt, "X")) {
543545 radix = 16;
544546 uppercase = true;
547 } else if (comptime std.mem.eql(u8, fmt, "o")) {
548 radix = 8;
549 uppercase = false;
545550 } else {
546551 @compileError("Unknown format string: '" ++ fmt ++ "'");
547552 }
548553
549 return formatInt(int_value, radix, uppercase, options, out_stream);
554 return formatInt(int_value, radix, uppercase, options, writer);
550555}
551556
552557fn formatFloatValue(
553 value: var,
558 value: anytype,
554559 comptime fmt: []const u8,
555560 options: FormatOptions,
556 out_stream: var,
561 writer: anytype,
557562) !void {
558563 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
559 return formatFloatScientific(value, options, out_stream);
564 return formatFloatScientific(value, options, writer);
560565 } else if (comptime std.mem.eql(u8, fmt, "d")) {
561 return formatFloatDecimal(value, options, out_stream);
566 return formatFloatDecimal(value, options, writer);
562567 } else {
563568 @compileError("Unknown format string: '" ++ fmt ++ "'");
564569 }
......@@ -568,13 +573,13 @@ pub fn formatText(
568573 bytes: []const u8,
569574 comptime fmt: []const u8,
570575 options: FormatOptions,
571 out_stream: var,
576 writer: anytype,
572577) !void {
573578 if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) {
574 return formatBuf(bytes, options, out_stream);
579 return formatBuf(bytes, options, writer);
575580 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
576581 for (bytes) |c| {
577 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream);
582 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);
578583 }
579584 return;
580585 } else {
......@@ -585,38 +590,38 @@ pub fn formatText(
585590pub fn formatAsciiChar(
586591 c: u8,
587592 options: FormatOptions,
588 out_stream: var,
593 writer: anytype,
589594) !void {
590 return out_stream.writeAll(@as(*const [1]u8, &c));
595 return writer.writeAll(@as(*const [1]u8, &c));
591596}
592597
593598pub fn formatBuf(
594599 buf: []const u8,
595600 options: FormatOptions,
596 out_stream: var,
601 writer: anytype,
597602) !void {
598603 const width = options.width orelse buf.len;
599604 var padding = if (width > buf.len) (width - buf.len) else 0;
600605 const pad_byte = [1]u8{options.fill};
601606 switch (options.alignment) {
602607 .Left => {
603 try out_stream.writeAll(buf);
608 try writer.writeAll(buf);
604609 while (padding > 0) : (padding -= 1) {
605 try out_stream.writeAll(&pad_byte);
610 try writer.writeAll(&pad_byte);
606611 }
607612 },
608613 .Center => {
609614 const padl = padding / 2;
610615 var i: usize = 0;
611 while (i < padl) : (i += 1) try out_stream.writeAll(&pad_byte);
612 try out_stream.writeAll(buf);
613 while (i < padding) : (i += 1) try out_stream.writeAll(&pad_byte);
616 while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);
617 try writer.writeAll(buf);
618 while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);
614619 },
615620 .Right => {
616621 while (padding > 0) : (padding -= 1) {
617 try out_stream.writeAll(&pad_byte);
622 try writer.writeAll(&pad_byte);
618623 }
619 try out_stream.writeAll(buf);
624 try writer.writeAll(buf);
620625 },
621626 }
622627}
......@@ -625,40 +630,40 @@ pub fn formatBuf(
625630// It should be the case that every full precision, printed value can be re-parsed back to the
626631// same type unambiguously.
627632pub fn formatFloatScientific(
628 value: var,
633 value: anytype,
629634 options: FormatOptions,
630 out_stream: var,
635 writer: anytype,
631636) !void {
632637 var x = @floatCast(f64, value);
633638
634639 // Errol doesn't handle these special cases.
635640 if (math.signbit(x)) {
636 try out_stream.writeAll("-");
641 try writer.writeAll("-");
637642 x = -x;
638643 }
639644
640645 if (math.isNan(x)) {
641 return out_stream.writeAll("nan");
646 return writer.writeAll("nan");
642647 }
643648 if (math.isPositiveInf(x)) {
644 return out_stream.writeAll("inf");
649 return writer.writeAll("inf");
645650 }
646651 if (x == 0.0) {
647 try out_stream.writeAll("0");
652 try writer.writeAll("0");
648653
649654 if (options.precision) |precision| {
650655 if (precision != 0) {
651 try out_stream.writeAll(".");
656 try writer.writeAll(".");
652657 var i: usize = 0;
653658 while (i < precision) : (i += 1) {
654 try out_stream.writeAll("0");
659 try writer.writeAll("0");
655660 }
656661 }
657662 } else {
658 try out_stream.writeAll(".0");
663 try writer.writeAll(".0");
659664 }
660665
661 try out_stream.writeAll("e+00");
666 try writer.writeAll("e+00");
662667 return;
663668 }
664669
......@@ -668,86 +673,86 @@ pub fn formatFloatScientific(
668673 if (options.precision) |precision| {
669674 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
670675
671 try out_stream.writeAll(float_decimal.digits[0..1]);
676 try writer.writeAll(float_decimal.digits[0..1]);
672677
673678 // {e0} case prints no `.`
674679 if (precision != 0) {
675 try out_stream.writeAll(".");
680 try writer.writeAll(".");
676681
677682 var printed: usize = 0;
678683 if (float_decimal.digits.len > 1) {
679684 const num_digits = math.min(float_decimal.digits.len, precision + 1);
680 try out_stream.writeAll(float_decimal.digits[1..num_digits]);
685 try writer.writeAll(float_decimal.digits[1..num_digits]);
681686 printed += num_digits - 1;
682687 }
683688
684689 while (printed < precision) : (printed += 1) {
685 try out_stream.writeAll("0");
690 try writer.writeAll("0");
686691 }
687692 }
688693 } else {
689 try out_stream.writeAll(float_decimal.digits[0..1]);
690 try out_stream.writeAll(".");
694 try writer.writeAll(float_decimal.digits[0..1]);
695 try writer.writeAll(".");
691696 if (float_decimal.digits.len > 1) {
692697 const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len;
693698
694 try out_stream.writeAll(float_decimal.digits[1..num_digits]);
699 try writer.writeAll(float_decimal.digits[1..num_digits]);
695700 } else {
696 try out_stream.writeAll("0");
701 try writer.writeAll("0");
697702 }
698703 }
699704
700 try out_stream.writeAll("e");
705 try writer.writeAll("e");
701706 const exp = float_decimal.exp - 1;
702707
703708 if (exp >= 0) {
704 try out_stream.writeAll("+");
709 try writer.writeAll("+");
705710 if (exp > -10 and exp < 10) {
706 try out_stream.writeAll("0");
711 try writer.writeAll("0");
707712 }
708 try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream);
713 try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, writer);
709714 } else {
710 try out_stream.writeAll("-");
715 try writer.writeAll("-");
711716 if (exp > -10 and exp < 10) {
712 try out_stream.writeAll("0");
717 try writer.writeAll("0");
713718 }
714 try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream);
719 try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, writer);
715720 }
716721}
717722
718723// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
719724// By default floats are printed at full precision (no rounding).
720725pub fn formatFloatDecimal(
721 value: var,
726 value: anytype,
722727 options: FormatOptions,
723 out_stream: var,
728 writer: anytype,
724729) !void {
725730 var x = @as(f64, value);
726731
727732 // Errol doesn't handle these special cases.
728733 if (math.signbit(x)) {
729 try out_stream.writeAll("-");
734 try writer.writeAll("-");
730735 x = -x;
731736 }
732737
733738 if (math.isNan(x)) {
734 return out_stream.writeAll("nan");
739 return writer.writeAll("nan");
735740 }
736741 if (math.isPositiveInf(x)) {
737 return out_stream.writeAll("inf");
742 return writer.writeAll("inf");
738743 }
739744 if (x == 0.0) {
740 try out_stream.writeAll("0");
745 try writer.writeAll("0");
741746
742747 if (options.precision) |precision| {
743748 if (precision != 0) {
744 try out_stream.writeAll(".");
749 try writer.writeAll(".");
745750 var i: usize = 0;
746751 while (i < precision) : (i += 1) {
747 try out_stream.writeAll("0");
752 try writer.writeAll("0");
748753 }
749754 } else {
750 try out_stream.writeAll(".0");
755 try writer.writeAll(".0");
751756 }
752757 }
753758
......@@ -769,14 +774,14 @@ pub fn formatFloatDecimal(
769774
770775 if (num_digits_whole > 0) {
771776 // We may have to zero pad, for instance 1e4 requires zero padding.
772 try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
777 try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
773778
774779 var i = num_digits_whole_no_pad;
775780 while (i < num_digits_whole) : (i += 1) {
776 try out_stream.writeAll("0");
781 try writer.writeAll("0");
777782 }
778783 } else {
779 try out_stream.writeAll("0");
784 try writer.writeAll("0");
780785 }
781786
782787 // {.0} special case doesn't want a trailing '.'
......@@ -784,7 +789,7 @@ pub fn formatFloatDecimal(
784789 return;
785790 }
786791
787 try out_stream.writeAll(".");
792 try writer.writeAll(".");
788793
789794 // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
790795 var printed: usize = 0;
......@@ -796,7 +801,7 @@ pub fn formatFloatDecimal(
796801
797802 var i: usize = 0;
798803 while (i < zeros_to_print) : (i += 1) {
799 try out_stream.writeAll("0");
804 try writer.writeAll("0");
800805 printed += 1;
801806 }
802807
......@@ -808,14 +813,14 @@ pub fn formatFloatDecimal(
808813 // Remaining fractional portion, zero-padding if insufficient.
809814 assert(precision >= printed);
810815 if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
811 try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
816 try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
812817 return;
813818 } else {
814 try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
819 try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
815820 printed += float_decimal.digits.len - num_digits_whole_no_pad;
816821
817822 while (printed < precision) : (printed += 1) {
818 try out_stream.writeAll("0");
823 try writer.writeAll("0");
819824 }
820825 }
821826 } else {
......@@ -827,14 +832,14 @@ pub fn formatFloatDecimal(
827832
828833 if (num_digits_whole > 0) {
829834 // We may have to zero pad, for instance 1e4 requires zero padding.
830 try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
835 try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
831836
832837 var i = num_digits_whole_no_pad;
833838 while (i < num_digits_whole) : (i += 1) {
834 try out_stream.writeAll("0");
839 try writer.writeAll("0");
835840 }
836841 } else {
837 try out_stream.writeAll("0");
842 try writer.writeAll("0");
838843 }
839844
840845 // Omit `.` if no fractional portion
......@@ -842,7 +847,7 @@ pub fn formatFloatDecimal(
842847 return;
843848 }
844849
845 try out_stream.writeAll(".");
850 try writer.writeAll(".");
846851
847852 // Zero-fill until we reach significant digits or run out of precision.
848853 if (float_decimal.exp < 0) {
......@@ -850,22 +855,22 @@ pub fn formatFloatDecimal(
850855
851856 var i: usize = 0;
852857 while (i < zero_digit_count) : (i += 1) {
853 try out_stream.writeAll("0");
858 try writer.writeAll("0");
854859 }
855860 }
856861
857 try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
862 try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
858863 }
859864}
860865
861866pub fn formatBytes(
862 value: var,
867 value: anytype,
863868 options: FormatOptions,
864869 comptime radix: usize,
865 out_stream: var,
870 writer: anytype,
866871) !void {
867872 if (value == 0) {
868 return out_stream.writeAll("0B");
873 return writer.writeAll("0B");
869874 }
870875
871876 const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value));
......@@ -885,10 +890,10 @@ pub fn formatBytes(
885890 else => unreachable,
886891 };
887892
888 try formatFloatDecimal(new_value, options, out_stream);
893 try formatFloatDecimal(new_value, options, writer);
889894
890895 if (suffix == ' ') {
891 return out_stream.writeAll("B");
896 return writer.writeAll("B");
892897 }
893898
894899 const buf = switch (radix) {
......@@ -896,15 +901,15 @@ pub fn formatBytes(
896901 1024 => &[_]u8{ suffix, 'i', 'B' },
897902 else => unreachable,
898903 };
899 return out_stream.writeAll(buf);
904 return writer.writeAll(buf);
900905}
901906
902907pub fn formatInt(
903 value: var,
908 value: anytype,
904909 base: u8,
905910 uppercase: bool,
906911 options: FormatOptions,
907 out_stream: var,
912 writer: anytype,
908913) !void {
909914 const int_value = if (@TypeOf(value) == comptime_int) blk: {
910915 const Int = math.IntFittingRange(value, value);
......@@ -913,18 +918,18 @@ pub fn formatInt(
913918 value;
914919
915920 if (@TypeOf(int_value).is_signed) {
916 return formatIntSigned(int_value, base, uppercase, options, out_stream);
921 return formatIntSigned(int_value, base, uppercase, options, writer);
917922 } else {
918 return formatIntUnsigned(int_value, base, uppercase, options, out_stream);
923 return formatIntUnsigned(int_value, base, uppercase, options, writer);
919924 }
920925}
921926
922927fn formatIntSigned(
923 value: var,
928 value: anytype,
924929 base: u8,
925930 uppercase: bool,
926931 options: FormatOptions,
927 out_stream: var,
932 writer: anytype,
928933) !void {
929934 const new_options = FormatOptions{
930935 .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
......@@ -934,24 +939,24 @@ fn formatIntSigned(
934939 const bit_count = @typeInfo(@TypeOf(value)).Int.bits;
935940 const Uint = std.meta.Int(false, bit_count);
936941 if (value < 0) {
937 try out_stream.writeAll("-");
942 try writer.writeAll("-");
938943 const new_value = math.absCast(value);
939 return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);
944 return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
940945 } else if (options.width == null or options.width.? == 0) {
941 return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream);
946 return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer);
942947 } else {
943 try out_stream.writeAll("+");
948 try writer.writeAll("+");
944949 const new_value = @intCast(Uint, value);
945 return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);
950 return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
946951 }
947952}
948953
949954fn formatIntUnsigned(
950 value: var,
955 value: anytype,
951956 base: u8,
952957 uppercase: bool,
953958 options: FormatOptions,
954 out_stream: var,
959 writer: anytype,
955960) !void {
956961 assert(base >= 2);
957962 var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;
......@@ -976,68 +981,96 @@ fn formatIntUnsigned(
976981 const zero_byte: u8 = options.fill;
977982 var leftover_padding = padding - index;
978983 while (true) {
979 try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
984 try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
980985 leftover_padding -= 1;
981986 if (leftover_padding == 0) break;
982987 }
983988 mem.set(u8, buf[0..index], options.fill);
984 return out_stream.writeAll(&buf);
989 return writer.writeAll(&buf);
985990 } else {
986991 const padded_buf = buf[index - padding ..];
987992 mem.set(u8, padded_buf[0..padding], options.fill);
988 return out_stream.writeAll(padded_buf);
993 return writer.writeAll(padded_buf);
989994 }
990995}
991996
992pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize {
997pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize {
993998 var fbs = std.io.fixedBufferStream(out_buf);
994 formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable;
999 formatInt(value, base, uppercase, options, fbs.writer()) catch unreachable;
9951000 return fbs.pos;
9961001}
9971002
998pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {
999 if (!T.is_signed) return parseUnsigned(T, buf, radix);
1000 if (buf.len == 0) return @as(T, 0);
1001 if (buf[0] == '-') {
1002 return math.negate(try parseUnsigned(T, buf[1..], radix));
1003 } else if (buf[0] == '+') {
1004 return parseUnsigned(T, buf[1..], radix);
1005 } else {
1006 return parseUnsigned(T, buf, radix);
1007 }
1008}
1009
1010test "parseInt" {
1011 std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10);
1012 std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10);
1013 std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
1014 std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter);
1015 std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter);
1016 std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255);
1017 std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
1018}
1019
1020pub const ParseUnsignedError = error{
1003pub const ParseIntError = error{
10211004 /// The result cannot fit in the type specified
10221005 Overflow,
10231006
1024 /// The input had a byte that was not a digit
1007 /// The input was empty or had a byte that was not a digit
10251008 InvalidCharacter,
10261009};
10271010
1028pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T {
1011pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T {
1012 if (buf.len == 0) return error.InvalidCharacter;
1013 if (buf[0] == '+') return parseWithSign(T, buf[1..], radix, .Pos);
1014 if (buf[0] == '-') return parseWithSign(T, buf[1..], radix, .Neg);
1015 return parseWithSign(T, buf, radix, .Pos);
1016}
1017
1018test "parseInt" {
1019 std.testing.expect((try parseInt(i32, "-10", 10)) == -10);
1020 std.testing.expect((try parseInt(i32, "+10", 10)) == 10);
1021 std.testing.expect((try parseInt(u32, "+10", 10)) == 10);
1022 std.testing.expectError(error.Overflow, parseInt(u32, "-10", 10));
1023 std.testing.expectError(error.InvalidCharacter, parseInt(u32, " 10", 10));
1024 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "10 ", 10));
1025 std.testing.expect((try parseInt(u8, "255", 10)) == 255);
1026 std.testing.expectError(error.Overflow, parseInt(u8, "256", 10));
1027
1028 // +0 and -0 should work for unsigned
1029 std.testing.expect((try parseInt(u8, "-0", 10)) == 0);
1030 std.testing.expect((try parseInt(u8, "+0", 10)) == 0);
1031
1032 // ensure minInt is parsed correctly
1033 std.testing.expect((try parseInt(i8, "-128", 10)) == math.minInt(i8));
1034 std.testing.expect((try parseInt(i43, "-4398046511104", 10)) == math.minInt(i43));
1035
1036 // empty string or bare +- is invalid
1037 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "", 10));
1038 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "", 10));
1039 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "+", 10));
1040 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "+", 10));
1041 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "-", 10));
1042 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "-", 10));
1043}
1044
1045fn parseWithSign(
1046 comptime T: type,
1047 buf: []const u8,
1048 radix: u8,
1049 comptime sign: enum { Pos, Neg },
1050) ParseIntError!T {
1051 if (buf.len == 0) return error.InvalidCharacter;
1052
1053 const add = switch (sign) {
1054 .Pos => math.add,
1055 .Neg => math.sub,
1056 };
1057
10291058 var x: T = 0;
10301059
10311060 for (buf) |c| {
10321061 const digit = try charToDigit(c, radix);
10331062
10341063 if (x != 0) x = try math.mul(T, x, try math.cast(T, radix));
1035 x = try math.add(T, x, try math.cast(T, digit));
1064 x = try add(T, x, try math.cast(T, digit));
10361065 }
10371066
10381067 return x;
10391068}
10401069
1070pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T {
1071 return parseWithSign(T, buf, radix, .Pos);
1072}
1073
10411074test "parseUnsigned" {
10421075 std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
10431076 std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
......@@ -1063,6 +1096,13 @@ test "parseUnsigned" {
10631096 std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1);
10641097 std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3);
10651098 std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16));
1099
1100 // parseUnsigned does not expect a sign
1101 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "+0", 10));
1102 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "-0", 10));
1103
1104 // test empty string error
1105 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "", 10));
10661106}
10671107
10681108pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
......@@ -1096,22 +1136,22 @@ pub const BufPrintError = error{
10961136 /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes.
10971137 NoSpaceLeft,
10981138};
1099pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 {
1139pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
11001140 var fbs = std.io.fixedBufferStream(buf);
1101 try format(fbs.outStream(), fmt, args);
1141 try format(fbs.writer(), fmt, args);
11021142 return fbs.getWritten();
11031143}
11041144
11051145// Count the characters needed for format. Useful for preallocating memory
1106pub fn count(comptime fmt: []const u8, args: var) u64 {
1107 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
1108 format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {};
1109 return counting_stream.bytes_written;
1146pub fn count(comptime fmt: []const u8, args: anytype) u64 {
1147 var counting_writer = std.io.countingWriter(std.io.null_writer);
1148 format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
1149 return counting_writer.bytes_written;
11101150}
11111151
11121152pub const AllocPrintError = error{OutOfMemory};
11131153
1114pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 {
1154pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![]u8 {
11151155 const size = math.cast(usize, count(fmt, args)) catch |err| switch (err) {
11161156 // Output too long. Can't possibly allocate enough memory to display it.
11171157 error.Overflow => return error.OutOfMemory,
......@@ -1122,7 +1162,7 @@ pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var
11221162 };
11231163}
11241164
1125pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 {
1165pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![:0]u8 {
11261166 const result = try allocPrint(allocator, fmt ++ "\x00", args);
11271167 return result[0 .. result.len - 1 :0];
11281168}
......@@ -1148,7 +1188,7 @@ test "bufPrintInt" {
11481188 std.testing.expectEqualSlices(u8, "-42", bufPrintIntToSlice(buf, @as(i32, -42), 10, false, FormatOptions{ .width = 3 }));
11491189}
11501190
1151fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 {
1191fn bufPrintIntToSlice(buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) []u8 {
11521192 return buf[0..formatIntBuf(buf, value, base, uppercase, options)];
11531193}
11541194
......@@ -1204,6 +1244,10 @@ test "int.specifier" {
12041244 const value: u8 = 0b1100;
12051245 try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value});
12061246 }
1247 {
1248 const value: u16 = 0o1234;
1249 try testFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value});
1250 }
12071251}
12081252
12091253test "int.padded" {
......@@ -1215,15 +1259,15 @@ test "buffer" {
12151259 {
12161260 var buf1: [32]u8 = undefined;
12171261 var fbs = std.io.fixedBufferStream(&buf1);
1218 try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth);
1262 try formatType(1234, "", FormatOptions{}, fbs.writer(), default_max_depth);
12191263 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));
12201264
12211265 fbs.reset();
1222 try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth);
1266 try formatType('a', "c", FormatOptions{}, fbs.writer(), default_max_depth);
12231267 std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));
12241268
12251269 fbs.reset();
1226 try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth);
1270 try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), default_max_depth);
12271271 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));
12281272 }
12291273}
......@@ -1321,6 +1365,9 @@ test "enum" {
13211365 try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value});
13221366 try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
13231367 try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
1368
1369 // test very large enum to verify ct branch quota is large enough
1370 try testFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
13241371}
13251372
13261373test "non-exhaustive enum" {
......@@ -1413,12 +1460,12 @@ test "custom" {
14131460 self: SelfType,
14141461 comptime fmt: []const u8,
14151462 options: FormatOptions,
1416 out_stream: var,
1463 writer: anytype,
14171464 ) !void {
14181465 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
1419 return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });
1466 return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
14201467 } else if (comptime std.mem.eql(u8, fmt, "d")) {
1421 return std.fmt.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y });
1468 return std.fmt.format(writer, "{d:.3}x{d:.3}", .{ self.x, self.y });
14221469 } else {
14231470 @compileError("Unknown format character: '" ++ fmt ++ "'");
14241471 }
......@@ -1534,7 +1581,7 @@ test "bytes.hex" {
15341581 try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
15351582}
15361583
1537fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void {
1584fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
15381585 var buf: [100]u8 = undefined;
15391586 const result = try bufPrint(buf[0..], template, args);
15401587 if (mem.eql(u8, result, expected)) return;
......@@ -1604,7 +1651,7 @@ test "formatIntValue with comptime_int" {
16041651
16051652 var buf: [20]u8 = undefined;
16061653 var fbs = std.io.fixedBufferStream(&buf);
1607 try formatIntValue(value, "", FormatOptions{}, fbs.outStream());
1654 try formatIntValue(value, "", FormatOptions{}, fbs.writer());
16081655 std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789"));
16091656}
16101657
......@@ -1613,7 +1660,7 @@ test "formatFloatValue with comptime_float" {
16131660
16141661 var buf: [20]u8 = undefined;
16151662 var fbs = std.io.fixedBufferStream(&buf);
1616 try formatFloatValue(value, "", FormatOptions{}, fbs.outStream());
1663 try formatFloatValue(value, "", FormatOptions{}, fbs.writer());
16171664 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00"));
16181665
16191666 try testFmt("1.0e+00", "{}", .{value});
......@@ -1630,10 +1677,10 @@ test "formatType max_depth" {
16301677 self: SelfType,
16311678 comptime fmt: []const u8,
16321679 options: FormatOptions,
1633 out_stream: var,
1680 writer: anytype,
16341681 ) !void {
16351682 if (fmt.len == 0) {
1636 return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });
1683 return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
16371684 } else {
16381685 @compileError("Unknown format string: '" ++ fmt ++ "'");
16391686 }
......@@ -1669,19 +1716,19 @@ test "formatType max_depth" {
16691716
16701717 var buf: [1000]u8 = undefined;
16711718 var fbs = std.io.fixedBufferStream(&buf);
1672 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0);
1719 try formatType(inst, "", FormatOptions{}, fbs.writer(), 0);
16731720 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }"));
16741721
16751722 fbs.reset();
1676 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1);
1723 try formatType(inst, "", FormatOptions{}, fbs.writer(), 1);
16771724 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
16781725
16791726 fbs.reset();
1680 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2);
1727 try formatType(inst, "", FormatOptions{}, fbs.writer(), 2);
16811728 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
16821729
16831730 fbs.reset();
1684 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3);
1731 try formatType(inst, "", FormatOptions{}, fbs.writer(), 3);
16851732 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
16861733}
16871734
lib/std/fs.zig+55-36
......@@ -261,17 +261,7 @@ pub const Dir = struct {
261261 name: []const u8,
262262 kind: Kind,
263263
264 pub const Kind = enum {
265 BlockDevice,
266 CharacterDevice,
267 Directory,
268 NamedPipe,
269 SymLink,
270 File,
271 UnixDomainSocket,
272 Whiteout,
273 Unknown,
274 };
264 pub const Kind = File.Kind;
275265 };
276266
277267 const IteratorError = error{AccessDenied} || os.UnexpectedError;
......@@ -463,6 +453,8 @@ pub const Dir = struct {
463453
464454 pub const Error = IteratorError;
465455
456 /// Memory such as file names referenced in this returned entry becomes invalid
457 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
466458 pub fn next(self: *Self) Error!?Entry {
467459 start_over: while (true) {
468460 const w = os.windows;
......@@ -545,14 +537,15 @@ pub const Dir = struct {
545537 w.EFAULT => unreachable,
546538 w.ENOTDIR => unreachable,
547539 w.EINVAL => unreachable,
540 w.ENOTCAPABLE => return error.AccessDenied,
548541 else => |err| return os.unexpectedErrno(err),
549542 }
550543 if (bufused == 0) return null;
551544 self.index = 0;
552545 self.end_index = bufused;
553546 }
554 const entry = @ptrCast(*align(1) os.wasi.dirent_t, &self.buf[self.index]);
555 const entry_size = @sizeOf(os.wasi.dirent_t);
547 const entry = @ptrCast(*align(1) w.dirent_t, &self.buf[self.index]);
548 const entry_size = @sizeOf(w.dirent_t);
556549 const name_index = self.index + entry_size;
557550 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);
558551
......@@ -566,12 +559,12 @@ pub const Dir = struct {
566559 }
567560
568561 const entry_kind = switch (entry.d_type) {
569 wasi.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice,
570 wasi.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice,
571 wasi.FILETYPE_DIRECTORY => Entry.Kind.Directory,
572 wasi.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink,
573 wasi.FILETYPE_REGULAR_FILE => Entry.Kind.File,
574 wasi.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket,
562 w.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice,
563 w.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice,
564 w.FILETYPE_DIRECTORY => Entry.Kind.Directory,
565 w.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink,
566 w.FILETYPE_REGULAR_FILE => Entry.Kind.File,
567 w.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket,
575568 else => Entry.Kind.Unknown,
576569 };
577570 return Entry{
......@@ -1109,6 +1102,7 @@ pub const Dir = struct {
11091102 .OBJECT_NAME_INVALID => unreachable,
11101103 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
11111104 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1105 .NOT_A_DIRECTORY => return error.NotDir,
11121106 .INVALID_PARAMETER => unreachable,
11131107 else => return w.unexpectedStatus(rc),
11141108 }
......@@ -1119,10 +1113,18 @@ pub const Dir = struct {
11191113 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
11201114 /// Asserts that the path parameter has no null bytes.
11211115 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1122 os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
1123 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1124 else => |e| return e,
1125 };
1116 if (builtin.os.tag == .windows) {
1117 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1118 return self.deleteFileW(sub_path_w.span().ptr);
1119 } else if (builtin.os.tag == .wasi) {
1120 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
1121 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1122 else => |e| return e,
1123 };
1124 } else {
1125 const sub_path_c = try os.toPosixPath(sub_path);
1126 return self.deleteFileZ(&sub_path_c);
1127 }
11261128 }
11271129
11281130 pub const deleteFileC = @compileError("deprecated: renamed to deleteFileZ");
......@@ -1131,6 +1133,17 @@ pub const Dir = struct {
11311133 pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {
11321134 os.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {
11331135 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1136 error.AccessDenied => |e| switch (builtin.os.tag) {
1137 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
1138 // we need to handle that case specifically and translate the error
1139 .macosx, .ios, .freebsd, .netbsd, .dragonfly => {
1140 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
1141 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT_SYMLINK_NOFOLLOW) catch return e;
1142 const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR;
1143 return if (is_dir) error.IsDir else e;
1144 },
1145 else => return e,
1146 },
11341147 else => |e| return e,
11351148 };
11361149 }
......@@ -1229,14 +1242,9 @@ pub const Dir = struct {
12291242 var file = try self.openFile(file_path, .{});
12301243 defer file.close();
12311244
1232 const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);
1233 if (size > max_bytes) return error.FileTooBig;
1234
1235 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
1236 errdefer allocator.free(buf);
1245 const stat_size = try file.getEndPos();
12371246
1238 try file.inStream().readNoEof(buf);
1239 return buf;
1247 return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel);
12401248 }
12411249
12421250 pub const DeleteTreeError = error{
......@@ -1532,9 +1540,9 @@ pub const Dir = struct {
15321540
15331541 var size: ?u64 = null;
15341542 const mode = options.override_mode orelse blk: {
1535 const stat = try in_file.stat();
1536 size = stat.size;
1537 break :blk stat.mode;
1543 const st = try in_file.stat();
1544 size = st.size;
1545 break :blk st.mode;
15381546 };
15391547
15401548 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
......@@ -1560,6 +1568,17 @@ pub const Dir = struct {
15601568 return AtomicFile.init(dest_path, options.mode, self, false);
15611569 }
15621570 }
1571
1572 pub const Stat = File.Stat;
1573 pub const StatError = File.StatError;
1574
1575 pub fn stat(self: Dir) StatError!Stat {
1576 const file: File = .{
1577 .handle = self.fd,
1578 .capable_io_mode = .blocking,
1579 };
1580 return file.stat();
1581 }
15631582};
15641583
15651584/// Returns an handle to the current working directory. It is not opened with iteration capability.
......@@ -1808,7 +1827,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
18081827 // TODO(#4812): Investigate other systems and whether it is possible to get
18091828 // this path by trying larger and larger buffers until one succeeds.
18101829 var buf: [MAX_PATH_BYTES]u8 = undefined;
1811 return mem.dupe(allocator, u8, try selfExePath(&buf));
1830 return allocator.dupe(u8, try selfExePath(&buf));
18121831}
18131832
18141833/// Get the path to the current executable.
......@@ -1871,7 +1890,7 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
18711890 // TODO(#4812): Investigate other systems and whether it is possible to get
18721891 // this path by trying larger and larger buffers until one succeeds.
18731892 var buf: [MAX_PATH_BYTES]u8 = undefined;
1874 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
1893 return allocator.dupe(u8, try selfExeDirPath(&buf));
18751894}
18761895
18771896/// Get the directory path that contains the current executable.
......@@ -1893,7 +1912,7 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
18931912 // paths. musl supports passing NULL but restricts the output to PATH_MAX
18941913 // anyway.
18951914 var buf: [MAX_PATH_BYTES]u8 = undefined;
1896 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
1915 return allocator.dupe(u8, try os.realpath(pathname, &buf));
18971916}
18981917
18991918test "" {
lib/std/fs/file.zig+64-2
......@@ -29,6 +29,18 @@ pub const File = struct {
2929 pub const Mode = os.mode_t;
3030 pub const INode = os.ino_t;
3131
32 pub const Kind = enum {
33 BlockDevice,
34 CharacterDevice,
35 Directory,
36 NamedPipe,
37 SymLink,
38 File,
39 UnixDomainSocket,
40 Whiteout,
41 Unknown,
42 };
43
3244 pub const default_mode = switch (builtin.os.tag) {
3345 .windows => 0,
3446 .wasi => 0,
......@@ -209,7 +221,7 @@ pub const File = struct {
209221 /// TODO: integrate with async I/O
210222 pub fn mode(self: File) ModeError!Mode {
211223 if (builtin.os.tag == .windows) {
212 return {};
224 return 0;
213225 }
214226 return (try self.stat()).mode;
215227 }
......@@ -219,13 +231,14 @@ pub const File = struct {
219231 /// unique across time, as some file systems may reuse an inode after its file has been deleted.
220232 /// Some systems may change the inode of a file over time.
221233 ///
222 /// On Linux, the inode _is_ structure that stores the metadata, and the inode _number_ is what
234 /// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what
223235 /// you see here: the index number of the inode.
224236 ///
225237 /// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.
226238 inode: INode,
227239 size: u64,
228240 mode: Mode,
241 kind: Kind,
229242
230243 /// Access time in nanoseconds, relative to UTC 1970-01-01.
231244 atime: i128,
......@@ -254,6 +267,7 @@ pub const File = struct {
254267 .inode = info.InternalInformation.IndexNumber,
255268 .size = @bitCast(u64, info.StandardInformation.EndOfFile),
256269 .mode = 0,
270 .kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
257271 .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
258272 .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
259273 .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
......@@ -268,6 +282,27 @@ pub const File = struct {
268282 .inode = st.ino,
269283 .size = @bitCast(u64, st.size),
270284 .mode = st.mode,
285 .kind = switch (builtin.os.tag) {
286 .wasi => switch (st.filetype) {
287 os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
288 os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
289 os.FILETYPE_DIRECTORY => Kind.Directory,
290 os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
291 os.FILETYPE_REGULAR_FILE => Kind.File,
292 os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
293 else => Kind.Unknown,
294 },
295 else => switch (st.mode & os.S_IFMT) {
296 os.S_IFBLK => Kind.BlockDevice,
297 os.S_IFCHR => Kind.CharacterDevice,
298 os.S_IFDIR => Kind.Directory,
299 os.S_IFIFO => Kind.NamedPipe,
300 os.S_IFLNK => Kind.SymLink,
301 os.S_IFREG => Kind.File,
302 os.S_IFSOCK => Kind.UnixDomainSocket,
303 else => Kind.Unknown,
304 },
305 },
271306 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
272307 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
273308 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
......@@ -306,6 +341,33 @@ pub const File = struct {
306341 try os.futimens(self.handle, &times);
307342 }
308343
344 /// On success, caller owns returned buffer.
345 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
346 pub fn readAllAlloc(self: File, allocator: *mem.Allocator, stat_size: u64, max_bytes: usize) ![]u8 {
347 return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);
348 }
349
350 /// On success, caller owns returned buffer.
351 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
352 /// Allows specifying alignment and a sentinel value.
353 pub fn readAllAllocOptions(
354 self: File,
355 allocator: *mem.Allocator,
356 stat_size: u64,
357 max_bytes: usize,
358 comptime alignment: u29,
359 comptime optional_sentinel: ?u8,
360 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
361 const size = math.cast(usize, stat_size) catch math.maxInt(usize);
362 if (size > max_bytes) return error.FileTooBig;
363
364 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
365 errdefer allocator.free(buf);
366
367 try self.reader().readNoEof(buf);
368 return buf;
369 }
370
309371 pub const ReadError = os.ReadError;
310372 pub const PReadError = os.PReadError;
311373
lib/std/fs/path.zig+2-2
......@@ -1034,7 +1034,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
10341034 var from_it = mem.tokenize(resolved_from, "/\\");
10351035 var to_it = mem.tokenize(resolved_to, "/\\");
10361036 while (true) {
1037 const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
1037 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
10381038 const to_rest = to_it.rest();
10391039 if (to_it.next()) |to_component| {
10401040 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
......@@ -1085,7 +1085,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
10851085 var from_it = mem.tokenize(resolved_from, "/");
10861086 var to_it = mem.tokenize(resolved_to, "/");
10871087 while (true) {
1088 const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
1088 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
10891089 const to_rest = to_it.rest();
10901090 if (to_it.next()) |to_component| {
10911091 if (mem.eql(u8, from_component, to_component))
lib/std/fs/test.zig+310-3
......@@ -1,7 +1,157 @@
11const std = @import("../std.zig");
2const testing = std.testing;
23const builtin = std.builtin;
34const fs = std.fs;
5const mem = std.mem;
6const wasi = std.os.wasi;
7
8const ArenaAllocator = std.heap.ArenaAllocator;
9const Dir = std.fs.Dir;
410const File = std.fs.File;
11const tmpDir = testing.tmpDir;
12
13test "Dir.Iterator" {
14 var tmp_dir = tmpDir(.{ .iterate = true });
15 defer tmp_dir.cleanup();
16
17 // First, create a couple of entries to iterate over.
18 const file = try tmp_dir.dir.createFile("some_file", .{});
19 file.close();
20
21 try tmp_dir.dir.makeDir("some_dir");
22
23 var arena = ArenaAllocator.init(testing.allocator);
24 defer arena.deinit();
25
26 var entries = std.ArrayList(Dir.Entry).init(&arena.allocator);
27
28 // Create iterator.
29 var iter = tmp_dir.dir.iterate();
30 while (try iter.next()) |entry| {
31 // We cannot just store `entry` as on Windows, we're re-using the name buffer
32 // which means we'll actually share the `name` pointer between entries!
33 const name = try arena.allocator.dupe(u8, entry.name);
34 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
35 }
36
37 testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
38 testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
39 testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
40}
41
42fn entry_eql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
43 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
44}
45
46fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
47 for (entries.items) |entry| {
48 if (entry_eql(entry, el)) return true;
49 }
50 return false;
51}
52
53test "readAllAlloc" {
54 var tmp_dir = tmpDir(.{});
55 defer tmp_dir.cleanup();
56
57 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
58 defer file.close();
59
60 const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);
61 defer testing.allocator.free(buf1);
62 testing.expect(buf1.len == 0);
63
64 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
65 try file.writeAll(write_buf);
66 try file.seekTo(0);
67 const file_size = try file.getEndPos();
68
69 // max_bytes > file_size
70 const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);
71 defer testing.allocator.free(buf2);
72 testing.expectEqual(write_buf.len, buf2.len);
73 testing.expect(std.mem.eql(u8, write_buf, buf2));
74 try file.seekTo(0);
75
76 // max_bytes == file_size
77 const buf3 = try file.readAllAlloc(testing.allocator, file_size, write_buf.len);
78 defer testing.allocator.free(buf3);
79 testing.expectEqual(write_buf.len, buf3.len);
80 testing.expect(std.mem.eql(u8, write_buf, buf3));
81
82 // max_bytes < file_size
83 testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, file_size, write_buf.len - 1));
84}
85
86test "directory operations on files" {
87 var tmp_dir = tmpDir(.{});
88 defer tmp_dir.cleanup();
89
90 const test_file_name = "test_file";
91
92 var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
93 file.close();
94
95 testing.expectError(error.PathAlreadyExists, tmp_dir.dir.makeDir(test_file_name));
96 testing.expectError(error.NotDir, tmp_dir.dir.openDir(test_file_name, .{}));
97 testing.expectError(error.NotDir, tmp_dir.dir.deleteDir(test_file_name));
98
99 if (builtin.os.tag != .wasi) {
100 // TODO: use Dir's realpath function once that exists
101 const absolute_path = blk: {
102 const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_file_name });
103 defer testing.allocator.free(relative_path);
104 break :blk try fs.realpathAlloc(testing.allocator, relative_path);
105 };
106 defer testing.allocator.free(absolute_path);
107
108 testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(absolute_path));
109 testing.expectError(error.NotDir, fs.deleteDirAbsolute(absolute_path));
110 }
111
112 // ensure the file still exists and is a file as a sanity check
113 file = try tmp_dir.dir.openFile(test_file_name, .{});
114 const stat = try file.stat();
115 testing.expect(stat.kind == .File);
116 file.close();
117}
118
119test "file operations on directories" {
120 var tmp_dir = tmpDir(.{});
121 defer tmp_dir.cleanup();
122
123 const test_dir_name = "test_dir";
124
125 try tmp_dir.dir.makeDir(test_dir_name);
126
127 testing.expectError(error.IsDir, tmp_dir.dir.createFile(test_dir_name, .{}));
128 testing.expectError(error.IsDir, tmp_dir.dir.deleteFile(test_dir_name));
129 // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle.
130 // TODO: Re-enable on WASI once https://github.com/bytecodealliance/wasmtime/issues/1935 is resolved.
131 if (builtin.os.tag != .wasi) {
132 testing.expectError(error.IsDir, tmp_dir.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize)));
133 }
134 // Note: The `.write = true` is necessary to ensure the error occurs on all platforms.
135 // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732
136 testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true }));
137
138 if (builtin.os.tag != .wasi) {
139 // TODO: use Dir's realpath function once that exists
140 const absolute_path = blk: {
141 const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_dir_name });
142 defer testing.allocator.free(relative_path);
143 break :blk try fs.realpathAlloc(testing.allocator, relative_path);
144 };
145 defer testing.allocator.free(absolute_path);
146
147 testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{}));
148 testing.expectError(error.IsDir, fs.deleteFileAbsolute(absolute_path));
149 }
150
151 // ensure the directory still exists as a sanity check
152 var dir = try tmp_dir.dir.openDir(test_dir_name, .{});
153 dir.close();
154}
5155
6156test "openSelfExe" {
7157 if (builtin.os.tag == .wasi) return error.SkipZigTest;
......@@ -10,6 +160,163 @@ test "openSelfExe" {
10160 self_exe_file.close();
11161}
12162
163test "makePath, put some files in it, deleteTree" {
164 var tmp = tmpDir(.{});
165 defer tmp.cleanup();
166
167 try tmp.dir.makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
168 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
169 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
170 try tmp.dir.deleteTree("os_test_tmp");
171 if (tmp.dir.openDir("os_test_tmp", .{})) |dir| {
172 @panic("expected error");
173 } else |err| {
174 testing.expect(err == error.FileNotFound);
175 }
176}
177
178test "access file" {
179 if (builtin.os.tag == .wasi) return error.SkipZigTest;
180
181 var tmp = tmpDir(.{});
182 defer tmp.cleanup();
183
184 try tmp.dir.makePath("os_test_tmp");
185 if (tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
186 @panic("expected error");
187 } else |err| {
188 testing.expect(err == error.FileNotFound);
189 }
190
191 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
192 try tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});
193 try tmp.dir.deleteTree("os_test_tmp");
194}
195
196test "sendfile" {
197 var tmp = tmpDir(.{});
198 defer tmp.cleanup();
199
200 try tmp.dir.makePath("os_test_tmp");
201 defer tmp.dir.deleteTree("os_test_tmp") catch {};
202
203 var dir = try tmp.dir.openDir("os_test_tmp", .{});
204 defer dir.close();
205
206 const line1 = "line1\n";
207 const line2 = "second line\n";
208 var vecs = [_]std.os.iovec_const{
209 .{
210 .iov_base = line1,
211 .iov_len = line1.len,
212 },
213 .{
214 .iov_base = line2,
215 .iov_len = line2.len,
216 },
217 };
218
219 var src_file = try dir.createFile("sendfile1.txt", .{ .read = true });
220 defer src_file.close();
221
222 try src_file.writevAll(&vecs);
223
224 var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true });
225 defer dest_file.close();
226
227 const header1 = "header1\n";
228 const header2 = "second header\n";
229 const trailer1 = "trailer1\n";
230 const trailer2 = "second trailer\n";
231 var hdtr = [_]std.os.iovec_const{
232 .{
233 .iov_base = header1,
234 .iov_len = header1.len,
235 },
236 .{
237 .iov_base = header2,
238 .iov_len = header2.len,
239 },
240 .{
241 .iov_base = trailer1,
242 .iov_len = trailer1.len,
243 },
244 .{
245 .iov_base = trailer2,
246 .iov_len = trailer2.len,
247 },
248 };
249
250 var written_buf: [100]u8 = undefined;
251 try dest_file.writeFileAll(src_file, .{
252 .in_offset = 1,
253 .in_len = 10,
254 .headers_and_trailers = &hdtr,
255 .header_count = 2,
256 });
257 const amt = try dest_file.preadAll(&written_buf, 0);
258 testing.expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
259}
260
261test "fs.copyFile" {
262 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
263 const src_file = "tmp_test_copy_file.txt";
264 const dest_file = "tmp_test_copy_file2.txt";
265 const dest_file2 = "tmp_test_copy_file3.txt";
266
267 var tmp = tmpDir(.{});
268 defer tmp.cleanup();
269
270 try tmp.dir.writeFile(src_file, data);
271 defer tmp.dir.deleteFile(src_file) catch {};
272
273 try tmp.dir.copyFile(src_file, tmp.dir, dest_file, .{});
274 defer tmp.dir.deleteFile(dest_file) catch {};
275
276 try tmp.dir.copyFile(src_file, tmp.dir, dest_file2, .{ .override_mode = File.default_mode });
277 defer tmp.dir.deleteFile(dest_file2) catch {};
278
279 try expectFileContents(tmp.dir, dest_file, data);
280 try expectFileContents(tmp.dir, dest_file2, data);
281}
282
283fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
284 const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
285 defer testing.allocator.free(contents);
286
287 testing.expectEqualSlices(u8, data, contents);
288}
289
290test "AtomicFile" {
291 const test_out_file = "tmp_atomic_file_test_dest.txt";
292 const test_content =
293 \\ hello!
294 \\ this is a test file
295 ;
296
297 var tmp = tmpDir(.{});
298 defer tmp.cleanup();
299
300 {
301 var af = try tmp.dir.atomicFile(test_out_file, .{});
302 defer af.deinit();
303 try af.file.writeAll(test_content);
304 try af.finish();
305 }
306 const content = try tmp.dir.readFileAlloc(testing.allocator, test_out_file, 9999);
307 defer testing.allocator.free(content);
308 testing.expect(mem.eql(u8, content, test_content));
309
310 try tmp.dir.deleteFile(test_out_file);
311}
312
313test "realpath" {
314 if (builtin.os.tag == .wasi) return error.SkipZigTest;
315
316 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
317 testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));
318}
319
13320const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms;
14321
15322test "open file with exclusive nonblocking lock twice" {
......@@ -116,7 +423,7 @@ test "create file, lock and read from multiple process at once" {
116423test "open file with exclusive nonblocking lock twice (absolute paths)" {
117424 if (builtin.os.tag == .wasi) return error.SkipZigTest;
118425
119 const allocator = std.testing.allocator;
426 const allocator = testing.allocator;
120427
121428 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
122429 const filename = try fs.path.resolve(allocator, &file_paths);
......@@ -126,7 +433,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
126433
127434 const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
128435 file1.close();
129 std.testing.expectError(error.WouldBlock, file2);
436 testing.expectError(error.WouldBlock, file2);
130437
131438 try fs.deleteFileAbsolute(filename);
132439}
......@@ -187,7 +494,7 @@ const FileLockTestContext = struct {
187494};
188495
189496fn run_lock_file_test(contexts: []FileLockTestContext) !void {
190 var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
497 var threads = std.ArrayList(*std.Thread).init(testing.allocator);
191498 defer {
192499 for (threads.items) |thread| {
193500 thread.wait();
lib/std/fs/wasi.zig+51-37
......@@ -1,17 +1,44 @@
11const std = @import("std");
22const os = std.os;
33const mem = std.mem;
4const math = std.math;
45const Allocator = mem.Allocator;
56
67usingnamespace std.os.wasi;
78
8/// Type of WASI preopen.
9/// Type-tag of WASI preopen.
910///
1011/// WASI currently offers only `Dir` as a valid preopen resource.
11pub const PreopenType = enum {
12pub const PreopenTypeTag = enum {
1213 Dir,
1314};
1415
16/// Type of WASI preopen.
17///
18/// WASI currently offers only `Dir` as a valid preopen resource.
19pub const PreopenType = union(PreopenTypeTag) {
20 /// Preopened directory type.
21 Dir: []const u8,
22
23 const Self = @This();
24
25 pub fn eql(self: Self, other: PreopenType) bool {
26 if (!mem.eql(u8, @tagName(self), @tagName(other))) return false;
27
28 switch (self) {
29 PreopenTypeTag.Dir => |this_path| return mem.eql(u8, this_path, other.Dir),
30 }
31 }
32
33 pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void {
34 try out_stream.print("PreopenType{{ ", .{});
35 switch (self) {
36 PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
37 }
38 return out_stream.print(" }}", .{});
39 }
40};
41
1542/// WASI preopen struct. This struct consists of a WASI file descriptor
1643/// and type of WASI preopen. It can be obtained directly from the WASI
1744/// runtime using `PreopenList.populate()` method.
......@@ -20,29 +47,15 @@ pub const Preopen = struct {
2047 fd: fd_t,
2148
2249 /// Type of the preopen.
23 @"type": union(PreopenType) {
24 /// Path to a preopened directory.
25 Dir: []const u8,
26 },
50 @"type": PreopenType,
2751
28 const Self = @This();
29
30 /// Construct new `Preopen` instance of type `PreopenType.Dir` from
31 /// WASI file descriptor and WASI path.
32 pub fn newDir(fd: fd_t, path: []const u8) Self {
33 return Self{
52 /// Construct new `Preopen` instance.
53 pub fn new(fd: fd_t, preopen_type: PreopenType) Preopen {
54 return Preopen{
3455 .fd = fd,
35 .@"type" = .{ .Dir = path },
56 .@"type" = preopen_type,
3657 };
3758 }
38
39 pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void {
40 try out_stream.print("{{ .fd = {}, ", .{self.fd});
41 switch (self.@"type") {
42 PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
43 }
44 return out_stream.print(" }}", .{});
45 }
4659};
4760
4861/// Dynamically-sized array list of WASI preopens. This struct is a
......@@ -60,7 +73,7 @@ pub const PreopenList = struct {
6073
6174 const Self = @This();
6275
63 pub const Error = os.UnexpectedError || Allocator.Error;
76 pub const Error = error{ OutOfMemory, Overflow } || os.UnexpectedError;
6477
6578 /// Deinitialize with `deinit`.
6679 pub fn init(allocator: *Allocator) Self {
......@@ -82,6 +95,12 @@ pub const PreopenList = struct {
8295 ///
8396 /// If called more than once, it will clear its contents every time before
8497 /// issuing the syscalls.
98 ///
99 /// In the unlinkely event of overflowing the number of available file descriptors,
100 /// returns `error.Overflow`. In this case, even though an error condition was reached
101 /// the preopen list still contains all valid preopened file descriptors that are valid
102 /// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally,
103 /// `deinit` still must be called!
85104 pub fn populate(self: *Self) Error!void {
86105 // Clear contents if we're being called again
87106 for (self.toOwnedSlice()) |preopen| {
......@@ -98,6 +117,7 @@ pub const PreopenList = struct {
98117 ESUCCESS => {},
99118 ENOTSUP => {
100119 // not a preopen, so keep going
120 fd = try math.add(fd_t, fd, 1);
101121 continue;
102122 },
103123 EBADF => {
......@@ -113,24 +133,18 @@ pub const PreopenList = struct {
113133 ESUCCESS => {},
114134 else => |err| return os.unexpectedErrno(err),
115135 }
116 const preopen = Preopen.newDir(fd, path_buf);
136 const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf });
117137 try self.buffer.append(preopen);
118 fd += 1;
138 fd = try math.add(fd_t, fd, 1);
119139 }
120140 }
121141
122 /// Find preopen by path. If the preopen exists, return it.
142 /// Find preopen by type. If the preopen exists, return it.
123143 /// Otherwise, return `null`.
124 ///
125 /// TODO make the function more generic by searching by `PreopenType` union. This will
126 /// be needed in the future when WASI extends its capabilities to resources
127 /// other than preopened directories.
128 pub fn find(self: Self, path: []const u8) ?*const Preopen {
129 for (self.buffer.items) |preopen| {
130 switch (preopen.@"type") {
131 PreopenType.Dir => |preopen_path| {
132 if (mem.eql(u8, path, preopen_path)) return &preopen;
133 },
144 pub fn find(self: Self, preopen_type: PreopenType) ?*const Preopen {
145 for (self.buffer.items) |*preopen| {
146 if (preopen.@"type".eql(preopen_type)) {
147 return preopen;
134148 }
135149 }
136150 return null;
......@@ -156,7 +170,7 @@ test "extracting WASI preopens" {
156170 try preopens.populate();
157171
158172 std.testing.expectEqual(@as(usize, 1), preopens.asSlice().len);
159 const preopen = preopens.find(".") orelse unreachable;
160 std.testing.expect(std.mem.eql(u8, ".", preopen.@"type".Dir));
173 const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
174 std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." }));
161175 std.testing.expectEqual(@as(usize, 3), preopen.fd);
162176}
lib/std/fs/watch.zig+1-1
......@@ -360,7 +360,7 @@ pub fn Watch(comptime V: type) type {
360360
361361 fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
362362 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
363 const dirname = try std.mem.dupe(self.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
363 const dirname = try self.allocator.dupe(u8, std.fs.path.dirname(file_path) orelse ".");
364364 var dirname_consumed = false;
365365 defer if (!dirname_consumed) self.allocator.free(dirname);
366366
lib/std/hash/auto_hash.zig+8-8
......@@ -21,7 +21,7 @@ pub const HashStrategy = enum {
2121};
2222
2323/// Helper function to hash a pointer and mutate the strategy if needed.
24pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
24pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
2525 const info = @typeInfo(@TypeOf(key));
2626
2727 switch (info.Pointer.size) {
......@@ -53,7 +53,7 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
5353}
5454
5555/// Helper function to hash a set of contiguous objects, from an array or slice.
56pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void {
56pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
5757 switch (strat) {
5858 .Shallow => {
5959 // TODO detect via a trait when Key has no padding bits to
......@@ -73,7 +73,7 @@ pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void {
7373
7474/// Provides generic hashing for any eligible type.
7575/// Strategy is provided to determine if pointers should be followed or not.
76pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void {
76pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
7777 const Key = @TypeOf(key);
7878 switch (@typeInfo(Key)) {
7979 .NoReturn,
......@@ -161,7 +161,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void {
161161/// Provides generic hashing for any eligible type.
162162/// Only hashes `key` itself, pointers are not followed.
163163/// Slices are rejected to avoid ambiguity on the user's intention.
164pub fn autoHash(hasher: var, key: var) void {
164pub fn autoHash(hasher: anytype, key: anytype) void {
165165 const Key = @TypeOf(key);
166166 if (comptime meta.trait.isSlice(Key)) {
167167 comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated
......@@ -181,28 +181,28 @@ pub fn autoHash(hasher: var, key: var) void {
181181const testing = std.testing;
182182const Wyhash = std.hash.Wyhash;
183183
184fn testHash(key: var) u64 {
184fn testHash(key: anytype) u64 {
185185 // Any hash could be used here, for testing autoHash.
186186 var hasher = Wyhash.init(0);
187187 hash(&hasher, key, .Shallow);
188188 return hasher.final();
189189}
190190
191fn testHashShallow(key: var) u64 {
191fn testHashShallow(key: anytype) u64 {
192192 // Any hash could be used here, for testing autoHash.
193193 var hasher = Wyhash.init(0);
194194 hash(&hasher, key, .Shallow);
195195 return hasher.final();
196196}
197197
198fn testHashDeep(key: var) u64 {
198fn testHashDeep(key: anytype) u64 {
199199 // Any hash could be used here, for testing autoHash.
200200 var hasher = Wyhash.init(0);
201201 hash(&hasher, key, .Deep);
202202 return hasher.final();
203203}
204204
205fn testHashDeepRecursive(key: var) u64 {
205fn testHashDeepRecursive(key: anytype) u64 {
206206 // Any hash could be used here, for testing autoHash.
207207 var hasher = Wyhash.init(0);
208208 hash(&hasher, key, .DeepRecursive);
lib/std/hash/benchmark.zig+5-5
......@@ -88,7 +88,7 @@ const Result = struct {
8888
8989const block_size: usize = 8 * 8192;
9090
91pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {
91pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result {
9292 var h = blk: {
9393 if (H.init_u8s) |init| {
9494 break :blk H.ty.init(init);
......@@ -119,7 +119,7 @@ pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {
119119 };
120120}
121121
122pub fn benchmarkHashSmallKeys(comptime H: var, key_size: usize, bytes: usize) !Result {
122pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize) !Result {
123123 const key_count = bytes / key_size;
124124 var block: [block_size]u8 = undefined;
125125 prng.random.bytes(block[0..]);
......@@ -172,7 +172,7 @@ fn mode(comptime x: comptime_int) comptime_int {
172172}
173173
174174pub fn main() !void {
175 const stdout = std.io.getStdOut().outStream();
175 const stdout = std.io.getStdOut().writer();
176176
177177 var buffer: [1024]u8 = undefined;
178178 var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
......@@ -248,13 +248,13 @@ pub fn main() !void {
248248 if (H.has_iterative_api) {
249249 prng.seed(seed);
250250 const result = try benchmarkHash(H, count);
251 try stdout.print(" iterative: {:4} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash });
251 try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash });
252252 }
253253
254254 if (!test_iterative_only) {
255255 prng.seed(seed);
256256 const result_small = try benchmarkHashSmallKeys(H, key_size, count);
257 try stdout.print(" small keys: {:4} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash });
257 try stdout.print(" small keys: {:5} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash });
258258 }
259259 }
260260 }
lib/std/hash/cityhash.zig+1-1
......@@ -354,7 +354,7 @@ pub const CityHash64 = struct {
354354 }
355355};
356356
357fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
357fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
358358 const hashbytes = hashbits / 8;
359359 var key: [256]u8 = undefined;
360360 var hashes: [hashbytes * 256]u8 = undefined;
lib/std/hash/murmur.zig+1-1
......@@ -279,7 +279,7 @@ pub const Murmur3_32 = struct {
279279 }
280280};
281281
282fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {
282fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
283283 const hashbytes = hashbits / 8;
284284 var key: [256]u8 = undefined;
285285 var hashes: [hashbytes * 256]u8 = undefined;
lib/std/hash_map.zig+791-310
......@@ -9,17 +9,23 @@ const autoHash = std.hash.autoHash;
99const Wyhash = std.hash.Wyhash;
1010const Allocator = mem.Allocator;
1111const builtin = @import("builtin");
12
13const want_modification_safety = std.debug.runtime_safety;
14const debug_u32 = if (want_modification_safety) u32 else void;
12const hash_map = @This();
1513
1614pub fn AutoHashMap(comptime K: type, comptime V: type) type {
17 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K));
15 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), autoEqlIsCheap(K));
16}
17
18pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type {
19 return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), autoEqlIsCheap(K));
1820}
1921
2022/// Builtin hashmap for strings as keys.
2123pub fn StringHashMap(comptime V: type) type {
22 return HashMap([]const u8, V, hashString, eqlString);
24 return HashMap([]const u8, V, hashString, eqlString, true);
25}
26
27pub fn StringHashMapUnmanaged(comptime V: type) type {
28 return HashMapUnmanaged([]const u8, V, hashString, eqlString, true);
2329}
2430
2531pub fn eqlString(a: []const u8, b: []const u8) bool {
......@@ -30,422 +36,860 @@ pub fn hashString(s: []const u8) u32 {
3036 return @truncate(u32, std.hash.Wyhash.hash(0, s));
3137}
3238
33pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
39/// Insertion order is preserved.
40/// Deletions perform a "swap removal" on the entries list.
41/// Modifying the hash map while iterating is allowed, however one must understand
42/// the (well defined) behavior when mixing insertions and deletions with iteration.
43/// For a hash map that can be initialized directly that does not store an Allocator
44/// field, see `HashMapUnmanaged`.
45/// When `store_hash` is `false`, this data structure is biased towards cheap `eql`
46/// functions. It does not store each item's hash in the table. Setting `store_hash`
47/// to `true` incurs slightly more memory cost by storing each key's hash in the table
48/// but only has to call `eql` for hash collisions.
49pub fn HashMap(
50 comptime K: type,
51 comptime V: type,
52 comptime hash: fn (key: K) u32,
53 comptime eql: fn (a: K, b: K) bool,
54 comptime store_hash: bool,
55) type {
3456 return struct {
35 entries: []Entry,
36 size: usize,
37 max_distance_from_start_index: usize,
57 unmanaged: Unmanaged,
3858 allocator: *Allocator,
3959
40 /// This is used to detect bugs where a hashtable is edited while an iterator is running.
41 modification_count: debug_u32,
42
43 const Self = @This();
44
45 /// A *KV is a mutable pointer into this HashMap's internal storage.
46 /// Modifying the key is undefined behavior.
47 /// Modifying the value is harmless.
48 /// *KV pointers become invalid whenever this HashMap is modified,
49 /// and then any access to the *KV is undefined behavior.
50 pub const KV = struct {
51 key: K,
52 value: V,
53 };
54
55 const Entry = struct {
56 used: bool,
57 distance_from_start_index: usize,
58 kv: KV,
59 };
60
61 pub const GetOrPutResult = struct {
62 kv: *KV,
63 found_existing: bool,
64 };
60 pub const Unmanaged = HashMapUnmanaged(K, V, hash, eql, store_hash);
61 pub const Entry = Unmanaged.Entry;
62 pub const Hash = Unmanaged.Hash;
63 pub const GetOrPutResult = Unmanaged.GetOrPutResult;
6564
65 /// Deprecated. Iterate using `items`.
6666 pub const Iterator = struct {
6767 hm: *const Self,
68 // how many items have we returned
69 count: usize,
70 // iterator through the entry array
68 /// Iterator through the entry array.
7169 index: usize,
72 // used to detect concurrent modification
73 initial_modification_count: debug_u32,
7470
75 pub fn next(it: *Iterator) ?*KV {
76 if (want_modification_safety) {
77 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
78 }
79 if (it.count >= it.hm.size) return null;
80 while (it.index < it.hm.entries.len) : (it.index += 1) {
81 const entry = &it.hm.entries[it.index];
82 if (entry.used) {
83 it.index += 1;
84 it.count += 1;
85 return &entry.kv;
86 }
87 }
88 unreachable; // no next item
71 pub fn next(it: *Iterator) ?*Entry {
72 if (it.index >= it.hm.unmanaged.entries.items.len) return null;
73 const result = &it.hm.unmanaged.entries.items[it.index];
74 it.index += 1;
75 return result;
8976 }
9077
91 // Reset the iterator to the initial index
78 /// Reset the iterator to the initial index
9279 pub fn reset(it: *Iterator) void {
93 it.count = 0;
9480 it.index = 0;
95 // Resetting the modification count too
96 it.initial_modification_count = it.hm.modification_count;
9781 }
9882 };
9983
84 const Self = @This();
85 const Index = Unmanaged.Index;
86
10087 pub fn init(allocator: *Allocator) Self {
101 return Self{
102 .entries = &[_]Entry{},
88 return .{
89 .unmanaged = .{},
10390 .allocator = allocator,
104 .size = 0,
105 .max_distance_from_start_index = 0,
106 .modification_count = if (want_modification_safety) 0 else {},
10791 };
10892 }
10993
110 pub fn deinit(hm: Self) void {
111 hm.allocator.free(hm.entries);
94 pub fn deinit(self: *Self) void {
95 self.unmanaged.deinit(self.allocator);
96 self.* = undefined;
11297 }
11398
114 pub fn clear(hm: *Self) void {
115 for (hm.entries) |*entry| {
116 entry.used = false;
117 }
118 hm.size = 0;
119 hm.max_distance_from_start_index = 0;
120 hm.incrementModificationCount();
99 pub fn clearRetainingCapacity(self: *Self) void {
100 return self.unmanaged.clearRetainingCapacity();
121101 }
122102
103 pub fn clearAndFree(self: *Self) void {
104 return self.unmanaged.clearAndFree(self.allocator);
105 }
106
107 /// Deprecated. Use `items().len`.
123108 pub fn count(self: Self) usize {
124 return self.size;
109 return self.items().len;
110 }
111
112 /// Deprecated. Iterate using `items`.
113 pub fn iterator(self: *const Self) Iterator {
114 return Iterator{
115 .hm = self,
116 .index = 0,
117 };
125118 }
126119
127120 /// If key exists this function cannot fail.
128121 /// If there is an existing item with `key`, then the result
129 /// kv pointer points to it, and found_existing is true.
122 /// `Entry` pointer points to it, and found_existing is true.
130123 /// Otherwise, puts a new item with undefined value, and
131 /// the kv pointer points to it. Caller should then initialize
132 /// the data.
124 /// the `Entry` pointer points to it. Caller should then initialize
125 /// the value (but not the key).
133126 pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
134 // TODO this implementation can be improved - we should only
135 // have to hash once and find the entry once.
136 if (self.get(key)) |kv| {
137 return GetOrPutResult{
138 .kv = kv,
139 .found_existing = true,
140 };
141 }
142 self.incrementModificationCount();
143 try self.autoCapacity();
144 const put_result = self.internalPut(key);
145 assert(put_result.old_kv == null);
146 return GetOrPutResult{
147 .kv = &put_result.new_entry.kv,
148 .found_existing = false,
149 };
127 return self.unmanaged.getOrPut(self.allocator, key);
150128 }
151129
152 pub fn getOrPutValue(self: *Self, key: K, value: V) !*KV {
153 const res = try self.getOrPut(key);
154 if (!res.found_existing)
155 res.kv.value = value;
130 /// If there is an existing item with `key`, then the result
131 /// `Entry` pointer points to it, and found_existing is true.
132 /// Otherwise, puts a new item with undefined value, and
133 /// the `Entry` pointer points to it. Caller should then initialize
134 /// the value (but not the key).
135 /// If a new entry needs to be stored, this function asserts there
136 /// is enough capacity to store it.
137 pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult {
138 return self.unmanaged.getOrPutAssumeCapacity(key);
139 }
140
141 pub fn getOrPutValue(self: *Self, key: K, value: V) !*Entry {
142 return self.unmanaged.getOrPutValue(self.allocator, key, value);
143 }
144
145 /// Increases capacity, guaranteeing that insertions up until the
146 /// `expected_count` will not cause an allocation, and therefore cannot fail.
147 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
148 return self.unmanaged.ensureCapacity(self.allocator, new_capacity);
149 }
150
151 /// Returns the number of total elements which may be present before it is
152 /// no longer guaranteed that no allocations will be performed.
153 pub fn capacity(self: *Self) usize {
154 return self.unmanaged.capacity();
155 }
156
157 /// Clobbers any existing data. To detect if a put would clobber
158 /// existing data, see `getOrPut`.
159 pub fn put(self: *Self, key: K, value: V) !void {
160 return self.unmanaged.put(self.allocator, key, value);
161 }
162
163 /// Inserts a key-value pair into the hash map, asserting that no previous
164 /// entry with the same key is already present
165 pub fn putNoClobber(self: *Self, key: K, value: V) !void {
166 return self.unmanaged.putNoClobber(self.allocator, key, value);
167 }
168
169 /// Asserts there is enough capacity to store the new key-value pair.
170 /// Clobbers any existing data. To detect if a put would clobber
171 /// existing data, see `getOrPutAssumeCapacity`.
172 pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
173 return self.unmanaged.putAssumeCapacity(key, value);
174 }
175
176 /// Asserts there is enough capacity to store the new key-value pair.
177 /// Asserts that it does not clobber any existing data.
178 /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`.
179 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
180 return self.unmanaged.putAssumeCapacityNoClobber(key, value);
181 }
156182
157 return res.kv;
183 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
184 pub fn fetchPut(self: *Self, key: K, value: V) !?Entry {
185 return self.unmanaged.fetchPut(self.allocator, key, value);
158186 }
159187
160 fn optimizedCapacity(expected_count: usize) usize {
161 // ensure that the hash map will be at most 60% full if
162 // expected_count items are put into it
163 var optimized_capacity = expected_count * 5 / 3;
164 // an overflow here would mean the amount of memory required would not
165 // be representable in the address space
166 return math.ceilPowerOfTwo(usize, optimized_capacity) catch unreachable;
188 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
189 /// If insertion happuns, asserts there is enough capacity without allocating.
190 pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?Entry {
191 return self.unmanaged.fetchPutAssumeCapacity(key, value);
167192 }
168193
169 /// Increases capacity so that the hash map will be at most
170 /// 60% full when expected_count items are put into it
171 pub fn ensureCapacity(self: *Self, expected_count: usize) !void {
172 if (expected_count == 0) return;
173 const optimized_capacity = optimizedCapacity(expected_count);
174 return self.ensureCapacityExact(optimized_capacity);
194 pub fn getEntry(self: Self, key: K) ?*Entry {
195 return self.unmanaged.getEntry(key);
175196 }
176197
177 /// Sets the capacity to the new capacity if the new
178 /// capacity is greater than the current capacity.
179 /// New capacity must be a power of two.
180 fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {
181 // capacity must always be a power of two to allow for modulo
182 // optimization in the constrainIndex fn
183 assert(math.isPowerOfTwo(new_capacity));
198 pub fn get(self: Self, key: K) ?V {
199 return self.unmanaged.get(key);
200 }
201
202 pub fn contains(self: Self, key: K) bool {
203 return self.unmanaged.contains(key);
204 }
205
206 /// If there is an `Entry` with a matching key, it is deleted from
207 /// the hash map, and then returned from this function.
208 pub fn remove(self: *Self, key: K) ?Entry {
209 return self.unmanaged.remove(key);
210 }
211
212 /// Asserts there is an `Entry` with matching key, deletes it from the hash map,
213 /// and discards it.
214 pub fn removeAssertDiscard(self: *Self, key: K) void {
215 return self.unmanaged.removeAssertDiscard(key);
216 }
217
218 pub fn items(self: Self) []Entry {
219 return self.unmanaged.items();
220 }
221
222 pub fn clone(self: Self) !Self {
223 var other = try self.unmanaged.clone(self.allocator);
224 return other.promote(self.allocator);
225 }
226 };
227}
228
229/// General purpose hash table.
230/// Insertion order is preserved.
231/// Deletions perform a "swap removal" on the entries list.
232/// Modifying the hash map while iterating is allowed, however one must understand
233/// the (well defined) behavior when mixing insertions and deletions with iteration.
234/// This type does not store an Allocator field - the Allocator must be passed in
235/// with each function call that requires it. See `HashMap` for a type that stores
236/// an Allocator field for convenience.
237/// Can be initialized directly using the default field values.
238/// This type is designed to have low overhead for small numbers of entries. When
239/// `store_hash` is `false` and the number of entries in the map is less than 9,
240/// the overhead cost of using `HashMapUnmanaged` rather than `std.ArrayList` is
241/// only a single pointer-sized integer.
242/// When `store_hash` is `false`, this data structure is biased towards cheap `eql`
243/// functions. It does not store each item's hash in the table. Setting `store_hash`
244/// to `true` incurs slightly more memory cost by storing each key's hash in the table
245/// but guarantees only one call to `eql` per insertion/deletion.
246pub fn HashMapUnmanaged(
247 comptime K: type,
248 comptime V: type,
249 comptime hash: fn (key: K) u32,
250 comptime eql: fn (a: K, b: K) bool,
251 comptime store_hash: bool,
252) type {
253 return struct {
254 /// It is permitted to access this field directly.
255 entries: std.ArrayListUnmanaged(Entry) = .{},
256
257 /// When entries length is less than `linear_scan_max`, this remains `null`.
258 /// Once entries length grows big enough, this field is allocated. There is
259 /// an IndexHeader followed by an array of Index(I) structs, where I is defined
260 /// by how many total indexes there are.
261 index_header: ?*IndexHeader = null,
262
263 /// Modifying the key is illegal behavior.
264 /// Modifying the value is allowed.
265 /// Entry pointers become invalid whenever this HashMap is modified,
266 /// unless `ensureCapacity` was previously used.
267 pub const Entry = struct {
268 /// This field is `void` if `store_hash` is `false`.
269 hash: Hash,
270 key: K,
271 value: V,
272 };
273
274 pub const Hash = if (store_hash) u32 else void;
275
276 pub const GetOrPutResult = struct {
277 entry: *Entry,
278 found_existing: bool,
279 };
280
281 pub const Managed = HashMap(K, V, hash, eql, store_hash);
282
283 const Self = @This();
284
285 const linear_scan_max = 8;
184286
185 if (new_capacity <= self.entries.len) {
186 return;
287 pub fn promote(self: Self, allocator: *Allocator) Managed {
288 return .{
289 .unmanaged = self,
290 .allocator = allocator,
291 };
292 }
293
294 pub fn deinit(self: *Self, allocator: *Allocator) void {
295 self.entries.deinit(allocator);
296 if (self.index_header) |header| {
297 header.free(allocator);
298 }
299 self.* = undefined;
300 }
301
302 pub fn clearRetainingCapacity(self: *Self) void {
303 self.entries.items.len = 0;
304 if (self.index_header) |header| {
305 header.max_distance_from_start_index = 0;
306 switch (header.capacityIndexType()) {
307 .u8 => mem.set(Index(u8), header.indexes(u8), Index(u8).empty),
308 .u16 => mem.set(Index(u16), header.indexes(u16), Index(u16).empty),
309 .u32 => mem.set(Index(u32), header.indexes(u32), Index(u32).empty),
310 .usize => mem.set(Index(usize), header.indexes(usize), Index(usize).empty),
311 }
312 }
313 }
314
315 pub fn clearAndFree(self: *Self, allocator: *Allocator) void {
316 self.entries.shrink(allocator, 0);
317 if (self.index_header) |header| {
318 header.free(allocator);
319 self.index_header = null;
187320 }
321 }
322
323 /// If key exists this function cannot fail.
324 /// If there is an existing item with `key`, then the result
325 /// `Entry` pointer points to it, and found_existing is true.
326 /// Otherwise, puts a new item with undefined value, and
327 /// the `Entry` pointer points to it. Caller should then initialize
328 /// the value (but not the key).
329 pub fn getOrPut(self: *Self, allocator: *Allocator, key: K) !GetOrPutResult {
330 self.ensureCapacity(allocator, self.entries.items.len + 1) catch |err| {
331 // "If key exists this function cannot fail."
332 return GetOrPutResult{
333 .entry = self.getEntry(key) orelse return err,
334 .found_existing = true,
335 };
336 };
337 return self.getOrPutAssumeCapacity(key);
338 }
188339
189 const old_entries = self.entries;
190 try self.initCapacity(new_capacity);
191 self.incrementModificationCount();
192 if (old_entries.len > 0) {
193 // dump all of the old elements into the new table
194 for (old_entries) |*old_entry| {
195 if (old_entry.used) {
196 self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value;
340 /// If there is an existing item with `key`, then the result
341 /// `Entry` pointer points to it, and found_existing is true.
342 /// Otherwise, puts a new item with undefined value, and
343 /// the `Entry` pointer points to it. Caller should then initialize
344 /// the value (but not the key).
345 /// If a new entry needs to be stored, this function asserts there
346 /// is enough capacity to store it.
347 pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult {
348 const header = self.index_header orelse {
349 // Linear scan.
350 const h = if (store_hash) hash(key) else {};
351 for (self.entries.items) |*item| {
352 if (item.hash == h and eql(key, item.key)) {
353 return GetOrPutResult{
354 .entry = item,
355 .found_existing = true,
356 };
197357 }
198358 }
199 self.allocator.free(old_entries);
359 const new_entry = self.entries.addOneAssumeCapacity();
360 new_entry.* = .{
361 .hash = if (store_hash) h else {},
362 .key = key,
363 .value = undefined,
364 };
365 return GetOrPutResult{
366 .entry = new_entry,
367 .found_existing = false,
368 };
369 };
370
371 switch (header.capacityIndexType()) {
372 .u8 => return self.getOrPutInternal(key, header, u8),
373 .u16 => return self.getOrPutInternal(key, header, u16),
374 .u32 => return self.getOrPutInternal(key, header, u32),
375 .usize => return self.getOrPutInternal(key, header, usize),
376 }
377 }
378
379 pub fn getOrPutValue(self: *Self, allocator: *Allocator, key: K, value: V) !*Entry {
380 const res = try self.getOrPut(allocator, key);
381 if (!res.found_existing)
382 res.entry.value = value;
383
384 return res.entry;
385 }
386
387 /// Increases capacity, guaranteeing that insertions up until the
388 /// `expected_count` will not cause an allocation, and therefore cannot fail.
389 pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
390 try self.entries.ensureCapacity(allocator, new_capacity);
391 if (new_capacity <= linear_scan_max) return;
392
393 // Ensure that the indexes will be at most 60% full if
394 // `new_capacity` items are put into it.
395 const needed_len = new_capacity * 5 / 3;
396 if (self.index_header) |header| {
397 if (needed_len > header.indexes_len) {
398 // An overflow here would mean the amount of memory required would not
399 // be representable in the address space.
400 const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable;
401 const new_header = try IndexHeader.alloc(allocator, new_indexes_len);
402 self.insertAllEntriesIntoNewHeader(new_header);
403 header.free(allocator);
404 self.index_header = new_header;
405 }
406 } else {
407 // An overflow here would mean the amount of memory required would not
408 // be representable in the address space.
409 const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable;
410 const header = try IndexHeader.alloc(allocator, new_indexes_len);
411 self.insertAllEntriesIntoNewHeader(header);
412 self.index_header = header;
200413 }
201414 }
202415
203 /// Returns the kv pair that was already there.
204 pub fn put(self: *Self, key: K, value: V) !?KV {
205 try self.autoCapacity();
206 return putAssumeCapacity(self, key, value);
416 /// Returns the number of total elements which may be present before it is
417 /// no longer guaranteed that no allocations will be performed.
418 pub fn capacity(self: Self) usize {
419 const entry_cap = self.entries.capacity;
420 const header = self.index_header orelse return math.min(linear_scan_max, entry_cap);
421 const indexes_cap = (header.indexes_len + 1) * 3 / 4;
422 return math.min(entry_cap, indexes_cap);
207423 }
208424
209 /// Calls put() and asserts that no kv pair is clobbered.
210 pub fn putNoClobber(self: *Self, key: K, value: V) !void {
211 assert((try self.put(key, value)) == null);
425 /// Clobbers any existing data. To detect if a put would clobber
426 /// existing data, see `getOrPut`.
427 pub fn put(self: *Self, allocator: *Allocator, key: K, value: V) !void {
428 const result = try self.getOrPut(allocator, key);
429 result.entry.value = value;
212430 }
213431
214 pub fn putAssumeCapacity(self: *Self, key: K, value: V) ?KV {
215 assert(self.count() < self.entries.len);
216 self.incrementModificationCount();
432 /// Inserts a key-value pair into the hash map, asserting that no previous
433 /// entry with the same key is already present
434 pub fn putNoClobber(self: *Self, allocator: *Allocator, key: K, value: V) !void {
435 const result = try self.getOrPut(allocator, key);
436 assert(!result.found_existing);
437 result.entry.value = value;
438 }
217439
218 const put_result = self.internalPut(key);
219 put_result.new_entry.kv.value = value;
220 return put_result.old_kv;
440 /// Asserts there is enough capacity to store the new key-value pair.
441 /// Clobbers any existing data. To detect if a put would clobber
442 /// existing data, see `getOrPutAssumeCapacity`.
443 pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
444 const result = self.getOrPutAssumeCapacity(key);
445 result.entry.value = value;
221446 }
222447
448 /// Asserts there is enough capacity to store the new key-value pair.
449 /// Asserts that it does not clobber any existing data.
450 /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`.
223451 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
224 assert(self.putAssumeCapacity(key, value) == null);
452 const result = self.getOrPutAssumeCapacity(key);
453 assert(!result.found_existing);
454 result.entry.value = value;
455 }
456
457 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
458 pub fn fetchPut(self: *Self, allocator: *Allocator, key: K, value: V) !?Entry {
459 const gop = try self.getOrPut(allocator, key);
460 var result: ?Entry = null;
461 if (gop.found_existing) {
462 result = gop.entry.*;
463 }
464 gop.entry.value = value;
465 return result;
225466 }
226467
227 pub fn get(hm: *const Self, key: K) ?*KV {
228 if (hm.entries.len == 0) {
468 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
469 /// If insertion happens, asserts there is enough capacity without allocating.
470 pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?Entry {
471 const gop = self.getOrPutAssumeCapacity(key);
472 var result: ?Entry = null;
473 if (gop.found_existing) {
474 result = gop.entry.*;
475 }
476 gop.entry.value = value;
477 return result;
478 }
479
480 pub fn getEntry(self: Self, key: K) ?*Entry {
481 const header = self.index_header orelse {
482 // Linear scan.
483 const h = if (store_hash) hash(key) else {};
484 for (self.entries.items) |*item| {
485 if (item.hash == h and eql(key, item.key)) {
486 return item;
487 }
488 }
229489 return null;
490 };
491
492 switch (header.capacityIndexType()) {
493 .u8 => return self.getInternal(key, header, u8),
494 .u16 => return self.getInternal(key, header, u16),
495 .u32 => return self.getInternal(key, header, u32),
496 .usize => return self.getInternal(key, header, usize),
230497 }
231 return hm.internalGet(key);
232498 }
233499
234 pub fn getValue(hm: *const Self, key: K) ?V {
235 return if (hm.get(key)) |kv| kv.value else null;
500 pub fn get(self: Self, key: K) ?V {
501 return if (self.getEntry(key)) |entry| entry.value else null;
236502 }
237503
238 pub fn contains(hm: *const Self, key: K) bool {
239 return hm.get(key) != null;
504 pub fn contains(self: Self, key: K) bool {
505 return self.getEntry(key) != null;
240506 }
241507
242 /// Returns any kv pair that was removed.
243 pub fn remove(hm: *Self, key: K) ?KV {
244 if (hm.entries.len == 0) return null;
245 hm.incrementModificationCount();
246 const start_index = hm.keyToIndex(key);
247 {
248 var roll_over: usize = 0;
249 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
250 const index = hm.constrainIndex(start_index + roll_over);
251 var entry = &hm.entries[index];
252
253 if (!entry.used) return null;
254
255 if (!eql(entry.kv.key, key)) continue;
256
257 const removed_kv = entry.kv;
258 while (roll_over < hm.entries.len) : (roll_over += 1) {
259 const next_index = hm.constrainIndex(start_index + roll_over + 1);
260 const next_entry = &hm.entries[next_index];
261 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
262 entry.used = false;
263 hm.size -= 1;
264 return removed_kv;
265 }
266 entry.* = next_entry.*;
267 entry.distance_from_start_index -= 1;
268 entry = next_entry;
508 /// If there is an `Entry` with a matching key, it is deleted from
509 /// the hash map, and then returned from this function.
510 pub fn remove(self: *Self, key: K) ?Entry {
511 const header = self.index_header orelse {
512 // Linear scan.
513 const h = if (store_hash) hash(key) else {};
514 for (self.entries.items) |item, i| {
515 if (item.hash == h and eql(key, item.key)) {
516 return self.entries.swapRemove(i);
269517 }
270 unreachable; // shifting everything in the table
271518 }
519 return null;
520 };
521 switch (header.capacityIndexType()) {
522 .u8 => return self.removeInternal(key, header, u8),
523 .u16 => return self.removeInternal(key, header, u16),
524 .u32 => return self.removeInternal(key, header, u32),
525 .usize => return self.removeInternal(key, header, usize),
272526 }
273 return null;
274527 }
275528
276 /// Calls remove(), asserts that a kv pair is removed, and discards it.
277 pub fn removeAssertDiscard(hm: *Self, key: K) void {
278 assert(hm.remove(key) != null);
529 /// Asserts there is an `Entry` with matching key, deletes it from the hash map,
530 /// and discards it.
531 pub fn removeAssertDiscard(self: *Self, key: K) void {
532 assert(self.remove(key) != null);
279533 }
280534
281 pub fn iterator(hm: *const Self) Iterator {
282 return Iterator{
283 .hm = hm,
284 .count = 0,
285 .index = 0,
286 .initial_modification_count = hm.modification_count,
287 };
535 pub fn items(self: Self) []Entry {
536 return self.entries.items;
288537 }
289538
290 pub fn clone(self: Self) !Self {
291 var other = Self.init(self.allocator);
292 try other.initCapacity(self.entries.len);
293 var it = self.iterator();
294 while (it.next()) |entry| {
295 try other.putNoClobber(entry.key, entry.value);
539 pub fn clone(self: Self, allocator: *Allocator) !Self {
540 var other: Self = .{};
541 try other.entries.appendSlice(allocator, self.entries.items);
542
543 if (self.index_header) |header| {
544 const new_header = try IndexHeader.alloc(allocator, header.indexes_len);
545 other.insertAllEntriesIntoNewHeader(new_header);
546 other.index_header = new_header;
296547 }
297548 return other;
298549 }
299550
300 fn autoCapacity(self: *Self) !void {
301 if (self.entries.len == 0) {
302 return self.ensureCapacityExact(16);
303 }
304 // if we get too full (60%), double the capacity
305 if (self.size * 5 >= self.entries.len * 3) {
306 return self.ensureCapacityExact(self.entries.len * 2);
307 }
308 }
551 fn removeInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) ?Entry {
552 const indexes = header.indexes(I);
553 const h = hash(key);
554 const start_index = header.constrainIndex(h);
555 var roll_over: usize = 0;
556 while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
557 const index_index = header.constrainIndex(start_index + roll_over);
558 var index = &indexes[index_index];
559 if (index.isEmpty())
560 return null;
309561
310 fn initCapacity(hm: *Self, capacity: usize) !void {
311 hm.entries = try hm.allocator.alloc(Entry, capacity);
312 hm.size = 0;
313 hm.max_distance_from_start_index = 0;
314 for (hm.entries) |*entry| {
315 entry.used = false;
562 const entry = &self.entries.items[index.entry_index];
563
564 const hash_match = if (store_hash) h == entry.hash else true;
565 if (!hash_match or !eql(key, entry.key))
566 continue;
567
568 const removed_entry = self.entries.swapRemove(index.entry_index);
569 if (self.entries.items.len > 0 and self.entries.items.len != index.entry_index) {
570 // Because of the swap remove, now we need to update the index that was
571 // pointing to the last entry and is now pointing to this removed item slot.
572 self.updateEntryIndex(header, self.entries.items.len, index.entry_index, I, indexes);
573 }
574
575 // Now we have to shift over the following indexes.
576 roll_over += 1;
577 while (roll_over < header.indexes_len) : (roll_over += 1) {
578 const next_index_index = header.constrainIndex(start_index + roll_over);
579 const next_index = &indexes[next_index_index];
580 if (next_index.isEmpty() or next_index.distance_from_start_index == 0) {
581 index.setEmpty();
582 return removed_entry;
583 }
584 index.* = next_index.*;
585 index.distance_from_start_index -= 1;
586 index = next_index;
587 }
588 unreachable;
316589 }
590 return null;
317591 }
318592
319 fn incrementModificationCount(hm: *Self) void {
320 if (want_modification_safety) {
321 hm.modification_count +%= 1;
593 fn updateEntryIndex(
594 self: *Self,
595 header: *IndexHeader,
596 old_entry_index: usize,
597 new_entry_index: usize,
598 comptime I: type,
599 indexes: []Index(I),
600 ) void {
601 const h = if (store_hash) self.entries.items[new_entry_index].hash else hash(self.entries.items[new_entry_index].key);
602 const start_index = header.constrainIndex(h);
603 var roll_over: usize = 0;
604 while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
605 const index_index = header.constrainIndex(start_index + roll_over);
606 const index = &indexes[index_index];
607 if (index.entry_index == old_entry_index) {
608 index.entry_index = @intCast(I, new_entry_index);
609 return;
610 }
322611 }
612 unreachable;
323613 }
324614
325 const InternalPutResult = struct {
326 new_entry: *Entry,
327 old_kv: ?KV,
328 };
329
330 /// Returns a pointer to the new entry.
331 /// Asserts that there is enough space for the new item.
332 fn internalPut(self: *Self, orig_key: K) InternalPutResult {
333 var key = orig_key;
334 var value: V = undefined;
335 const start_index = self.keyToIndex(key);
615 /// Must ensureCapacity before calling this.
616 fn getOrPutInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) GetOrPutResult {
617 const indexes = header.indexes(I);
618 const h = hash(key);
619 const start_index = header.constrainIndex(h);
336620 var roll_over: usize = 0;
337621 var distance_from_start_index: usize = 0;
338 var got_result_entry = false;
339 var result = InternalPutResult{
340 .new_entry = undefined,
341 .old_kv = null,
342 };
343 while (roll_over < self.entries.len) : ({
622 while (roll_over <= header.indexes_len) : ({
344623 roll_over += 1;
345624 distance_from_start_index += 1;
346625 }) {
347 const index = self.constrainIndex(start_index + roll_over);
348 const entry = &self.entries[index];
349
350 if (entry.used and !eql(entry.kv.key, key)) {
351 if (entry.distance_from_start_index < distance_from_start_index) {
352 // robin hood to the rescue
353 const tmp = entry.*;
354 self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index);
355 if (!got_result_entry) {
356 got_result_entry = true;
357 result.new_entry = entry;
626 const index_index = header.constrainIndex(start_index + roll_over);
627 const index = indexes[index_index];
628 if (index.isEmpty()) {
629 indexes[index_index] = .{
630 .distance_from_start_index = @intCast(I, distance_from_start_index),
631 .entry_index = @intCast(I, self.entries.items.len),
632 };
633 header.maybeBumpMax(distance_from_start_index);
634 const new_entry = self.entries.addOneAssumeCapacity();
635 new_entry.* = .{
636 .hash = if (store_hash) h else {},
637 .key = key,
638 .value = undefined,
639 };
640 return .{
641 .found_existing = false,
642 .entry = new_entry,
643 };
644 }
645
646 // This pointer survives the following append because we call
647 // entries.ensureCapacity before getOrPutInternal.
648 const entry = &self.entries.items[index.entry_index];
649 const hash_match = if (store_hash) h == entry.hash else true;
650 if (hash_match and eql(key, entry.key)) {
651 return .{
652 .found_existing = true,
653 .entry = entry,
654 };
655 }
656 if (index.distance_from_start_index < distance_from_start_index) {
657 // In this case, we did not find the item. We will put a new entry.
658 // However, we will use this index for the new entry, and move
659 // the previous index down the line, to keep the max_distance_from_start_index
660 // as small as possible.
661 indexes[index_index] = .{
662 .distance_from_start_index = @intCast(I, distance_from_start_index),
663 .entry_index = @intCast(I, self.entries.items.len),
664 };
665 header.maybeBumpMax(distance_from_start_index);
666 const new_entry = self.entries.addOneAssumeCapacity();
667 new_entry.* = .{
668 .hash = if (store_hash) h else {},
669 .key = key,
670 .value = undefined,
671 };
672
673 distance_from_start_index = index.distance_from_start_index;
674 var prev_entry_index = index.entry_index;
675
676 // Find somewhere to put the index we replaced by shifting
677 // following indexes backwards.
678 roll_over += 1;
679 distance_from_start_index += 1;
680 while (roll_over < header.indexes_len) : ({
681 roll_over += 1;
682 distance_from_start_index += 1;
683 }) {
684 const next_index_index = header.constrainIndex(start_index + roll_over);
685 const next_index = indexes[next_index_index];
686 if (next_index.isEmpty()) {
687 header.maybeBumpMax(distance_from_start_index);
688 indexes[next_index_index] = .{
689 .entry_index = prev_entry_index,
690 .distance_from_start_index = @intCast(I, distance_from_start_index),
691 };
692 return .{
693 .found_existing = false,
694 .entry = new_entry,
695 };
696 }
697 if (next_index.distance_from_start_index < distance_from_start_index) {
698 header.maybeBumpMax(distance_from_start_index);
699 indexes[next_index_index] = .{
700 .entry_index = prev_entry_index,
701 .distance_from_start_index = @intCast(I, distance_from_start_index),
702 };
703 distance_from_start_index = next_index.distance_from_start_index;
704 prev_entry_index = next_index.entry_index;
358705 }
359 entry.* = Entry{
360 .used = true,
361 .distance_from_start_index = distance_from_start_index,
362 .kv = KV{
363 .key = key,
364 .value = value,
365 },
366 };
367 key = tmp.kv.key;
368 value = tmp.kv.value;
369 distance_from_start_index = tmp.distance_from_start_index;
370706 }
371 continue;
707 unreachable;
372708 }
709 }
710 unreachable;
711 }
373712
374 if (entry.used) {
375 result.old_kv = entry.kv;
376 } else {
377 // adding an entry. otherwise overwriting old value with
378 // same key
379 self.size += 1;
380 }
713 fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry {
714 const indexes = header.indexes(I);
715 const h = hash(key);
716 const start_index = header.constrainIndex(h);
717 var roll_over: usize = 0;
718 while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
719 const index_index = header.constrainIndex(start_index + roll_over);
720 const index = indexes[index_index];
721 if (index.isEmpty())
722 return null;
723
724 const entry = &self.entries.items[index.entry_index];
725 const hash_match = if (store_hash) h == entry.hash else true;
726 if (hash_match and eql(key, entry.key))
727 return entry;
728 }
729 return null;
730 }
381731
382 self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index);
383 if (!got_result_entry) {
384 result.new_entry = entry;
385 }
386 entry.* = Entry{
387 .used = true,
388 .distance_from_start_index = distance_from_start_index,
389 .kv = KV{
390 .key = key,
391 .value = value,
392 },
393 };
394 return result;
732 fn insertAllEntriesIntoNewHeader(self: *Self, header: *IndexHeader) void {
733 switch (header.capacityIndexType()) {
734 .u8 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u8),
735 .u16 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u16),
736 .u32 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u32),
737 .usize => return self.insertAllEntriesIntoNewHeaderGeneric(header, usize),
395738 }
396 unreachable; // put into a full map
397739 }
398740
399 fn internalGet(hm: Self, key: K) ?*KV {
400 const start_index = hm.keyToIndex(key);
401 {
741 fn insertAllEntriesIntoNewHeaderGeneric(self: *Self, header: *IndexHeader, comptime I: type) void {
742 const indexes = header.indexes(I);
743 entry_loop: for (self.entries.items) |entry, i| {
744 const h = if (store_hash) entry.hash else hash(entry.key);
745 const start_index = header.constrainIndex(h);
746 var entry_index = i;
402747 var roll_over: usize = 0;
403 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
404 const index = hm.constrainIndex(start_index + roll_over);
405 const entry = &hm.entries[index];
406
407 if (!entry.used) return null;
408 if (eql(entry.kv.key, key)) return &entry.kv;
748 var distance_from_start_index: usize = 0;
749 while (roll_over < header.indexes_len) : ({
750 roll_over += 1;
751 distance_from_start_index += 1;
752 }) {
753 const index_index = header.constrainIndex(start_index + roll_over);
754 const next_index = indexes[index_index];
755 if (next_index.isEmpty()) {
756 header.maybeBumpMax(distance_from_start_index);
757 indexes[index_index] = .{
758 .distance_from_start_index = @intCast(I, distance_from_start_index),
759 .entry_index = @intCast(I, entry_index),
760 };
761 continue :entry_loop;
762 }
763 if (next_index.distance_from_start_index < distance_from_start_index) {
764 header.maybeBumpMax(distance_from_start_index);
765 indexes[index_index] = .{
766 .distance_from_start_index = @intCast(I, distance_from_start_index),
767 .entry_index = @intCast(I, entry_index),
768 };
769 distance_from_start_index = next_index.distance_from_start_index;
770 entry_index = next_index.entry_index;
771 }
409772 }
773 unreachable;
410774 }
411 return null;
412775 }
776 };
777}
778
779const CapacityIndexType = enum { u8, u16, u32, usize };
780
781fn capacityIndexType(indexes_len: usize) CapacityIndexType {
782 if (indexes_len < math.maxInt(u8))
783 return .u8;
784 if (indexes_len < math.maxInt(u16))
785 return .u16;
786 if (indexes_len < math.maxInt(u32))
787 return .u32;
788 return .usize;
789}
790
791fn capacityIndexSize(indexes_len: usize) usize {
792 switch (capacityIndexType(indexes_len)) {
793 .u8 => return @sizeOf(Index(u8)),
794 .u16 => return @sizeOf(Index(u16)),
795 .u32 => return @sizeOf(Index(u32)),
796 .usize => return @sizeOf(Index(usize)),
797 }
798}
799
800fn Index(comptime I: type) type {
801 return extern struct {
802 entry_index: I,
803 distance_from_start_index: I,
804
805 const Self = @This();
806
807 const empty = Self{
808 .entry_index = math.maxInt(I),
809 .distance_from_start_index = undefined,
810 };
413811
414 fn keyToIndex(hm: Self, key: K) usize {
415 return hm.constrainIndex(@as(usize, hash(key)));
812 fn isEmpty(idx: Self) bool {
813 return idx.entry_index == math.maxInt(I);
416814 }
417815
418 fn constrainIndex(hm: Self, i: usize) usize {
419 // this is an optimization for modulo of power of two integers;
420 // it requires hm.entries.len to always be a power of two
421 return i & (hm.entries.len - 1);
816 fn setEmpty(idx: *Self) void {
817 idx.entry_index = math.maxInt(I);
422818 }
423819 };
424820}
425821
822/// This struct is trailed by an array of `Index(I)`, where `I`
823/// and the array length are determined by `indexes_len`.
824const IndexHeader = struct {
825 max_distance_from_start_index: usize,
826 indexes_len: usize,
827
828 fn constrainIndex(header: IndexHeader, i: usize) usize {
829 // This is an optimization for modulo of power of two integers;
830 // it requires `indexes_len` to always be a power of two.
831 return i & (header.indexes_len - 1);
832 }
833
834 fn indexes(header: *IndexHeader, comptime I: type) []Index(I) {
835 const start = @ptrCast([*]Index(I), @ptrCast([*]u8, header) + @sizeOf(IndexHeader));
836 return start[0..header.indexes_len];
837 }
838
839 fn capacityIndexType(header: IndexHeader) CapacityIndexType {
840 return hash_map.capacityIndexType(header.indexes_len);
841 }
842
843 fn maybeBumpMax(header: *IndexHeader, distance_from_start_index: usize) void {
844 if (distance_from_start_index > header.max_distance_from_start_index) {
845 header.max_distance_from_start_index = distance_from_start_index;
846 }
847 }
848
849 fn alloc(allocator: *Allocator, len: usize) !*IndexHeader {
850 const index_size = hash_map.capacityIndexSize(len);
851 const nbytes = @sizeOf(IndexHeader) + index_size * len;
852 const bytes = try allocator.allocAdvanced(u8, @alignOf(IndexHeader), nbytes, .exact);
853 @memset(bytes.ptr + @sizeOf(IndexHeader), 0xff, bytes.len - @sizeOf(IndexHeader));
854 const result = @ptrCast(*IndexHeader, bytes.ptr);
855 result.* = .{
856 .max_distance_from_start_index = 0,
857 .indexes_len = len,
858 };
859 return result;
860 }
861
862 fn free(header: *IndexHeader, allocator: *Allocator) void {
863 const index_size = hash_map.capacityIndexSize(header.indexes_len);
864 const ptr = @ptrCast([*]u8, header);
865 const slice = ptr[0 .. @sizeOf(IndexHeader) + header.indexes_len * index_size];
866 allocator.free(slice);
867 }
868};
869
426870test "basic hash map usage" {
427871 var map = AutoHashMap(i32, i32).init(std.testing.allocator);
428872 defer map.deinit();
429873
430 testing.expect((try map.put(1, 11)) == null);
431 testing.expect((try map.put(2, 22)) == null);
432 testing.expect((try map.put(3, 33)) == null);
433 testing.expect((try map.put(4, 44)) == null);
874 testing.expect((try map.fetchPut(1, 11)) == null);
875 testing.expect((try map.fetchPut(2, 22)) == null);
876 testing.expect((try map.fetchPut(3, 33)) == null);
877 testing.expect((try map.fetchPut(4, 44)) == null);
434878
435879 try map.putNoClobber(5, 55);
436 testing.expect((try map.put(5, 66)).?.value == 55);
437 testing.expect((try map.put(5, 55)).?.value == 66);
880 testing.expect((try map.fetchPut(5, 66)).?.value == 55);
881 testing.expect((try map.fetchPut(5, 55)).?.value == 66);
438882
439883 const gop1 = try map.getOrPut(5);
440884 testing.expect(gop1.found_existing == true);
441 testing.expect(gop1.kv.value == 55);
442 gop1.kv.value = 77;
443 testing.expect(map.get(5).?.value == 77);
885 testing.expect(gop1.entry.value == 55);
886 gop1.entry.value = 77;
887 testing.expect(map.getEntry(5).?.value == 77);
444888
445889 const gop2 = try map.getOrPut(99);
446890 testing.expect(gop2.found_existing == false);
447 gop2.kv.value = 42;
448 testing.expect(map.get(99).?.value == 42);
891 gop2.entry.value = 42;
892 testing.expect(map.getEntry(99).?.value == 42);
449893
450894 const gop3 = try map.getOrPutValue(5, 5);
451895 testing.expect(gop3.value == 77);
......@@ -454,15 +898,15 @@ test "basic hash map usage" {
454898 testing.expect(gop4.value == 41);
455899
456900 testing.expect(map.contains(2));
457 testing.expect(map.get(2).?.value == 22);
458 testing.expect(map.getValue(2).? == 22);
901 testing.expect(map.getEntry(2).?.value == 22);
902 testing.expect(map.get(2).? == 22);
459903
460904 const rmv1 = map.remove(2);
461905 testing.expect(rmv1.?.key == 2);
462906 testing.expect(rmv1.?.value == 22);
463907 testing.expect(map.remove(2) == null);
908 testing.expect(map.getEntry(2) == null);
464909 testing.expect(map.get(2) == null);
465 testing.expect(map.getValue(2) == null);
466910
467911 map.removeAssertDiscard(3);
468912}
......@@ -498,8 +942,8 @@ test "iterator hash map" {
498942 it.reset();
499943
500944 var count: usize = 0;
501 while (it.next()) |kv| : (count += 1) {
502 buffer[@intCast(usize, kv.key)] = kv.value;
945 while (it.next()) |entry| : (count += 1) {
946 buffer[@intCast(usize, entry.key)] = entry.value;
503947 }
504948 testing.expect(count == 3);
505949 testing.expect(it.next() == null);
......@@ -510,8 +954,8 @@ test "iterator hash map" {
510954
511955 it.reset();
512956 count = 0;
513 while (it.next()) |kv| {
514 buffer[@intCast(usize, kv.key)] = kv.value;
957 while (it.next()) |entry| {
958 buffer[@intCast(usize, entry.key)] = entry.value;
515959 count += 1;
516960 if (count >= 2) break;
517961 }
......@@ -531,14 +975,33 @@ test "ensure capacity" {
531975 defer map.deinit();
532976
533977 try map.ensureCapacity(20);
534 const initialCapacity = map.entries.len;
535 testing.expect(initialCapacity >= 20);
978 const initial_capacity = map.capacity();
979 testing.expect(initial_capacity >= 20);
536980 var i: i32 = 0;
537981 while (i < 20) : (i += 1) {
538 testing.expect(map.putAssumeCapacity(i, i + 10) == null);
982 testing.expect(map.fetchPutAssumeCapacity(i, i + 10) == null);
539983 }
540984 // shouldn't resize from putAssumeCapacity
541 testing.expect(initialCapacity == map.entries.len);
985 testing.expect(initial_capacity == map.capacity());
986}
987
988test "clone" {
989 var original = AutoHashMap(i32, i32).init(std.testing.allocator);
990 defer original.deinit();
991
992 // put more than `linear_scan_max` so we can test that the index header is properly cloned
993 var i: u8 = 0;
994 while (i < 10) : (i += 1) {
995 try original.putNoClobber(i, i * 10);
996 }
997
998 var copy = try original.clone();
999 defer copy.deinit();
1000
1001 i = 0;
1002 while (i < 10) : (i += 1) {
1003 testing.expect(copy.get(i).? == i * 10);
1004 }
5421005}
5431006
5441007pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
......@@ -575,6 +1038,24 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
5751038 }.eql;
5761039}
5771040
1041pub fn autoEqlIsCheap(comptime K: type) bool {
1042 return switch (@typeInfo(K)) {
1043 .Bool,
1044 .Int,
1045 .Float,
1046 .Pointer,
1047 .ComptimeFloat,
1048 .ComptimeInt,
1049 .Enum,
1050 .Fn,
1051 .ErrorSet,
1052 .AnyFrame,
1053 .EnumLiteral,
1054 => true,
1055 else => false,
1056 };
1057}
1058
5781059pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) {
5791060 return struct {
5801061 fn hash(key: K) u32 {
lib/std/heap.zig+287-325
......@@ -15,23 +15,59 @@ pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
1515
1616const Allocator = mem.Allocator;
1717
18usingnamespace if (comptime @hasDecl(c, "malloc_size"))
19 struct {
20 pub const supports_malloc_size = true;
21 pub const malloc_size = c.malloc_size;
22 }
23else if (comptime @hasDecl(c, "malloc_usable_size"))
24 struct {
25 pub const supports_malloc_size = true;
26 pub const malloc_size = c.malloc_usable_size;
27 }
28else
29 struct {
30 pub const supports_malloc_size = false;
31 };
32
1833pub const c_allocator = &c_allocator_state;
1934var c_allocator_state = Allocator{
20 .reallocFn = cRealloc,
21 .shrinkFn = cShrink,
35 .allocFn = cAlloc,
36 .resizeFn = cResize,
2237};
2338
24fn cRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
25 assert(new_align <= @alignOf(c_longdouble));
26 const old_ptr = if (old_mem.len == 0) null else @ptrCast(*c_void, old_mem.ptr);
27 const buf = c.realloc(old_ptr, new_size) orelse return error.OutOfMemory;
28 return @ptrCast([*]u8, buf)[0..new_size];
39fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 {
40 assert(ptr_align <= @alignOf(c_longdouble));
41 const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory);
42 if (len_align == 0) {
43 return ptr[0..len];
44 }
45 const full_len = init: {
46 if (supports_malloc_size) {
47 const s = malloc_size(ptr);
48 assert(s >= len);
49 break :init s;
50 }
51 break :init len;
52 };
53 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
2954}
3055
31fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
32 const old_ptr = @ptrCast(*c_void, old_mem.ptr);
33 const buf = c.realloc(old_ptr, new_size) orelse return old_mem[0..new_size];
34 return @ptrCast([*]u8, buf)[0..new_size];
56fn cResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize {
57 if (new_len == 0) {
58 c.free(buf.ptr);
59 return 0;
60 }
61 if (new_len <= buf.len) {
62 return mem.alignAllocLen(buf.len, new_len, len_align);
63 }
64 if (supports_malloc_size) {
65 const full_len = malloc_size(buf.ptr);
66 if (new_len <= full_len) {
67 return mem.alignAllocLen(full_len, new_len, len_align);
68 }
69 }
70 return error.OutOfMemory;
3571}
3672
3773/// This allocator makes a syscall directly for every allocation and free.
......@@ -44,19 +80,27 @@ else
4480 &page_allocator_state;
4581
4682var page_allocator_state = Allocator{
47 .reallocFn = PageAllocator.realloc,
48 .shrinkFn = PageAllocator.shrink,
83 .allocFn = PageAllocator.alloc,
84 .resizeFn = PageAllocator.resize,
4985};
5086var wasm_page_allocator_state = Allocator{
51 .reallocFn = WasmPageAllocator.realloc,
52 .shrinkFn = WasmPageAllocator.shrink,
87 .allocFn = WasmPageAllocator.alloc,
88 .resizeFn = WasmPageAllocator.resize,
5389};
5490
5591pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator");
5692
93/// Verifies that the adjusted length will still map to the full length
94pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {
95 const aligned_len = mem.alignAllocLen(full_len, len, len_align);
96 assert(mem.alignForward(aligned_len, mem.page_size) == full_len);
97 return aligned_len;
98}
99
57100const PageAllocator = struct {
58 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
59 if (n == 0) return &[0]u8{};
101 fn alloc(allocator: *Allocator, n: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 {
102 assert(n > 0);
103 const alignedLen = mem.alignForward(n, mem.page_size);
60104
61105 if (builtin.os.tag == .windows) {
62106 const w = os.windows;
......@@ -68,21 +112,21 @@ const PageAllocator = struct {
68112 // see https://devblogs.microsoft.com/oldnewthing/?p=42223
69113 const addr = w.VirtualAlloc(
70114 null,
71 n,
115 alignedLen,
72116 w.MEM_COMMIT | w.MEM_RESERVE,
73117 w.PAGE_READWRITE,
74118 ) catch return error.OutOfMemory;
75119
76120 // If the allocation is sufficiently aligned, use it.
77121 if (@ptrToInt(addr) & (alignment - 1) == 0) {
78 return @ptrCast([*]u8, addr)[0..n];
122 return @ptrCast([*]u8, addr)[0..alignPageAllocLen(alignedLen, n, len_align)];
79123 }
80124
81125 // If it wasn't, actually do an explicitely aligned allocation.
82126 w.VirtualFree(addr, 0, w.MEM_RELEASE);
83 const alloc_size = n + alignment;
127 const alloc_size = n + alignment - mem.page_size;
84128
85 const final_addr = while (true) {
129 while (true) {
86130 // Reserve a range of memory large enough to find a sufficiently
87131 // aligned address.
88132 const reserved_addr = w.VirtualAlloc(
......@@ -102,48 +146,49 @@ const PageAllocator = struct {
102146 // until it succeeds.
103147 const ptr = w.VirtualAlloc(
104148 @intToPtr(*c_void, aligned_addr),
105 n,
149 alignedLen,
106150 w.MEM_COMMIT | w.MEM_RESERVE,
107151 w.PAGE_READWRITE,
108152 ) catch continue;
109153
110 return @ptrCast([*]u8, ptr)[0..n];
111 };
112
113 return @ptrCast([*]u8, final_addr)[0..n];
154 return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(alignedLen, n, len_align)];
155 }
114156 }
115157
116 const alloc_size = if (alignment <= mem.page_size) n else n + alignment;
158 const maxDropLen = alignment - std.math.min(alignment, mem.page_size);
159 const allocLen = if (maxDropLen <= alignedLen - n) alignedLen else mem.alignForward(alignedLen + maxDropLen, mem.page_size);
117160 const slice = os.mmap(
118161 null,
119 mem.alignForward(alloc_size, mem.page_size),
162 allocLen,
120163 os.PROT_READ | os.PROT_WRITE,
121164 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
122165 -1,
123166 0,
124167 ) catch return error.OutOfMemory;
125 if (alloc_size == n) return slice[0..n];
168 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));
126169
127170 const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment);
128171
129172 // Unmap the extra bytes that were only requested in order to guarantee
130173 // that the range of memory we were provided had a proper alignment in
131174 // it somewhere. The extra bytes could be at the beginning, or end, or both.
132 const unused_start_len = aligned_addr - @ptrToInt(slice.ptr);
133 if (unused_start_len != 0) {
134 os.munmap(slice[0..unused_start_len]);
175 const dropLen = aligned_addr - @ptrToInt(slice.ptr);
176 if (dropLen != 0) {
177 os.munmap(slice[0..dropLen]);
135178 }
136 const aligned_end_addr = mem.alignForward(aligned_addr + n, mem.page_size);
137 const unused_end_len = @ptrToInt(slice.ptr) + slice.len - aligned_end_addr;
138 if (unused_end_len != 0) {
139 os.munmap(@intToPtr([*]align(mem.page_size) u8, aligned_end_addr)[0..unused_end_len]);
179
180 // Unmap extra pages
181 const alignedBufferLen = allocLen - dropLen;
182 if (alignedBufferLen > alignedLen) {
183 os.munmap(@alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr))[alignedLen..alignedBufferLen]);
140184 }
141185
142 return @intToPtr([*]u8, aligned_addr)[0..n];
186 return @intToPtr([*]u8, aligned_addr)[0..alignPageAllocLen(alignedLen, n, len_align)];
143187 }
144188
145 fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
146 const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
189 fn resize(allocator: *Allocator, buf_unaligned: []u8, new_size: usize, len_align: u29) Allocator.Error!usize {
190 const new_size_aligned = mem.alignForward(new_size, mem.page_size);
191
147192 if (builtin.os.tag == .windows) {
148193 const w = os.windows;
149194 if (new_size == 0) {
......@@ -153,100 +198,45 @@ const PageAllocator = struct {
153198 // is reserved in the initial allocation call to VirtualAlloc."
154199 // So we can only use MEM_RELEASE when actually releasing the
155200 // whole allocation.
156 w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE);
157 } else {
158 const base_addr = @ptrToInt(old_mem.ptr);
159 const old_addr_end = base_addr + old_mem.len;
160 const new_addr_end = base_addr + new_size;
161 const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size);
162 if (old_addr_end > new_addr_end_rounded) {
201 w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE);
202 return 0;
203 }
204 if (new_size < buf_unaligned.len) {
205 const base_addr = @ptrToInt(buf_unaligned.ptr);
206 const old_addr_end = base_addr + buf_unaligned.len;
207 const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size);
208 if (old_addr_end > new_addr_end) {
163209 // For shrinking that is not releasing, we will only
164210 // decommit the pages not needed anymore.
165211 w.VirtualFree(
166 @intToPtr(*c_void, new_addr_end_rounded),
167 old_addr_end - new_addr_end_rounded,
212 @intToPtr(*c_void, new_addr_end),
213 old_addr_end - new_addr_end,
168214 w.MEM_DECOMMIT,
169215 );
170216 }
217 return alignPageAllocLen(new_size_aligned, new_size, len_align);
171218 }
172 return old_mem[0..new_size];
173 }
174 const base_addr = @ptrToInt(old_mem.ptr);
175 const old_addr_end = base_addr + old_mem.len;
176 const new_addr_end = base_addr + new_size;
177 const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size);
178 if (old_addr_end > new_addr_end_rounded) {
179 const ptr = @intToPtr([*]align(mem.page_size) u8, new_addr_end_rounded);
180 os.munmap(ptr[0 .. old_addr_end - new_addr_end_rounded]);
181 }
182 return old_mem[0..new_size];
183 }
184
185 fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
186 const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
187 if (builtin.os.tag == .windows) {
188 if (old_mem.len == 0) {
189 return alloc(allocator, new_size, new_align);
190 }
191
192 if (new_size <= old_mem.len and new_align <= old_align) {
193 return shrink(allocator, old_mem, old_align, new_size, new_align);
194 }
195
196 const w = os.windows;
197 const base_addr = @ptrToInt(old_mem.ptr);
198
199 if (new_align > old_align and base_addr & (new_align - 1) != 0) {
200 // Current allocation doesn't satisfy the new alignment.
201 // For now we'll do a new one no matter what, but maybe
202 // there is something smarter to do instead.
203 const result = try alloc(allocator, new_size, new_align);
204 assert(old_mem.len != 0);
205 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
206 w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE);
207
208 return result;
209 }
210
211 const old_addr_end = base_addr + old_mem.len;
212 const old_addr_end_rounded = mem.alignForward(old_addr_end, mem.page_size);
213 const new_addr_end = base_addr + new_size;
214 const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size);
215 if (new_addr_end_rounded == old_addr_end_rounded) {
216 // The reallocation fits in the already allocated pages.
217 return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
219 if (new_size == buf_unaligned.len) {
220 return alignPageAllocLen(new_size_aligned, new_size, len_align);
218221 }
219 assert(new_addr_end_rounded > old_addr_end_rounded);
222 // new_size > buf_unaligned.len not implemented
223 return error.OutOfMemory;
224 }
220225
221 // We need to commit new pages.
222 const additional_size = new_addr_end - old_addr_end_rounded;
223 const realloc_addr = w.kernel32.VirtualAlloc(
224 @intToPtr(*c_void, old_addr_end_rounded),
225 additional_size,
226 w.MEM_COMMIT | w.MEM_RESERVE,
227 w.PAGE_READWRITE,
228 ) orelse {
229 // Committing new pages at the end of the existing allocation
230 // failed, we need to try a new one.
231 const new_alloc_mem = try alloc(allocator, new_size, new_align);
232 @memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len);
233 w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE);
234
235 return new_alloc_mem;
236 };
226 const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size);
227 if (new_size_aligned == buf_aligned_len)
228 return alignPageAllocLen(new_size_aligned, new_size, len_align);
237229
238 assert(@ptrToInt(realloc_addr) == old_addr_end_rounded);
239 return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
240 }
241 if (new_size <= old_mem.len and new_align <= old_align) {
242 return shrink(allocator, old_mem, old_align, new_size, new_align);
230 if (new_size_aligned < buf_aligned_len) {
231 const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned);
232 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);
233 if (new_size_aligned == 0)
234 return 0;
235 return alignPageAllocLen(new_size_aligned, new_size, len_align);
243236 }
244 const result = try alloc(allocator, new_size, new_align);
245 if (old_mem.len != 0) {
246 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
247 os.munmap(old_mem);
248 }
249 return result;
237
238 // TODO: call mremap
239 return error.OutOfMemory;
250240 }
251241};
252242
......@@ -299,7 +289,7 @@ const WasmPageAllocator = struct {
299289 // Revisit if this is settled: https://github.com/ziglang/zig/issues/3806
300290 const not_found = std.math.maxInt(usize);
301291
302 fn useRecycled(self: FreeBlock, num_pages: usize) usize {
292 fn useRecycled(self: FreeBlock, num_pages: usize, alignment: u29) usize {
303293 @setCold(true);
304294 for (self.data) |segment, i| {
305295 const spills_into_next = @bitCast(i128, segment) < 0;
......@@ -312,7 +302,8 @@ const WasmPageAllocator = struct {
312302 var count: usize = 0;
313303 while (j + count < self.totalPages() and self.getBit(j + count) == .free) {
314304 count += 1;
315 if (count >= num_pages) {
305 const addr = j * mem.page_size;
306 if (count >= num_pages and mem.isAligned(addr, alignment)) {
316307 self.setBits(j, num_pages, .used);
317308 return j;
318309 }
......@@ -338,73 +329,72 @@ const WasmPageAllocator = struct {
338329 }
339330
340331 fn nPages(memsize: usize) usize {
341 return std.mem.alignForward(memsize, std.mem.page_size) / std.mem.page_size;
332 return mem.alignForward(memsize, mem.page_size) / mem.page_size;
342333 }
343334
344 fn alloc(allocator: *Allocator, page_count: usize, alignment: u29) error{OutOfMemory}!usize {
345 var idx = conventional.useRecycled(page_count);
346 if (idx != FreeBlock.not_found) {
347 return idx;
335 fn alloc(allocator: *Allocator, len: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 {
336 const page_count = nPages(len);
337 const page_idx = try allocPages(page_count, alignment);
338 return @intToPtr([*]u8, page_idx * mem.page_size)[0..alignPageAllocLen(page_count * mem.page_size, len, len_align)];
339 }
340 fn allocPages(page_count: usize, alignment: u29) !usize {
341 {
342 const idx = conventional.useRecycled(page_count, alignment);
343 if (idx != FreeBlock.not_found) {
344 return idx;
345 }
348346 }
349347
350 idx = extended.useRecycled(page_count);
348 const idx = extended.useRecycled(page_count, alignment);
351349 if (idx != FreeBlock.not_found) {
352350 return idx + extendedOffset();
353351 }
354352
355 const prev_page_count = @wasmMemoryGrow(0, @intCast(u32, page_count));
356 if (prev_page_count <= 0) {
353 const next_page_idx = @wasmMemorySize(0);
354 const next_page_addr = next_page_idx * mem.page_size;
355 const aligned_addr = mem.alignForward(next_page_addr, alignment);
356 const drop_page_count = @divExact(aligned_addr - next_page_addr, mem.page_size);
357 const result = @wasmMemoryGrow(0, @intCast(u32, drop_page_count + page_count));
358 if (result <= 0)
357359 return error.OutOfMemory;
360 assert(result == next_page_idx);
361 const aligned_page_idx = next_page_idx + drop_page_count;
362 if (drop_page_count > 0) {
363 freePages(next_page_idx, aligned_page_idx);
358364 }
359
360 return @intCast(usize, prev_page_count);
365 return @intCast(usize, aligned_page_idx);
361366 }
362367
363 pub fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) Allocator.Error![]u8 {
364 if (new_align > std.mem.page_size) {
365 return error.OutOfMemory;
368 fn freePages(start: usize, end: usize) void {
369 if (start < extendedOffset()) {
370 conventional.recycle(start, std.math.min(extendedOffset(), end) - start);
366371 }
367
368 if (nPages(new_size) == nPages(old_mem.len)) {
369 return old_mem.ptr[0..new_size];
370 } else if (new_size < old_mem.len) {
371 return shrink(allocator, old_mem, old_align, new_size, new_align);
372 } else {
373 const page_idx = try alloc(allocator, nPages(new_size), new_align);
374 const new_mem = @intToPtr([*]u8, page_idx * std.mem.page_size)[0..new_size];
375 std.mem.copy(u8, new_mem, old_mem);
376 _ = shrink(allocator, old_mem, old_align, 0, 0);
377 return new_mem;
372 if (end > extendedOffset()) {
373 var new_end = end;
374 if (!extended.isInitialized()) {
375 // Steal the last page from the memory currently being recycled
376 // TODO: would it be better if we use the first page instead?
377 new_end -= 1;
378
379 extended.data = @intToPtr([*]u128, new_end * mem.page_size)[0 .. mem.page_size / @sizeOf(u128)];
380 // Since this is the first page being freed and we consume it, assume *nothing* is free.
381 mem.set(u128, extended.data, PageStatus.none_free);
382 }
383 const clamped_start = std.math.max(extendedOffset(), start);
384 extended.recycle(clamped_start - extendedOffset(), new_end - clamped_start);
378385 }
379386 }
380387
381 pub fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
382 @setCold(true);
383 const free_start = nPages(@ptrToInt(old_mem.ptr) + new_size);
384 var free_end = nPages(@ptrToInt(old_mem.ptr) + old_mem.len);
385
386 if (free_end > free_start) {
387 if (free_start < extendedOffset()) {
388 const clamped_end = std.math.min(extendedOffset(), free_end);
389 conventional.recycle(free_start, clamped_end - free_start);
390 }
391
392 if (free_end > extendedOffset()) {
393 if (!extended.isInitialized()) {
394 // Steal the last page from the memory currently being recycled
395 // TODO: would it be better if we use the first page instead?
396 free_end -= 1;
397
398 extended.data = @intToPtr([*]u128, free_end * std.mem.page_size)[0 .. std.mem.page_size / @sizeOf(u128)];
399 // Since this is the first page being freed and we consume it, assume *nothing* is free.
400 std.mem.set(u128, extended.data, PageStatus.none_free);
401 }
402 const clamped_start = std.math.max(extendedOffset(), free_start);
403 extended.recycle(clamped_start - extendedOffset(), free_end - clamped_start);
404 }
388 fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
389 const aligned_len = mem.alignForward(buf.len, mem.page_size);
390 if (new_len > aligned_len) return error.OutOfMemory;
391 const current_n = nPages(aligned_len);
392 const new_n = nPages(new_len);
393 if (new_n != current_n) {
394 const base = nPages(@ptrToInt(buf.ptr));
395 freePages(base + new_n, base + current_n);
405396 }
406
407 return old_mem[0..new_size];
397 return if (new_len == 0) 0 else alignPageAllocLen(new_n * mem.page_size, new_len, len_align);
408398 }
409399};
410400
......@@ -418,8 +408,8 @@ pub const HeapAllocator = switch (builtin.os.tag) {
418408 pub fn init() HeapAllocator {
419409 return HeapAllocator{
420410 .allocator = Allocator{
421 .reallocFn = realloc,
422 .shrinkFn = shrink,
411 .allocFn = alloc,
412 .resizeFn = resize,
423413 },
424414 .heap_handle = null,
425415 };
......@@ -431,11 +421,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {
431421 }
432422 }
433423
434 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
424 fn getRecordPtr(buf: []u8) *align(1) usize {
425 return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len);
426 }
427
428 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
435429 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
436 if (n == 0) return &[0]u8{};
437430
438 const amt = n + alignment + @sizeOf(usize);
431 const amt = n + ptr_align - 1 + @sizeOf(usize);
439432 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
440433 const heap_handle = optional_heap_handle orelse blk: {
441434 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;
......@@ -446,66 +439,60 @@ pub const HeapAllocator = switch (builtin.os.tag) {
446439 };
447440 const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
448441 const root_addr = @ptrToInt(ptr);
449 const adjusted_addr = mem.alignForward(root_addr, alignment);
450 const record_addr = adjusted_addr + n;
451 @intToPtr(*align(1) usize, record_addr).* = root_addr;
452 return @intToPtr([*]u8, adjusted_addr)[0..n];
453 }
454
455 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
456 return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
457 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
458 const old_record_addr = old_adjusted_addr + old_mem.len;
459 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
460 const old_ptr = @intToPtr(*c_void, root_addr);
461 const new_record_addr = old_record_addr - new_size + old_mem.len;
462 @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
463 return old_mem[0..new_size];
442 const aligned_addr = mem.alignForward(root_addr, ptr_align);
443 const return_len = init: {
444 if (len_align == 0) break :init n;
445 const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr);
446 assert(full_len != std.math.maxInt(usize));
447 assert(full_len >= amt);
448 break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr), len_align);
464449 };
450 const buf = @intToPtr([*]u8, aligned_addr)[0..return_len];
451 getRecordPtr(buf).* = root_addr;
452 return buf;
465453 }
466454
467 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
468 if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
469
455 fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize {
470456 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
471 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
472 const old_record_addr = old_adjusted_addr + old_mem.len;
473 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
474 const old_ptr = @intToPtr(*c_void, root_addr);
475
476457 if (new_size == 0) {
477 os.windows.HeapFree(self.heap_handle.?, 0, old_ptr);
478 return old_mem[0..0];
458 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void, getRecordPtr(buf).*));
459 return 0;
479460 }
480461
481 const amt = new_size + new_align + @sizeOf(usize);
462 const root_addr = getRecordPtr(buf).*;
463 const align_offset = @ptrToInt(buf.ptr) - root_addr;
464 const amt = align_offset + new_size + @sizeOf(usize);
482465 const new_ptr = os.windows.kernel32.HeapReAlloc(
483466 self.heap_handle.?,
484 0,
485 old_ptr,
467 os.windows.HEAP_REALLOC_IN_PLACE_ONLY,
468 @intToPtr(*c_void, root_addr),
486469 amt,
487470 ) orelse return error.OutOfMemory;
488 const offset = old_adjusted_addr - root_addr;
489 const new_root_addr = @ptrToInt(new_ptr);
490 var new_adjusted_addr = new_root_addr + offset;
491 const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt;
492 const offset_is_aligned = new_adjusted_addr % new_align == 0;
493 if (!offset_is_valid or !offset_is_aligned) {
494 // If HeapReAlloc didn't happen to move the memory to the new alignment,
495 // or the memory starting at the old offset would be outside of the new allocation,
496 // then we need to copy the memory to a valid aligned address and use that
497 const new_aligned_addr = mem.alignForward(new_root_addr, new_align);
498 @memcpy(@intToPtr([*]u8, new_aligned_addr), @intToPtr([*]u8, new_adjusted_addr), std.math.min(old_mem.len, new_size));
499 new_adjusted_addr = new_aligned_addr;
500 }
501 const new_record_addr = new_adjusted_addr + new_size;
502 @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
503 return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
471 assert(new_ptr == @intToPtr(*c_void, root_addr));
472 const return_len = init: {
473 if (len_align == 0) break :init new_size;
474 const full_len = os.windows.kernel32.HeapSize(self.heap_handle.?, 0, new_ptr);
475 assert(full_len != std.math.maxInt(usize));
476 assert(full_len >= amt);
477 break :init mem.alignBackwardAnyAlign(full_len - align_offset, len_align);
478 };
479 getRecordPtr(buf.ptr[0..return_len]).* = root_addr;
480 return return_len;
504481 }
505482 },
506483 else => @compileError("Unsupported OS"),
507484};
508485
486fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool {
487 return @ptrToInt(ptr) >= @ptrToInt(container.ptr) and
488 @ptrToInt(ptr) < (@ptrToInt(container.ptr) + container.len);
489}
490
491fn sliceContainsSlice(container: []u8, slice: []u8) bool {
492 return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and
493 (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len);
494}
495
509496pub const FixedBufferAllocator = struct {
510497 allocator: Allocator,
511498 end_index: usize,
......@@ -514,19 +501,33 @@ pub const FixedBufferAllocator = struct {
514501 pub fn init(buffer: []u8) FixedBufferAllocator {
515502 return FixedBufferAllocator{
516503 .allocator = Allocator{
517 .reallocFn = realloc,
518 .shrinkFn = shrink,
504 .allocFn = alloc,
505 .resizeFn = resize,
519506 },
520507 .buffer = buffer,
521508 .end_index = 0,
522509 };
523510 }
524511
525 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
512 pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool {
513 return sliceContainsPtr(self.buffer, ptr);
514 }
515
516 pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool {
517 return sliceContainsSlice(self.buffer, slice);
518 }
519
520 /// NOTE: this will not work in all cases, if the last allocation had an adjusted_index
521 /// then we won't be able to determine what the last allocation was. This is because
522 /// the alignForward operation done in alloc is not reverisible.
523 pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool {
524 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
525 }
526
527 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
526528 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
527 const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
528 const adjusted_addr = mem.alignForward(addr, alignment);
529 const adjusted_index = self.end_index + (adjusted_addr - addr);
529 const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align);
530 const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr);
530531 const new_end_index = adjusted_index + n;
531532 if (new_end_index > self.buffer.len) {
532533 return error.OutOfMemory;
......@@ -537,30 +538,28 @@ pub const FixedBufferAllocator = struct {
537538 return result;
538539 }
539540
540 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
541 fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) Allocator.Error!usize {
541542 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
542 assert(old_mem.len <= self.end_index);
543 if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len and
544 mem.alignForward(@ptrToInt(old_mem.ptr), new_align) == @ptrToInt(old_mem.ptr))
545 {
546 const start_index = self.end_index - old_mem.len;
547 const new_end_index = start_index + new_size;
548 if (new_end_index > self.buffer.len) return error.OutOfMemory;
549 const result = self.buffer[start_index..new_end_index];
550 self.end_index = new_end_index;
551 return result;
552 } else if (new_size <= old_mem.len and new_align <= old_align) {
553 // We can't do anything with the memory, so tell the client to keep it.
554 return error.OutOfMemory;
555 } else {
556 const result = try alloc(allocator, new_size, new_align);
557 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
558 return result;
543 assert(self.ownsSlice(buf)); // sanity check
544
545 if (!self.isLastAllocation(buf)) {
546 if (new_size > buf.len)
547 return error.OutOfMemory;
548 return if (new_size == 0) 0 else mem.alignAllocLen(buf.len, new_size, len_align);
549 }
550
551 if (new_size <= buf.len) {
552 const sub = buf.len - new_size;
553 self.end_index -= sub;
554 return if (new_size == 0) 0 else mem.alignAllocLen(buf.len - sub, new_size, len_align);
559555 }
560 }
561556
562 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
563 return old_mem[0..new_size];
557 const add = new_size - buf.len;
558 if (add + self.end_index > self.buffer.len) {
559 return error.OutOfMemory;
560 }
561 self.end_index += add;
562 return new_size;
564563 }
565564
566565 pub fn reset(self: *FixedBufferAllocator) void {
......@@ -581,20 +580,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
581580 pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
582581 return ThreadSafeFixedBufferAllocator{
583582 .allocator = Allocator{
584 .reallocFn = realloc,
585 .shrinkFn = shrink,
583 .allocFn = alloc,
584 .resizeFn = Allocator.noResize,
586585 },
587586 .buffer = buffer,
588587 .end_index = 0,
589588 };
590589 }
591590
592 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
591 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
593592 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
594593 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
595594 while (true) {
596595 const addr = @ptrToInt(self.buffer.ptr) + end_index;
597 const adjusted_addr = mem.alignForward(addr, alignment);
596 const adjusted_addr = mem.alignForward(addr, ptr_align);
598597 const adjusted_index = end_index + (adjusted_addr - addr);
599598 const new_end_index = adjusted_index + n;
600599 if (new_end_index > self.buffer.len) {
......@@ -604,21 +603,6 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
604603 }
605604 }
606605
607 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
608 if (new_size <= old_mem.len and new_align <= old_align) {
609 // We can't do anything useful with the memory, tell the client to keep it.
610 return error.OutOfMemory;
611 } else {
612 const result = try alloc(allocator, new_size, new_align);
613 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
614 return result;
615 }
616 }
617
618 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
619 return old_mem[0..new_size];
620 }
621
622606 pub fn reset(self: *ThreadSafeFixedBufferAllocator) void {
623607 self.end_index = 0;
624608 }
......@@ -632,8 +616,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack
632616 .fallback_allocator = fallback_allocator,
633617 .fixed_buffer_allocator = undefined,
634618 .allocator = Allocator{
635 .reallocFn = StackFallbackAllocator(size).realloc,
636 .shrinkFn = StackFallbackAllocator(size).shrink,
619 .allocFn = StackFallbackAllocator(size).realloc,
620 .resizeFn = StackFallbackAllocator(size).resize,
637621 },
638622 };
639623}
......@@ -652,58 +636,19 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
652636 return &self.allocator;
653637 }
654638
655 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
639 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![*]u8 {
656640 const self = @fieldParentPtr(Self, "allocator", allocator);
657 const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and
658 @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
659 if (in_buffer) {
660 return FixedBufferAllocator.realloc(
661 &self.fixed_buffer_allocator.allocator,
662 old_mem,
663 old_align,
664 new_size,
665 new_align,
666 ) catch {
667 const result = try self.fallback_allocator.reallocFn(
668 self.fallback_allocator,
669 &[0]u8{},
670 undefined,
671 new_size,
672 new_align,
673 );
674 mem.copy(u8, result, old_mem);
675 return result;
676 };
677 }
678 return self.fallback_allocator.reallocFn(
679 self.fallback_allocator,
680 old_mem,
681 old_align,
682 new_size,
683 new_align,
684 );
641 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch
642 return fallback_allocator.alloc(len, ptr_align);
685643 }
686644
687 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
645 fn resize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!void {
688646 const self = @fieldParentPtr(Self, "allocator", allocator);
689 const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and
690 @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
691 if (in_buffer) {
692 return FixedBufferAllocator.shrink(
693 &self.fixed_buffer_allocator.allocator,
694 old_mem,
695 old_align,
696 new_size,
697 new_align,
698 );
647 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
648 try self.fixed_buffer_allocator.callResizeFn(buf, new_len);
649 } else {
650 try self.fallback_allocator.callResizeFn(buf, new_len);
699651 }
700 return self.fallback_allocator.shrinkFn(
701 self.fallback_allocator,
702 old_mem,
703 old_align,
704 new_size,
705 new_align,
706 );
707652 }
708653 };
709654}
......@@ -718,8 +663,8 @@ test "c_allocator" {
718663
719664test "WasmPageAllocator internals" {
720665 if (comptime std.Target.current.isWasm()) {
721 const conventional_memsize = WasmPageAllocator.conventional.totalPages() * std.mem.page_size;
722 const initial = try page_allocator.alloc(u8, std.mem.page_size);
666 const conventional_memsize = WasmPageAllocator.conventional.totalPages() * mem.page_size;
667 const initial = try page_allocator.alloc(u8, mem.page_size);
723668 std.debug.assert(@ptrToInt(initial.ptr) < conventional_memsize); // If this isn't conventional, the rest of these tests don't make sense. Also we have a serious memory leak in the test suite.
724669
725670 var inplace = try page_allocator.realloc(initial, 1);
......@@ -772,6 +717,11 @@ test "PageAllocator" {
772717 slice[127] = 0x34;
773718 allocator.free(slice);
774719 }
720 {
721 var buf = try allocator.alloc(u8, mem.page_size + 1);
722 defer allocator.free(buf);
723 buf = try allocator.realloc(buf, 1); // shrink past the page boundary
724 }
775725}
776726
777727test "HeapAllocator" {
......@@ -799,7 +749,7 @@ test "ArenaAllocator" {
799749
800750var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined;
801751test "FixedBufferAllocator" {
802 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
752 var fixed_buffer_allocator = mem.validationWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]));
803753
804754 try testAllocator(&fixed_buffer_allocator.allocator);
805755 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
......@@ -865,7 +815,10 @@ test "ThreadSafeFixedBufferAllocator" {
865815 try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
866816}
867817
868fn testAllocator(allocator: *mem.Allocator) !void {
818pub fn testAllocator(base_allocator: *mem.Allocator) !void {
819 var validationAllocator = mem.validationWrap(base_allocator);
820 const allocator = &validationAllocator.allocator;
821
869822 var slice = try allocator.alloc(*i32, 100);
870823 testing.expect(slice.len == 100);
871824 for (slice) |*item, i| {
......@@ -893,7 +846,10 @@ fn testAllocator(allocator: *mem.Allocator) !void {
893846 allocator.free(slice);
894847}
895848
896fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void {
849pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {
850 var validationAllocator = mem.validationWrap(base_allocator);
851 const allocator = &validationAllocator.allocator;
852
897853 // initial
898854 var slice = try allocator.alignedAlloc(u8, alignment, 10);
899855 testing.expect(slice.len == 10);
......@@ -917,7 +873,10 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi
917873 testing.expect(slice.len == 0);
918874}
919875
920fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void {
876pub fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator.Error!void {
877 var validationAllocator = mem.validationWrap(base_allocator);
878 const allocator = &validationAllocator.allocator;
879
921880 //Maybe a platform's page_size is actually the same as or
922881 // very near usize?
923882 if (mem.page_size << 2 > maxInt(usize)) return;
......@@ -946,7 +905,10 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo
946905 allocator.free(slice);
947906}
948907
949fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void {
908pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator.Error!void {
909 var validationAllocator = mem.validationWrap(base_allocator);
910 const allocator = &validationAllocator.allocator;
911
950912 var debug_buffer: [1000]u8 = undefined;
951913 const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator;
952914
lib/std/heap/arena_allocator.zig+8-24
......@@ -20,8 +20,8 @@ pub const ArenaAllocator = struct {
2020 pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator {
2121 return .{
2222 .allocator = Allocator{
23 .reallocFn = realloc,
24 .shrinkFn = shrink,
23 .allocFn = alloc,
24 .resizeFn = Allocator.noResize,
2525 },
2626 .child_allocator = child_allocator,
2727 .state = self,
......@@ -49,9 +49,8 @@ pub const ArenaAllocator = struct {
4949 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);
5050 const big_enough_len = prev_len + actual_min_size;
5151 const len = big_enough_len + big_enough_len / 2;
52 const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len);
53 const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]);
54 const buf_node = &buf_node_slice[0];
52 const buf = try self.child_allocator.callAllocFn(len, @alignOf(BufNode), 1);
53 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr));
5554 buf_node.* = BufNode{
5655 .data = buf,
5756 .next = null,
......@@ -61,18 +60,18 @@ pub const ArenaAllocator = struct {
6160 return buf_node;
6261 }
6362
64 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
63 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
6564 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
6665
67 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);
66 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align);
6867 while (true) {
6968 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
7069 const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index;
71 const adjusted_addr = mem.alignForward(addr, alignment);
70 const adjusted_addr = mem.alignForward(addr, ptr_align);
7271 const adjusted_index = self.state.end_index + (adjusted_addr - addr);
7372 const new_end_index = adjusted_index + n;
7473 if (new_end_index > cur_buf.len) {
75 cur_node = try self.createNode(cur_buf.len, n + alignment);
74 cur_node = try self.createNode(cur_buf.len, n + ptr_align);
7675 continue;
7776 }
7877 const result = cur_buf[adjusted_index..new_end_index];
......@@ -80,19 +79,4 @@ pub const ArenaAllocator = struct {
8079 return result;
8180 }
8281 }
83
84 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
85 if (new_size <= old_mem.len and new_align <= new_size) {
86 // We can't do anything with the memory, so tell the client to keep it.
87 return error.OutOfMemory;
88 } else {
89 const result = try alloc(allocator, new_size, new_align);
90 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
91 return result;
92 }
93 }
94
95 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
96 return old_mem[0..new_size];
97 }
9882};
lib/std/heap/logging_allocator.zig+38-25
......@@ -15,62 +15,75 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
1515 pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
1616 return Self{
1717 .allocator = Allocator{
18 .reallocFn = realloc,
19 .shrinkFn = shrink,
18 .allocFn = alloc,
19 .resizeFn = resize,
2020 },
2121 .parent_allocator = parent_allocator,
2222 .out_stream = out_stream,
2323 };
2424 }
2525
26 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
26 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
2727 const self = @fieldParentPtr(Self, "allocator", allocator);
28 if (old_mem.len == 0) {
29 self.out_stream.print("allocation of {} ", .{new_size}) catch {};
30 } else {
31 self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {};
32 }
33 const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
28 self.out_stream.print("alloc : {}", .{len}) catch {};
29 const result = self.parent_allocator.callAllocFn(len, ptr_align, len_align);
3430 if (result) |buff| {
35 self.out_stream.print("success!\n", .{}) catch {};
31 self.out_stream.print(" success!\n", .{}) catch {};
3632 } else |err| {
37 self.out_stream.print("failure!\n", .{}) catch {};
33 self.out_stream.print(" failure!\n", .{}) catch {};
3834 }
3935 return result;
4036 }
4137
42 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
38 fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
4339 const self = @fieldParentPtr(Self, "allocator", allocator);
44 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
45 if (new_size == 0) {
46 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};
40 if (new_len == 0) {
41 self.out_stream.print("free : {}\n", .{buf.len}) catch {};
42 } else if (new_len <= buf.len) {
43 self.out_stream.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
4744 } else {
48 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};
45 self.out_stream.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
46 }
47 if (self.parent_allocator.callResizeFn(buf, new_len, len_align)) |resized_len| {
48 if (new_len > buf.len) {
49 self.out_stream.print(" success!\n", .{}) catch {};
50 }
51 return resized_len;
52 } else |e| {
53 std.debug.assert(new_len > buf.len);
54 self.out_stream.print(" failure!\n", .{}) catch {};
55 return e;
4956 }
50 return result;
5157 }
5258 };
5359}
5460
5561pub fn loggingAllocator(
5662 parent_allocator: *Allocator,
57 out_stream: var,
63 out_stream: anytype,
5864) LoggingAllocator(@TypeOf(out_stream)) {
5965 return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);
6066}
6167
6268test "LoggingAllocator" {
63 var buf: [255]u8 = undefined;
64 var fbs = std.io.fixedBufferStream(&buf);
69 var log_buf: [255]u8 = undefined;
70 var fbs = std.io.fixedBufferStream(&log_buf);
6571
66 const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator;
72 var allocator_buf: [10]u8 = undefined;
73 var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
74 const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator;
6775
68 const ptr = try allocator.alloc(u8, 10);
69 allocator.free(ptr);
76 var a = try allocator.alloc(u8, 10);
77 a.len = allocator.shrinkBytes(a, 5, 0);
78 std.debug.assert(a.len == 5);
79 std.testing.expectError(error.OutOfMemory, allocator.callResizeFn(a, 20, 0));
80 allocator.free(a);
7081
7182 std.testing.expectEqualSlices(u8,
72 \\allocation of 10 success!
73 \\free of 10 bytes success!
83 \\alloc : 10 success!
84 \\shrink: 10 to 5
85 \\expand: 5 to 20 failure!
86 \\free : 5
7487 \\
7588 , fbs.getWritten());
7689}
lib/std/http/headers.zig+83-87
......@@ -27,7 +27,6 @@ fn never_index_default(name: []const u8) bool {
2727}
2828
2929const HeaderEntry = struct {
30 allocator: *Allocator,
3130 name: []const u8,
3231 value: []u8,
3332 never_index: bool,
......@@ -36,23 +35,22 @@ const HeaderEntry = struct {
3635
3736 fn init(allocator: *Allocator, name: []const u8, value: []const u8, never_index: ?bool) !Self {
3837 return Self{
39 .allocator = allocator,
4038 .name = name, // takes reference
41 .value = try mem.dupe(allocator, u8, value),
39 .value = try allocator.dupe(u8, value),
4240 .never_index = never_index orelse never_index_default(name),
4341 };
4442 }
4543
46 fn deinit(self: Self) void {
47 self.allocator.free(self.value);
44 fn deinit(self: Self, allocator: *Allocator) void {
45 allocator.free(self.value);
4846 }
4947
50 pub fn modify(self: *Self, value: []const u8, never_index: ?bool) !void {
48 pub fn modify(self: *Self, allocator: *Allocator, value: []const u8, never_index: ?bool) !void {
5149 const old_len = self.value.len;
5250 if (value.len > old_len) {
53 self.value = try self.allocator.realloc(self.value, value.len);
51 self.value = try allocator.realloc(self.value, value.len);
5452 } else if (value.len < old_len) {
55 self.value = self.allocator.shrink(self.value, value.len);
53 self.value = allocator.shrink(self.value, value.len);
5654 }
5755 mem.copy(u8, self.value, value);
5856 self.never_index = never_index orelse never_index_default(self.name);
......@@ -85,22 +83,22 @@ const HeaderEntry = struct {
8583
8684test "HeaderEntry" {
8785 var e = try HeaderEntry.init(testing.allocator, "foo", "bar", null);
88 defer e.deinit();
86 defer e.deinit(testing.allocator);
8987 testing.expectEqualSlices(u8, "foo", e.name);
9088 testing.expectEqualSlices(u8, "bar", e.value);
9189 testing.expectEqual(false, e.never_index);
9290
93 try e.modify("longer value", null);
91 try e.modify(testing.allocator, "longer value", null);
9492 testing.expectEqualSlices(u8, "longer value", e.value);
9593
9694 // shorter value
97 try e.modify("x", null);
95 try e.modify(testing.allocator, "x", null);
9896 testing.expectEqualSlices(u8, "x", e.value);
9997}
10098
101const HeaderList = std.ArrayList(HeaderEntry);
102const HeaderIndexList = std.ArrayList(usize);
103const HeaderIndex = std.StringHashMap(HeaderIndexList);
99const HeaderList = std.ArrayListUnmanaged(HeaderEntry);
100const HeaderIndexList = std.ArrayListUnmanaged(usize);
101const HeaderIndex = std.StringHashMapUnmanaged(HeaderIndexList);
104102
105103pub const Headers = struct {
106104 // the owned header field name is stored in the index as part of the key
......@@ -113,62 +111,62 @@ pub const Headers = struct {
113111 pub fn init(allocator: *Allocator) Self {
114112 return Self{
115113 .allocator = allocator,
116 .data = HeaderList.init(allocator),
117 .index = HeaderIndex.init(allocator),
114 .data = HeaderList{},
115 .index = HeaderIndex{},
118116 };
119117 }
120118
121 pub fn deinit(self: Self) void {
119 pub fn deinit(self: *Self) void {
122120 {
123 var it = self.index.iterator();
124 while (it.next()) |kv| {
125 var dex = &kv.value;
126 dex.deinit();
127 self.allocator.free(kv.key);
121 for (self.index.items()) |*entry| {
122 const dex = &entry.value;
123 dex.deinit(self.allocator);
124 self.allocator.free(entry.key);
128125 }
129 self.index.deinit();
126 self.index.deinit(self.allocator);
130127 }
131128 {
132 for (self.data.span()) |entry| {
133 entry.deinit();
129 for (self.data.items) |entry| {
130 entry.deinit(self.allocator);
134131 }
135 self.data.deinit();
132 self.data.deinit(self.allocator);
136133 }
134 self.* = undefined;
137135 }
138136
139137 pub fn clone(self: Self, allocator: *Allocator) !Self {
140138 var other = Headers.init(allocator);
141139 errdefer other.deinit();
142 try other.data.ensureCapacity(self.data.items.len);
143 try other.index.initCapacity(self.index.entries.len);
144 for (self.data.span()) |entry| {
140 try other.data.ensureCapacity(allocator, self.data.items.len);
141 try other.index.initCapacity(allocator, self.index.entries.len);
142 for (self.data.items) |entry| {
145143 try other.append(entry.name, entry.value, entry.never_index);
146144 }
147145 return other;
148146 }
149147
150148 pub fn toSlice(self: Self) []const HeaderEntry {
151 return self.data.span();
149 return self.data.items;
152150 }
153151
154152 pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void {
155153 const n = self.data.items.len + 1;
156 try self.data.ensureCapacity(n);
154 try self.data.ensureCapacity(self.allocator, n);
157155 var entry: HeaderEntry = undefined;
158 if (self.index.get(name)) |kv| {
156 if (self.index.getEntry(name)) |kv| {
159157 entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index);
160 errdefer entry.deinit();
161 var dex = &kv.value;
162 try dex.append(n - 1);
158 errdefer entry.deinit(self.allocator);
159 const dex = &kv.value;
160 try dex.append(self.allocator, n - 1);
163161 } else {
164 const name_dup = try mem.dupe(self.allocator, u8, name);
162 const name_dup = try self.allocator.dupe(u8, name);
165163 errdefer self.allocator.free(name_dup);
166164 entry = try HeaderEntry.init(self.allocator, name_dup, value, never_index);
167 errdefer entry.deinit();
168 var dex = HeaderIndexList.init(self.allocator);
169 try dex.append(n - 1);
170 errdefer dex.deinit();
171 _ = try self.index.put(name_dup, dex);
165 errdefer entry.deinit(self.allocator);
166 var dex = HeaderIndexList{};
167 try dex.append(self.allocator, n - 1);
168 errdefer dex.deinit(self.allocator);
169 _ = try self.index.put(self.allocator, name_dup, dex);
172170 }
173171 self.data.appendAssumeCapacity(entry);
174172 }
......@@ -194,8 +192,8 @@ pub const Headers = struct {
194192
195193 /// Returns boolean indicating if something was deleted.
196194 pub fn delete(self: *Self, name: []const u8) bool {
197 if (self.index.remove(name)) |kv| {
198 var dex = &kv.value;
195 if (self.index.remove(name)) |*kv| {
196 const dex = &kv.value;
199197 // iterate backwards
200198 var i = dex.items.len;
201199 while (i > 0) {
......@@ -203,11 +201,11 @@ pub const Headers = struct {
203201 const data_index = dex.items[i];
204202 const removed = self.data.orderedRemove(data_index);
205203 assert(mem.eql(u8, removed.name, name));
206 removed.deinit();
204 removed.deinit(self.allocator);
207205 }
208 dex.deinit();
206 dex.deinit(self.allocator);
209207 self.allocator.free(kv.key);
210 self.rebuild_index();
208 self.rebuildIndex();
211209 return true;
212210 } else {
213211 return false;
......@@ -216,45 +214,52 @@ pub const Headers = struct {
216214
217215 /// Removes the element at the specified index.
218216 /// Moves items down to fill the empty space.
217 /// TODO this implementation can be replaced by adding
218 /// orderedRemove to the new hash table implementation as an
219 /// alternative to swapRemove.
219220 pub fn orderedRemove(self: *Self, i: usize) void {
220221 const removed = self.data.orderedRemove(i);
221 const kv = self.index.get(removed.name).?;
222 var dex = &kv.value;
222 const kv = self.index.getEntry(removed.name).?;
223 const dex = &kv.value;
223224 if (dex.items.len == 1) {
224225 // was last item; delete the index
225 _ = self.index.remove(kv.key);
226 dex.deinit();
227 removed.deinit();
228 self.allocator.free(kv.key);
226 dex.deinit(self.allocator);
227 removed.deinit(self.allocator);
228 const key = kv.key;
229 _ = self.index.remove(key); // invalidates `kv` and `dex`
230 self.allocator.free(key);
229231 } else {
230 dex.shrink(dex.items.len - 1);
231 removed.deinit();
232 dex.shrink(self.allocator, dex.items.len - 1);
233 removed.deinit(self.allocator);
232234 }
233235 // if it was the last item; no need to rebuild index
234236 if (i != self.data.items.len) {
235 self.rebuild_index();
237 self.rebuildIndex();
236238 }
237239 }
238240
239241 /// Removes the element at the specified index.
240242 /// The empty slot is filled from the end of the list.
243 /// TODO this implementation can be replaced by simply using the
244 /// new hash table which does swap removal.
241245 pub fn swapRemove(self: *Self, i: usize) void {
242246 const removed = self.data.swapRemove(i);
243 const kv = self.index.get(removed.name).?;
244 var dex = &kv.value;
247 const kv = self.index.getEntry(removed.name).?;
248 const dex = &kv.value;
245249 if (dex.items.len == 1) {
246250 // was last item; delete the index
247 _ = self.index.remove(kv.key);
248 dex.deinit();
249 removed.deinit();
250 self.allocator.free(kv.key);
251 dex.deinit(self.allocator);
252 removed.deinit(self.allocator);
253 const key = kv.key;
254 _ = self.index.remove(key); // invalidates `kv` and `dex`
255 self.allocator.free(key);
251256 } else {
252 dex.shrink(dex.items.len - 1);
253 removed.deinit();
257 dex.shrink(self.allocator, dex.items.len - 1);
258 removed.deinit(self.allocator);
254259 }
255260 // if it was the last item; no need to rebuild index
256261 if (i != self.data.items.len) {
257 self.rebuild_index();
262 self.rebuildIndex();
258263 }
259264 }
260265
......@@ -266,11 +271,7 @@ pub const Headers = struct {
266271 /// Returns a list of indices containing headers with the given name.
267272 /// The returned list should not be modified by the caller.
268273 pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList {
269 if (self.index.get(name)) |kv| {
270 return kv.value;
271 } else {
272 return null;
273 }
274 return self.index.get(name);
274275 }
275276
276277 /// Returns a slice containing each header with the given name.
......@@ -279,7 +280,7 @@ pub const Headers = struct {
279280
280281 const buf = try allocator.alloc(HeaderEntry, dex.items.len);
281282 var n: usize = 0;
282 for (dex.span()) |idx| {
283 for (dex.items) |idx| {
283284 buf[n] = self.data.items[idx];
284285 n += 1;
285286 }
......@@ -302,7 +303,7 @@ pub const Headers = struct {
302303 // adapted from mem.join
303304 const total_len = blk: {
304305 var sum: usize = dex.items.len - 1; // space for separator(s)
305 for (dex.span()) |idx|
306 for (dex.items) |idx|
306307 sum += self.data.items[idx].value.len;
307308 break :blk sum;
308309 };
......@@ -325,32 +326,27 @@ pub const Headers = struct {
325326 return buf;
326327 }
327328
328 fn rebuild_index(self: *Self) void {
329 { // clear out the indexes
330 var it = self.index.iterator();
331 while (it.next()) |kv| {
332 var dex = &kv.value;
333 dex.items.len = 0; // keeps capacity available
334 }
329 fn rebuildIndex(self: *Self) void {
330 // clear out the indexes
331 for (self.index.items()) |*entry| {
332 entry.value.shrinkRetainingCapacity(0);
335333 }
336 { // fill up indexes again; we know capacity is fine from before
337 for (self.data.span()) |entry, i| {
338 var dex = &self.index.get(entry.name).?.value;
339 dex.appendAssumeCapacity(i);
340 }
334 // fill up indexes again; we know capacity is fine from before
335 for (self.data.items) |entry, i| {
336 self.index.getEntry(entry.name).?.value.appendAssumeCapacity(i);
341337 }
342338 }
343339
344340 pub fn sort(self: *Self) void {
345341 std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare);
346 self.rebuild_index();
342 self.rebuildIndex();
347343 }
348344
349345 pub fn format(
350346 self: Self,
351347 comptime fmt: []const u8,
352348 options: std.fmt.FormatOptions,
353 out_stream: var,
349 out_stream: anytype,
354350 ) !void {
355351 for (self.toSlice()) |entry| {
356352 try out_stream.writeAll(entry.name);
......@@ -495,8 +491,8 @@ test "Headers.getIndices" {
495491 try h.append("set-cookie", "y=2", null);
496492
497493 testing.expect(null == h.getIndices("not-present"));
498 testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.span());
499 testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.span());
494 testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.items);
495 testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.items);
500496}
501497
502498test "Headers.get" {
lib/std/io/bit_reader.zig+1-1
......@@ -170,7 +170,7 @@ pub fn BitReader(endian: builtin.Endian, comptime ReaderType: type) type {
170170
171171pub fn bitReader(
172172 comptime endian: builtin.Endian,
173 underlying_stream: var,
173 underlying_stream: anytype,
174174) BitReader(endian, @TypeOf(underlying_stream)) {
175175 return BitReader(endian, @TypeOf(underlying_stream)).init(underlying_stream);
176176}
lib/std/io/bit_writer.zig+2-2
......@@ -34,7 +34,7 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {
3434 /// Write the specified number of bits to the stream from the least significant bits of
3535 /// the specified unsigned int value. Bits will only be written to the stream when there
3636 /// are enough to fill a byte.
37 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {
37 pub fn writeBits(self: *Self, value: anytype, bits: usize) Error!void {
3838 if (bits == 0) return;
3939
4040 const U = @TypeOf(value);
......@@ -145,7 +145,7 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {
145145
146146pub fn bitWriter(
147147 comptime endian: builtin.Endian,
148 underlying_stream: var,
148 underlying_stream: anytype,
149149) BitWriter(endian, @TypeOf(underlying_stream)) {
150150 return BitWriter(endian, @TypeOf(underlying_stream)).init(underlying_stream);
151151}
lib/std/io/buffered_out_stream.zig+1-1
......@@ -2,4 +2,4 @@
22pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter;
33
44/// Deprecated: use `std.io.buffered_writer.bufferedWriter`
5pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter
5pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter;
lib/std/io/buffered_reader.zig+1-1
......@@ -48,7 +48,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
4848 };
4949}
5050
51pub fn bufferedReader(underlying_stream: var) BufferedReader(4096, @TypeOf(underlying_stream)) {
51pub fn bufferedReader(underlying_stream: anytype) BufferedReader(4096, @TypeOf(underlying_stream)) {
5252 return .{ .unbuffered_reader = underlying_stream };
5353}
5454
lib/std/io/buffered_writer.zig+1-1
......@@ -43,6 +43,6 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty
4343 };
4444}
4545
46pub fn bufferedWriter(underlying_stream: var) BufferedWriter(4096, @TypeOf(underlying_stream)) {
46pub fn bufferedWriter(underlying_stream: anytype) BufferedWriter(4096, @TypeOf(underlying_stream)) {
4747 return .{ .unbuffered_writer = underlying_stream };
4848}
lib/std/io/counting_writer.zig+1-1
......@@ -32,7 +32,7 @@ pub fn CountingWriter(comptime WriterType: type) type {
3232 };
3333}
3434
35pub fn countingWriter(child_stream: var) CountingWriter(@TypeOf(child_stream)) {
35pub fn countingWriter(child_stream: anytype) CountingWriter(@TypeOf(child_stream)) {
3636 return .{ .bytes_written = 0, .child_stream = child_stream };
3737}
3838
lib/std/io/fixed_buffer_stream.zig+1-1
......@@ -127,7 +127,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
127127 };
128128}
129129
130pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
130pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
131131 return .{ .buffer = mem.span(buffer), .pos = 0 };
132132}
133133
lib/std/io/multi_writer.zig+1-1
......@@ -43,7 +43,7 @@ pub fn MultiWriter(comptime Writers: type) type {
4343 };
4444}
4545
46pub fn multiWriter(streams: var) MultiWriter(@TypeOf(streams)) {
46pub fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) {
4747 return .{ .streams = streams };
4848}
4949
lib/std/io/peek_stream.zig+1-1
......@@ -80,7 +80,7 @@ pub fn PeekStream(
8080
8181pub fn peekStream(
8282 comptime lookahead: comptime_int,
83 underlying_stream: var,
83 underlying_stream: anytype,
8484) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) {
8585 return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream);
8686}
lib/std/io/reader.zig+1-2
......@@ -40,8 +40,7 @@ pub fn Reader(
4040 return index;
4141 }
4242
43 /// Returns the number of bytes read. If the number read would be smaller than buf.len,
44 /// error.EndOfStream is returned instead.
43 /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
4544 pub fn readNoEof(self: Self, buf: []u8) !void {
4645 const amt_read = try self.readAll(buf);
4746 if (amt_read < buf.len) return error.EndOfStream;
lib/std/io/serialization.zig+33-29
......@@ -16,14 +16,16 @@ pub const Packing = enum {
1616};
1717
1818/// Creates a deserializer that deserializes types from any stream.
19/// If `is_packed` is true, the data stream is treated as bit-packed,
20/// otherwise data is expected to be packed to the smallest byte.
21/// Types may implement a custom deserialization routine with a
22/// function named `deserialize` in the form of:
23/// pub fn deserialize(self: *Self, deserializer: var) !void
24/// which will be called when the deserializer is used to deserialize
25/// that type. It will pass a pointer to the type instance to deserialize
26/// into and a pointer to the deserializer struct.
19/// If `is_packed` is true, the data stream is treated as bit-packed,
20/// otherwise data is expected to be packed to the smallest byte.
21/// Types may implement a custom deserialization routine with a
22/// function named `deserialize` in the form of:
23/// ```
24/// pub fn deserialize(self: *Self, deserializer: anytype) !void
25/// ```
26/// which will be called when the deserializer is used to deserialize
27/// that type. It will pass a pointer to the type instance to deserialize
28/// into and a pointer to the deserializer struct.
2729pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type {
2830 return struct {
2931 in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType,
......@@ -93,7 +95,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
9395 }
9496
9597 /// Deserializes data into the type pointed to by `ptr`
96 pub fn deserializeInto(self: *Self, ptr: var) !void {
98 pub fn deserializeInto(self: *Self, ptr: anytype) !void {
9799 const T = @TypeOf(ptr);
98100 comptime assert(trait.is(.Pointer)(T));
99101
......@@ -108,7 +110,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
108110 const C = comptime meta.Child(T);
109111 const child_type_id = @typeInfo(C);
110112
111 //custom deserializer: fn(self: *Self, deserializer: var) !void
113 //custom deserializer: fn(self: *Self, deserializer: anytype) !void
112114 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
113115
114116 if (comptime trait.isPacked(C) and packing != .Bit) {
......@@ -190,24 +192,26 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
190192pub fn deserializer(
191193 comptime endian: builtin.Endian,
192194 comptime packing: Packing,
193 in_stream: var,
195 in_stream: anytype,
194196) Deserializer(endian, packing, @TypeOf(in_stream)) {
195197 return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
196198}
197199
198200/// Creates a serializer that serializes types to any stream.
199/// If `is_packed` is true, the data will be bit-packed into the stream.
200/// Note that the you must call `serializer.flush()` when you are done
201/// writing bit-packed data in order ensure any unwritten bits are committed.
202/// If `is_packed` is false, data is packed to the smallest byte. In the case
203/// of packed structs, the struct will written bit-packed and with the specified
204/// endianess, after which data will resume being written at the next byte boundary.
205/// Types may implement a custom serialization routine with a
206/// function named `serialize` in the form of:
207/// pub fn serialize(self: Self, serializer: var) !void
208/// which will be called when the serializer is used to serialize that type. It will
209/// pass a const pointer to the type instance to be serialized and a pointer
210/// to the serializer struct.
201/// If `is_packed` is true, the data will be bit-packed into the stream.
202/// Note that the you must call `serializer.flush()` when you are done
203/// writing bit-packed data in order ensure any unwritten bits are committed.
204/// If `is_packed` is false, data is packed to the smallest byte. In the case
205/// of packed structs, the struct will written bit-packed and with the specified
206/// endianess, after which data will resume being written at the next byte boundary.
207/// Types may implement a custom serialization routine with a
208/// function named `serialize` in the form of:
209/// ```
210/// pub fn serialize(self: Self, serializer: anytype) !void
211/// ```
212/// which will be called when the serializer is used to serialize that type. It will
213/// pass a const pointer to the type instance to be serialized and a pointer
214/// to the serializer struct.
211215pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
212216 return struct {
213217 out_stream: if (packing == .Bit) io.BitOutStream(endian, OutStreamType) else OutStreamType,
......@@ -229,7 +233,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
229233 if (packing == .Bit) return self.out_stream.flushBits();
230234 }
231235
232 fn serializeInt(self: *Self, value: var) Error!void {
236 fn serializeInt(self: *Self, value: anytype) Error!void {
233237 const T = @TypeOf(value);
234238 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
235239
......@@ -261,7 +265,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
261265 }
262266
263267 /// Serializes the passed value into the stream
264 pub fn serialize(self: *Self, value: var) Error!void {
268 pub fn serialize(self: *Self, value: anytype) Error!void {
265269 const T = comptime @TypeOf(value);
266270
267271 if (comptime trait.isIndexable(T)) {
......@@ -270,7 +274,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
270274 return;
271275 }
272276
273 //custom serializer: fn(self: Self, serializer: var) !void
277 //custom serializer: fn(self: Self, serializer: anytype) !void
274278 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
275279
276280 if (comptime trait.isPacked(T) and packing != .Bit) {
......@@ -346,7 +350,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
346350pub fn serializer(
347351 comptime endian: builtin.Endian,
348352 comptime packing: Packing,
349 out_stream: var,
353 out_stream: anytype,
350354) Serializer(endian, packing, @TypeOf(out_stream)) {
351355 return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
352356}
......@@ -462,7 +466,7 @@ test "Serializer/Deserializer Int: Inf/NaN" {
462466 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
463467}
464468
465fn testAlternateSerializer(self: var, _serializer: var) !void {
469fn testAlternateSerializer(self: anytype, _serializer: anytype) !void {
466470 try _serializer.serialize(self.f_f16);
467471}
468472
......@@ -503,7 +507,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing:
503507 f_f16: f16,
504508 f_unused_u32: u32,
505509
506 pub fn deserialize(self: *@This(), _deserializer: var) !void {
510 pub fn deserialize(self: *@This(), _deserializer: anytype) !void {
507511 try _deserializer.deserializeInto(&self.f_f16);
508512 self.f_unused_u32 = 47;
509513 }
lib/std/io/writer.zig+1-1
......@@ -24,7 +24,7 @@ pub fn Writer(
2424 }
2525 }
2626
27 pub fn print(self: Self, comptime format: []const u8, args: var) Error!void {
27 pub fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
2828 return std.fmt.format(self, format, args);
2929 }
3030
lib/std/json.zig+43-44
......@@ -239,7 +239,7 @@ pub const StreamingParser = struct {
239239 NullLiteral3,
240240
241241 // Only call this function to generate array/object final state.
242 pub fn fromInt(x: var) State {
242 pub fn fromInt(x: anytype) State {
243243 debug.assert(x == 0 or x == 1);
244244 const T = @TagType(State);
245245 return @intToEnum(State, @intCast(T, x));
......@@ -1236,7 +1236,7 @@ pub const Value = union(enum) {
12361236 pub fn jsonStringify(
12371237 value: @This(),
12381238 options: StringifyOptions,
1239 out_stream: var,
1239 out_stream: anytype,
12401240 ) @TypeOf(out_stream).Error!void {
12411241 switch (value) {
12421242 .Null => try stringify(null, options, out_stream),
......@@ -1288,7 +1288,7 @@ pub const Value = union(enum) {
12881288 var held = std.debug.getStderrMutex().acquire();
12891289 defer held.release();
12901290
1291 const stderr = std.debug.getStderrStream();
1291 const stderr = io.getStdErr().writer();
12921292 std.json.stringify(self, std.json.StringifyOptions{ .whitespace = null }, stderr) catch return;
12931293 }
12941294};
......@@ -1535,7 +1535,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
15351535 const allocator = options.allocator orelse return error.AllocatorRequired;
15361536 switch (ptrInfo.size) {
15371537 .One => {
1538 const r: T = allocator.create(ptrInfo.child);
1538 const r: T = try allocator.create(ptrInfo.child);
15391539 r.* = try parseInternal(ptrInfo.child, token, tokens, options);
15401540 return r;
15411541 },
......@@ -1567,7 +1567,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
15671567 if (ptrInfo.child != u8) return error.UnexpectedToken;
15681568 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
15691569 switch (stringToken.escapes) {
1570 .None => return mem.dupe(allocator, u8, source_slice),
1570 .None => return allocator.dupe(u8, source_slice),
15711571 .Some => |some_escapes| {
15721572 const output = try allocator.alloc(u8, stringToken.decodedLength());
15731573 errdefer allocator.free(output);
......@@ -1629,7 +1629,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
16291629 switch (ptrInfo.size) {
16301630 .One => {
16311631 parseFree(ptrInfo.child, value.*, options);
1632 allocator.destroy(v);
1632 allocator.destroy(value);
16331633 },
16341634 .Slice => {
16351635 for (value) |v| {
......@@ -2043,7 +2043,7 @@ pub const Parser = struct {
20432043 fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value {
20442044 const slice = s.slice(input, i);
20452045 switch (s.escapes) {
2046 .None => return Value{ .String = if (p.copy_strings) try mem.dupe(allocator, u8, slice) else slice },
2046 .None => return Value{ .String = if (p.copy_strings) try allocator.dupe(u8, slice) else slice },
20472047 .Some => |some_escapes| {
20482048 const output = try allocator.alloc(u8, s.decodedLength());
20492049 errdefer allocator.free(output);
......@@ -2149,27 +2149,27 @@ test "json.parser.dynamic" {
21492149
21502150 var root = tree.root;
21512151
2152 var image = root.Object.get("Image").?.value;
2152 var image = root.Object.get("Image").?;
21532153
2154 const width = image.Object.get("Width").?.value;
2154 const width = image.Object.get("Width").?;
21552155 testing.expect(width.Integer == 800);
21562156
2157 const height = image.Object.get("Height").?.value;
2157 const height = image.Object.get("Height").?;
21582158 testing.expect(height.Integer == 600);
21592159
2160 const title = image.Object.get("Title").?.value;
2160 const title = image.Object.get("Title").?;
21612161 testing.expect(mem.eql(u8, title.String, "View from 15th Floor"));
21622162
2163 const animated = image.Object.get("Animated").?.value;
2163 const animated = image.Object.get("Animated").?;
21642164 testing.expect(animated.Bool == false);
21652165
2166 const array_of_object = image.Object.get("ArrayOfObject").?.value;
2166 const array_of_object = image.Object.get("ArrayOfObject").?;
21672167 testing.expect(array_of_object.Array.items.len == 1);
21682168
2169 const obj0 = array_of_object.Array.items[0].Object.get("n").?.value;
2169 const obj0 = array_of_object.Array.items[0].Object.get("n").?;
21702170 testing.expect(mem.eql(u8, obj0.String, "m"));
21712171
2172 const double = image.Object.get("double").?.value;
2172 const double = image.Object.get("double").?;
21732173 testing.expect(double.Float == 1.3412);
21742174}
21752175
......@@ -2217,12 +2217,12 @@ test "write json then parse it" {
22172217 var tree = try parser.parse(fixed_buffer_stream.getWritten());
22182218 defer tree.deinit();
22192219
2220 testing.expect(tree.root.Object.get("f").?.value.Bool == false);
2221 testing.expect(tree.root.Object.get("t").?.value.Bool == true);
2222 testing.expect(tree.root.Object.get("int").?.value.Integer == 1234);
2223 testing.expect(tree.root.Object.get("array").?.value.Array.items[0].Null == {});
2224 testing.expect(tree.root.Object.get("array").?.value.Array.items[1].Float == 12.34);
2225 testing.expect(mem.eql(u8, tree.root.Object.get("str").?.value.String, "hello"));
2220 testing.expect(tree.root.Object.get("f").?.Bool == false);
2221 testing.expect(tree.root.Object.get("t").?.Bool == true);
2222 testing.expect(tree.root.Object.get("int").?.Integer == 1234);
2223 testing.expect(tree.root.Object.get("array").?.Array.items[0].Null == {});
2224 testing.expect(tree.root.Object.get("array").?.Array.items[1].Float == 12.34);
2225 testing.expect(mem.eql(u8, tree.root.Object.get("str").?.String, "hello"));
22262226}
22272227
22282228fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value {
......@@ -2245,7 +2245,7 @@ test "integer after float has proper type" {
22452245 \\ "ints": [1, 2, 3]
22462246 \\}
22472247 );
2248 std.testing.expect(json.Object.getValue("ints").?.Array.items[0] == .Integer);
2248 std.testing.expect(json.Object.get("ints").?.Array.items[0] == .Integer);
22492249}
22502250
22512251test "escaped characters" {
......@@ -2271,16 +2271,16 @@ test "escaped characters" {
22712271
22722272 const obj = (try test_parse(&arena_allocator.allocator, input)).Object;
22732273
2274 testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\");
2275 testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/");
2276 testing.expectEqualSlices(u8, obj.get("newline").?.value.String, "\n");
2277 testing.expectEqualSlices(u8, obj.get("carriagereturn").?.value.String, "\r");
2278 testing.expectEqualSlices(u8, obj.get("tab").?.value.String, "\t");
2279 testing.expectEqualSlices(u8, obj.get("formfeed").?.value.String, "\x0C");
2280 testing.expectEqualSlices(u8, obj.get("backspace").?.value.String, "\x08");
2281 testing.expectEqualSlices(u8, obj.get("doublequote").?.value.String, "\"");
2282 testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą");
2283 testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "😂");
2274 testing.expectEqualSlices(u8, obj.get("backslash").?.String, "\\");
2275 testing.expectEqualSlices(u8, obj.get("forwardslash").?.String, "/");
2276 testing.expectEqualSlices(u8, obj.get("newline").?.String, "\n");
2277 testing.expectEqualSlices(u8, obj.get("carriagereturn").?.String, "\r");
2278 testing.expectEqualSlices(u8, obj.get("tab").?.String, "\t");
2279 testing.expectEqualSlices(u8, obj.get("formfeed").?.String, "\x0C");
2280 testing.expectEqualSlices(u8, obj.get("backspace").?.String, "\x08");
2281 testing.expectEqualSlices(u8, obj.get("doublequote").?.String, "\"");
2282 testing.expectEqualSlices(u8, obj.get("unicode").?.String, "ą");
2283 testing.expectEqualSlices(u8, obj.get("surrogatepair").?.String, "😂");
22842284}
22852285
22862286test "string copy option" {
......@@ -2306,11 +2306,11 @@ test "string copy option" {
23062306 const obj_copy = tree_copy.root.Object;
23072307
23082308 for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| {
2309 testing.expectEqualSlices(u8, obj_nocopy.getValue(field_name).?.String, obj_copy.getValue(field_name).?.String);
2309 testing.expectEqualSlices(u8, obj_nocopy.get(field_name).?.String, obj_copy.get(field_name).?.String);
23102310 }
23112311
2312 const nocopy_addr = &obj_nocopy.getValue("noescape").?.String[0];
2313 const copy_addr = &obj_copy.getValue("noescape").?.String[0];
2312 const nocopy_addr = &obj_nocopy.get("noescape").?.String[0];
2313 const copy_addr = &obj_copy.get("noescape").?.String[0];
23142314
23152315 var found_nocopy = false;
23162316 for (input) |_, index| {
......@@ -2338,7 +2338,7 @@ pub const StringifyOptions = struct {
23382338
23392339 pub fn outputIndent(
23402340 whitespace: @This(),
2341 out_stream: var,
2341 out_stream: anytype,
23422342 ) @TypeOf(out_stream).Error!void {
23432343 var char: u8 = undefined;
23442344 var n_chars: usize = undefined;
......@@ -2380,7 +2380,7 @@ pub const StringifyOptions = struct {
23802380
23812381fn outputUnicodeEscape(
23822382 codepoint: u21,
2383 out_stream: var,
2383 out_stream: anytype,
23842384) !void {
23852385 if (codepoint <= 0xFFFF) {
23862386 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
......@@ -2402,9 +2402,9 @@ fn outputUnicodeEscape(
24022402}
24032403
24042404pub fn stringify(
2405 value: var,
2405 value: anytype,
24062406 options: StringifyOptions,
2407 out_stream: var,
2407 out_stream: anytype,
24082408) @TypeOf(out_stream).Error!void {
24092409 const T = @TypeOf(value);
24102410 switch (@typeInfo(T)) {
......@@ -2576,15 +2576,15 @@ pub fn stringify(
25762576 },
25772577 .Array => return stringify(&value, options, out_stream),
25782578 .Vector => |info| {
2579 const array: [info.len]info.child = value;
2580 return stringify(&array, options, out_stream);
2579 const array: [info.len]info.child = value;
2580 return stringify(&array, options, out_stream);
25812581 },
25822582 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
25832583 }
25842584 unreachable;
25852585}
25862586
2587fn teststringify(expected: []const u8, value: var, options: StringifyOptions) !void {
2587fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions) !void {
25882588 const ValidationOutStream = struct {
25892589 const Self = @This();
25902590 pub const OutStream = std.io.OutStream(*Self, Error, write);
......@@ -2758,7 +2758,7 @@ test "stringify struct with custom stringifier" {
27582758 pub fn jsonStringify(
27592759 value: Self,
27602760 options: StringifyOptions,
2761 out_stream: var,
2761 out_stream: anytype,
27622762 ) !void {
27632763 try out_stream.writeAll("[\"something special\",");
27642764 try stringify(42, options, out_stream);
......@@ -2770,4 +2770,3 @@ test "stringify struct with custom stringifier" {
27702770test "stringify vector" {
27712771 try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});
27722772}
2773
lib/std/json/write_stream.zig+3-3
......@@ -152,7 +152,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
152152 self: *Self,
153153 /// An integer, float, or `std.math.BigInt`. Emitted as a bare number if it fits losslessly
154154 /// in a IEEE 754 double float, otherwise emitted as a string to the full precision.
155 value: var,
155 value: anytype,
156156 ) !void {
157157 assert(self.state[self.state_index] == State.Value);
158158 switch (@typeInfo(@TypeOf(value))) {
......@@ -215,7 +215,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
215215 self.state_index -= 1;
216216 }
217217
218 fn stringify(self: *Self, value: var) !void {
218 fn stringify(self: *Self, value: anytype) !void {
219219 try std.json.stringify(value, std.json.StringifyOptions{
220220 .whitespace = self.whitespace,
221221 }, self.stream);
......@@ -224,7 +224,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
224224}
225225
226226pub fn writeStream(
227 out_stream: var,
227 out_stream: anytype,
228228 comptime max_depth: usize,
229229) WriteStream(@TypeOf(out_stream), max_depth) {
230230 return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
lib/std/log.zig created+202
......@@ -0,0 +1,202 @@
1const std = @import("std.zig");
2const builtin = std.builtin;
3const root = @import("root");
4
5//! std.log is standardized interface for logging which allows for the logging
6//! of programs and libraries using this interface to be formatted and filtered
7//! by the implementer of the root.log function.
8//!
9//! The scope parameter should be used to give context to the logging. For
10//! example, a library called 'libfoo' might use .libfoo as its scope.
11//!
12//! An example root.log might look something like this:
13//!
14//! ```
15//! const std = @import("std");
16//!
17//! // Set the log level to warning
18//! pub const log_level: std.log.Level = .warn;
19//!
20//! // Define root.log to override the std implementation
21//! pub fn log(
22//! comptime level: std.log.Level,
23//! comptime scope: @TypeOf(.EnumLiteral),
24//! comptime format: []const u8,
25//! args: anytype,
26//! ) void {
27//! // Ignore all non-critical logging from sources other than
28//! // .my_project and .nice_library
29//! const scope_prefix = "(" ++ switch (scope) {
30//! .my_project, .nice_library => @tagName(scope),
31//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
32//! @tagName(scope)
33//! else
34//! return,
35//! } ++ "): ";
36//!
37//! const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
38//!
39//! // Print the message to stderr, silently ignoring any errors
40//! const held = std.debug.getStderrMutex().acquire();
41//! defer held.release();
42//! const stderr = std.debug.getStderrStream();
43//! nosuspend stderr.print(prefix ++ format, args) catch return;
44//! }
45//!
46//! pub fn main() void {
47//! // Won't be printed as log_level is .warn
48//! std.log.info(.my_project, "Starting up.\n", .{});
49//! std.log.err(.nice_library, "Something went very wrong, sorry.\n", .{});
50//! // Won't be printed as it gets filtered out by our log function
51//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1\n", .{});
52//! }
53//! ```
54//! Which produces the following output:
55//! ```
56//! [err] (nice_library): Something went very wrong, sorry.
57//! ```
58
59pub const Level = enum {
60 /// Emergency: a condition that cannot be handled, usually followed by a
61 /// panic.
62 emerg,
63 /// Alert: a condition that should be corrected immediately (e.g. database
64 /// corruption).
65 alert,
66 /// Critical: A bug has been detected or something has gone wrong and it
67 /// will have an effect on the operation of the program.
68 crit,
69 /// Error: A bug has been detected or something has gone wrong but it is
70 /// recoverable.
71 err,
72 /// Warning: it is uncertain if something has gone wrong or not, but the
73 /// circumstances would be worth investigating.
74 warn,
75 /// Notice: non-error but significant conditions.
76 notice,
77 /// Informational: general messages about the state of the program.
78 info,
79 /// Debug: messages only useful for debugging.
80 debug,
81};
82
83/// The default log level is based on build mode. Note that in ReleaseSmall
84/// builds the default level is emerg but no messages will be stored/logged
85/// by the default logger to save space.
86pub const default_level: Level = switch (builtin.mode) {
87 .Debug => .debug,
88 .ReleaseSafe => .notice,
89 .ReleaseFast => .err,
90 .ReleaseSmall => .emerg,
91};
92
93/// The current log level. This is set to root.log_level if present, otherwise
94/// log.default_level.
95pub const level: Level = if (@hasDecl(root, "log_level"))
96 root.log_level
97else
98 default_level;
99
100fn log(
101 comptime message_level: Level,
102 comptime scope: @Type(.EnumLiteral),
103 comptime format: []const u8,
104 args: anytype,
105) void {
106 if (@enumToInt(message_level) <= @enumToInt(level)) {
107 if (@hasDecl(root, "log")) {
108 root.log(message_level, scope, format, args);
109 } else if (builtin.mode != .ReleaseSmall) {
110 const held = std.debug.getStderrMutex().acquire();
111 defer held.release();
112 const stderr = std.io.getStdErr().writer();
113 nosuspend stderr.print(format, args) catch return;
114 }
115 }
116}
117
118/// Log an emergency message to stderr. This log level is intended to be used
119/// for conditions that cannot be handled and is usually followed by a panic.
120pub fn emerg(
121 comptime scope: @Type(.EnumLiteral),
122 comptime format: []const u8,
123 args: anytype,
124) void {
125 @setCold(true);
126 log(.emerg, scope, format, args);
127}
128
129/// Log an alert message to stderr. This log level is intended to be used for
130/// conditions that should be corrected immediately (e.g. database corruption).
131pub fn alert(
132 comptime scope: @Type(.EnumLiteral),
133 comptime format: []const u8,
134 args: anytype,
135) void {
136 @setCold(true);
137 log(.alert, scope, format, args);
138}
139
140/// Log a critical message to stderr. This log level is intended to be used
141/// when a bug has been detected or something has gone wrong and it will have
142/// an effect on the operation of the program.
143pub fn crit(
144 comptime scope: @Type(.EnumLiteral),
145 comptime format: []const u8,
146 args: anytype,
147) void {
148 @setCold(true);
149 log(.crit, scope, format, args);
150}
151
152/// Log an error message to stderr. This log level is intended to be used when
153/// a bug has been detected or something has gone wrong but it is recoverable.
154pub fn err(
155 comptime scope: @Type(.EnumLiteral),
156 comptime format: []const u8,
157 args: anytype,
158) void {
159 @setCold(true);
160 log(.err, scope, format, args);
161}
162
163/// Log a warning message to stderr. This log level is intended to be used if
164/// it is uncertain whether something has gone wrong or not, but the
165/// circumstances would be worth investigating.
166pub fn warn(
167 comptime scope: @Type(.EnumLiteral),
168 comptime format: []const u8,
169 args: anytype,
170) void {
171 log(.warn, scope, format, args);
172}
173
174/// Log a notice message to stderr. This log level is intended to be used for
175/// non-error but significant conditions.
176pub fn notice(
177 comptime scope: @Type(.EnumLiteral),
178 comptime format: []const u8,
179 args: anytype,
180) void {
181 log(.notice, scope, format, args);
182}
183
184/// Log an info message to stderr. This log level is intended to be used for
185/// general messages about the state of the program.
186pub fn info(
187 comptime scope: @Type(.EnumLiteral),
188 comptime format: []const u8,
189 args: anytype,
190) void {
191 log(.info, scope, format, args);
192}
193
194/// Log a debug message to stderr. This log level is intended to be used for
195/// messages which are only useful for debugging.
196pub fn debug(
197 comptime scope: @Type(.EnumLiteral),
198 comptime format: []const u8,
199 args: anytype,
200) void {
201 log(.debug, scope, format, args);
202}
lib/std/math.zig+23-23
......@@ -104,7 +104,7 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
104104}
105105
106106// TODO: Hide the following in an internal module.
107pub fn forceEval(value: var) void {
107pub fn forceEval(value: anytype) void {
108108 const T = @TypeOf(value);
109109 switch (T) {
110110 f16 => {
......@@ -122,6 +122,11 @@ pub fn forceEval(value: var) void {
122122 const p = @ptrCast(*volatile f64, &x);
123123 p.* = x;
124124 },
125 f128 => {
126 var x: f128 = undefined;
127 const p = @ptrCast(*volatile f128, &x);
128 p.* = x;
129 },
125130 else => {
126131 @compileError("forceEval not implemented for " ++ @typeName(T));
127132 },
......@@ -254,7 +259,7 @@ pub fn Min(comptime A: type, comptime B: type) type {
254259
255260/// Returns the smaller number. When one of the parameter's type's full range fits in the other,
256261/// the return type is the smaller type.
257pub fn min(x: var, y: var) Min(@TypeOf(x), @TypeOf(y)) {
262pub fn min(x: anytype, y: anytype) Min(@TypeOf(x), @TypeOf(y)) {
258263 const Result = Min(@TypeOf(x), @TypeOf(y));
259264 if (x < y) {
260265 // TODO Zig should allow this as an implicit cast because x is immutable and in this
......@@ -305,7 +310,7 @@ test "math.min" {
305310 }
306311}
307312
308pub fn max(x: var, y: var) @TypeOf(x, y) {
313pub fn max(x: anytype, y: anytype) @TypeOf(x, y) {
309314 return if (x > y) x else y;
310315}
311316
......@@ -313,7 +318,7 @@ test "math.max" {
313318 testing.expect(max(@as(i32, -1), @as(i32, 2)) == 2);
314319}
315320
316pub fn clamp(val: var, lower: var, upper: var) @TypeOf(val, lower, upper) {
321pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) {
317322 assert(lower <= upper);
318323 return max(lower, min(val, upper));
319324}
......@@ -349,7 +354,7 @@ pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
349354 return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
350355}
351356
352pub fn negate(x: var) !@TypeOf(x) {
357pub fn negate(x: anytype) !@TypeOf(x) {
353358 return sub(@TypeOf(x), 0, x);
354359}
355360
......@@ -360,7 +365,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
360365
361366/// Shifts left. Overflowed bits are truncated.
362367/// A negative shift amount results in a right shift.
363pub fn shl(comptime T: type, a: T, shift_amt: var) T {
368pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
364369 const abs_shift_amt = absCast(shift_amt);
365370 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
366371
......@@ -386,7 +391,7 @@ test "math.shl" {
386391
387392/// Shifts right. Overflowed bits are truncated.
388393/// A negative shift amount results in a left shift.
389pub fn shr(comptime T: type, a: T, shift_amt: var) T {
394pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
390395 const abs_shift_amt = absCast(shift_amt);
391396 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
392397
......@@ -414,7 +419,7 @@ test "math.shr" {
414419
415420/// Rotates right. Only unsigned values can be rotated.
416421/// Negative shift values results in shift modulo the bit count.
417pub fn rotr(comptime T: type, x: T, r: var) T {
422pub fn rotr(comptime T: type, x: T, r: anytype) T {
418423 if (T.is_signed) {
419424 @compileError("cannot rotate signed integer");
420425 } else {
......@@ -433,7 +438,7 @@ test "math.rotr" {
433438
434439/// Rotates left. Only unsigned values can be rotated.
435440/// Negative shift values results in shift modulo the bit count.
436pub fn rotl(comptime T: type, x: T, r: var) T {
441pub fn rotl(comptime T: type, x: T, r: anytype) T {
437442 if (T.is_signed) {
438443 @compileError("cannot rotate signed integer");
439444 } else {
......@@ -536,7 +541,7 @@ fn testOverflow() void {
536541 testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000);
537542}
538543
539pub fn absInt(x: var) !@TypeOf(x) {
544pub fn absInt(x: anytype) !@TypeOf(x) {
540545 const T = @TypeOf(x);
541546 comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt
542547 comptime assert(T.is_signed); // must pass a signed integer to absInt
......@@ -684,7 +689,7 @@ fn testRem() void {
684689
685690/// Returns the absolute value of the integer parameter.
686691/// Result is an unsigned integer.
687pub fn absCast(x: var) switch (@typeInfo(@TypeOf(x))) {
692pub fn absCast(x: anytype) switch (@typeInfo(@TypeOf(x))) {
688693 .ComptimeInt => comptime_int,
689694 .Int => |intInfo| std.meta.Int(false, intInfo.bits),
690695 else => @compileError("absCast only accepts integers"),
......@@ -719,7 +724,7 @@ test "math.absCast" {
719724
720725/// Returns the negation of the integer parameter.
721726/// Result is a signed integer.
722pub fn negateCast(x: var) !std.meta.Int(true, @TypeOf(x).bit_count) {
727pub fn negateCast(x: anytype) !std.meta.Int(true, @TypeOf(x).bit_count) {
723728 if (@TypeOf(x).is_signed) return negate(x);
724729
725730 const int = std.meta.Int(true, @TypeOf(x).bit_count);
......@@ -742,7 +747,7 @@ test "math.negateCast" {
742747
743748/// Cast an integer to a different integer type. If the value doesn't fit,
744749/// return an error.
745pub fn cast(comptime T: type, x: var) (error{Overflow}!T) {
750pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) {
746751 comptime assert(@typeInfo(T) == .Int); // must pass an integer
747752 comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer
748753 if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) {
......@@ -767,7 +772,7 @@ test "math.cast" {
767772pub const AlignCastError = error{UnalignedMemory};
768773
769774/// Align cast a pointer but return an error if it's the wrong alignment
770pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alignCast(alignment, ptr)) {
775pub fn alignCast(comptime alignment: u29, ptr: anytype) AlignCastError!@TypeOf(@alignCast(alignment, ptr)) {
771776 const addr = @ptrToInt(ptr);
772777 if (addr % alignment != 0) {
773778 return error.UnalignedMemory;
......@@ -775,7 +780,7 @@ pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alig
775780 return @alignCast(alignment, ptr);
776781}
777782
778pub fn isPowerOfTwo(v: var) bool {
783pub fn isPowerOfTwo(v: anytype) bool {
779784 assert(v != 0);
780785 return (v & (v - 1)) == 0;
781786}
......@@ -892,7 +897,7 @@ test "std.math.log2_int_ceil" {
892897 testing.expect(log2_int_ceil(u32, 10) == 4);
893898}
894899
895pub fn lossyCast(comptime T: type, value: var) T {
900pub fn lossyCast(comptime T: type, value: anytype) T {
896901 switch (@typeInfo(@TypeOf(value))) {
897902 .Int => return @intToFloat(T, value),
898903 .Float => return @floatCast(T, value),
......@@ -1026,7 +1031,7 @@ pub const Order = enum {
10261031};
10271032
10281033/// Given two numbers, this function returns the order they are with respect to each other.
1029pub fn order(a: var, b: var) Order {
1034pub fn order(a: anytype, b: anytype) Order {
10301035 if (a == b) {
10311036 return .eq;
10321037 } else if (a < b) {
......@@ -1042,19 +1047,14 @@ pub fn order(a: var, b: var) Order {
10421047pub const CompareOperator = enum {
10431048 /// Less than (`<`)
10441049 lt,
1045
10461050 /// Less than or equal (`<=`)
10471051 lte,
1048
10491052 /// Equal (`==`)
10501053 eq,
1051
10521054 /// Greater than or equal (`>=`)
10531055 gte,
1054
10551056 /// Greater than (`>`)
10561057 gt,
1057
10581058 /// Not equal (`!=`)
10591059 neq,
10601060};
......@@ -1062,7 +1062,7 @@ pub const CompareOperator = enum {
10621062/// This function does the same thing as comparison operators, however the
10631063/// operator is a runtime-known enum value. Works on any operands that
10641064/// support comparison operators.
1065pub fn compare(a: var, op: CompareOperator, b: var) bool {
1065pub fn compare(a: anytype, op: CompareOperator, b: anytype) bool {
10661066 return switch (op) {
10671067 .lt => a < b,
10681068 .lte => a <= b,
lib/std/math/acos.zig+1-1
......@@ -12,7 +12,7 @@ const expect = std.testing.expect;
1212///
1313/// Special cases:
1414/// - acos(x) = nan if x < -1 or x > 1
15pub fn acos(x: var) @TypeOf(x) {
15pub fn acos(x: anytype) @TypeOf(x) {
1616 const T = @TypeOf(x);
1717 return switch (T) {
1818 f32 => acos32(x),
lib/std/math/acosh.zig+1-1
......@@ -14,7 +14,7 @@ const expect = std.testing.expect;
1414/// Special cases:
1515/// - acosh(x) = snan if x < 1
1616/// - acosh(nan) = nan
17pub fn acosh(x: var) @TypeOf(x) {
17pub fn acosh(x: anytype) @TypeOf(x) {
1818 const T = @TypeOf(x);
1919 return switch (T) {
2020 f32 => acosh32(x),
lib/std/math/asin.zig+1-1
......@@ -13,7 +13,7 @@ const expect = std.testing.expect;
1313/// Special Cases:
1414/// - asin(+-0) = +-0
1515/// - asin(x) = nan if x < -1 or x > 1
16pub fn asin(x: var) @TypeOf(x) {
16pub fn asin(x: anytype) @TypeOf(x) {
1717 const T = @TypeOf(x);
1818 return switch (T) {
1919 f32 => asin32(x),
lib/std/math/asinh.zig+1-1
......@@ -15,7 +15,7 @@ const maxInt = std.math.maxInt;
1515/// - asinh(+-0) = +-0
1616/// - asinh(+-inf) = +-inf
1717/// - asinh(nan) = nan
18pub fn asinh(x: var) @TypeOf(x) {
18pub fn asinh(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 return switch (T) {
2121 f32 => asinh32(x),
lib/std/math/atan.zig+1-1
......@@ -13,7 +13,7 @@ const expect = std.testing.expect;
1313/// Special Cases:
1414/// - atan(+-0) = +-0
1515/// - atan(+-inf) = +-pi/2
16pub fn atan(x: var) @TypeOf(x) {
16pub fn atan(x: anytype) @TypeOf(x) {
1717 const T = @TypeOf(x);
1818 return switch (T) {
1919 f32 => atan32(x),
lib/std/math/atanh.zig+1-1
......@@ -15,7 +15,7 @@ const maxInt = std.math.maxInt;
1515/// - atanh(+-1) = +-inf with signal
1616/// - atanh(x) = nan if |x| > 1 with signal
1717/// - atanh(nan) = nan
18pub fn atanh(x: var) @TypeOf(x) {
18pub fn atanh(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 return switch (T) {
2121 f32 => atanh_32(x),
lib/std/math/big/int.zig+11-11
......@@ -12,7 +12,7 @@ const assert = std.debug.assert;
1212
1313/// Returns the number of limbs needed to store `scalar`, which must be a
1414/// primitive integer value.
15pub fn calcLimbLen(scalar: var) usize {
15pub fn calcLimbLen(scalar: anytype) usize {
1616 const T = @TypeOf(scalar);
1717 switch (@typeInfo(T)) {
1818 .Int => |info| {
......@@ -110,7 +110,7 @@ pub const Mutable = struct {
110110 /// `value` is a primitive integer type.
111111 /// Asserts the value fits within the provided `limbs_buffer`.
112112 /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`.
113 pub fn init(limbs_buffer: []Limb, value: var) Mutable {
113 pub fn init(limbs_buffer: []Limb, value: anytype) Mutable {
114114 limbs_buffer[0] = 0;
115115 var self: Mutable = .{
116116 .limbs = limbs_buffer,
......@@ -169,7 +169,7 @@ pub const Mutable = struct {
169169 /// Asserts the value fits within the limbs buffer.
170170 /// Note: `calcLimbLen` can be used to figure out how big the limbs buffer
171171 /// needs to be to store a specific value.
172 pub fn set(self: *Mutable, value: var) void {
172 pub fn set(self: *Mutable, value: anytype) void {
173173 const T = @TypeOf(value);
174174
175175 switch (@typeInfo(T)) {
......@@ -281,7 +281,7 @@ pub const Mutable = struct {
281281 ///
282282 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
283283 /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
284 pub fn addScalar(r: *Mutable, a: Const, scalar: var) void {
284 pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void {
285285 var limbs: [calcLimbLen(scalar)]Limb = undefined;
286286 const operand = init(&limbs, scalar).toConst();
287287 return add(r, a, operand);
......@@ -1058,7 +1058,7 @@ pub const Const = struct {
10581058 self: Const,
10591059 comptime fmt: []const u8,
10601060 options: std.fmt.FormatOptions,
1061 out_stream: var,
1061 out_stream: anytype,
10621062 ) !void {
10631063 comptime var radix = 10;
10641064 comptime var uppercase = false;
......@@ -1105,7 +1105,7 @@ pub const Const = struct {
11051105 assert(base <= 16);
11061106
11071107 if (self.eqZero()) {
1108 return mem.dupe(allocator, u8, "0");
1108 return allocator.dupe(u8, "0");
11091109 }
11101110 const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base));
11111111 errdefer allocator.free(string);
......@@ -1261,7 +1261,7 @@ pub const Const = struct {
12611261 }
12621262
12631263 /// Same as `order` but the right-hand operand is a primitive integer.
1264 pub fn orderAgainstScalar(lhs: Const, scalar: var) math.Order {
1264 pub fn orderAgainstScalar(lhs: Const, scalar: anytype) math.Order {
12651265 var limbs: [calcLimbLen(scalar)]Limb = undefined;
12661266 const rhs = Mutable.init(&limbs, scalar);
12671267 return order(lhs, rhs.toConst());
......@@ -1333,7 +1333,7 @@ pub const Managed = struct {
13331333 /// Creates a new `Managed` with value `value`.
13341334 ///
13351335 /// This is identical to an `init`, followed by a `set`.
1336 pub fn initSet(allocator: *Allocator, value: var) !Managed {
1336 pub fn initSet(allocator: *Allocator, value: anytype) !Managed {
13371337 var s = try Managed.init(allocator);
13381338 try s.set(value);
13391339 return s;
......@@ -1496,7 +1496,7 @@ pub const Managed = struct {
14961496 }
14971497
14981498 /// Sets an Managed to value. Value must be an primitive integer type.
1499 pub fn set(self: *Managed, value: var) Allocator.Error!void {
1499 pub fn set(self: *Managed, value: anytype) Allocator.Error!void {
15001500 try self.ensureCapacity(calcLimbLen(value));
15011501 var m = self.toMutable();
15021502 m.set(value);
......@@ -1549,7 +1549,7 @@ pub const Managed = struct {
15491549 self: Managed,
15501550 comptime fmt: []const u8,
15511551 options: std.fmt.FormatOptions,
1552 out_stream: var,
1552 out_stream: anytype,
15531553 ) !void {
15541554 return self.toConst().format(fmt, options, out_stream);
15551555 }
......@@ -1607,7 +1607,7 @@ pub const Managed = struct {
16071607 /// scalar is a primitive integer type.
16081608 ///
16091609 /// Returns an error if memory could not be allocated.
1610 pub fn addScalar(r: *Managed, a: Const, scalar: var) Allocator.Error!void {
1610 pub fn addScalar(r: *Managed, a: Const, scalar: anytype) Allocator.Error!void {
16111611 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
16121612 var m = r.toMutable();
16131613 m.addScalar(a, scalar);
lib/std/math/big/rational.zig+2-2
......@@ -43,7 +43,7 @@ pub const Rational = struct {
4343 }
4444
4545 /// Set a Rational from a primitive integer type.
46 pub fn setInt(self: *Rational, a: var) !void {
46 pub fn setInt(self: *Rational, a: anytype) !void {
4747 try self.p.set(a);
4848 try self.q.set(1);
4949 }
......@@ -280,7 +280,7 @@ pub const Rational = struct {
280280 }
281281
282282 /// Set a rational from an integer ratio.
283 pub fn setRatio(self: *Rational, p: var, q: var) !void {
283 pub fn setRatio(self: *Rational, p: anytype, q: anytype) !void {
284284 try self.p.set(p);
285285 try self.q.set(q);
286286
lib/std/math/cbrt.zig+1-1
......@@ -14,7 +14,7 @@ const expect = std.testing.expect;
1414/// - cbrt(+-0) = +-0
1515/// - cbrt(+-inf) = +-inf
1616/// - cbrt(nan) = nan
17pub fn cbrt(x: var) @TypeOf(x) {
17pub fn cbrt(x: anytype) @TypeOf(x) {
1818 const T = @TypeOf(x);
1919 return switch (T) {
2020 f32 => cbrt32(x),
lib/std/math/ceil.zig+44-1
......@@ -15,11 +15,12 @@ const expect = std.testing.expect;
1515/// - ceil(+-0) = +-0
1616/// - ceil(+-inf) = +-inf
1717/// - ceil(nan) = nan
18pub fn ceil(x: var) @TypeOf(x) {
18pub fn ceil(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 return switch (T) {
2121 f32 => ceil32(x),
2222 f64 => ceil64(x),
23 f128 => ceil128(x),
2324 else => @compileError("ceil not implemented for " ++ @typeName(T)),
2425 };
2526}
......@@ -86,9 +87,37 @@ fn ceil64(x: f64) f64 {
8687 }
8788}
8889
90fn ceil128(x: f128) f128 {
91 const u = @bitCast(u128, x);
92 const e = (u >> 112) & 0x7FFF;
93 var y: f128 = undefined;
94
95 if (e >= 0x3FFF + 112 or x == 0) return x;
96
97 if (u >> 127 != 0) {
98 y = x - math.f128_toint + math.f128_toint - x;
99 } else {
100 y = x + math.f128_toint - math.f128_toint - x;
101 }
102
103 if (e <= 0x3FFF - 1) {
104 math.forceEval(y);
105 if (u >> 127 != 0) {
106 return -0.0;
107 } else {
108 return 1.0;
109 }
110 } else if (y < 0) {
111 return x + y + 1;
112 } else {
113 return x + y;
114 }
115}
116
89117test "math.ceil" {
90118 expect(ceil(@as(f32, 0.0)) == ceil32(0.0));
91119 expect(ceil(@as(f64, 0.0)) == ceil64(0.0));
120 expect(ceil(@as(f128, 0.0)) == ceil128(0.0));
92121}
93122
94123test "math.ceil32" {
......@@ -103,6 +132,12 @@ test "math.ceil64" {
103132 expect(ceil64(0.2) == 1.0);
104133}
105134
135test "math.ceil128" {
136 expect(ceil128(1.3) == 2.0);
137 expect(ceil128(-1.3) == -1.0);
138 expect(ceil128(0.2) == 1.0);
139}
140
106141test "math.ceil32.special" {
107142 expect(ceil32(0.0) == 0.0);
108143 expect(ceil32(-0.0) == -0.0);
......@@ -118,3 +153,11 @@ test "math.ceil64.special" {
118153 expect(math.isNegativeInf(ceil64(-math.inf(f64))));
119154 expect(math.isNan(ceil64(math.nan(f64))));
120155}
156
157test "math.ceil128.special" {
158 expect(ceil128(0.0) == 0.0);
159 expect(ceil128(-0.0) == -0.0);
160 expect(math.isPositiveInf(ceil128(math.inf(f128))));
161 expect(math.isNegativeInf(ceil128(-math.inf(f128))));
162 expect(math.isNan(ceil128(math.nan(f128))));
163}
lib/std/math/complex/abs.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the absolute value (modulus) of z.
8pub fn abs(z: var) @TypeOf(z.re) {
8pub fn abs(z: anytype) @TypeOf(z.re) {
99 const T = @TypeOf(z.re);
1010 return math.hypot(T, z.re, z.im);
1111}
lib/std/math/complex/acos.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the arc-cosine of z.
8pub fn acos(z: var) Complex(@TypeOf(z.re)) {
8pub fn acos(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const q = cmath.asin(z);
1111 return Complex(T).new(@as(T, math.pi) / 2 - q.re, -q.im);
lib/std/math/complex/acosh.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the hyperbolic arc-cosine of z.
8pub fn acosh(z: var) Complex(@TypeOf(z.re)) {
8pub fn acosh(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const q = cmath.acos(z);
1111 return Complex(T).new(-q.im, q.re);
lib/std/math/complex/arg.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the angular component (in radians) of z.
8pub fn arg(z: var) @TypeOf(z.re) {
8pub fn arg(z: anytype) @TypeOf(z.re) {
99 const T = @TypeOf(z.re);
1010 return math.atan2(T, z.im, z.re);
1111}
lib/std/math/complex/asin.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77// Returns the arc-sine of z.
8pub fn asin(z: var) Complex(@TypeOf(z.re)) {
8pub fn asin(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const x = z.re;
1111 const y = z.im;
lib/std/math/complex/asinh.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the hyperbolic arc-sine of z.
8pub fn asinh(z: var) Complex(@TypeOf(z.re)) {
8pub fn asinh(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const q = Complex(T).new(-z.im, z.re);
1111 const r = cmath.asin(q);
lib/std/math/complex/atan.zig+1-1
......@@ -12,7 +12,7 @@ const cmath = math.complex;
1212const Complex = cmath.Complex;
1313
1414/// Returns the arc-tangent of z.
15pub fn atan(z: var) @TypeOf(z) {
15pub fn atan(z: anytype) @TypeOf(z) {
1616 const T = @TypeOf(z.re);
1717 return switch (T) {
1818 f32 => atan32(z),
lib/std/math/complex/atanh.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the hyperbolic arc-tangent of z.
8pub fn atanh(z: var) Complex(@TypeOf(z.re)) {
8pub fn atanh(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const q = Complex(T).new(-z.im, z.re);
1111 const r = cmath.atan(q);
lib/std/math/complex/conj.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the complex conjugate of z.
8pub fn conj(z: var) Complex(@TypeOf(z.re)) {
8pub fn conj(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 return Complex(T).new(z.re, -z.im);
1111}
lib/std/math/complex/cos.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the cosine of z.
8pub fn cos(z: var) Complex(@TypeOf(z.re)) {
8pub fn cos(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const p = Complex(T).new(-z.im, z.re);
1111 return cmath.cosh(p);
lib/std/math/complex/cosh.zig+1-1
......@@ -14,7 +14,7 @@ const Complex = cmath.Complex;
1414const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
1515
1616/// Returns the hyperbolic arc-cosine of z.
17pub fn cosh(z: var) Complex(@TypeOf(z.re)) {
17pub fn cosh(z: anytype) Complex(@TypeOf(z.re)) {
1818 const T = @TypeOf(z.re);
1919 return switch (T) {
2020 f32 => cosh32(z),
lib/std/math/complex/exp.zig+1-1
......@@ -14,7 +14,7 @@ const Complex = cmath.Complex;
1414const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
1515
1616/// Returns e raised to the power of z (e^z).
17pub fn exp(z: var) @TypeOf(z) {
17pub fn exp(z: anytype) @TypeOf(z) {
1818 const T = @TypeOf(z.re);
1919
2020 return switch (T) {
lib/std/math/complex/ldexp.zig+1-1
......@@ -11,7 +11,7 @@ const cmath = math.complex;
1111const Complex = cmath.Complex;
1212
1313/// Returns exp(z) scaled to avoid overflow.
14pub fn ldexp_cexp(z: var, expt: i32) @TypeOf(z) {
14pub fn ldexp_cexp(z: anytype, expt: i32) @TypeOf(z) {
1515 const T = @TypeOf(z.re);
1616
1717 return switch (T) {
lib/std/math/complex/log.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the natural logarithm of z.
8pub fn log(z: var) Complex(@TypeOf(z.re)) {
8pub fn log(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const r = cmath.abs(z);
1111 const phi = cmath.arg(z);
lib/std/math/complex/proj.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the projection of z onto the riemann sphere.
8pub fn proj(z: var) Complex(@TypeOf(z.re)) {
8pub fn proj(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010
1111 if (math.isInf(z.re) or math.isInf(z.im)) {
lib/std/math/complex/sin.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the sine of z.
8pub fn sin(z: var) Complex(@TypeOf(z.re)) {
8pub fn sin(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const p = Complex(T).new(-z.im, z.re);
1111 const q = cmath.sinh(p);
lib/std/math/complex/sinh.zig+1-1
......@@ -14,7 +14,7 @@ const Complex = cmath.Complex;
1414const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
1515
1616/// Returns the hyperbolic sine of z.
17pub fn sinh(z: var) @TypeOf(z) {
17pub fn sinh(z: anytype) @TypeOf(z) {
1818 const T = @TypeOf(z.re);
1919 return switch (T) {
2020 f32 => sinh32(z),
lib/std/math/complex/sqrt.zig+1-1
......@@ -12,7 +12,7 @@ const Complex = cmath.Complex;
1212
1313/// Returns the square root of z. The real and imaginary parts of the result have the same sign
1414/// as the imaginary part of z.
15pub fn sqrt(z: var) @TypeOf(z) {
15pub fn sqrt(z: anytype) @TypeOf(z) {
1616 const T = @TypeOf(z.re);
1717
1818 return switch (T) {
lib/std/math/complex/tan.zig+1-1
......@@ -5,7 +5,7 @@ const cmath = math.complex;
55const Complex = cmath.Complex;
66
77/// Returns the tanget of z.
8pub fn tan(z: var) Complex(@TypeOf(z.re)) {
8pub fn tan(z: anytype) Complex(@TypeOf(z.re)) {
99 const T = @TypeOf(z.re);
1010 const q = Complex(T).new(-z.im, z.re);
1111 const r = cmath.tanh(q);
lib/std/math/complex/tanh.zig+1-1
......@@ -12,7 +12,7 @@ const cmath = math.complex;
1212const Complex = cmath.Complex;
1313
1414/// Returns the hyperbolic tangent of z.
15pub fn tanh(z: var) @TypeOf(z) {
15pub fn tanh(z: anytype) @TypeOf(z) {
1616 const T = @TypeOf(z.re);
1717 return switch (T) {
1818 f32 => tanh32(z),
lib/std/math/cos.zig+1-1
......@@ -13,7 +13,7 @@ const expect = std.testing.expect;
1313/// Special Cases:
1414/// - cos(+-inf) = nan
1515/// - cos(nan) = nan
16pub fn cos(x: var) @TypeOf(x) {
16pub fn cos(x: anytype) @TypeOf(x) {
1717 const T = @TypeOf(x);
1818 return switch (T) {
1919 f32 => cos_(f32, x),
lib/std/math/cosh.zig+1-1
......@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;
1717/// - cosh(+-0) = 1
1818/// - cosh(+-inf) = +inf
1919/// - cosh(nan) = nan
20pub fn cosh(x: var) @TypeOf(x) {
20pub fn cosh(x: anytype) @TypeOf(x) {
2121 const T = @TypeOf(x);
2222 return switch (T) {
2323 f32 => cosh32(x),
lib/std/math/exp.zig+1-1
......@@ -14,7 +14,7 @@ const builtin = @import("builtin");
1414/// Special Cases:
1515/// - exp(+inf) = +inf
1616/// - exp(nan) = nan
17pub fn exp(x: var) @TypeOf(x) {
17pub fn exp(x: anytype) @TypeOf(x) {
1818 const T = @TypeOf(x);
1919 return switch (T) {
2020 f32 => exp32(x),
lib/std/math/exp2.zig+1-1
......@@ -13,7 +13,7 @@ const expect = std.testing.expect;
1313/// Special Cases:
1414/// - exp2(+inf) = +inf
1515/// - exp2(nan) = nan
16pub fn exp2(x: var) @TypeOf(x) {
16pub fn exp2(x: anytype) @TypeOf(x) {
1717 const T = @TypeOf(x);
1818 return switch (T) {
1919 f32 => exp2_32(x),
lib/std/math/expm1.zig+1-1
......@@ -18,7 +18,7 @@ const expect = std.testing.expect;
1818/// - expm1(+inf) = +inf
1919/// - expm1(-inf) = -1
2020/// - expm1(nan) = nan
21pub fn expm1(x: var) @TypeOf(x) {
21pub fn expm1(x: anytype) @TypeOf(x) {
2222 const T = @TypeOf(x);
2323 return switch (T) {
2424 f32 => expm1_32(x),
lib/std/math/expo2.zig+1-1
......@@ -7,7 +7,7 @@
77const math = @import("../math.zig");
88
99/// Returns exp(x) / 2 for x >= log(maxFloat(T)).
10pub fn expo2(x: var) @TypeOf(x) {
10pub fn expo2(x: anytype) @TypeOf(x) {
1111 const T = @TypeOf(x);
1212 return switch (T) {
1313 f32 => expo2f(x),
lib/std/math/fabs.zig+1-1
......@@ -14,7 +14,7 @@ const maxInt = std.math.maxInt;
1414/// Special Cases:
1515/// - fabs(+-inf) = +inf
1616/// - fabs(nan) = nan
17pub fn fabs(x: var) @TypeOf(x) {
17pub fn fabs(x: anytype) @TypeOf(x) {
1818 const T = @TypeOf(x);
1919 return switch (T) {
2020 f16 => fabs16(x),
lib/std/math/floor.zig+44-1
......@@ -15,12 +15,13 @@ const math = std.math;
1515/// - floor(+-0) = +-0
1616/// - floor(+-inf) = +-inf
1717/// - floor(nan) = nan
18pub fn floor(x: var) @TypeOf(x) {
18pub fn floor(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 return switch (T) {
2121 f16 => floor16(x),
2222 f32 => floor32(x),
2323 f64 => floor64(x),
24 f128 => floor128(x),
2425 else => @compileError("floor not implemented for " ++ @typeName(T)),
2526 };
2627}
......@@ -122,10 +123,38 @@ fn floor64(x: f64) f64 {
122123 }
123124}
124125
126fn floor128(x: f128) f128 {
127 const u = @bitCast(u128, x);
128 const e = (u >> 112) & 0x7FFF;
129 var y: f128 = undefined;
130
131 if (e >= 0x3FFF + 112 or x == 0) return x;
132
133 if (u >> 127 != 0) {
134 y = x - math.f128_toint + math.f128_toint - x;
135 } else {
136 y = x + math.f128_toint - math.f128_toint - x;
137 }
138
139 if (e <= 0x3FFF - 1) {
140 math.forceEval(y);
141 if (u >> 127 != 0) {
142 return -1.0;
143 } else {
144 return 0.0;
145 }
146 } else if (y > 0) {
147 return x + y - 1;
148 } else {
149 return x + y;
150 }
151}
152
125153test "math.floor" {
126154 expect(floor(@as(f16, 1.3)) == floor16(1.3));
127155 expect(floor(@as(f32, 1.3)) == floor32(1.3));
128156 expect(floor(@as(f64, 1.3)) == floor64(1.3));
157 expect(floor(@as(f128, 1.3)) == floor128(1.3));
129158}
130159
131160test "math.floor16" {
......@@ -146,6 +175,12 @@ test "math.floor64" {
146175 expect(floor64(0.2) == 0.0);
147176}
148177
178test "math.floor128" {
179 expect(floor128(1.3) == 1.0);
180 expect(floor128(-1.3) == -2.0);
181 expect(floor128(0.2) == 0.0);
182}
183
149184test "math.floor16.special" {
150185 expect(floor16(0.0) == 0.0);
151186 expect(floor16(-0.0) == -0.0);
......@@ -169,3 +204,11 @@ test "math.floor64.special" {
169204 expect(math.isNegativeInf(floor64(-math.inf(f64))));
170205 expect(math.isNan(floor64(math.nan(f64))));
171206}
207
208test "math.floor128.special" {
209 expect(floor128(0.0) == 0.0);
210 expect(floor128(-0.0) == -0.0);
211 expect(math.isPositiveInf(floor128(math.inf(f128))));
212 expect(math.isNegativeInf(floor128(-math.inf(f128))));
213 expect(math.isNan(floor128(math.nan(f128))));
214}
lib/std/math/frexp.zig+1-1
......@@ -24,7 +24,7 @@ pub const frexp64_result = frexp_result(f64);
2424/// - frexp(+-0) = +-0, 0
2525/// - frexp(+-inf) = +-inf, 0
2626/// - frexp(nan) = nan, undefined
27pub fn frexp(x: var) frexp_result(@TypeOf(x)) {
27pub fn frexp(x: anytype) frexp_result(@TypeOf(x)) {
2828 const T = @TypeOf(x);
2929 return switch (T) {
3030 f32 => frexp32(x),
lib/std/math/ilogb.zig+1-1
......@@ -16,7 +16,7 @@ const minInt = std.math.minInt;
1616/// - ilogb(+-inf) = maxInt(i32)
1717/// - ilogb(0) = maxInt(i32)
1818/// - ilogb(nan) = maxInt(i32)
19pub fn ilogb(x: var) i32 {
19pub fn ilogb(x: anytype) i32 {
2020 const T = @TypeOf(x);
2121 return switch (T) {
2222 f32 => ilogb32(x),
lib/std/math/isfinite.zig+1-1
......@@ -4,7 +4,7 @@ const expect = std.testing.expect;
44const maxInt = std.math.maxInt;
55
66/// Returns whether x is a finite value.
7pub fn isFinite(x: var) bool {
7pub fn isFinite(x: anytype) bool {
88 const T = @TypeOf(x);
99 switch (T) {
1010 f16 => {
lib/std/math/isinf.zig+3-3
......@@ -4,7 +4,7 @@ const expect = std.testing.expect;
44const maxInt = std.math.maxInt;
55
66/// Returns whether x is an infinity, ignoring sign.
7pub fn isInf(x: var) bool {
7pub fn isInf(x: anytype) bool {
88 const T = @TypeOf(x);
99 switch (T) {
1010 f16 => {
......@@ -30,7 +30,7 @@ pub fn isInf(x: var) bool {
3030}
3131
3232/// Returns whether x is an infinity with a positive sign.
33pub fn isPositiveInf(x: var) bool {
33pub fn isPositiveInf(x: anytype) bool {
3434 const T = @TypeOf(x);
3535 switch (T) {
3636 f16 => {
......@@ -52,7 +52,7 @@ pub fn isPositiveInf(x: var) bool {
5252}
5353
5454/// Returns whether x is an infinity with a negative sign.
55pub fn isNegativeInf(x: var) bool {
55pub fn isNegativeInf(x: anytype) bool {
5656 const T = @TypeOf(x);
5757 switch (T) {
5858 f16 => {
lib/std/math/isnan.zig+2-2
......@@ -4,12 +4,12 @@ const expect = std.testing.expect;
44const maxInt = std.math.maxInt;
55
66/// Returns whether x is a nan.
7pub fn isNan(x: var) bool {
7pub fn isNan(x: anytype) bool {
88 return x != x;
99}
1010
1111/// Returns whether x is a signalling nan.
12pub fn isSignalNan(x: var) bool {
12pub fn isSignalNan(x: anytype) bool {
1313 // Note: A signalling nan is identical to a standard nan right now but may have a different bit
1414 // representation in the future when required.
1515 return isNan(x);
lib/std/math/isnormal.zig+1-1
......@@ -4,7 +4,7 @@ const expect = std.testing.expect;
44const maxInt = std.math.maxInt;
55
66// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1).
7pub fn isNormal(x: var) bool {
7pub fn isNormal(x: anytype) bool {
88 const T = @TypeOf(x);
99 switch (T) {
1010 f16 => {
lib/std/math/ln.zig+1-1
......@@ -15,7 +15,7 @@ const expect = std.testing.expect;
1515/// - ln(0) = -inf
1616/// - ln(x) = nan if x < 0
1717/// - ln(nan) = nan
18pub fn ln(x: var) @TypeOf(x) {
18pub fn ln(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 switch (@typeInfo(T)) {
2121 .ComptimeFloat => {
lib/std/math/log10.zig+1-1
......@@ -16,7 +16,7 @@ const maxInt = std.math.maxInt;
1616/// - log10(0) = -inf
1717/// - log10(x) = nan if x < 0
1818/// - log10(nan) = nan
19pub fn log10(x: var) @TypeOf(x) {
19pub fn log10(x: anytype) @TypeOf(x) {
2020 const T = @TypeOf(x);
2121 switch (@typeInfo(T)) {
2222 .ComptimeFloat => {
lib/std/math/log1p.zig+1-1
......@@ -17,7 +17,7 @@ const expect = std.testing.expect;
1717/// - log1p(-1) = -inf
1818/// - log1p(x) = nan if x < -1
1919/// - log1p(nan) = nan
20pub fn log1p(x: var) @TypeOf(x) {
20pub fn log1p(x: anytype) @TypeOf(x) {
2121 const T = @TypeOf(x);
2222 return switch (T) {
2323 f32 => log1p_32(x),
lib/std/math/log2.zig+1-1
......@@ -16,7 +16,7 @@ const maxInt = std.math.maxInt;
1616/// - log2(0) = -inf
1717/// - log2(x) = nan if x < 0
1818/// - log2(nan) = nan
19pub fn log2(x: var) @TypeOf(x) {
19pub fn log2(x: anytype) @TypeOf(x) {
2020 const T = @TypeOf(x);
2121 switch (@typeInfo(T)) {
2222 .ComptimeFloat => {
lib/std/math/modf.zig+1-1
......@@ -24,7 +24,7 @@ pub const modf64_result = modf_result(f64);
2424/// Special Cases:
2525/// - modf(+-inf) = +-inf, nan
2626/// - modf(nan) = nan, nan
27pub fn modf(x: var) modf_result(@TypeOf(x)) {
27pub fn modf(x: anytype) modf_result(@TypeOf(x)) {
2828 const T = @TypeOf(x);
2929 return switch (T) {
3030 f32 => modf32(x),
lib/std/math/round.zig+51-1
......@@ -15,11 +15,12 @@ const math = std.math;
1515/// - round(+-0) = +-0
1616/// - round(+-inf) = +-inf
1717/// - round(nan) = nan
18pub fn round(x: var) @TypeOf(x) {
18pub fn round(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 return switch (T) {
2121 f32 => round32(x),
2222 f64 => round64(x),
23 f128 => round128(x),
2324 else => @compileError("round not implemented for " ++ @typeName(T)),
2425 };
2526}
......@@ -90,9 +91,43 @@ fn round64(x_: f64) f64 {
9091 }
9192}
9293
94fn round128(x_: f128) f128 {
95 var x = x_;
96 const u = @bitCast(u128, x);
97 const e = (u >> 112) & 0x7FFF;
98 var y: f128 = undefined;
99
100 if (e >= 0x3FFF + 112) {
101 return x;
102 }
103 if (u >> 127 != 0) {
104 x = -x;
105 }
106 if (e < 0x3FFF - 1) {
107 math.forceEval(x + math.f64_toint);
108 return 0 * @bitCast(f128, u);
109 }
110
111 y = x + math.f128_toint - math.f128_toint - x;
112 if (y > 0.5) {
113 y = y + x - 1;
114 } else if (y <= -0.5) {
115 y = y + x + 1;
116 } else {
117 y = y + x;
118 }
119
120 if (u >> 127 != 0) {
121 return -y;
122 } else {
123 return y;
124 }
125}
126
93127test "math.round" {
94128 expect(round(@as(f32, 1.3)) == round32(1.3));
95129 expect(round(@as(f64, 1.3)) == round64(1.3));
130 expect(round(@as(f128, 1.3)) == round128(1.3));
96131}
97132
98133test "math.round32" {
......@@ -109,6 +144,13 @@ test "math.round64" {
109144 expect(round64(1.8) == 2.0);
110145}
111146
147test "math.round128" {
148 expect(round128(1.3) == 1.0);
149 expect(round128(-1.3) == -1.0);
150 expect(round128(0.2) == 0.0);
151 expect(round128(1.8) == 2.0);
152}
153
112154test "math.round32.special" {
113155 expect(round32(0.0) == 0.0);
114156 expect(round32(-0.0) == -0.0);
......@@ -124,3 +166,11 @@ test "math.round64.special" {
124166 expect(math.isNegativeInf(round64(-math.inf(f64))));
125167 expect(math.isNan(round64(math.nan(f64))));
126168}
169
170test "math.round128.special" {
171 expect(round128(0.0) == 0.0);
172 expect(round128(-0.0) == -0.0);
173 expect(math.isPositiveInf(round128(math.inf(f128))));
174 expect(math.isNegativeInf(round128(-math.inf(f128))));
175 expect(math.isNan(round128(math.nan(f128))));
176}
lib/std/math/scalbn.zig+1-1
......@@ -9,7 +9,7 @@ const math = std.math;
99const expect = std.testing.expect;
1010
1111/// Returns x * 2^n.
12pub fn scalbn(x: var, n: i32) @TypeOf(x) {
12pub fn scalbn(x: anytype, n: i32) @TypeOf(x) {
1313 const T = @TypeOf(x);
1414 return switch (T) {
1515 f32 => scalbn32(x, n),
lib/std/math/signbit.zig+1-1
......@@ -3,7 +3,7 @@ const math = std.math;
33const expect = std.testing.expect;
44
55/// Returns whether x is negative or negative 0.
6pub fn signbit(x: var) bool {
6pub fn signbit(x: anytype) bool {
77 const T = @TypeOf(x);
88 return switch (T) {
99 f16 => signbit16(x),
lib/std/math/sin.zig+1-1
......@@ -14,7 +14,7 @@ const expect = std.testing.expect;
1414/// - sin(+-0) = +-0
1515/// - sin(+-inf) = nan
1616/// - sin(nan) = nan
17pub fn sin(x: var) @TypeOf(x) {
17pub fn sin(x: anytype) @TypeOf(x) {
1818 const T = @TypeOf(x);
1919 return switch (T) {
2020 f32 => sin_(T, x),
lib/std/math/sinh.zig+1-1
......@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;
1717/// - sinh(+-0) = +-0
1818/// - sinh(+-inf) = +-inf
1919/// - sinh(nan) = nan
20pub fn sinh(x: var) @TypeOf(x) {
20pub fn sinh(x: anytype) @TypeOf(x) {
2121 const T = @TypeOf(x);
2222 return switch (T) {
2323 f32 => sinh32(x),
lib/std/math/sqrt.zig+1-1
......@@ -13,7 +13,7 @@ const maxInt = std.math.maxInt;
1313/// - sqrt(x) = nan if x < 0
1414/// - sqrt(nan) = nan
1515/// TODO Decide if all this logic should be implemented directly in the @sqrt bultin function.
16pub fn sqrt(x: var) Sqrt(@TypeOf(x)) {
16pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
1717 const T = @TypeOf(x);
1818 switch (@typeInfo(T)) {
1919 .Float, .ComptimeFloat => return @sqrt(x),
lib/std/math/tan.zig+1-1
......@@ -14,7 +14,7 @@ const expect = std.testing.expect;
1414/// - tan(+-0) = +-0
1515/// - tan(+-inf) = nan
1616/// - tan(nan) = nan
17pub fn tan(x: var) @TypeOf(x) {
17pub fn tan(x: anytype) @TypeOf(x) {
1818 const T = @TypeOf(x);
1919 return switch (T) {
2020 f32 => tan_(f32, x),
lib/std/math/tanh.zig+1-1
......@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;
1717/// - sinh(+-0) = +-0
1818/// - sinh(+-inf) = +-1
1919/// - sinh(nan) = nan
20pub fn tanh(x: var) @TypeOf(x) {
20pub fn tanh(x: anytype) @TypeOf(x) {
2121 const T = @TypeOf(x);
2222 return switch (T) {
2323 f32 => tanh32(x),
lib/std/math/trunc.zig+38-1
......@@ -15,11 +15,12 @@ const maxInt = std.math.maxInt;
1515/// - trunc(+-0) = +-0
1616/// - trunc(+-inf) = +-inf
1717/// - trunc(nan) = nan
18pub fn trunc(x: var) @TypeOf(x) {
18pub fn trunc(x: anytype) @TypeOf(x) {
1919 const T = @TypeOf(x);
2020 return switch (T) {
2121 f32 => trunc32(x),
2222 f64 => trunc64(x),
23 f128 => trunc128(x),
2324 else => @compileError("trunc not implemented for " ++ @typeName(T)),
2425 };
2526}
......@@ -66,9 +67,31 @@ fn trunc64(x: f64) f64 {
6667 }
6768}
6869
70fn trunc128(x: f128) f128 {
71 const u = @bitCast(u128, x);
72 var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16;
73 var m: u128 = undefined;
74
75 if (e >= 112 + 16) {
76 return x;
77 }
78 if (e < 16) {
79 e = 1;
80 }
81
82 m = @as(u128, maxInt(u128)) >> @intCast(u7, e);
83 if (u & m == 0) {
84 return x;
85 } else {
86 math.forceEval(x + 0x1p120);
87 return @bitCast(f128, u & ~m);
88 }
89}
90
6991test "math.trunc" {
7092 expect(trunc(@as(f32, 1.3)) == trunc32(1.3));
7193 expect(trunc(@as(f64, 1.3)) == trunc64(1.3));
94 expect(trunc(@as(f128, 1.3)) == trunc128(1.3));
7295}
7396
7497test "math.trunc32" {
......@@ -83,6 +106,12 @@ test "math.trunc64" {
83106 expect(trunc64(0.2) == 0.0);
84107}
85108
109test "math.trunc128" {
110 expect(trunc128(1.3) == 1.0);
111 expect(trunc128(-1.3) == -1.0);
112 expect(trunc128(0.2) == 0.0);
113}
114
86115test "math.trunc32.special" {
87116 expect(trunc32(0.0) == 0.0); // 0x3F800000
88117 expect(trunc32(-0.0) == -0.0);
......@@ -98,3 +127,11 @@ test "math.trunc64.special" {
98127 expect(math.isNegativeInf(trunc64(-math.inf(f64))));
99128 expect(math.isNan(trunc64(math.nan(f64))));
100129}
130
131test "math.trunc128.special" {
132 expect(trunc128(0.0) == 0.0);
133 expect(trunc128(-0.0) == -0.0);
134 expect(math.isPositiveInf(trunc128(math.inf(f128))));
135 expect(math.isNegativeInf(trunc128(-math.inf(f128))));
136 expect(math.isNan(trunc128(math.nan(f128))));
137}
lib/std/mem.zig+350-86
......@@ -8,6 +8,7 @@ const meta = std.meta;
88const trait = meta.trait;
99const testing = std.testing;
1010
11// https://github.com/ziglang/zig/issues/2564
1112pub const page_size = switch (builtin.arch) {
1213 .wasm32, .wasm64 => 64 * 1024,
1314 else => 4 * 1024,
......@@ -16,6 +17,52 @@ pub const page_size = switch (builtin.arch) {
1617pub const Allocator = struct {
1718 pub const Error = error{OutOfMemory};
1819
20 /// Attempt to allocate at least `len` bytes aligned to `ptr_align`.
21 ///
22 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
23 /// otherwise, the length must be aligned to `len_align`.
24 ///
25 /// `len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
26 allocFn: fn (self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Error![]u8,
27
28 /// Attempt to expand or shrink memory in place. `buf.len` must equal the most recent
29 /// length returned by `allocFn` or `resizeFn`.
30 ///
31 /// Passing a `new_len` of 0 frees and invalidates the buffer such that it can no
32 /// longer be passed to `resizeFn`.
33 ///
34 /// error.OutOfMemory can only be returned if `new_len` is greater than `buf.len`.
35 /// If `buf` cannot be expanded to accomodate `new_len`, then the allocation MUST be
36 /// unmodified and error.OutOfMemory MUST be returned.
37 ///
38 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
39 /// otherwise, the length must be aligned to `len_align`.
40 ///
41 /// `new_len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
42 resizeFn: fn (self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize,
43
44 pub fn callAllocFn(self: *Allocator, new_len: usize, alignment: u29, len_align: u29) Error![]u8 {
45 return self.allocFn(self, new_len, alignment, len_align);
46 }
47
48 pub fn callResizeFn(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize {
49 return self.resizeFn(self, buf, new_len, len_align);
50 }
51
52 /// Set to resizeFn if in-place resize is not supported.
53 pub fn noResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize {
54 if (new_len > buf.len)
55 return error.OutOfMemory;
56 return new_len;
57 }
58
59 /// Call `resizeFn`, but caller guarantees that `new_len` <= `buf.len` meaning
60 /// error.OutOfMemory should be impossible.
61 pub fn shrinkBytes(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) usize {
62 assert(new_len <= buf.len);
63 return self.callResizeFn(buf, new_len, len_align) catch unreachable;
64 }
65
1966 /// Realloc is used to modify the size or alignment of an existing allocation,
2067 /// as well as to provide the allocator with an opportunity to move an allocation
2168 /// to a better location.
......@@ -24,7 +71,7 @@ pub const Allocator = struct {
2471 /// When the size/alignment is less than or equal to the previous allocation,
2572 /// this function returns `error.OutOfMemory` when the allocator decides the client
2673 /// would be better off keeping the extra alignment/size. Clients will call
27 /// `shrinkFn` when they require the allocator to track a new alignment/size,
74 /// `callResizeFn` when they require the allocator to track a new alignment/size,
2875 /// and so this function should only return success when the allocator considers
2976 /// the reallocation desirable from the allocator's perspective.
3077 /// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
......@@ -37,16 +84,15 @@ pub const Allocator = struct {
3784 /// as `old_mem` was when `reallocFn` is called. The bytes of
3885 /// `return_value[old_mem.len..]` have undefined values.
3986 /// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
40 reallocFn: fn (
87 fn reallocBytes(
4188 self: *Allocator,
4289 /// Guaranteed to be the same as what was returned from most recent call to
43 /// `reallocFn` or `shrinkFn`.
90 /// `allocFn` or `resizeFn`.
4491 /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
4592 /// is guaranteed to be >= 1.
4693 old_mem: []u8,
4794 /// If `old_mem.len == 0` then this is `undefined`, otherwise:
48 /// Guaranteed to be the same as what was returned from most recent call to
49 /// `reallocFn` or `shrinkFn`.
95 /// Guaranteed to be the same as what was passed to `allocFn`.
5096 /// Guaranteed to be >= 1.
5197 /// Guaranteed to be a power of 2.
5298 old_alignment: u29,
......@@ -57,23 +103,49 @@ pub const Allocator = struct {
57103 /// Guaranteed to be a power of 2.
58104 /// Returned slice's pointer must have this alignment.
59105 new_alignment: u29,
60 ) Error![]u8,
106 /// 0 indicates the length of the slice returned MUST match `new_byte_count` exactly
107 /// non-zero means the length of the returned slice must be aligned by `len_align`
108 /// `new_len` must be aligned by `len_align`
109 len_align: u29,
110 ) Error![]u8 {
111 if (old_mem.len == 0) {
112 const new_mem = try self.callAllocFn(new_byte_count, new_alignment, len_align);
113 @memset(new_mem.ptr, undefined, new_byte_count);
114 return new_mem;
115 }
61116
62 /// This function deallocates memory. It must succeed.
63 shrinkFn: fn (
64 self: *Allocator,
65 /// Guaranteed to be the same as what was returned from most recent call to
66 /// `reallocFn` or `shrinkFn`.
67 old_mem: []u8,
68 /// Guaranteed to be the same as what was returned from most recent call to
69 /// `reallocFn` or `shrinkFn`.
70 old_alignment: u29,
71 /// Guaranteed to be less than or equal to `old_mem.len`.
72 new_byte_count: usize,
73 /// If `new_byte_count == 0` then this is `undefined`, otherwise:
74 /// Guaranteed to be less than or equal to `old_alignment`.
75 new_alignment: u29,
76 ) []u8,
117 if (isAligned(@ptrToInt(old_mem.ptr), new_alignment)) {
118 if (new_byte_count <= old_mem.len) {
119 const shrunk_len = self.shrinkBytes(old_mem, new_byte_count, len_align);
120 return old_mem.ptr[0..shrunk_len];
121 }
122 if (self.callResizeFn(old_mem, new_byte_count, len_align)) |resized_len| {
123 assert(resized_len >= new_byte_count);
124 @memset(old_mem.ptr + new_byte_count, undefined, resized_len - new_byte_count);
125 return old_mem.ptr[0..resized_len];
126 } else |_| {}
127 }
128 if (new_byte_count <= old_mem.len and new_alignment <= old_alignment) {
129 return error.OutOfMemory;
130 }
131 return self.moveBytes(old_mem, new_byte_count, new_alignment, len_align);
132 }
133
134 /// Move the given memory to a new location in the given allocator to accomodate a new
135 /// size and alignment.
136 fn moveBytes(self: *Allocator, old_mem: []u8, new_len: usize, new_alignment: u29, len_align: u29) Error![]u8 {
137 assert(old_mem.len > 0);
138 assert(new_len > 0);
139 const new_mem = try self.callAllocFn(new_len, new_alignment, len_align);
140 @memcpy(new_mem.ptr, old_mem.ptr, std.math.min(new_len, old_mem.len));
141 // DISABLED TO AVOID BUGS IN TRANSLATE C
142 // use './zig build test-translate-c' to reproduce, some of the symbols in the
143 // generated C code will be a sequence of 0xaa (the undefined value), meaning
144 // it is printing data that has been freed
145 //@memset(old_mem.ptr, undefined, old_mem.len);
146 _ = self.shrinkBytes(old_mem, 0, 0);
147 return new_mem;
148 }
77149
78150 /// Returns a pointer to undefined memory.
79151 /// Call `destroy` with the result to free the memory.
......@@ -85,12 +157,11 @@ pub const Allocator = struct {
85157
86158 /// `ptr` should be the return value of `create`, or otherwise
87159 /// have the same address and alignment property.
88 pub fn destroy(self: *Allocator, ptr: var) void {
160 pub fn destroy(self: *Allocator, ptr: anytype) void {
89161 const T = @TypeOf(ptr).Child;
90162 if (@sizeOf(T) == 0) return;
91163 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
92 const shrink_result = self.shrinkFn(self, non_const_ptr[0..@sizeOf(T)], @alignOf(T), 0, 1);
93 assert(shrink_result.len == 0);
164 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], 0, 0);
94165 }
95166
96167 /// Allocates an array of `n` items of type `T` and sets all the
......@@ -144,15 +215,28 @@ pub const Allocator = struct {
144215 return self.allocWithOptions(Elem, n, null, sentinel);
145216 }
146217
218 /// Deprecated: use `allocAdvanced`
147219 pub fn alignedAlloc(
148220 self: *Allocator,
149221 comptime T: type,
150222 /// null means naturally aligned
151223 comptime alignment: ?u29,
152224 n: usize,
225 ) Error![]align(alignment orelse @alignOf(T)) T {
226 return self.allocAdvanced(T, alignment, n, .exact);
227 }
228
229 const Exact = enum { exact, at_least };
230 pub fn allocAdvanced(
231 self: *Allocator,
232 comptime T: type,
233 /// null means naturally aligned
234 comptime alignment: ?u29,
235 n: usize,
236 exact: Exact,
153237 ) Error![]align(alignment orelse @alignOf(T)) T {
154238 const a = if (alignment) |a| blk: {
155 if (a == @alignOf(T)) return alignedAlloc(self, T, null, n);
239 if (a == @alignOf(T)) return allocAdvanced(self, T, null, n, exact);
156240 break :blk a;
157241 } else @alignOf(T);
158242
......@@ -161,15 +245,19 @@ pub const Allocator = struct {
161245 }
162246
163247 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
164 const byte_slice = try self.reallocFn(self, &[0]u8{}, undefined, byte_count, a);
165 assert(byte_slice.len == byte_count);
248 // TODO The `if (alignment == null)` blocks are workarounds for zig not being able to
249 // access certain type information about T without creating a circular dependency in async
250 // functions that heap-allocate their own frame with @Frame(func).
251 const sizeOfT = if (alignment == null) @intCast(u29, @divExact(byte_count, n)) else @sizeOf(T);
252 const byte_slice = try self.callAllocFn(byte_count, a, if (exact == .exact) @as(u29, 0) else sizeOfT);
253 switch (exact) {
254 .exact => assert(byte_slice.len == byte_count),
255 .at_least => assert(byte_slice.len >= byte_count),
256 }
166257 @memset(byte_slice.ptr, undefined, byte_slice.len);
167258 if (alignment == null) {
168 // TODO This is a workaround for zig not being able to successfully do
169 // @bytesToSlice(T, @alignCast(a, byte_slice)) without resolving alignment of T,
170 // which causes a circular dependency in async functions which try to heap-allocate
171 // their own frame with @Frame(func).
172 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..n];
259 // This if block is a workaround (see comment above)
260 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..@divExact(byte_slice.len, @sizeOf(T))];
173261 } else {
174262 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
175263 }
......@@ -185,27 +273,46 @@ pub const Allocator = struct {
185273 /// in `std.ArrayList.shrink`.
186274 /// If you need guaranteed success, call `shrink`.
187275 /// If `new_n` is 0, this is the same as `free` and it always succeeds.
188 pub fn realloc(self: *Allocator, old_mem: var, new_n: usize) t: {
276 pub fn realloc(self: *Allocator, old_mem: anytype, new_n: usize) t: {
277 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
278 break :t Error![]align(Slice.alignment) Slice.child;
279 } {
280 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
281 return self.reallocAdvanced(old_mem, old_alignment, new_n, .exact);
282 }
283
284 pub fn reallocAtLeast(self: *Allocator, old_mem: anytype, new_n: usize) t: {
189285 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
190286 break :t Error![]align(Slice.alignment) Slice.child;
191287 } {
192288 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
193 return self.alignedRealloc(old_mem, old_alignment, new_n);
289 return self.reallocAdvanced(old_mem, old_alignment, new_n, .at_least);
290 }
291
292 // Deprecated: use `reallocAdvanced`
293 pub fn alignedRealloc(
294 self: *Allocator,
295 old_mem: anytype,
296 comptime new_alignment: u29,
297 new_n: usize,
298 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
299 return self.reallocAdvanced(old_mem, new_alignment, new_n, .exact);
194300 }
195301
196302 /// This is the same as `realloc`, except caller may additionally request
197303 /// a new alignment, which can be larger, smaller, or the same as the old
198304 /// allocation.
199 pub fn alignedRealloc(
305 pub fn reallocAdvanced(
200306 self: *Allocator,
201 old_mem: var,
307 old_mem: anytype,
202308 comptime new_alignment: u29,
203309 new_n: usize,
310 exact: Exact,
204311 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
205312 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
206313 const T = Slice.child;
207314 if (old_mem.len == 0) {
208 return self.alignedAlloc(T, new_alignment, new_n);
315 return self.allocAdvanced(T, new_alignment, new_n, exact);
209316 }
210317 if (new_n == 0) {
211318 self.free(old_mem);
......@@ -215,12 +322,8 @@ pub const Allocator = struct {
215322 const old_byte_slice = mem.sliceAsBytes(old_mem);
216323 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
217324 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
218 const byte_slice = try self.reallocFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);
219 assert(byte_slice.len == byte_count);
220 if (new_n > old_mem.len) {
221 @memset(byte_slice.ptr + old_byte_slice.len, undefined, byte_slice.len - old_byte_slice.len);
222 }
223 return mem.bytesAsSlice(T, @alignCast(new_alignment, byte_slice));
325 const new_byte_slice = try self.reallocBytes(old_byte_slice, Slice.alignment, byte_count, new_alignment, if (exact == .exact) @as(u29, 0) else @sizeOf(T));
326 return mem.bytesAsSlice(T, @alignCast(new_alignment, new_byte_slice));
224327 }
225328
226329 /// Prefer calling realloc to shrink if you can tolerate failure, such as
......@@ -228,7 +331,7 @@ pub const Allocator = struct {
228331 /// Shrink always succeeds, and `new_n` must be <= `old_mem.len`.
229332 /// Returned slice has same alignment as old_mem.
230333 /// Shrinking to 0 is the same as calling `free`.
231 pub fn shrink(self: *Allocator, old_mem: var, new_n: usize) t: {
334 pub fn shrink(self: *Allocator, old_mem: anytype, new_n: usize) t: {
232335 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
233336 break :t []align(Slice.alignment) Slice.child;
234337 } {
......@@ -241,19 +344,16 @@ pub const Allocator = struct {
241344 /// allocation.
242345 pub fn alignedShrink(
243346 self: *Allocator,
244 old_mem: var,
347 old_mem: anytype,
245348 comptime new_alignment: u29,
246349 new_n: usize,
247350 ) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
248351 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
249352 const T = Slice.child;
250353
251 if (new_n == 0) {
252 self.free(old_mem);
253 return old_mem[0..0];
254 }
255
256 assert(new_n <= old_mem.len);
354 if (new_n == old_mem.len)
355 return old_mem;
356 assert(new_n < old_mem.len);
257357 assert(new_alignment <= Slice.alignment);
258358
259359 // Here we skip the overflow checking on the multiplication because
......@@ -262,22 +362,20 @@ pub const Allocator = struct {
262362
263363 const old_byte_slice = mem.sliceAsBytes(old_mem);
264364 @memset(old_byte_slice.ptr + byte_count, undefined, old_byte_slice.len - byte_count);
265 const byte_slice = self.shrinkFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);
266 assert(byte_slice.len == byte_count);
267 return mem.bytesAsSlice(T, @alignCast(new_alignment, byte_slice));
365 _ = self.shrinkBytes(old_byte_slice, byte_count, 0);
366 return old_mem[0..new_n];
268367 }
269368
270369 /// Free an array allocated with `alloc`. To free a single item,
271370 /// see `destroy`.
272 pub fn free(self: *Allocator, memory: var) void {
371 pub fn free(self: *Allocator, memory: anytype) void {
273372 const Slice = @typeInfo(@TypeOf(memory)).Pointer;
274373 const bytes = mem.sliceAsBytes(memory);
275374 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
276375 if (bytes_len == 0) return;
277376 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
278377 @memset(non_const_ptr, undefined, bytes_len);
279 const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1);
280 assert(shrink_result.len == 0);
378 _ = self.shrinkBytes(non_const_ptr[0..bytes_len], 0, 0);
281379 }
282380
283381 /// Copies `m` to newly allocated memory. Caller owns the memory.
......@@ -296,16 +394,96 @@ pub const Allocator = struct {
296394 }
297395};
298396
397/// Detects and asserts if the std.mem.Allocator interface is violated by the caller
398/// or the allocator.
399pub fn ValidationAllocator(comptime T: type) type {
400 return struct {
401 const Self = @This();
402 allocator: Allocator,
403 underlying_allocator: T,
404 pub fn init(allocator: T) @This() {
405 return .{
406 .allocator = .{
407 .allocFn = alloc,
408 .resizeFn = resize,
409 },
410 .underlying_allocator = allocator,
411 };
412 }
413 fn getUnderlyingAllocatorPtr(self: *@This()) *Allocator {
414 if (T == *Allocator) return self.underlying_allocator;
415 if (*T == *Allocator) return &self.underlying_allocator;
416 return &self.underlying_allocator.allocator;
417 }
418 pub fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 {
419 assert(n > 0);
420 assert(mem.isValidAlign(ptr_align));
421 if (len_align != 0) {
422 assert(mem.isAlignedAnyAlign(n, len_align));
423 assert(n >= len_align);
424 }
425
426 const self = @fieldParentPtr(@This(), "allocator", allocator);
427 const result = try self.getUnderlyingAllocatorPtr().callAllocFn(n, ptr_align, len_align);
428 assert(mem.isAligned(@ptrToInt(result.ptr), ptr_align));
429 if (len_align == 0) {
430 assert(result.len == n);
431 } else {
432 assert(result.len >= n);
433 assert(mem.isAlignedAnyAlign(result.len, len_align));
434 }
435 return result;
436 }
437 pub fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize {
438 assert(buf.len > 0);
439 if (len_align != 0) {
440 assert(mem.isAlignedAnyAlign(new_len, len_align));
441 assert(new_len >= len_align);
442 }
443 const self = @fieldParentPtr(@This(), "allocator", allocator);
444 const result = try self.getUnderlyingAllocatorPtr().callResizeFn(buf, new_len, len_align);
445 if (len_align == 0) {
446 assert(result == new_len);
447 } else {
448 assert(result >= new_len);
449 assert(mem.isAlignedAnyAlign(result, len_align));
450 }
451 return result;
452 }
453 pub usingnamespace if (T == *Allocator or !@hasDecl(T, "reset")) struct {} else struct {
454 pub fn reset(self: *Self) void {
455 self.underlying_allocator.reset();
456 }
457 };
458 };
459}
460
461pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) {
462 return ValidationAllocator(@TypeOf(allocator)).init(allocator);
463}
464
465/// An allocator helper function. Adjusts an allocation length satisfy `len_align`.
466/// `full_len` should be the full capacity of the allocation which may be greater
467/// than the `len` that was requsted. This function should only be used by allocators
468/// that are unaffected by `len_align`.
469pub fn alignAllocLen(full_len: usize, alloc_len: usize, len_align: u29) usize {
470 assert(alloc_len > 0);
471 assert(alloc_len >= len_align);
472 assert(full_len >= alloc_len);
473 if (len_align == 0)
474 return alloc_len;
475 const adjusted = alignBackwardAnyAlign(full_len, len_align);
476 assert(adjusted >= alloc_len);
477 return adjusted;
478}
479
299480var failAllocator = Allocator{
300 .reallocFn = failAllocatorRealloc,
301 .shrinkFn = failAllocatorShrink,
481 .allocFn = failAllocatorAlloc,
482 .resizeFn = Allocator.noResize,
302483};
303fn failAllocatorRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
484fn failAllocatorAlloc(self: *Allocator, n: usize, alignment: u29, len_align: u29) Allocator.Error![]u8 {
304485 return error.OutOfMemory;
305486}
306fn failAllocatorShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
307 @panic("failAllocatorShrink should never be called because it cannot allocate");
308}
309487
310488test "mem.Allocator basics" {
311489 testing.expectError(error.OutOfMemory, failAllocator.alloc(u8, 1));
......@@ -341,6 +519,7 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
341519 }
342520}
343521
522/// Sets all elements of `dest` to `value`.
344523pub fn set(comptime T: type, dest: []T, value: T) void {
345524 for (dest) |*d|
346525 d.* = value;
......@@ -373,7 +552,7 @@ pub fn zeroes(comptime T: type) T {
373552 if (@sizeOf(T) == 0) return T{};
374553 if (comptime meta.containerLayout(T) == .Extern) {
375554 var item: T = undefined;
376 @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));
555 set(u8, asBytes(&item), 0);
377556 return item;
378557 } else {
379558 var structure: T = undefined;
......@@ -498,6 +677,8 @@ test "mem.zeroes" {
498677 }
499678}
500679
680/// Sets a slice to zeroes.
681/// Prevents the store from being optimized out.
501682pub fn secureZero(comptime T: type, s: []T) void {
502683 // NOTE: We do not use a volatile slice cast here since LLVM cannot
503684 // see that it can be replaced by a memset.
......@@ -519,7 +700,7 @@ test "mem.secureZero" {
519700/// Initializes all fields of the struct with their default value, or zero values if no default value is present.
520701/// If the field is present in the provided initial values, it will have that value instead.
521702/// Structs are initialized recursively.
522pub fn zeroInit(comptime T: type, init: var) T {
703pub fn zeroInit(comptime T: type, init: anytype) T {
523704 comptime const Init = @TypeOf(init);
524705
525706 switch (@typeInfo(T)) {
......@@ -528,6 +709,13 @@ pub fn zeroInit(comptime T: type, init: var) T {
528709 .Struct => |init_info| {
529710 var value = std.mem.zeroes(T);
530711
712 if (init_info.is_tuple) {
713 inline for (init_info.fields) |field, i| {
714 @field(value, struct_info.fields[i].name) = @field(init, field.name);
715 }
716 return value;
717 }
718
531719 inline for (init_info.fields) |field| {
532720 if (!@hasField(T, field.name)) {
533721 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
......@@ -544,8 +732,8 @@ pub fn zeroInit(comptime T: type, init: var) T {
544732 @field(value, field.name) = @field(init, field.name);
545733 },
546734 }
547 } else if (field.default_value != null) {
548 @field(value, field.name) = field.default_value;
735 } else if (field.default_value) |default_value| {
736 @field(value, field.name) = default_value;
549737 }
550738 }
551739
......@@ -572,24 +760,40 @@ test "zeroInit" {
572760 b: ?bool,
573761 c: I,
574762 e: [3]u8,
575 f: i64,
763 f: i64 = -1,
576764 };
577765
578766 const s = zeroInit(S, .{
579767 .a = 42,
580768 });
581769
582 testing.expectEqual(s, S{
770 testing.expectEqual(S{
583771 .a = 42,
584772 .b = null,
585773 .c = .{
586774 .d = 0,
587775 },
588776 .e = [3]u8{ 0, 0, 0 },
589 .f = 0,
590 });
777 .f = -1,
778 }, s);
779
780 const Color = struct {
781 r: u8,
782 g: u8,
783 b: u8,
784 a: u8,
785 };
786
787 const c = zeroInit(Color, .{ 255, 255 });
788 testing.expectEqual(Color{
789 .r = 255,
790 .g = 255,
791 .b = 0,
792 .a = 0,
793 }, c);
591794}
592795
796/// Compares two slices of numbers lexicographically. O(n).
593797pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {
594798 const n = math.min(lhs.len, rhs.len);
595799 var i: usize = 0;
......@@ -719,7 +923,7 @@ test "Span" {
719923///
720924/// When there is both a sentinel and an array length or slice length, the
721925/// length value is used instead of the sentinel.
722pub fn span(ptr: var) Span(@TypeOf(ptr)) {
926pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {
723927 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
724928 if (ptr) |non_null| {
725929 return span(non_null);
......@@ -747,7 +951,7 @@ test "span" {
747951/// Same as `span`, except when there is both a sentinel and an array
748952/// length or slice length, scans the memory for the sentinel value
749953/// rather than using the length.
750pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) {
954pub fn spanZ(ptr: anytype) Span(@TypeOf(ptr)) {
751955 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
752956 if (ptr) |non_null| {
753957 return spanZ(non_null);
......@@ -776,7 +980,7 @@ test "spanZ" {
776980/// or a slice, and returns the length.
777981/// In the case of a sentinel-terminated array, it uses the array length.
778982/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
779pub fn len(value: var) usize {
983pub fn len(value: anytype) usize {
780984 return switch (@typeInfo(@TypeOf(value))) {
781985 .Array => |info| info.len,
782986 .Vector => |info| info.len,
......@@ -824,7 +1028,7 @@ test "len" {
8241028/// In the case of a sentinel-terminated array, it scans the array
8251029/// for a sentinel and uses that for the length, rather than using the array length.
8261030/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
827pub fn lenZ(ptr: var) usize {
1031pub fn lenZ(ptr: anytype) usize {
8281032 return switch (@typeInfo(@TypeOf(ptr))) {
8291033 .Array => |info| if (info.sentinel) |sentinel|
8301034 indexOfSentinel(info.child, sentinel, &ptr)
......@@ -1492,12 +1696,23 @@ pub const SplitIterator = struct {
14921696/// Naively combines a series of slices with a separator.
14931697/// Allocates memory for the result, which must be freed by the caller.
14941698pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
1699 return joinMaybeZ(allocator, separator, slices, false);
1700}
1701
1702/// Naively combines a series of slices with a separator and null terminator.
1703/// Allocates memory for the result, which must be freed by the caller.
1704pub fn joinZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![:0]u8 {
1705 const out = try joinMaybeZ(allocator, separator, slices, true);
1706 return out[0 .. out.len - 1 :0];
1707}
1708
1709fn joinMaybeZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8, zero: bool) ![]u8 {
14951710 if (slices.len == 0) return &[0]u8{};
14961711
14971712 const total_len = blk: {
14981713 var sum: usize = separator.len * (slices.len - 1);
1499 for (slices) |slice|
1500 sum += slice.len;
1714 for (slices) |slice| sum += slice.len;
1715 if (zero) sum += 1;
15011716 break :blk sum;
15021717 };
15031718
......@@ -1513,6 +1728,8 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons
15131728 buf_index += slice.len;
15141729 }
15151730
1731 if (zero) buf[buf.len - 1] = 0;
1732
15161733 // No need for shrink since buf is exactly the correct size.
15171734 return buf;
15181735}
......@@ -1535,6 +1752,27 @@ test "mem.join" {
15351752 }
15361753}
15371754
1755test "mem.joinZ" {
1756 {
1757 const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
1758 defer testing.allocator.free(str);
1759 testing.expect(eql(u8, str, "a,b,c"));
1760 testing.expectEqual(str[str.len], 0);
1761 }
1762 {
1763 const str = try joinZ(testing.allocator, ",", &[_][]const u8{"a"});
1764 defer testing.allocator.free(str);
1765 testing.expect(eql(u8, str, "a"));
1766 testing.expectEqual(str[str.len], 0);
1767 }
1768 {
1769 const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" });
1770 defer testing.allocator.free(str);
1771 testing.expect(eql(u8, str, "a,,b,,c"));
1772 testing.expectEqual(str[str.len], 0);
1773 }
1774}
1775
15381776/// Copies each T from slices into a new slice that exactly holds all the elements.
15391777pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {
15401778 if (slices.len == 0) return &[0]T{};
......@@ -1727,6 +1965,8 @@ fn testWriteIntImpl() void {
17271965 }));
17281966}
17291967
1968/// Returns the smallest number in a slice. O(n).
1969/// `slice` must not be empty.
17301970pub fn min(comptime T: type, slice: []const T) T {
17311971 var best = slice[0];
17321972 for (slice[1..]) |item| {
......@@ -1739,6 +1979,8 @@ test "mem.min" {
17391979 testing.expect(min(u8, "abcdefg") == 'a');
17401980}
17411981
1982/// Returns the largest number in a slice. O(n).
1983/// `slice` must not be empty.
17421984pub fn max(comptime T: type, slice: []const T) T {
17431985 var best = slice[0];
17441986 for (slice[1..]) |item| {
......@@ -1855,7 +2097,7 @@ fn AsBytesReturnType(comptime P: type) type {
18552097}
18562098
18572099/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
1858pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) {
2100pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {
18592101 const P = @TypeOf(ptr);
18602102 return @ptrCast(AsBytesReturnType(P), ptr);
18612103}
......@@ -1894,8 +2136,8 @@ test "asBytes" {
18942136 testing.expect(eql(u8, asBytes(&zero), ""));
18952137}
18962138
1897///Given any value, returns a copy of its bytes in an array.
1898pub fn toBytes(value: var) [@sizeOf(@TypeOf(value))]u8 {
2139/// Given any value, returns a copy of its bytes in an array.
2140pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {
18992141 return asBytes(&value).*;
19002142}
19012143
......@@ -1928,9 +2170,9 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
19282170 return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
19292171}
19302172
1931///Given a pointer to an array of bytes, returns a pointer to a value of the specified type
2173/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
19322174/// backed by those bytes, preserving constness.
1933pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @TypeOf(bytes)) {
2175pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
19342176 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);
19352177}
19362178
......@@ -1971,9 +2213,9 @@ test "bytesAsValue" {
19712213 testing.expect(meta.eql(inst, inst2.*));
19722214}
19732215
1974///Given a pointer to an array of bytes, returns a value of the specified type backed by a
2216/// Given a pointer to an array of bytes, returns a value of the specified type backed by a
19752217/// copy of those bytes.
1976pub fn bytesToValue(comptime T: type, bytes: var) T {
2218pub fn bytesToValue(comptime T: type, bytes: anytype) T {
19772219 return bytesAsValue(T, bytes).*;
19782220}
19792221test "bytesToValue" {
......@@ -2001,7 +2243,7 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
20012243 return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T;
20022244}
20032245
2004pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
2246pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
20052247 // let's not give an undefined pointer to @ptrCast
20062248 // it may be equal to zero and fail a null check
20072249 if (bytes.len == 0) {
......@@ -2080,7 +2322,7 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type {
20802322 return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8;
20812323}
20822324
2083pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) {
2325pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
20842326 const Slice = @TypeOf(slice);
20852327
20862328 // let's not give an undefined pointer to @ptrCast
......@@ -2190,6 +2432,15 @@ test "alignForward" {
21902432 testing.expect(alignForward(17, 8) == 24);
21912433}
21922434
2435/// Round an address up to the previous aligned address
2436/// Unlike `alignBackward`, `alignment` can be any positive number, not just a power of 2.
2437pub fn alignBackwardAnyAlign(i: usize, alignment: usize) usize {
2438 if (@popCount(usize, alignment) == 1)
2439 return alignBackward(i, alignment);
2440 assert(alignment != 0);
2441 return i - @mod(i, alignment);
2442}
2443
21932444/// Round an address up to the previous aligned address
21942445/// The alignment must be a power of 2 and greater than 0.
21952446pub fn alignBackward(addr: usize, alignment: usize) usize {
......@@ -2206,6 +2457,19 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
22062457 return addr & ~(alignment - 1);
22072458}
22082459
2460/// Returns whether `alignment` is a valid alignment, meaning it is
2461/// a positive power of 2.
2462pub fn isValidAlign(alignment: u29) bool {
2463 return @popCount(u29, alignment) == 1;
2464}
2465
2466pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool {
2467 if (@popCount(usize, alignment) == 1)
2468 return isAligned(i, alignment);
2469 assert(alignment != 0);
2470 return 0 == @mod(i, alignment);
2471}
2472
22092473/// Given an address and an alignment, return true if the address is a multiple of the alignment
22102474/// The alignment must be a power of 2 and greater than 0.
22112475pub fn isAligned(addr: usize, alignment: usize) bool {
lib/std/meta.zig+93-10
......@@ -6,10 +6,11 @@ const math = std.math;
66const testing = std.testing;
77
88pub const trait = @import("meta/trait.zig");
9pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
910
1011const TypeInfo = builtin.TypeInfo;
1112
12pub fn tagName(v: var) []const u8 {
13pub fn tagName(v: anytype) []const u8 {
1314 const T = @TypeOf(v);
1415 switch (@typeInfo(T)) {
1516 .ErrorSet => return @errorName(v),
......@@ -250,7 +251,7 @@ test "std.meta.containerLayout" {
250251 testing.expect(containerLayout(U3) == .Extern);
251252}
252253
253pub fn declarations(comptime T: type) []TypeInfo.Declaration {
254pub fn declarations(comptime T: type) []const TypeInfo.Declaration {
254255 return switch (@typeInfo(T)) {
255256 .Struct => |info| info.decls,
256257 .Enum => |info| info.decls,
......@@ -274,7 +275,7 @@ test "std.meta.declarations" {
274275 fn a() void {}
275276 };
276277
277 const decls = comptime [_][]TypeInfo.Declaration{
278 const decls = comptime [_][]const TypeInfo.Declaration{
278279 declarations(E1),
279280 declarations(S1),
280281 declarations(U1),
......@@ -323,10 +324,10 @@ test "std.meta.declarationInfo" {
323324}
324325
325326pub fn fields(comptime T: type) switch (@typeInfo(T)) {
326 .Struct => []TypeInfo.StructField,
327 .Union => []TypeInfo.UnionField,
328 .ErrorSet => []TypeInfo.Error,
329 .Enum => []TypeInfo.EnumField,
327 .Struct => []const TypeInfo.StructField,
328 .Union => []const TypeInfo.UnionField,
329 .ErrorSet => []const TypeInfo.Error,
330 .Enum => []const TypeInfo.EnumField,
330331 else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
331332} {
332333 return switch (@typeInfo(T)) {
......@@ -430,7 +431,7 @@ test "std.meta.TagType" {
430431}
431432
432433///Returns the active tag of a tagged union
433pub fn activeTag(u: var) @TagType(@TypeOf(u)) {
434pub fn activeTag(u: anytype) @TagType(@TypeOf(u)) {
434435 const T = @TypeOf(u);
435436 return @as(@TagType(T), u);
436437}
......@@ -480,7 +481,7 @@ test "std.meta.TagPayloadType" {
480481
481482/// Compares two of any type for equality. Containers are compared on a field-by-field basis,
482483/// where possible. Pointers are not followed.
483pub fn eql(a: var, b: @TypeOf(a)) bool {
484pub fn eql(a: anytype, b: @TypeOf(a)) bool {
484485 const T = @TypeOf(a);
485486
486487 switch (@typeInfo(T)) {
......@@ -627,7 +628,7 @@ test "intToEnum with error return" {
627628
628629pub const IntToEnumError = error{InvalidEnumTag};
629630
630pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag {
631pub fn intToEnum(comptime Tag: type, tag_int: anytype) IntToEnumError!Tag {
631632 inline for (@typeInfo(Tag).Enum.fields) |f| {
632633 const this_tag_value = @field(Tag, f.name);
633634 if (tag_int == @enumToInt(this_tag_value)) {
......@@ -693,3 +694,85 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
693694 },
694695 });
695696}
697
698/// Given a type and value, cast the value to the type as c would.
699/// This is for translate-c and is not intended for general use.
700pub fn cast(comptime DestType: type, target: anytype) DestType {
701 const TargetType = @TypeOf(target);
702 switch (@typeInfo(DestType)) {
703 .Pointer => {
704 switch (@typeInfo(TargetType)) {
705 .Int, .ComptimeInt => {
706 return @intToPtr(DestType, target);
707 },
708 .Pointer => |ptr| {
709 return @ptrCast(DestType, @alignCast(ptr.alignment, target));
710 },
711 .Optional => |opt| {
712 if (@typeInfo(opt.child) == .Pointer) {
713 return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));
714 }
715 },
716 else => {},
717 }
718 },
719 .Optional => |opt| {
720 if (@typeInfo(opt.child) == .Pointer) {
721 switch (@typeInfo(TargetType)) {
722 .Int, .ComptimeInt => {
723 return @intToPtr(DestType, target);
724 },
725 .Pointer => |ptr| {
726 return @ptrCast(DestType, @alignCast(ptr.alignment, target));
727 },
728 .Optional => |target_opt| {
729 if (@typeInfo(target_opt.child) == .Pointer) {
730 return @ptrCast(DestType, @alignCast(@alignOf(target_opt.child.Child), target));
731 }
732 },
733 else => {},
734 }
735 }
736 },
737 .Enum, .EnumLiteral => {
738 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
739 return @intToEnum(DestType, target);
740 }
741 },
742 .Int, .ComptimeInt => {
743 switch (@typeInfo(TargetType)) {
744 .Pointer => {
745 return @as(DestType, @ptrToInt(target));
746 },
747 .Optional => |opt| {
748 if (@typeInfo(opt.child) == .Pointer) {
749 return @as(DestType, @ptrToInt(target));
750 }
751 },
752 .Enum, .EnumLiteral => {
753 return @as(DestType, @enumToInt(target));
754 },
755 else => {},
756 }
757 },
758 else => {},
759 }
760 return @as(DestType, target);
761}
762
763test "std.meta.cast" {
764 const E = enum(u2) {
765 Zero,
766 One,
767 Two,
768 };
769
770 var i = @as(i64, 10);
771
772 testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
773 testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));
774 testing.expect(cast(u64, @as(u32, 10)) == @as(u64, 10));
775 testing.expect(cast(E, 1) == .One);
776 testing.expect(cast(u8, E.Two) == 2);
777 testing.expect(cast(*u64, &i).* == @as(u64, 10));
778}
lib/std/meta/trailer_flags.zig created+145
......@@ -0,0 +1,145 @@
1const std = @import("../std.zig");
2const meta = std.meta;
3const testing = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
6
7/// This is useful for saving memory when allocating an object that has many
8/// optional components. The optional objects are allocated sequentially in
9/// memory, and a single integer is used to represent each optional object
10/// and whether it is present based on each corresponding bit.
11pub fn TrailerFlags(comptime Fields: type) type {
12 return struct {
13 bits: Int,
14
15 pub const Int = @Type(.{ .Int = .{ .bits = bit_count, .is_signed = false } });
16 pub const bit_count = @typeInfo(Fields).Struct.fields.len;
17
18 pub const Self = @This();
19
20 pub fn has(self: Self, comptime name: []const u8) bool {
21 const field_index = meta.fieldIndex(Fields, name).?;
22 return (self.bits & (1 << field_index)) != 0;
23 }
24
25 pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) ?Field(name) {
26 if (!self.has(name))
27 return null;
28 return self.ptrConst(p, name).*;
29 }
30
31 pub fn setFlag(self: *Self, comptime name: []const u8) void {
32 const field_index = meta.fieldIndex(Fields, name).?;
33 self.bits |= 1 << field_index;
34 }
35
36 /// `fields` is a struct with each field set to an optional value.
37 /// Missing fields are assumed to be `null`.
38 /// Only the non-null bits are observed and are used to set the flag bits.
39 pub fn init(fields: anytype) Self {
40 var self: Self = .{ .bits = 0 };
41 inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
42 const opt: ?Field(field.name) = @field(fields, field.name);
43 const field_index = meta.fieldIndex(Fields, field.name).?;
44 self.bits |= @as(Int, @boolToInt(opt != null)) << field_index;
45 }
46 return self;
47 }
48
49 /// `fields` is a struct with each field set to an optional value (same as `init`).
50 /// Missing fields are assumed to be `null`.
51 pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: anytype) void {
52 inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
53 const opt: ?Field(field.name) = @field(fields, field.name);
54 if (opt) |value| {
55 self.set(p, field.name, value);
56 }
57 }
58 }
59
60 pub fn set(
61 self: Self,
62 p: [*]align(@alignOf(Fields)) u8,
63 comptime name: []const u8,
64 value: Field(name),
65 ) void {
66 self.ptr(p, name).* = value;
67 }
68
69 pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime name: []const u8) *Field(name) {
70 if (@sizeOf(Field(name)) == 0)
71 return undefined;
72 const off = self.offset(p, name);
73 return @ptrCast(*Field(name), @alignCast(@alignOf(Field(name)), p + off));
74 }
75
76 pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) *const Field(name) {
77 if (@sizeOf(Field(name)) == 0)
78 return undefined;
79 const off = self.offset(p, name);
80 return @ptrCast(*const Field(name), @alignCast(@alignOf(Field(name)), p + off));
81 }
82
83 pub fn offset(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) usize {
84 var off: usize = 0;
85 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
86 const active = (self.bits & (1 << i)) != 0;
87 if (comptime mem.eql(u8, field.name, name)) {
88 assert(active);
89 return mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
90 } else if (active) {
91 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
92 off += @sizeOf(field.field_type);
93 }
94 }
95 @compileError("no field named " ++ name ++ " in type " ++ @typeName(Fields));
96 }
97
98 pub fn Field(comptime name: []const u8) type {
99 return meta.fieldInfo(Fields, name).field_type;
100 }
101
102 pub fn sizeInBytes(self: Self) usize {
103 var off: usize = 0;
104 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
105 if (@sizeOf(field.field_type) == 0)
106 continue;
107 if ((self.bits & (1 << i)) != 0) {
108 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
109 off += @sizeOf(field.field_type);
110 }
111 }
112 return off;
113 }
114 };
115}
116
117test "TrailerFlags" {
118 const Flags = TrailerFlags(struct {
119 a: i32,
120 b: bool,
121 c: u64,
122 });
123 var flags = Flags.init(.{
124 .b = true,
125 .c = 1234,
126 });
127 const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);
128 defer testing.allocator.free(slice);
129
130 flags.set(slice.ptr, "b", false);
131 flags.set(slice.ptr, "c", 12345678);
132
133 testing.expect(flags.get(slice.ptr, "a") == null);
134 testing.expect(!flags.get(slice.ptr, "b").?);
135 testing.expect(flags.get(slice.ptr, "c").? == 12345678);
136
137 flags.setMany(slice.ptr, .{
138 .b = true,
139 .c = 5678,
140 });
141
142 testing.expect(flags.get(slice.ptr, "a") == null);
143 testing.expect(flags.get(slice.ptr, "b").?);
144 testing.expect(flags.get(slice.ptr, "c").? == 5678);
145}
lib/std/meta/trait.zig+17-4
......@@ -9,7 +9,7 @@ const meta = @import("../meta.zig");
99
1010pub const TraitFn = fn (type) bool;
1111
12pub fn multiTrait(comptime traits: var) TraitFn {
12pub fn multiTrait(comptime traits: anytype) TraitFn {
1313 const Closure = struct {
1414 pub fn trait(comptime T: type) bool {
1515 inline for (traits) |t|
......@@ -342,7 +342,20 @@ test "std.meta.trait.isContainer" {
342342 testing.expect(!isContainer(u8));
343343}
344344
345pub fn hasDecls(comptime T: type, comptime names: var) bool {
345pub fn isTuple(comptime T: type) bool {
346 return is(.Struct)(T) and @typeInfo(T).Struct.is_tuple;
347}
348
349test "std.meta.trait.isTuple" {
350 const t1 = struct {};
351 const t2 = .{ .a = 0 };
352 const t3 = .{ 1, 2, 3 };
353 testing.expect(!isTuple(t1));
354 testing.expect(!isTuple(@TypeOf(t2)));
355 testing.expect(isTuple(@TypeOf(t3)));
356}
357
358pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
346359 inline for (names) |name| {
347360 if (!@hasDecl(T, name))
348361 return false;
......@@ -368,7 +381,7 @@ test "std.meta.trait.hasDecls" {
368381 testing.expect(!hasDecls(TestStruct2, tuple));
369382}
370383
371pub fn hasFields(comptime T: type, comptime names: var) bool {
384pub fn hasFields(comptime T: type, comptime names: anytype) bool {
372385 inline for (names) |name| {
373386 if (!@hasField(T, name))
374387 return false;
......@@ -394,7 +407,7 @@ test "std.meta.trait.hasFields" {
394407 testing.expect(!hasFields(TestStruct2, .{ "a", "b", "useless" }));
395408}
396409
397pub fn hasFunctions(comptime T: type, comptime names: var) bool {
410pub fn hasFunctions(comptime T: type, comptime names: anytype) bool {
398411 inline for (names) |name| {
399412 if (!hasFn(name)(T))
400413 return false;
lib/std/net.zig+4-4
......@@ -427,7 +427,7 @@ pub const Address = extern union {
427427 self: Address,
428428 comptime fmt: []const u8,
429429 options: std.fmt.FormatOptions,
430 out_stream: var,
430 out_stream: anytype,
431431 ) !void {
432432 switch (self.any.family) {
433433 os.AF_INET => {
......@@ -682,7 +682,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
682682
683683 if (info.canonname) |n| {
684684 if (result.canon_name == null) {
685 result.canon_name = try mem.dupe(arena, u8, mem.spanZ(n));
685 result.canon_name = try arena.dupe(u8, mem.spanZ(n));
686686 }
687687 }
688688 i += 1;
......@@ -1404,8 +1404,8 @@ fn resMSendRc(
14041404
14051405fn dnsParse(
14061406 r: []const u8,
1407 ctx: var,
1408 comptime callback: var,
1407 ctx: anytype,
1408 comptime callback: anytype,
14091409) !void {
14101410 // This implementation is ported from musl libc.
14111411 // A more idiomatic "ziggy" implementation would be welcome.
lib/std/os.zig+241-40
......@@ -300,6 +300,10 @@ pub const ReadError = error{
300300 /// This error occurs when no global event loop is configured,
301301 /// and reading from the file descriptor would block.
302302 WouldBlock,
303
304 /// In WASI, this error occurs when the file descriptor does
305 /// not hold the required rights to read from it.
306 AccessDenied,
303307} || UnexpectedError;
304308
305309/// Returns the number of bytes that were read, which can be less than
......@@ -335,6 +339,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
335339 wasi.ENOMEM => return error.SystemResources,
336340 wasi.ECONNRESET => return error.ConnectionResetByPeer,
337341 wasi.ETIMEDOUT => return error.ConnectionTimedOut,
342 wasi.ENOTCAPABLE => return error.AccessDenied,
338343 else => |err| return unexpectedErrno(err),
339344 }
340345 }
......@@ -402,6 +407,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
402407 wasi.EISDIR => return error.IsDir,
403408 wasi.ENOBUFS => return error.SystemResources,
404409 wasi.ENOMEM => return error.SystemResources,
410 wasi.ENOTCAPABLE => return error.AccessDenied,
405411 else => |err| return unexpectedErrno(err),
406412 }
407413 }
......@@ -466,6 +472,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
466472 wasi.ENXIO => return error.Unseekable,
467473 wasi.ESPIPE => return error.Unseekable,
468474 wasi.EOVERFLOW => return error.Unseekable,
475 wasi.ENOTCAPABLE => return error.AccessDenied,
469476 else => |err| return unexpectedErrno(err),
470477 }
471478 }
......@@ -500,8 +507,11 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
500507pub const TruncateError = error{
501508 FileTooBig,
502509 InputOutput,
503 CannotTruncate,
504510 FileBusy,
511
512 /// In WASI, this error occurs when the file descriptor does
513 /// not hold the required rights to call `ftruncate` on it.
514 AccessDenied,
505515} || UnexpectedError;
506516
507517pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
......@@ -522,7 +532,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
522532 switch (rc) {
523533 .SUCCESS => return,
524534 .INVALID_HANDLE => unreachable, // Handle not open for writing
525 .ACCESS_DENIED => return error.CannotTruncate,
535 .ACCESS_DENIED => return error.AccessDenied,
526536 else => return windows.unexpectedStatus(rc),
527537 }
528538 }
......@@ -532,10 +542,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
532542 wasi.EINTR => unreachable,
533543 wasi.EFBIG => return error.FileTooBig,
534544 wasi.EIO => return error.InputOutput,
535 wasi.EPERM => return error.CannotTruncate,
545 wasi.EPERM => return error.AccessDenied,
536546 wasi.ETXTBSY => return error.FileBusy,
537547 wasi.EBADF => unreachable, // Handle not open for writing
538548 wasi.EINVAL => unreachable, // Handle not open for writing
549 wasi.ENOTCAPABLE => return error.AccessDenied,
539550 else => |err| return unexpectedErrno(err),
540551 }
541552 }
......@@ -554,7 +565,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
554565 EINTR => continue,
555566 EFBIG => return error.FileTooBig,
556567 EIO => return error.InputOutput,
557 EPERM => return error.CannotTruncate,
568 EPERM => return error.AccessDenied,
558569 ETXTBSY => return error.FileBusy,
559570 EBADF => unreachable, // Handle not open for writing
560571 EINVAL => unreachable, // Handle not open for writing
......@@ -604,6 +615,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
604615 wasi.ENXIO => return error.Unseekable,
605616 wasi.ESPIPE => return error.Unseekable,
606617 wasi.EOVERFLOW => return error.Unseekable,
618 wasi.ENOTCAPABLE => return error.AccessDenied,
607619 else => |err| return unexpectedErrno(err),
608620 }
609621 }
......@@ -641,6 +653,9 @@ pub const WriteError = error{
641653 FileTooBig,
642654 InputOutput,
643655 NoSpaceLeft,
656
657 /// In WASI, this error may occur when the file descriptor does
658 /// not hold the required rights to write to it.
644659 AccessDenied,
645660 BrokenPipe,
646661 SystemResources,
......@@ -697,6 +712,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
697712 wasi.ENOSPC => return error.NoSpaceLeft,
698713 wasi.EPERM => return error.AccessDenied,
699714 wasi.EPIPE => return error.BrokenPipe,
715 wasi.ENOTCAPABLE => return error.AccessDenied,
700716 else => |err| return unexpectedErrno(err),
701717 }
702718 }
......@@ -774,6 +790,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
774790 wasi.ENOSPC => return error.NoSpaceLeft,
775791 wasi.EPERM => return error.AccessDenied,
776792 wasi.EPIPE => return error.BrokenPipe,
793 wasi.ENOTCAPABLE => return error.AccessDenied,
777794 else => |err| return unexpectedErrno(err),
778795 }
779796 }
......@@ -856,6 +873,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
856873 wasi.ENXIO => return error.Unseekable,
857874 wasi.ESPIPE => return error.Unseekable,
858875 wasi.EOVERFLOW => return error.Unseekable,
876 wasi.ENOTCAPABLE => return error.AccessDenied,
859877 else => |err| return unexpectedErrno(err),
860878 }
861879 }
......@@ -949,6 +967,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
949967 wasi.ENXIO => return error.Unseekable,
950968 wasi.ESPIPE => return error.Unseekable,
951969 wasi.EOVERFLOW => return error.Unseekable,
970 wasi.ENOTCAPABLE => return error.AccessDenied,
952971 else => |err| return unexpectedErrno(err),
953972 }
954973 }
......@@ -984,6 +1003,8 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
9841003}
9851004
9861005pub const OpenError = error{
1006 /// In WASI, this error may occur when the file descriptor does
1007 /// not hold the required rights to open a new resource relative to it.
9871008 AccessDenied,
9881009 SymLinkLoop,
9891010 ProcessFdQuotaExceeded,
......@@ -1113,6 +1134,7 @@ pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags
11131134 wasi.EPERM => return error.AccessDenied,
11141135 wasi.EEXIST => return error.PathAlreadyExists,
11151136 wasi.EBUSY => return error.DeviceBusy,
1137 wasi.ENOTCAPABLE => return error.AccessDenied,
11161138 else => |err| return unexpectedErrno(err),
11171139 }
11181140 }
......@@ -1499,6 +1521,8 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
14991521}
15001522
15011523pub const SymLinkError = error{
1524 /// In WASI, this error may occur when the file descriptor does
1525 /// not hold the required rights to create a new symbolic link relative to it.
15021526 AccessDenied,
15031527 DiskQuota,
15041528 PathAlreadyExists,
......@@ -1520,15 +1544,17 @@ pub const SymLinkError = error{
15201544/// If `sym_link_path` exists, it will not be overwritten.
15211545/// See also `symlinkC` and `symlinkW`.
15221546pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
1547 if (builtin.os.tag == .wasi) {
1548 @compileError("symlink is not supported in WASI; use symlinkat instead");
1549 }
15231550 if (builtin.os.tag == .windows) {
15241551 const target_path_w = try windows.sliceToPrefixedFileW(target_path);
15251552 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
15261553 return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);
1527 } else {
1528 const target_path_c = try toPosixPath(target_path);
1529 const sym_link_path_c = try toPosixPath(sym_link_path);
1530 return symlinkZ(&target_path_c, &sym_link_path_c);
15311554 }
1555 const target_path_c = try toPosixPath(target_path);
1556 const sym_link_path_c = try toPosixPath(sym_link_path);
1557 return symlinkZ(&target_path_c, &sym_link_path_c);
15321558}
15331559
15341560pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");
......@@ -1561,15 +1587,66 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
15611587 }
15621588}
15631589
1590/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
1591/// `target_path` **relative** to `newdirfd` directory handle.
1592/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
1593/// one; the latter case is known as a dangling link.
1594/// If `sym_link_path` exists, it will not be overwritten.
1595/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
15641596pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
1597 if (builtin.os.tag == .wasi) {
1598 return symlinkatWasi(target_path, newdirfd, sym_link_path);
1599 }
1600 if (builtin.os.tag == .windows) {
1601 const target_path_w = try windows.sliceToPrefixedFileW(target_path);
1602 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
1603 return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path_w.span().ptr);
1604 }
15651605 const target_path_c = try toPosixPath(target_path);
15661606 const sym_link_path_c = try toPosixPath(sym_link_path);
1567 return symlinkatZ(target_path_c, newdirfd, sym_link_path_c);
1607 return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
15681608}
15691609
15701610pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
15711611
1612/// WASI-only. The same as `symlinkat` but targeting WASI.
1613/// See also `symlinkat`.
1614pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
1615 switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
1616 wasi.ESUCCESS => {},
1617 wasi.EFAULT => unreachable,
1618 wasi.EINVAL => unreachable,
1619 wasi.EACCES => return error.AccessDenied,
1620 wasi.EPERM => return error.AccessDenied,
1621 wasi.EDQUOT => return error.DiskQuota,
1622 wasi.EEXIST => return error.PathAlreadyExists,
1623 wasi.EIO => return error.FileSystem,
1624 wasi.ELOOP => return error.SymLinkLoop,
1625 wasi.ENAMETOOLONG => return error.NameTooLong,
1626 wasi.ENOENT => return error.FileNotFound,
1627 wasi.ENOTDIR => return error.NotDir,
1628 wasi.ENOMEM => return error.SystemResources,
1629 wasi.ENOSPC => return error.NoSpaceLeft,
1630 wasi.EROFS => return error.ReadOnlyFileSystem,
1631 wasi.ENOTCAPABLE => return error.AccessDenied,
1632 else => |err| return unexpectedErrno(err),
1633 }
1634}
1635
1636/// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded.
1637/// See also `symlinkat`.
1638pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymLinkError!void {
1639 @compileError("TODO implement on Windows");
1640}
1641
1642/// The same as `symlinkat` except the parameters are null-terminated pointers.
1643/// See also `symlinkat`.
15721644pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
1645 if (builtin.os.tag == .windows) {
1646 const target_path_w = try windows.cStrToPrefixedFileW(target_path);
1647 const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
1648 return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path.span().ptr);
1649 }
15731650 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
15741651 0 => return,
15751652 EFAULT => unreachable,
......@@ -1592,6 +1669,9 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:
15921669
15931670pub const UnlinkError = error{
15941671 FileNotFound,
1672
1673 /// In WASI, this error may occur when the file descriptor does
1674 /// not hold the required rights to unlink a resource by path relative to it.
15951675 AccessDenied,
15961676 FileBusy,
15971677 FileSystem,
......@@ -1613,7 +1693,9 @@ pub const UnlinkError = error{
16131693/// Delete a name and possibly the file it refers to.
16141694/// See also `unlinkC`.
16151695pub fn unlink(file_path: []const u8) UnlinkError!void {
1616 if (builtin.os.tag == .windows) {
1696 if (builtin.os.tag == .wasi) {
1697 @compileError("unlink is not supported in WASI; use unlinkat instead");
1698 } else if (builtin.os.tag == .windows) {
16171699 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
16181700 return windows.DeleteFileW(file_path_w.span().ptr);
16191701 } else {
......@@ -1670,6 +1752,8 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
16701752
16711753pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");
16721754
1755/// WASI-only. Same as `unlinkat` but targeting WASI.
1756/// See also `unlinkat`.
16731757pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
16741758 const remove_dir = (flags & AT_REMOVEDIR) != 0;
16751759 const res = if (remove_dir)
......@@ -1691,6 +1775,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
16911775 wasi.ENOMEM => return error.SystemResources,
16921776 wasi.EROFS => return error.ReadOnlyFileSystem,
16931777 wasi.ENOTEMPTY => return error.DirNotEmpty,
1778 wasi.ENOTCAPABLE => return error.AccessDenied,
16941779
16951780 wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component
16961781 wasi.EBADF => unreachable, // always a race condition
......@@ -1793,6 +1878,8 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr
17931878}
17941879
17951880const RenameError = error{
1881 /// In WASI, this error may occur when the file descriptor does
1882 /// not hold the required rights to rename a resource by path relative to it.
17961883 AccessDenied,
17971884 FileBusy,
17981885 DiskQuota,
......@@ -1816,7 +1903,9 @@ const RenameError = error{
18161903
18171904/// Change the name or location of a file.
18181905pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
1819 if (builtin.os.tag == .windows) {
1906 if (builtin.os.tag == .wasi) {
1907 @compileError("rename is not supported in WASI; use renameat instead");
1908 } else if (builtin.os.tag == .windows) {
18201909 const old_path_w = try windows.sliceToPrefixedFileW(old_path);
18211910 const new_path_w = try windows.sliceToPrefixedFileW(new_path);
18221911 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
......@@ -1887,7 +1976,8 @@ pub fn renameat(
18871976 }
18881977}
18891978
1890/// Same as `renameat` expect only WASI.
1979/// WASI-only. Same as `renameat` expect targeting WASI.
1980/// See also `renameat`.
18911981pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {
18921982 switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {
18931983 wasi.ESUCCESS => return,
......@@ -1909,6 +1999,7 @@ pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, ne
19091999 wasi.ENOTEMPTY => return error.PathAlreadyExists,
19102000 wasi.EROFS => return error.ReadOnlyFileSystem,
19112001 wasi.EXDEV => return error.RenameAcrossMountPoints,
2002 wasi.ENOTCAPABLE => return error.AccessDenied,
19122003 else => |err| return unexpectedErrno(err),
19132004 }
19142005}
......@@ -2007,23 +2098,6 @@ pub fn renameatW(
20072098 }
20082099}
20092100
2010pub const MakeDirError = error{
2011 AccessDenied,
2012 DiskQuota,
2013 PathAlreadyExists,
2014 SymLinkLoop,
2015 LinkQuotaExceeded,
2016 NameTooLong,
2017 FileNotFound,
2018 SystemResources,
2019 NoSpaceLeft,
2020 NotDir,
2021 ReadOnlyFileSystem,
2022 InvalidUtf8,
2023 BadPathName,
2024 NoDevice,
2025} || UnexpectedError;
2026
20272101pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
20282102 if (builtin.os.tag == .windows) {
20292103 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
......@@ -2055,6 +2129,7 @@ pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirErr
20552129 wasi.ENOSPC => return error.NoSpaceLeft,
20562130 wasi.ENOTDIR => return error.NotDir,
20572131 wasi.EROFS => return error.ReadOnlyFileSystem,
2132 wasi.ENOTCAPABLE => return error.AccessDenied,
20582133 else => |err| return unexpectedErrno(err),
20592134 }
20602135}
......@@ -2089,10 +2164,31 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirErro
20892164 windows.CloseHandle(sub_dir_handle);
20902165}
20912166
2167pub const MakeDirError = error{
2168 /// In WASI, this error may occur when the file descriptor does
2169 /// not hold the required rights to create a new directory relative to it.
2170 AccessDenied,
2171 DiskQuota,
2172 PathAlreadyExists,
2173 SymLinkLoop,
2174 LinkQuotaExceeded,
2175 NameTooLong,
2176 FileNotFound,
2177 SystemResources,
2178 NoSpaceLeft,
2179 NotDir,
2180 ReadOnlyFileSystem,
2181 InvalidUtf8,
2182 BadPathName,
2183 NoDevice,
2184} || UnexpectedError;
2185
20922186/// Create a directory.
20932187/// `mode` is ignored on Windows.
20942188pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2095 if (builtin.os.tag == .windows) {
2189 if (builtin.os.tag == .wasi) {
2190 @compileError("mkdir is not supported in WASI; use mkdirat instead");
2191 } else if (builtin.os.tag == .windows) {
20962192 const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
20972193 windows.CloseHandle(sub_dir_handle);
20982194 return;
......@@ -2145,7 +2241,9 @@ pub const DeleteDirError = error{
21452241
21462242/// Deletes an empty directory.
21472243pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
2148 if (builtin.os.tag == .windows) {
2244 if (builtin.os.tag == .wasi) {
2245 @compileError("rmdir is not supported in WASI; use unlinkat instead");
2246 } else if (builtin.os.tag == .windows) {
21492247 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
21502248 return windows.RemoveDirectoryW(dir_path_w.span().ptr);
21512249 } else {
......@@ -2194,7 +2292,9 @@ pub const ChangeCurDirError = error{
21942292/// Changes the current working directory of the calling process.
21952293/// `dir_path` is recommended to be a UTF-8 encoded string.
21962294pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
2197 if (builtin.os.tag == .windows) {
2295 if (builtin.os.tag == .wasi) {
2296 @compileError("chdir is not supported in WASI");
2297 } else if (builtin.os.tag == .windows) {
21982298 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
21992299 @compileError("TODO implement chdir for Windows");
22002300 } else {
......@@ -2246,6 +2346,8 @@ pub fn fchdir(dirfd: fd_t) FchdirError!void {
22462346}
22472347
22482348pub const ReadLinkError = error{
2349 /// In WASI, this error may occur when the file descriptor does
2350 /// not hold the required rights to read value of a symbolic link relative to it.
22492351 AccessDenied,
22502352 FileSystem,
22512353 SymLinkLoop,
......@@ -2258,9 +2360,11 @@ pub const ReadLinkError = error{
22582360/// Read value of a symbolic link.
22592361/// The return value is a slice of `out_buffer` from index 0.
22602362pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2261 if (builtin.os.tag == .windows) {
2363 if (builtin.os.tag == .wasi) {
2364 @compileError("readlink is not supported in WASI; use readlinkat instead");
2365 } else if (builtin.os.tag == .windows) {
22622366 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2263 @compileError("TODO implement readlink for Windows");
2367 return readlinkW(file_path_w.span().ptr, out_buffer);
22642368 } else {
22652369 const file_path_c = try toPosixPath(file_path);
22662370 return readlinkZ(&file_path_c, out_buffer);
......@@ -2269,11 +2373,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
22692373
22702374pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
22712375
2376/// Windows-only. Same as `readlink` expecte `file_path` is null-terminated, WTF16 encoded.
2377/// Seel also `readlinkZ`.
2378pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2379 @compileError("TODO implement readlink for Windows");
2380}
2381
22722382/// Same as `readlink` except `file_path` is null-terminated.
22732383pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
22742384 if (builtin.os.tag == .windows) {
22752385 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2276 @compileError("TODO implement readlink for Windows");
2386 return readlinkW(file_path_w.span().ptr, out_buffer);
22772387 }
22782388 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
22792389 switch (errno(rc)) {
......@@ -2291,12 +2401,55 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
22912401 }
22922402}
22932403
2404/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
2405/// The return value is a slice of `out_buffer` from index 0.
2406/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
2407pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2408 if (builtin.os.tag == .wasi) {
2409 return readlinkatWasi(dirfd, file_path, out_buffer);
2410 }
2411 if (builtin.os.tag == .windows) {
2412 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2413 return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
2414 }
2415 const file_path_c = try toPosixPath(file_path);
2416 return readlinkatZ(dirfd, &file_path_c, out_buffer);
2417}
2418
22942419pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
22952420
2421/// WASI-only. Same as `readlinkat` but targets WASI.
2422/// See also `readlinkat`.
2423pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2424 var bufused: usize = undefined;
2425 switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
2426 wasi.ESUCCESS => return out_buffer[0..bufused],
2427 wasi.EACCES => return error.AccessDenied,
2428 wasi.EFAULT => unreachable,
2429 wasi.EINVAL => unreachable,
2430 wasi.EIO => return error.FileSystem,
2431 wasi.ELOOP => return error.SymLinkLoop,
2432 wasi.ENAMETOOLONG => return error.NameTooLong,
2433 wasi.ENOENT => return error.FileNotFound,
2434 wasi.ENOMEM => return error.SystemResources,
2435 wasi.ENOTDIR => return error.NotDir,
2436 wasi.ENOTCAPABLE => return error.AccessDenied,
2437 else => |err| return unexpectedErrno(err),
2438 }
2439}
2440
2441/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
2442/// See also `readlinkat`.
2443pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2444 @compileError("TODO implement on Windows");
2445}
2446
2447/// Same as `readlinkat` except `file_path` is null-terminated.
2448/// See also `readlinkat`.
22962449pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
22972450 if (builtin.os.tag == .windows) {
22982451 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2299 @compileError("TODO implement readlink for Windows");
2452 return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
23002453 }
23012454 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
23022455 switch (errno(rc)) {
......@@ -2958,9 +3111,13 @@ pub fn waitpid(pid: i32, flags: u32) u32 {
29583111
29593112pub const FStatError = error{
29603113 SystemResources,
3114
3115 /// In WASI, this error may occur when the file descriptor does
3116 /// not hold the required rights to get its filestat information.
29613117 AccessDenied,
29623118} || UnexpectedError;
29633119
3120/// Return information about a file descriptor.
29643121pub fn fstat(fd: fd_t) FStatError!Stat {
29653122 if (builtin.os.tag == .wasi) {
29663123 var stat: wasi.filestat_t = undefined;
......@@ -2970,9 +3127,13 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
29703127 wasi.EBADF => unreachable, // Always a race condition.
29713128 wasi.ENOMEM => return error.SystemResources,
29723129 wasi.EACCES => return error.AccessDenied,
3130 wasi.ENOTCAPABLE => return error.AccessDenied,
29733131 else => |err| return unexpectedErrno(err),
29743132 }
29753133 }
3134 if (builtin.os.tag == .windows) {
3135 @compileError("fstat is not yet implemented on Windows");
3136 }
29763137
29773138 var stat: Stat = undefined;
29783139 switch (errno(system.fstat(fd, &stat))) {
......@@ -2987,13 +3148,43 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
29873148
29883149pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound };
29893150
3151/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
3152/// which is relative to `dirfd` handle.
3153/// See also `fstatatZ` and `fstatatWasi`.
29903154pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
2991 const pathname_c = try toPosixPath(pathname);
2992 return fstatatZ(dirfd, &pathname_c, flags);
3155 if (builtin.os.tag == .wasi) {
3156 return fstatatWasi(dirfd, pathname, flags);
3157 } else if (builtin.os.tag == .windows) {
3158 @compileError("fstatat is not yet implemented on Windows");
3159 } else {
3160 const pathname_c = try toPosixPath(pathname);
3161 return fstatatZ(dirfd, &pathname_c, flags);
3162 }
29933163}
29943164
29953165pub const fstatatC = @compileError("deprecated: renamed to fstatatZ");
29963166
3167/// WASI-only. Same as `fstatat` but targeting WASI.
3168/// See also `fstatat`.
3169pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
3170 var stat: wasi.filestat_t = undefined;
3171 switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
3172 wasi.ESUCCESS => return Stat.fromFilestat(stat),
3173 wasi.EINVAL => unreachable,
3174 wasi.EBADF => unreachable, // Always a race condition.
3175 wasi.ENOMEM => return error.SystemResources,
3176 wasi.EACCES => return error.AccessDenied,
3177 wasi.EFAULT => unreachable,
3178 wasi.ENAMETOOLONG => return error.NameTooLong,
3179 wasi.ENOENT => return error.FileNotFound,
3180 wasi.ENOTDIR => return error.FileNotFound,
3181 wasi.ENOTCAPABLE => return error.AccessDenied,
3182 else => |err| return unexpectedErrno(err),
3183 }
3184}
3185
3186/// Same as `fstatat` but `pathname` is null-terminated.
3187/// See also `fstatat`.
29973188pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
29983189 var stat: Stat = undefined;
29993190 switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {
......@@ -3493,7 +3684,13 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
34933684 }
34943685}
34953686
3496pub const SeekError = error{Unseekable} || UnexpectedError;
3687pub const SeekError = error{
3688 Unseekable,
3689
3690 /// In WASI, this error may occur when the file descriptor does
3691 /// not hold the required rights to seek on it.
3692 AccessDenied,
3693} || UnexpectedError;
34973694
34983695/// Repositions read/write file offset relative to the beginning.
34993696pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
......@@ -3521,6 +3718,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
35213718 wasi.EOVERFLOW => return error.Unseekable,
35223719 wasi.ESPIPE => return error.Unseekable,
35233720 wasi.ENXIO => return error.Unseekable,
3721 wasi.ENOTCAPABLE => return error.AccessDenied,
35243722 else => |err| return unexpectedErrno(err),
35253723 }
35263724 }
......@@ -3562,6 +3760,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
35623760 wasi.EOVERFLOW => return error.Unseekable,
35633761 wasi.ESPIPE => return error.Unseekable,
35643762 wasi.ENXIO => return error.Unseekable,
3763 wasi.ENOTCAPABLE => return error.AccessDenied,
35653764 else => |err| return unexpectedErrno(err),
35663765 }
35673766 }
......@@ -3602,6 +3801,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
36023801 wasi.EOVERFLOW => return error.Unseekable,
36033802 wasi.ESPIPE => return error.Unseekable,
36043803 wasi.ENXIO => return error.Unseekable,
3804 wasi.ENOTCAPABLE => return error.AccessDenied,
36053805 else => |err| return unexpectedErrno(err),
36063806 }
36073807 }
......@@ -3642,6 +3842,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
36423842 wasi.EOVERFLOW => return error.Unseekable,
36433843 wasi.ESPIPE => return error.Unseekable,
36443844 wasi.ENXIO => return error.Unseekable,
3845 wasi.ENOTCAPABLE => return error.AccessDenied,
36453846 else => |err| return unexpectedErrno(err),
36463847 }
36473848 }
......@@ -3867,7 +4068,7 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
38674068}
38684069
38694070pub fn dl_iterate_phdr(
3870 context: var,
4071 context: anytype,
38714072 comptime Error: type,
38724073 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
38734074) Error!void {
lib/std/os/test.zig+27-143
......@@ -18,135 +18,49 @@ const AtomicOrder = builtin.AtomicOrder;
1818const tmpDir = std.testing.tmpDir;
1919const Dir = std.fs.Dir;
2020
21test "makePath, put some files in it, deleteTree" {
22 var tmp = tmpDir(.{});
23 defer tmp.cleanup();
24
25 try tmp.dir.makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
26 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
27 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
28 try tmp.dir.deleteTree("os_test_tmp");
29 if (tmp.dir.openDir("os_test_tmp", .{})) |dir| {
30 @panic("expected error");
31 } else |err| {
32 expect(err == error.FileNotFound);
33 }
34}
35
36test "access file" {
37 if (builtin.os.tag == .wasi) return error.SkipZigTest;
38
39 var tmp = tmpDir(.{});
40 defer tmp.cleanup();
21test "fstatat" {
22 // enable when `fstat` and `fstatat` are implemented on Windows
23 if (builtin.os.tag == .windows) return error.SkipZigTest;
4124
42 try tmp.dir.makePath("os_test_tmp");
43 if (tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
44 @panic("expected error");
45 } else |err| {
46 expect(err == error.FileNotFound);
47 }
48
49 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
50 try tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});
51 try tmp.dir.deleteTree("os_test_tmp");
52}
53
54fn testThreadIdFn(thread_id: *Thread.Id) void {
55 thread_id.* = Thread.getCurrentId();
56}
57
58test "sendfile" {
5925 var tmp = tmpDir(.{});
6026 defer tmp.cleanup();
6127
62 try tmp.dir.makePath("os_test_tmp");
63 defer tmp.dir.deleteTree("os_test_tmp") catch {};
64
65 var dir = try tmp.dir.openDir("os_test_tmp", .{});
66 defer dir.close();
67
68 const line1 = "line1\n";
69 const line2 = "second line\n";
70 var vecs = [_]os.iovec_const{
71 .{
72 .iov_base = line1,
73 .iov_len = line1.len,
74 },
75 .{
76 .iov_base = line2,
77 .iov_len = line2.len,
78 },
79 };
28 // create dummy file
29 const contents = "nonsense";
30 try tmp.dir.writeFile("file.txt", contents);
8031
81 var src_file = try dir.createFile("sendfile1.txt", .{ .read = true });
82 defer src_file.close();
83
84 try src_file.writevAll(&vecs);
85
86 var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true });
87 defer dest_file.close();
88
89 const header1 = "header1\n";
90 const header2 = "second header\n";
91 const trailer1 = "trailer1\n";
92 const trailer2 = "second trailer\n";
93 var hdtr = [_]os.iovec_const{
94 .{
95 .iov_base = header1,
96 .iov_len = header1.len,
97 },
98 .{
99 .iov_base = header2,
100 .iov_len = header2.len,
101 },
102 .{
103 .iov_base = trailer1,
104 .iov_len = trailer1.len,
105 },
106 .{
107 .iov_base = trailer2,
108 .iov_len = trailer2.len,
109 },
110 };
32 // fetch file's info on the opened fd directly
33 const file = try tmp.dir.openFile("file.txt", .{});
34 const stat = try os.fstat(file.handle);
35 defer file.close();
11136
112 var written_buf: [100]u8 = undefined;
113 try dest_file.writeFileAll(src_file, .{
114 .in_offset = 1,
115 .in_len = 10,
116 .headers_and_trailers = &hdtr,
117 .header_count = 2,
118 });
119 const amt = try dest_file.preadAll(&written_buf, 0);
120 expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
37 // now repeat but using `fstatat` instead
38 const flags = if (builtin.os.tag == .wasi) 0x0 else os.AT_SYMLINK_NOFOLLOW;
39 const statat = try os.fstatat(tmp.dir.fd, "file.txt", flags);
40 expectEqual(stat, statat);
12141}
12242
123test "fs.copyFile" {
124 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
125 const src_file = "tmp_test_copy_file.txt";
126 const dest_file = "tmp_test_copy_file2.txt";
127 const dest_file2 = "tmp_test_copy_file3.txt";
43test "readlinkat" {
44 // enable when `readlinkat` and `symlinkat` are implemented on Windows
45 if (builtin.os.tag == .windows) return error.SkipZigTest;
12846
12947 var tmp = tmpDir(.{});
13048 defer tmp.cleanup();
13149
132 try tmp.dir.writeFile(src_file, data);
133 defer tmp.dir.deleteFile(src_file) catch {};
50 // create file
51 try tmp.dir.writeFile("file.txt", "nonsense");
13452
135 try tmp.dir.copyFile(src_file, tmp.dir, dest_file, .{});
136 defer tmp.dir.deleteFile(dest_file) catch {};
53 // create a symbolic link
54 try os.symlinkat("file.txt", tmp.dir.fd, "link");
13755
138 try tmp.dir.copyFile(src_file, tmp.dir, dest_file2, .{ .override_mode = File.default_mode });
139 defer tmp.dir.deleteFile(dest_file2) catch {};
140
141 try expectFileContents(tmp.dir, dest_file, data);
142 try expectFileContents(tmp.dir, dest_file2, data);
56 // read the link
57 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
58 const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
59 expect(mem.eql(u8, "file.txt", read_link));
14360}
14461
145fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
146 const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
147 defer testing.allocator.free(contents);
148
149 testing.expectEqualSlices(u8, data, contents);
62fn testThreadIdFn(thread_id: *Thread.Id) void {
63 thread_id.* = Thread.getCurrentId();
15064}
15165
15266test "std.Thread.getCurrentId" {
......@@ -201,29 +115,6 @@ test "cpu count" {
201115 expect(cpu_count >= 1);
202116}
203117
204test "AtomicFile" {
205 const test_out_file = "tmp_atomic_file_test_dest.txt";
206 const test_content =
207 \\ hello!
208 \\ this is a test file
209 ;
210
211 var tmp = tmpDir(.{});
212 defer tmp.cleanup();
213
214 {
215 var af = try tmp.dir.atomicFile(test_out_file, .{});
216 defer af.deinit();
217 try af.file.writeAll(test_content);
218 try af.finish();
219 }
220 const content = try tmp.dir.readFileAlloc(testing.allocator, test_out_file, 9999);
221 defer testing.allocator.free(content);
222 expect(mem.eql(u8, content, test_content));
223
224 try tmp.dir.deleteFile(test_out_file);
225}
226
227118test "thread local storage" {
228119 if (builtin.single_threaded) return error.SkipZigTest;
229120 const thread1 = try Thread.spawn({}, testTls);
......@@ -258,13 +149,6 @@ test "getcwd" {
258149 _ = os.getcwd(&buf) catch undefined;
259150}
260151
261test "realpath" {
262 if (builtin.os.tag == .wasi) return error.SkipZigTest;
263
264 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
265 testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));
266}
267
268152test "sigaltstack" {
269153 if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest;
270154
lib/std/os/uefi.zig+1-1
......@@ -28,7 +28,7 @@ pub const Guid = extern struct {
2828 self: @This(),
2929 comptime f: []const u8,
3030 options: std.fmt.FormatOptions,
31 out_stream: var,
31 out_stream: anytype,
3232 ) Errors!void {
3333 if (f.len == 0) {
3434 return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
lib/std/os/windows.zig+10-1
......@@ -901,7 +901,13 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
901901 var wsadata: ws2_32.WSADATA = undefined;
902902 return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
903903 0 => wsadata,
904 else => |err| unexpectedWSAError(@intToEnum(ws2_32.WinsockError, @intCast(u16, err))),
904 else => |err_int| switch (@intToEnum(ws2_32.WinsockError, @intCast(u16, err_int))) {
905 .WSASYSNOTREADY => return error.SystemNotAvailable,
906 .WSAVERNOTSUPPORTED => return error.VersionNotSupported,
907 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
908 .WSAEPROCLIM => return error.SystemResources,
909 else => |err| return unexpectedWSAError(err),
910 },
905911 };
906912}
907913
......@@ -909,6 +915,9 @@ pub fn WSACleanup() !void {
909915 return switch (ws2_32.WSACleanup()) {
910916 0 => {},
911917 ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
918 .WSANOTINITIALISED => return error.NotInitialized,
919 .WSAENETDOWN => return error.NetworkNotAvailable,
920 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
912921 else => |err| return unexpectedWSAError(err),
913922 },
914923 else => unreachable,
lib/std/os/windows/bits.zig+1
......@@ -593,6 +593,7 @@ pub const FILE_CURRENT = 1;
593593pub const FILE_END = 2;
594594
595595pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
596pub const HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
596597pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
597598pub const HEAP_NO_SERIALIZE = 0x00000001;
598599
lib/std/os/windows/ws2_32.zig+9-9
......@@ -163,16 +163,16 @@ pub const IPPROTO_UDP = 17;
163163pub const IPPROTO_ICMPV6 = 58;
164164pub const IPPROTO_RM = 113;
165165
166pub const AI_PASSIVE = 0x00001;
167pub const AI_CANONNAME = 0x00002;
168pub const AI_NUMERICHOST = 0x00004;
169pub const AI_NUMERICSERV = 0x00008;
170pub const AI_ADDRCONFIG = 0x00400;
171pub const AI_V4MAPPED = 0x00800;
172pub const AI_NON_AUTHORITATIVE = 0x04000;
173pub const AI_SECURE = 0x08000;
166pub const AI_PASSIVE = 0x00001;
167pub const AI_CANONNAME = 0x00002;
168pub const AI_NUMERICHOST = 0x00004;
169pub const AI_NUMERICSERV = 0x00008;
170pub const AI_ADDRCONFIG = 0x00400;
171pub const AI_V4MAPPED = 0x00800;
172pub const AI_NON_AUTHORITATIVE = 0x04000;
173pub const AI_SECURE = 0x08000;
174174pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
175pub const AI_DISABLE_IDN_ENCODING = 0x80000;
175pub const AI_DISABLE_IDN_ENCODING = 0x80000;
176176
177177pub const FIONBIO = -2147195266;
178178
lib/std/pdb.zig+1-1
......@@ -469,7 +469,7 @@ pub const Pdb = struct {
469469
470470 msf: Msf,
471471
472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []const u8) !void {
473473 self.in_file = try fs.cwd().openFile(file_name, .{ .intended_io_mode = .blocking });
474474 self.allocator = coff_ptr.allocator;
475475 self.coff = coff_ptr;
lib/std/priority_queue.zig+1-1
......@@ -333,7 +333,7 @@ test "std.PriorityQueue: addSlice" {
333333
334334test "std.PriorityQueue: fromOwnedSlice" {
335335 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
336 const heap_items = try std.mem.dupe(testing.allocator, u32, items[0..]);
336 const heap_items = try testing.allocator.dupe(u32, items[0..]);
337337 var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, heap_items[0..]);
338338 defer queue.deinit();
339339
lib/std/process.zig+12-39
......@@ -30,7 +30,7 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
3030 var current_buf: []u8 = &stack_buf;
3131 while (true) {
3232 if (os.getcwd(current_buf)) |slice| {
33 return mem.dupe(allocator, u8, slice);
33 return allocator.dupe(u8, slice);
3434 } else |err| switch (err) {
3535 error.NameTooLong => {
3636 // The path is too long to fit in stack_buf. Allocate geometrically
......@@ -169,7 +169,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
169169 };
170170 } else {
171171 const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound;
172 return mem.dupe(allocator, u8, result);
172 return allocator.dupe(u8, result);
173173 }
174174}
175175
......@@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct {
281281pub const ArgIteratorWindows = struct {
282282 index: usize,
283283 cmd_line: [*]const u8,
284 in_quote: bool,
285 quote_count: usize,
286 seen_quote_count: usize,
287284
288285 pub const NextError = error{OutOfMemory};
289286
......@@ -295,9 +292,6 @@ pub const ArgIteratorWindows = struct {
295292 return ArgIteratorWindows{
296293 .index = 0,
297294 .cmd_line = cmd_line,
298 .in_quote = false,
299 .quote_count = countQuotes(cmd_line),
300 .seen_quote_count = 0,
301295 };
302296 }
303297
......@@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct {
328322 }
329323
330324 var backslash_count: usize = 0;
325 var in_quote = false;
331326 while (true) : (self.index += 1) {
332327 const byte = self.cmd_line[self.index];
333328 switch (byte) {
......@@ -335,14 +330,14 @@ pub const ArgIteratorWindows = struct {
335330 '"' => {
336331 const quote_is_real = backslash_count % 2 == 0;
337332 if (quote_is_real) {
338 self.seen_quote_count += 1;
333 in_quote = !in_quote;
339334 }
340335 },
341336 '\\' => {
342337 backslash_count += 1;
343338 },
344339 ' ', '\t' => {
345 if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
340 if (!in_quote) {
346341 return true;
347342 }
348343 backslash_count = 0;
......@@ -360,6 +355,7 @@ pub const ArgIteratorWindows = struct {
360355 defer buf.deinit();
361356
362357 var backslash_count: usize = 0;
358 var in_quote = false;
363359 while (true) : (self.index += 1) {
364360 const byte = self.cmd_line[self.index];
365361 switch (byte) {
......@@ -370,10 +366,7 @@ pub const ArgIteratorWindows = struct {
370366 backslash_count = 0;
371367
372368 if (quote_is_real) {
373 self.seen_quote_count += 1;
374 if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) {
375 try buf.append('"');
376 }
369 in_quote = !in_quote;
377370 } else {
378371 try buf.append('"');
379372 }
......@@ -384,7 +377,7 @@ pub const ArgIteratorWindows = struct {
384377 ' ', '\t' => {
385378 try self.emitBackslashes(&buf, backslash_count);
386379 backslash_count = 0;
387 if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
380 if (in_quote) {
388381 try buf.append(byte);
389382 } else {
390383 return buf.toOwnedSlice();
......@@ -405,26 +398,6 @@ pub const ArgIteratorWindows = struct {
405398 try buf.append('\\');
406399 }
407400 }
408
409 fn countQuotes(cmd_line: [*]const u8) usize {
410 var result: usize = 0;
411 var backslash_count: usize = 0;
412 var index: usize = 0;
413 while (true) : (index += 1) {
414 const byte = cmd_line[index];
415 switch (byte) {
416 0 => return result,
417 '\\' => backslash_count += 1,
418 '"' => {
419 result += 1 - (backslash_count % 2);
420 backslash_count = 0;
421 },
422 else => {
423 backslash_count = 0;
424 },
425 }
426 }
427 }
428401};
429402
430403pub const ArgIterator = struct {
......@@ -463,7 +436,7 @@ pub const ArgIterator = struct {
463436 if (builtin.os.tag == .windows) {
464437 return self.inner.next(allocator);
465438 } else {
466 return mem.dupe(allocator, u8, self.inner.next() orelse return null);
439 return allocator.dupe(u8, self.inner.next() orelse return null);
467440 }
468441 }
469442
......@@ -578,7 +551,7 @@ test "windows arg parsing" {
578551 testWindowsCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" });
579552 testWindowsCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" });
580553 testWindowsCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" });
581 testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "\"d", "f" });
554 testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "d f" });
582555
583556 testWindowsCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{
584557 ".\\..\\zig-cache\\build",
......@@ -745,7 +718,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
745718 fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void {
746719 const name = info.dlpi_name orelse return;
747720 if (name[0] == '/') {
748 const item = try mem.dupeZ(list.allocator, u8, mem.spanZ(name));
721 const item = try list.allocator.dupeZ(u8, mem.spanZ(name));
749722 errdefer list.allocator.free(item);
750723 try list.append(item);
751724 }
......@@ -766,7 +739,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
766739 var i: u32 = 0;
767740 while (i < img_count) : (i += 1) {
768741 const name = std.c._dyld_get_image_name(i);
769 const item = try mem.dupeZ(allocator, u8, mem.spanZ(name));
742 const item = try allocator.dupeZ(u8, mem.spanZ(name));
770743 errdefer allocator.free(item);
771744 try paths.append(item);
772745 }
lib/std/progress.zig+2-2
......@@ -224,7 +224,7 @@ pub const Progress = struct {
224224 self.prev_refresh_timestamp = self.timer.read();
225225 }
226226
227 pub fn log(self: *Progress, comptime format: []const u8, args: var) void {
227 pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
228228 const file = self.terminal orelse return;
229229 self.refresh();
230230 file.outStream().print(format, args) catch {
......@@ -234,7 +234,7 @@ pub const Progress = struct {
234234 self.columns_written = 0;
235235 }
236236
237 fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: var) void {
237 fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
238238 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
239239 const amt = written.len;
240240 end.* += amt;
lib/std/segmented_list.zig+2-2
......@@ -122,7 +122,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
122122 self.* = undefined;
123123 }
124124
125 pub fn at(self: var, i: usize) AtType(@TypeOf(self)) {
125 pub fn at(self: anytype, i: usize) AtType(@TypeOf(self)) {
126126 assert(i < self.len);
127127 return self.uncheckedAt(i);
128128 }
......@@ -241,7 +241,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
241241 }
242242 }
243243
244 pub fn uncheckedAt(self: var, index: usize) AtType(@TypeOf(self)) {
244 pub fn uncheckedAt(self: anytype, index: usize) AtType(@TypeOf(self)) {
245245 if (index < prealloc_item_count) {
246246 return &self.prealloc_segment[index];
247247 }
lib/std/sort.zig+19-19
......@@ -9,7 +9,7 @@ pub fn binarySearch(
99 comptime T: type,
1010 key: T,
1111 items: []const T,
12 context: var,
12 context: anytype,
1313 comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order,
1414) ?usize {
1515 var left: usize = 0;
......@@ -76,7 +76,7 @@ test "binarySearch" {
7676pub fn insertionSort(
7777 comptime T: type,
7878 items: []T,
79 context: var,
79 context: anytype,
8080 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
8181) void {
8282 var i: usize = 1;
......@@ -182,7 +182,7 @@ const Pull = struct {
182182pub fn sort(
183183 comptime T: type,
184184 items: []T,
185 context: var,
185 context: anytype,
186186 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
187187) void {
188188 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
......@@ -813,7 +813,7 @@ fn mergeInPlace(
813813 items: []T,
814814 A_arg: Range,
815815 B_arg: Range,
816 context: var,
816 context: anytype,
817817 comptime lessThan: fn (@TypeOf(context), T, T) bool,
818818) void {
819819 if (A_arg.length() == 0 or B_arg.length() == 0) return;
......@@ -862,7 +862,7 @@ fn mergeInternal(
862862 items: []T,
863863 A: Range,
864864 B: Range,
865 context: var,
865 context: anytype,
866866 comptime lessThan: fn (@TypeOf(context), T, T) bool,
867867 buffer: Range,
868868) void {
......@@ -906,7 +906,7 @@ fn findFirstForward(
906906 items: []T,
907907 value: T,
908908 range: Range,
909 context: var,
909 context: anytype,
910910 comptime lessThan: fn (@TypeOf(context), T, T) bool,
911911 unique: usize,
912912) usize {
......@@ -928,7 +928,7 @@ fn findFirstBackward(
928928 items: []T,
929929 value: T,
930930 range: Range,
931 context: var,
931 context: anytype,
932932 comptime lessThan: fn (@TypeOf(context), T, T) bool,
933933 unique: usize,
934934) usize {
......@@ -950,7 +950,7 @@ fn findLastForward(
950950 items: []T,
951951 value: T,
952952 range: Range,
953 context: var,
953 context: anytype,
954954 comptime lessThan: fn (@TypeOf(context), T, T) bool,
955955 unique: usize,
956956) usize {
......@@ -972,7 +972,7 @@ fn findLastBackward(
972972 items: []T,
973973 value: T,
974974 range: Range,
975 context: var,
975 context: anytype,
976976 comptime lessThan: fn (@TypeOf(context), T, T) bool,
977977 unique: usize,
978978) usize {
......@@ -994,7 +994,7 @@ fn binaryFirst(
994994 items: []T,
995995 value: T,
996996 range: Range,
997 context: var,
997 context: anytype,
998998 comptime lessThan: fn (@TypeOf(context), T, T) bool,
999999) usize {
10001000 var curr = range.start;
......@@ -1017,7 +1017,7 @@ fn binaryLast(
10171017 items: []T,
10181018 value: T,
10191019 range: Range,
1020 context: var,
1020 context: anytype,
10211021 comptime lessThan: fn (@TypeOf(context), T, T) bool,
10221022) usize {
10231023 var curr = range.start;
......@@ -1040,7 +1040,7 @@ fn mergeInto(
10401040 from: []T,
10411041 A: Range,
10421042 B: Range,
1043 context: var,
1043 context: anytype,
10441044 comptime lessThan: fn (@TypeOf(context), T, T) bool,
10451045 into: []T,
10461046) void {
......@@ -1078,7 +1078,7 @@ fn mergeExternal(
10781078 items: []T,
10791079 A: Range,
10801080 B: Range,
1081 context: var,
1081 context: anytype,
10821082 comptime lessThan: fn (@TypeOf(context), T, T) bool,
10831083 cache: []T,
10841084) void {
......@@ -1112,7 +1112,7 @@ fn mergeExternal(
11121112fn swap(
11131113 comptime T: type,
11141114 items: []T,
1115 context: var,
1115 context: anytype,
11161116 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
11171117 order: *[8]u8,
11181118 x: usize,
......@@ -1358,7 +1358,7 @@ fn fuzzTest(rng: *std.rand.Random) !void {
13581358pub fn argMin(
13591359 comptime T: type,
13601360 items: []const T,
1361 context: var,
1361 context: anytype,
13621362 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
13631363) ?usize {
13641364 if (items.len == 0) {
......@@ -1390,7 +1390,7 @@ test "argMin" {
13901390pub fn min(
13911391 comptime T: type,
13921392 items: []const T,
1393 context: var,
1393 context: anytype,
13941394 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
13951395) ?T {
13961396 const i = argMin(T, items, context, lessThan) orelse return null;
......@@ -1410,7 +1410,7 @@ test "min" {
14101410pub fn argMax(
14111411 comptime T: type,
14121412 items: []const T,
1413 context: var,
1413 context: anytype,
14141414 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
14151415) ?usize {
14161416 if (items.len == 0) {
......@@ -1442,7 +1442,7 @@ test "argMax" {
14421442pub fn max(
14431443 comptime T: type,
14441444 items: []const T,
1445 context: var,
1445 context: anytype,
14461446 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
14471447) ?T {
14481448 const i = argMax(T, items, context, lessThan) orelse return null;
......@@ -1462,7 +1462,7 @@ test "max" {
14621462pub fn isSorted(
14631463 comptime T: type,
14641464 items: []const T,
1465 context: var,
1465 context: anytype,
14661466 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
14671467) bool {
14681468 var i: usize = 1;
lib/std/special/build_runner.zig+2-2
......@@ -135,7 +135,7 @@ fn runBuild(builder: *Builder) anyerror!void {
135135 }
136136}
137137
138fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
138fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
139139 // run the build script to collect the options
140140 if (!already_ran_build) {
141141 builder.setInstallPrefix(null);
......@@ -202,7 +202,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
202202 );
203203}
204204
205fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) void {
205fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void {
206206 usage(builder, already_ran_build, out_stream) catch {};
207207 process.exit(1);
208208}
lib/std/special/compiler_rt/clzsi2_test.zig+1-1
......@@ -4,7 +4,7 @@ const testing = @import("std").testing;
44fn test__clzsi2(a: u32, expected: i32) void {
55 var nakedClzsi2 = clzsi2.__clzsi2;
66 var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2);
7 var x = @intCast(i32, a);
7 var x = @bitCast(i32, a);
88 var result = actualClzsi2(x);
99 testing.expectEqual(expected, result);
1010}
lib/std/special/compiler_rt/int.zig+1-1
......@@ -244,7 +244,7 @@ pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 {
244244 // r.all -= d.all;
245245 // carry = 1;
246246 // }
247 const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1);
247 const s = @bitCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1);
248248 carry = @intCast(u32, s & 1);
249249 r -= d & @bitCast(u32, s);
250250 }
lib/std/special/compiler_rt/udivmod.zig+1-1
......@@ -184,7 +184,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
184184 // carry = 1;
185185 // }
186186 r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421
187 const s: SignedDoubleInt = @intCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1);
187 const s: SignedDoubleInt = @bitCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1);
188188 carry = @intCast(u32, s & 1);
189189 r_all -= b & @bitCast(DoubleInt, s);
190190 r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421
lib/std/special/test_runner.zig+13-1
......@@ -21,6 +21,7 @@ pub fn main() anyerror!void {
2121
2222 for (test_fn_list) |test_fn, i| {
2323 std.testing.base_allocator_instance.reset();
24 std.testing.log_level = .warn;
2425
2526 var test_node = root_node.start(test_fn.name, null);
2627 test_node.activate();
......@@ -35,7 +36,7 @@ pub fn main() anyerror!void {
3536 async_frame_buffer = try std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size);
3637 }
3738 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);
38 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn);
39 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});
3940 },
4041 .blocking => {
4142 skip_count += 1;
......@@ -73,3 +74,14 @@ pub fn main() anyerror!void {
7374 std.debug.warn("{} passed; {} skipped.\n", .{ ok_count, skip_count });
7475 }
7576}
77
78pub fn log(
79 comptime message_level: std.log.Level,
80 comptime scope: @Type(.EnumLiteral),
81 comptime format: []const u8,
82 args: anytype,
83) void {
84 if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {
85 std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args);
86 }
87}
lib/std/start.zig+1-1
......@@ -246,7 +246,7 @@ inline fn initEventLoopAndCallMain(comptime Out: type, comptime mainFunc: fn ()
246246
247247 var result: u8 = undefined;
248248 var frame: @Frame(callMainAsync) = undefined;
249 _ = @asyncCall(&frame, &result, callMainAsync, u8, mainFunc, loop);
249 _ = @asyncCall(&frame, &result, callMainAsync, .{u8, mainFunc, loop});
250250 loop.run();
251251 return result;
252252 }
lib/std/std.zig+7-3
......@@ -3,14 +3,16 @@ pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned;
33pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged;
44pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled;
55pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged;
6pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
6pub const AutoHashMap = hash_map.AutoHashMap;
7pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
78pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
89pub const BufMap = @import("buf_map.zig").BufMap;
910pub const BufSet = @import("buf_set.zig").BufSet;
1011pub const ChildProcess = @import("child_process.zig").ChildProcess;
1112pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
1213pub const DynLib = @import("dynamic_library.zig").DynLib;
13pub const HashMap = @import("hash_map.zig").HashMap;
14pub const HashMap = hash_map.HashMap;
15pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
1416pub const Mutex = @import("mutex.zig").Mutex;
1517pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
1618pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
......@@ -22,7 +24,8 @@ pub const ResetEvent = @import("reset_event.zig").ResetEvent;
2224pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
2325pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
2426pub const SpinLock = @import("spinlock.zig").SpinLock;
25pub const StringHashMap = @import("hash_map.zig").StringHashMap;
27pub const StringHashMap = hash_map.StringHashMap;
28pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged;
2629pub const TailQueue = @import("linked_list.zig").TailQueue;
2730pub const Target = @import("target.zig").Target;
2831pub const Thread = @import("thread.zig").Thread;
......@@ -49,6 +52,7 @@ pub const heap = @import("heap.zig");
4952pub const http = @import("http.zig");
5053pub const io = @import("io.zig");
5154pub const json = @import("json.zig");
55pub const log = @import("log.zig");
5256pub const macho = @import("macho.zig");
5357pub const math = @import("math.zig");
5458pub const mem = @import("mem.zig");
lib/std/target.zig+51-16
......@@ -101,6 +101,31 @@ pub const Target = struct {
101101 return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);
102102 }
103103 };
104
105 /// This function is defined to serialize a Zig source code representation of this
106 /// type, that, when parsed, will deserialize into the same data.
107 pub fn format(
108 self: WindowsVersion,
109 comptime fmt: []const u8,
110 options: std.fmt.FormatOptions,
111 out_stream: anytype,
112 ) !void {
113 if (fmt.len > 0 and fmt[0] == 's') {
114 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) {
115 try std.fmt.format(out_stream, ".{}", .{@tagName(self)});
116 } else {
117 try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, {})", .{@enumToInt(self)});
118 }
119 } else {
120 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) {
121 try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)});
122 } else {
123 try std.fmt.format(out_stream, "WindowsVersion(", .{@typeName(@This())});
124 try std.fmt.format(out_stream, "{}", .{@enumToInt(self)});
125 try out_stream.writeAll(")");
126 }
127 }
128 }
104129 };
105130
106131 pub const LinuxVersionRange = struct {
......@@ -410,6 +435,7 @@ pub const Target = struct {
410435 elf,
411436 macho,
412437 wasm,
438 c,
413439 };
414440
415441 pub const SubSystem = enum {
......@@ -871,25 +897,34 @@ pub const Target = struct {
871897 /// All processors Zig is aware of, sorted lexicographically by name.
872898 pub fn allCpuModels(arch: Arch) []const *const Cpu.Model {
873899 return switch (arch) {
874 .arm, .armeb, .thumb, .thumbeb => arm.all_cpus,
875 .aarch64, .aarch64_be, .aarch64_32 => aarch64.all_cpus,
876 .avr => avr.all_cpus,
877 .bpfel, .bpfeb => bpf.all_cpus,
878 .hexagon => hexagon.all_cpus,
879 .mips, .mipsel, .mips64, .mips64el => mips.all_cpus,
880 .msp430 => msp430.all_cpus,
881 .powerpc, .powerpc64, .powerpc64le => powerpc.all_cpus,
882 .amdgcn => amdgpu.all_cpus,
883 .riscv32, .riscv64 => riscv.all_cpus,
884 .sparc, .sparcv9, .sparcel => sparc.all_cpus,
885 .s390x => systemz.all_cpus,
886 .i386, .x86_64 => x86.all_cpus,
887 .nvptx, .nvptx64 => nvptx.all_cpus,
888 .wasm32, .wasm64 => wasm.all_cpus,
900 .arm, .armeb, .thumb, .thumbeb => comptime allCpusFromDecls(arm.cpu),
901 .aarch64, .aarch64_be, .aarch64_32 => comptime allCpusFromDecls(aarch64.cpu),
902 .avr => comptime allCpusFromDecls(avr.cpu),
903 .bpfel, .bpfeb => comptime allCpusFromDecls(bpf.cpu),
904 .hexagon => comptime allCpusFromDecls(hexagon.cpu),
905 .mips, .mipsel, .mips64, .mips64el => comptime allCpusFromDecls(mips.cpu),
906 .msp430 => comptime allCpusFromDecls(msp430.cpu),
907 .powerpc, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu),
908 .amdgcn => comptime allCpusFromDecls(amdgpu.cpu),
909 .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu),
910 .sparc, .sparcv9, .sparcel => comptime allCpusFromDecls(sparc.cpu),
911 .s390x => comptime allCpusFromDecls(systemz.cpu),
912 .i386, .x86_64 => comptime allCpusFromDecls(x86.cpu),
913 .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu),
914 .wasm32, .wasm64 => comptime allCpusFromDecls(wasm.cpu),
889915
890916 else => &[0]*const Model{},
891917 };
892918 }
919
920 fn allCpusFromDecls(comptime cpus: type) []const *const Cpu.Model {
921 const decls = std.meta.declarations(cpus);
922 var array: [decls.len]*const Cpu.Model = undefined;
923 for (decls) |decl, i| {
924 array[i] = &@field(cpus, decl.name);
925 }
926 return &array;
927 }
893928 };
894929
895930 pub const Model = struct {
......@@ -1157,7 +1192,7 @@ pub const Target = struct {
11571192 pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
11581193 var result: DynamicLinker = .{};
11591194 const S = struct {
1160 fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker {
1195 fn print(r: *DynamicLinker, comptime fmt: []const u8, args: anytype) DynamicLinker {
11611196 r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1);
11621197 return r.*;
11631198 }
lib/std/target/aarch64.zig-45
......@@ -1505,48 +1505,3 @@ pub const cpu = struct {
15051505 }),
15061506 };
15071507};
1508
1509/// All aarch64 CPUs, sorted alphabetically by name.
1510/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
1511/// compiler has inefficient memory and CPU usage, affecting build times.
1512pub const all_cpus = &[_]*const CpuModel{
1513 &cpu.apple_a10,
1514 &cpu.apple_a11,
1515 &cpu.apple_a12,
1516 &cpu.apple_a13,
1517 &cpu.apple_a7,
1518 &cpu.apple_a8,
1519 &cpu.apple_a9,
1520 &cpu.apple_latest,
1521 &cpu.apple_s4,
1522 &cpu.apple_s5,
1523 &cpu.cortex_a35,
1524 &cpu.cortex_a53,
1525 &cpu.cortex_a55,
1526 &cpu.cortex_a57,
1527 &cpu.cortex_a65,
1528 &cpu.cortex_a65ae,
1529 &cpu.cortex_a72,
1530 &cpu.cortex_a73,
1531 &cpu.cortex_a75,
1532 &cpu.cortex_a76,
1533 &cpu.cortex_a76ae,
1534 &cpu.cyclone,
1535 &cpu.exynos_m1,
1536 &cpu.exynos_m2,
1537 &cpu.exynos_m3,
1538 &cpu.exynos_m4,
1539 &cpu.exynos_m5,
1540 &cpu.falkor,
1541 &cpu.generic,
1542 &cpu.kryo,
1543 &cpu.neoverse_e1,
1544 &cpu.neoverse_n1,
1545 &cpu.saphira,
1546 &cpu.thunderx,
1547 &cpu.thunderx2t99,
1548 &cpu.thunderxt81,
1549 &cpu.thunderxt83,
1550 &cpu.thunderxt88,
1551 &cpu.tsv110,
1552};
lib/std/target/amdgpu.zig-45
......@@ -1276,48 +1276,3 @@ pub const cpu = struct {
12761276 }),
12771277 };
12781278};
1279
1280/// All amdgpu CPUs, sorted alphabetically by name.
1281/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
1282/// compiler has inefficient memory and CPU usage, affecting build times.
1283pub const all_cpus = &[_]*const CpuModel{
1284 &cpu.bonaire,
1285 &cpu.carrizo,
1286 &cpu.fiji,
1287 &cpu.generic,
1288 &cpu.generic_hsa,
1289 &cpu.gfx1010,
1290 &cpu.gfx1011,
1291 &cpu.gfx1012,
1292 &cpu.gfx600,
1293 &cpu.gfx601,
1294 &cpu.gfx700,
1295 &cpu.gfx701,
1296 &cpu.gfx702,
1297 &cpu.gfx703,
1298 &cpu.gfx704,
1299 &cpu.gfx801,
1300 &cpu.gfx802,
1301 &cpu.gfx803,
1302 &cpu.gfx810,
1303 &cpu.gfx900,
1304 &cpu.gfx902,
1305 &cpu.gfx904,
1306 &cpu.gfx906,
1307 &cpu.gfx908,
1308 &cpu.gfx909,
1309 &cpu.hainan,
1310 &cpu.hawaii,
1311 &cpu.iceland,
1312 &cpu.kabini,
1313 &cpu.kaveri,
1314 &cpu.mullins,
1315 &cpu.oland,
1316 &cpu.pitcairn,
1317 &cpu.polaris10,
1318 &cpu.polaris11,
1319 &cpu.stoney,
1320 &cpu.tahiti,
1321 &cpu.tonga,
1322 &cpu.verde,
1323};
lib/std/target/arm.zig-89
......@@ -2145,92 +2145,3 @@ pub const cpu = struct {
21452145 }),
21462146 };
21472147};
2148
2149/// All arm CPUs, sorted alphabetically by name.
2150/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
2151/// compiler has inefficient memory and CPU usage, affecting build times.
2152pub const all_cpus = &[_]*const CpuModel{
2153 &cpu.arm1020e,
2154 &cpu.arm1020t,
2155 &cpu.arm1022e,
2156 &cpu.arm10e,
2157 &cpu.arm10tdmi,
2158 &cpu.arm1136j_s,
2159 &cpu.arm1136jf_s,
2160 &cpu.arm1156t2_s,
2161 &cpu.arm1156t2f_s,
2162 &cpu.arm1176j_s,
2163 &cpu.arm1176jz_s,
2164 &cpu.arm1176jzf_s,
2165 &cpu.arm710t,
2166 &cpu.arm720t,
2167 &cpu.arm7tdmi,
2168 &cpu.arm7tdmi_s,
2169 &cpu.arm8,
2170 &cpu.arm810,
2171 &cpu.arm9,
2172 &cpu.arm920,
2173 &cpu.arm920t,
2174 &cpu.arm922t,
2175 &cpu.arm926ej_s,
2176 &cpu.arm940t,
2177 &cpu.arm946e_s,
2178 &cpu.arm966e_s,
2179 &cpu.arm968e_s,
2180 &cpu.arm9e,
2181 &cpu.arm9tdmi,
2182 &cpu.cortex_a12,
2183 &cpu.cortex_a15,
2184 &cpu.cortex_a17,
2185 &cpu.cortex_a32,
2186 &cpu.cortex_a35,
2187 &cpu.cortex_a5,
2188 &cpu.cortex_a53,
2189 &cpu.cortex_a55,
2190 &cpu.cortex_a57,
2191 &cpu.cortex_a7,
2192 &cpu.cortex_a72,
2193 &cpu.cortex_a73,
2194 &cpu.cortex_a75,
2195 &cpu.cortex_a76,
2196 &cpu.cortex_a76ae,
2197 &cpu.cortex_a8,
2198 &cpu.cortex_a9,
2199 &cpu.cortex_m0,
2200 &cpu.cortex_m0plus,
2201 &cpu.cortex_m1,
2202 &cpu.cortex_m23,
2203 &cpu.cortex_m3,
2204 &cpu.cortex_m33,
2205 &cpu.cortex_m35p,
2206 &cpu.cortex_m4,
2207 &cpu.cortex_m7,
2208 &cpu.cortex_r4,
2209 &cpu.cortex_r4f,
2210 &cpu.cortex_r5,
2211 &cpu.cortex_r52,
2212 &cpu.cortex_r7,
2213 &cpu.cortex_r8,
2214 &cpu.cyclone,
2215 &cpu.ep9312,
2216 &cpu.exynos_m1,
2217 &cpu.exynos_m2,
2218 &cpu.exynos_m3,
2219 &cpu.exynos_m4,
2220 &cpu.exynos_m5,
2221 &cpu.generic,
2222 &cpu.iwmmxt,
2223 &cpu.krait,
2224 &cpu.kryo,
2225 &cpu.mpcore,
2226 &cpu.mpcorenovfp,
2227 &cpu.neoverse_n1,
2228 &cpu.sc000,
2229 &cpu.sc300,
2230 &cpu.strongarm,
2231 &cpu.strongarm110,
2232 &cpu.strongarm1100,
2233 &cpu.strongarm1110,
2234 &cpu.swift,
2235 &cpu.xscale,
2236};
lib/std/target/avr.zig-263
......@@ -2116,266 +2116,3 @@ pub const cpu = struct {
21162116 }),
21172117 };
21182118};
2119
2120/// All avr CPUs, sorted alphabetically by name.
2121/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
2122/// compiler has inefficient memory and CPU usage, affecting build times.
2123pub const all_cpus = &[_]*const CpuModel{
2124 &cpu.at43usb320,
2125 &cpu.at43usb355,
2126 &cpu.at76c711,
2127 &cpu.at86rf401,
2128 &cpu.at90c8534,
2129 &cpu.at90can128,
2130 &cpu.at90can32,
2131 &cpu.at90can64,
2132 &cpu.at90pwm1,
2133 &cpu.at90pwm161,
2134 &cpu.at90pwm2,
2135 &cpu.at90pwm216,
2136 &cpu.at90pwm2b,
2137 &cpu.at90pwm3,
2138 &cpu.at90pwm316,
2139 &cpu.at90pwm3b,
2140 &cpu.at90pwm81,
2141 &cpu.at90s1200,
2142 &cpu.at90s2313,
2143 &cpu.at90s2323,
2144 &cpu.at90s2333,
2145 &cpu.at90s2343,
2146 &cpu.at90s4414,
2147 &cpu.at90s4433,
2148 &cpu.at90s4434,
2149 &cpu.at90s8515,
2150 &cpu.at90s8535,
2151 &cpu.at90scr100,
2152 &cpu.at90usb1286,
2153 &cpu.at90usb1287,
2154 &cpu.at90usb162,
2155 &cpu.at90usb646,
2156 &cpu.at90usb647,
2157 &cpu.at90usb82,
2158 &cpu.at94k,
2159 &cpu.ata5272,
2160 &cpu.ata5505,
2161 &cpu.ata5790,
2162 &cpu.ata5795,
2163 &cpu.ata6285,
2164 &cpu.ata6286,
2165 &cpu.ata6289,
2166 &cpu.atmega103,
2167 &cpu.atmega128,
2168 &cpu.atmega1280,
2169 &cpu.atmega1281,
2170 &cpu.atmega1284,
2171 &cpu.atmega1284p,
2172 &cpu.atmega1284rfr2,
2173 &cpu.atmega128a,
2174 &cpu.atmega128rfa1,
2175 &cpu.atmega128rfr2,
2176 &cpu.atmega16,
2177 &cpu.atmega161,
2178 &cpu.atmega162,
2179 &cpu.atmega163,
2180 &cpu.atmega164a,
2181 &cpu.atmega164p,
2182 &cpu.atmega164pa,
2183 &cpu.atmega165,
2184 &cpu.atmega165a,
2185 &cpu.atmega165p,
2186 &cpu.atmega165pa,
2187 &cpu.atmega168,
2188 &cpu.atmega168a,
2189 &cpu.atmega168p,
2190 &cpu.atmega168pa,
2191 &cpu.atmega169,
2192 &cpu.atmega169a,
2193 &cpu.atmega169p,
2194 &cpu.atmega169pa,
2195 &cpu.atmega16a,
2196 &cpu.atmega16hva,
2197 &cpu.atmega16hva2,
2198 &cpu.atmega16hvb,
2199 &cpu.atmega16hvbrevb,
2200 &cpu.atmega16m1,
2201 &cpu.atmega16u2,
2202 &cpu.atmega16u4,
2203 &cpu.atmega2560,
2204 &cpu.atmega2561,
2205 &cpu.atmega2564rfr2,
2206 &cpu.atmega256rfr2,
2207 &cpu.atmega32,
2208 &cpu.atmega323,
2209 &cpu.atmega324a,
2210 &cpu.atmega324p,
2211 &cpu.atmega324pa,
2212 &cpu.atmega325,
2213 &cpu.atmega3250,
2214 &cpu.atmega3250a,
2215 &cpu.atmega3250p,
2216 &cpu.atmega3250pa,
2217 &cpu.atmega325a,
2218 &cpu.atmega325p,
2219 &cpu.atmega325pa,
2220 &cpu.atmega328,
2221 &cpu.atmega328p,
2222 &cpu.atmega329,
2223 &cpu.atmega3290,
2224 &cpu.atmega3290a,
2225 &cpu.atmega3290p,
2226 &cpu.atmega3290pa,
2227 &cpu.atmega329a,
2228 &cpu.atmega329p,
2229 &cpu.atmega329pa,
2230 &cpu.atmega32a,
2231 &cpu.atmega32c1,
2232 &cpu.atmega32hvb,
2233 &cpu.atmega32hvbrevb,
2234 &cpu.atmega32m1,
2235 &cpu.atmega32u2,
2236 &cpu.atmega32u4,
2237 &cpu.atmega32u6,
2238 &cpu.atmega406,
2239 &cpu.atmega48,
2240 &cpu.atmega48a,
2241 &cpu.atmega48p,
2242 &cpu.atmega48pa,
2243 &cpu.atmega64,
2244 &cpu.atmega640,
2245 &cpu.atmega644,
2246 &cpu.atmega644a,
2247 &cpu.atmega644p,
2248 &cpu.atmega644pa,
2249 &cpu.atmega644rfr2,
2250 &cpu.atmega645,
2251 &cpu.atmega6450,
2252 &cpu.atmega6450a,
2253 &cpu.atmega6450p,
2254 &cpu.atmega645a,
2255 &cpu.atmega645p,
2256 &cpu.atmega649,
2257 &cpu.atmega6490,
2258 &cpu.atmega6490a,
2259 &cpu.atmega6490p,
2260 &cpu.atmega649a,
2261 &cpu.atmega649p,
2262 &cpu.atmega64a,
2263 &cpu.atmega64c1,
2264 &cpu.atmega64hve,
2265 &cpu.atmega64m1,
2266 &cpu.atmega64rfr2,
2267 &cpu.atmega8,
2268 &cpu.atmega8515,
2269 &cpu.atmega8535,
2270 &cpu.atmega88,
2271 &cpu.atmega88a,
2272 &cpu.atmega88p,
2273 &cpu.atmega88pa,
2274 &cpu.atmega8a,
2275 &cpu.atmega8hva,
2276 &cpu.atmega8u2,
2277 &cpu.attiny10,
2278 &cpu.attiny102,
2279 &cpu.attiny104,
2280 &cpu.attiny11,
2281 &cpu.attiny12,
2282 &cpu.attiny13,
2283 &cpu.attiny13a,
2284 &cpu.attiny15,
2285 &cpu.attiny1634,
2286 &cpu.attiny167,
2287 &cpu.attiny20,
2288 &cpu.attiny22,
2289 &cpu.attiny2313,
2290 &cpu.attiny2313a,
2291 &cpu.attiny24,
2292 &cpu.attiny24a,
2293 &cpu.attiny25,
2294 &cpu.attiny26,
2295 &cpu.attiny261,
2296 &cpu.attiny261a,
2297 &cpu.attiny28,
2298 &cpu.attiny4,
2299 &cpu.attiny40,
2300 &cpu.attiny4313,
2301 &cpu.attiny43u,
2302 &cpu.attiny44,
2303 &cpu.attiny44a,
2304 &cpu.attiny45,
2305 &cpu.attiny461,
2306 &cpu.attiny461a,
2307 &cpu.attiny48,
2308 &cpu.attiny5,
2309 &cpu.attiny828,
2310 &cpu.attiny84,
2311 &cpu.attiny84a,
2312 &cpu.attiny85,
2313 &cpu.attiny861,
2314 &cpu.attiny861a,
2315 &cpu.attiny87,
2316 &cpu.attiny88,
2317 &cpu.attiny9,
2318 &cpu.atxmega128a1,
2319 &cpu.atxmega128a1u,
2320 &cpu.atxmega128a3,
2321 &cpu.atxmega128a3u,
2322 &cpu.atxmega128a4u,
2323 &cpu.atxmega128b1,
2324 &cpu.atxmega128b3,
2325 &cpu.atxmega128c3,
2326 &cpu.atxmega128d3,
2327 &cpu.atxmega128d4,
2328 &cpu.atxmega16a4,
2329 &cpu.atxmega16a4u,
2330 &cpu.atxmega16c4,
2331 &cpu.atxmega16d4,
2332 &cpu.atxmega16e5,
2333 &cpu.atxmega192a3,
2334 &cpu.atxmega192a3u,
2335 &cpu.atxmega192c3,
2336 &cpu.atxmega192d3,
2337 &cpu.atxmega256a3,
2338 &cpu.atxmega256a3b,
2339 &cpu.atxmega256a3bu,
2340 &cpu.atxmega256a3u,
2341 &cpu.atxmega256c3,
2342 &cpu.atxmega256d3,
2343 &cpu.atxmega32a4,
2344 &cpu.atxmega32a4u,
2345 &cpu.atxmega32c4,
2346 &cpu.atxmega32d4,
2347 &cpu.atxmega32e5,
2348 &cpu.atxmega32x1,
2349 &cpu.atxmega384c3,
2350 &cpu.atxmega384d3,
2351 &cpu.atxmega64a1,
2352 &cpu.atxmega64a1u,
2353 &cpu.atxmega64a3,
2354 &cpu.atxmega64a3u,
2355 &cpu.atxmega64a4u,
2356 &cpu.atxmega64b1,
2357 &cpu.atxmega64b3,
2358 &cpu.atxmega64c3,
2359 &cpu.atxmega64d3,
2360 &cpu.atxmega64d4,
2361 &cpu.atxmega8e5,
2362 &cpu.avr1,
2363 &cpu.avr2,
2364 &cpu.avr25,
2365 &cpu.avr3,
2366 &cpu.avr31,
2367 &cpu.avr35,
2368 &cpu.avr4,
2369 &cpu.avr5,
2370 &cpu.avr51,
2371 &cpu.avr6,
2372 &cpu.avrtiny,
2373 &cpu.avrxmega1,
2374 &cpu.avrxmega2,
2375 &cpu.avrxmega3,
2376 &cpu.avrxmega4,
2377 &cpu.avrxmega5,
2378 &cpu.avrxmega6,
2379 &cpu.avrxmega7,
2380 &cpu.m3000,
2381};
lib/std/target/bpf.zig-11
......@@ -64,14 +64,3 @@ pub const cpu = struct {
6464 .features = featureSet(&[_]Feature{}),
6565 };
6666};
67
68/// All bpf CPUs, sorted alphabetically by name.
69/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
70/// compiler has inefficient memory and CPU usage, affecting build times.
71pub const all_cpus = &[_]*const CpuModel{
72 &cpu.generic,
73 &cpu.probe,
74 &cpu.v1,
75 &cpu.v2,
76 &cpu.v3,
77};
lib/std/target/hexagon.zig-13
......@@ -298,16 +298,3 @@ pub const cpu = struct {
298298 }),
299299 };
300300};
301
302/// All hexagon CPUs, sorted alphabetically by name.
303/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
304/// compiler has inefficient memory and CPU usage, affecting build times.
305pub const all_cpus = &[_]*const CpuModel{
306 &cpu.generic,
307 &cpu.hexagonv5,
308 &cpu.hexagonv55,
309 &cpu.hexagonv60,
310 &cpu.hexagonv62,
311 &cpu.hexagonv65,
312 &cpu.hexagonv66,
313};
lib/std/target/mips.zig-25
......@@ -524,28 +524,3 @@ pub const cpu = struct {
524524 }),
525525 };
526526};
527
528/// All mips CPUs, sorted alphabetically by name.
529/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
530/// compiler has inefficient memory and CPU usage, affecting build times.
531pub const all_cpus = &[_]*const CpuModel{
532 &cpu.generic,
533 &cpu.mips1,
534 &cpu.mips2,
535 &cpu.mips3,
536 &cpu.mips32,
537 &cpu.mips32r2,
538 &cpu.mips32r3,
539 &cpu.mips32r5,
540 &cpu.mips32r6,
541 &cpu.mips4,
542 &cpu.mips5,
543 &cpu.mips64,
544 &cpu.mips64r2,
545 &cpu.mips64r3,
546 &cpu.mips64r5,
547 &cpu.mips64r6,
548 &cpu.octeon,
549 &cpu.@"octeon+",
550 &cpu.p5600,
551};
lib/std/target/msp430.zig-9
......@@ -62,12 +62,3 @@ pub const cpu = struct {
6262 }),
6363 };
6464};
65
66/// All msp430 CPUs, sorted alphabetically by name.
67/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
68/// compiler has inefficient memory and CPU usage, affecting build times.
69pub const all_cpus = &[_]*const CpuModel{
70 &cpu.generic,
71 &cpu.msp430,
72 &cpu.msp430x,
73};
lib/std/target/nvptx.zig-21
......@@ -287,24 +287,3 @@ pub const cpu = struct {
287287 }),
288288 };
289289};
290
291/// All nvptx CPUs, sorted alphabetically by name.
292/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
293/// compiler has inefficient memory and CPU usage, affecting build times.
294pub const all_cpus = &[_]*const CpuModel{
295 &cpu.sm_20,
296 &cpu.sm_21,
297 &cpu.sm_30,
298 &cpu.sm_32,
299 &cpu.sm_35,
300 &cpu.sm_37,
301 &cpu.sm_50,
302 &cpu.sm_52,
303 &cpu.sm_53,
304 &cpu.sm_60,
305 &cpu.sm_61,
306 &cpu.sm_62,
307 &cpu.sm_70,
308 &cpu.sm_72,
309 &cpu.sm_75,
310};
lib/std/target/powerpc.zig-44
......@@ -944,47 +944,3 @@ pub const cpu = struct {
944944 }),
945945 };
946946};
947
948/// All powerpc CPUs, sorted alphabetically by name.
949/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
950/// compiler has inefficient memory and CPU usage, affecting build times.
951pub const all_cpus = &[_]*const CpuModel{
952 &cpu.@"440",
953 &cpu.@"450",
954 &cpu.@"601",
955 &cpu.@"602",
956 &cpu.@"603",
957 &cpu.@"603e",
958 &cpu.@"603ev",
959 &cpu.@"604",
960 &cpu.@"604e",
961 &cpu.@"620",
962 &cpu.@"7400",
963 &cpu.@"7450",
964 &cpu.@"750",
965 &cpu.@"970",
966 &cpu.a2,
967 &cpu.a2q,
968 &cpu.e500,
969 &cpu.e500mc,
970 &cpu.e5500,
971 &cpu.future,
972 &cpu.g3,
973 &cpu.g4,
974 &cpu.@"g4+",
975 &cpu.g5,
976 &cpu.generic,
977 &cpu.ppc,
978 &cpu.ppc32,
979 &cpu.ppc64,
980 &cpu.ppc64le,
981 &cpu.pwr3,
982 &cpu.pwr4,
983 &cpu.pwr5,
984 &cpu.pwr5x,
985 &cpu.pwr6,
986 &cpu.pwr6x,
987 &cpu.pwr7,
988 &cpu.pwr8,
989 &cpu.pwr9,
990};
lib/std/target/riscv.zig-10
......@@ -303,13 +303,3 @@ pub const cpu = struct {
303303 }),
304304 };
305305};
306
307/// All riscv CPUs, sorted alphabetically by name.
308/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
309/// compiler has inefficient memory and CPU usage, affecting build times.
310pub const all_cpus = &[_]*const CpuModel{
311 &cpu.baseline_rv32,
312 &cpu.baseline_rv64,
313 &cpu.generic_rv32,
314 &cpu.generic_rv64,
315};
lib/std/target/sparc.zig-46
......@@ -448,49 +448,3 @@ pub const cpu = struct {
448448 }),
449449 };
450450};
451
452/// All sparc CPUs, sorted alphabetically by name.
453/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
454/// compiler has inefficient memory and CPU usage, affecting build times.
455pub const all_cpus = &[_]*const CpuModel{
456 &cpu.at697e,
457 &cpu.at697f,
458 &cpu.f934,
459 &cpu.generic,
460 &cpu.gr712rc,
461 &cpu.gr740,
462 &cpu.hypersparc,
463 &cpu.leon2,
464 &cpu.leon3,
465 &cpu.leon4,
466 &cpu.ma2080,
467 &cpu.ma2085,
468 &cpu.ma2100,
469 &cpu.ma2150,
470 &cpu.ma2155,
471 &cpu.ma2450,
472 &cpu.ma2455,
473 &cpu.ma2480,
474 &cpu.ma2485,
475 &cpu.ma2x5x,
476 &cpu.ma2x8x,
477 &cpu.myriad2,
478 &cpu.myriad2_1,
479 &cpu.myriad2_2,
480 &cpu.myriad2_3,
481 &cpu.niagara,
482 &cpu.niagara2,
483 &cpu.niagara3,
484 &cpu.niagara4,
485 &cpu.sparclet,
486 &cpu.sparclite,
487 &cpu.sparclite86x,
488 &cpu.supersparc,
489 &cpu.tsc701,
490 &cpu.ultrasparc,
491 &cpu.ultrasparc3,
492 &cpu.ut699,
493 &cpu.v7,
494 &cpu.v8,
495 &cpu.v9,
496};
lib/std/target/systemz.zig-19
......@@ -532,22 +532,3 @@ pub const cpu = struct {
532532 }),
533533 };
534534};
535
536/// All systemz CPUs, sorted alphabetically by name.
537/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
538/// compiler has inefficient memory and CPU usage, affecting build times.
539pub const all_cpus = &[_]*const CpuModel{
540 &cpu.arch10,
541 &cpu.arch11,
542 &cpu.arch12,
543 &cpu.arch13,
544 &cpu.arch8,
545 &cpu.arch9,
546 &cpu.generic,
547 &cpu.z10,
548 &cpu.z13,
549 &cpu.z14,
550 &cpu.z15,
551 &cpu.z196,
552 &cpu.zEC12,
553};
lib/std/target/wasm.zig-9
......@@ -104,12 +104,3 @@ pub const cpu = struct {
104104 .features = featureSet(&[_]Feature{}),
105105 };
106106};
107
108/// All wasm CPUs, sorted alphabetically by name.
109/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
110/// compiler has inefficient memory and CPU usage, affecting build times.
111pub const all_cpus = &[_]*const CpuModel{
112 &cpu.bleeding_edge,
113 &cpu.generic,
114 &cpu.mvp,
115};
lib/std/target/x86.zig-85
......@@ -2943,88 +2943,3 @@ pub const cpu = struct {
29432943 }),
29442944 };
29452945};
2946
2947/// All x86 CPUs, sorted alphabetically by name.
2948/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
2949/// compiler has inefficient memory and CPU usage, affecting build times.
2950pub const all_cpus = &[_]*const CpuModel{
2951 &cpu.amdfam10,
2952 &cpu.athlon,
2953 &cpu.athlon_4,
2954 &cpu.athlon_fx,
2955 &cpu.athlon_mp,
2956 &cpu.athlon_tbird,
2957 &cpu.athlon_xp,
2958 &cpu.athlon64,
2959 &cpu.athlon64_sse3,
2960 &cpu.atom,
2961 &cpu.barcelona,
2962 &cpu.bdver1,
2963 &cpu.bdver2,
2964 &cpu.bdver3,
2965 &cpu.bdver4,
2966 &cpu.bonnell,
2967 &cpu.broadwell,
2968 &cpu.btver1,
2969 &cpu.btver2,
2970 &cpu.c3,
2971 &cpu.c3_2,
2972 &cpu.cannonlake,
2973 &cpu.cascadelake,
2974 &cpu.cooperlake,
2975 &cpu.core_avx_i,
2976 &cpu.core_avx2,
2977 &cpu.core2,
2978 &cpu.corei7,
2979 &cpu.corei7_avx,
2980 &cpu.generic,
2981 &cpu.geode,
2982 &cpu.goldmont,
2983 &cpu.goldmont_plus,
2984 &cpu.haswell,
2985 &cpu._i386,
2986 &cpu._i486,
2987 &cpu._i586,
2988 &cpu._i686,
2989 &cpu.icelake_client,
2990 &cpu.icelake_server,
2991 &cpu.ivybridge,
2992 &cpu.k6,
2993 &cpu.k6_2,
2994 &cpu.k6_3,
2995 &cpu.k8,
2996 &cpu.k8_sse3,
2997 &cpu.knl,
2998 &cpu.knm,
2999 &cpu.lakemont,
3000 &cpu.nehalem,
3001 &cpu.nocona,
3002 &cpu.opteron,
3003 &cpu.opteron_sse3,
3004 &cpu.penryn,
3005 &cpu.pentium,
3006 &cpu.pentium_m,
3007 &cpu.pentium_mmx,
3008 &cpu.pentium2,
3009 &cpu.pentium3,
3010 &cpu.pentium3m,
3011 &cpu.pentium4,
3012 &cpu.pentium4m,
3013 &cpu.pentiumpro,
3014 &cpu.prescott,
3015 &cpu.sandybridge,
3016 &cpu.silvermont,
3017 &cpu.skx,
3018 &cpu.skylake,
3019 &cpu.skylake_avx512,
3020 &cpu.slm,
3021 &cpu.tigerlake,
3022 &cpu.tremont,
3023 &cpu.westmere,
3024 &cpu.winchip_c6,
3025 &cpu.winchip2,
3026 &cpu.x86_64,
3027 &cpu.yonah,
3028 &cpu.znver1,
3029 &cpu.znver2,
3030};
lib/std/testing.zig+7-4
......@@ -11,12 +11,15 @@ pub var allocator_instance = LeakCountAllocator.init(&base_allocator_instance.al
1111pub const failing_allocator = &failing_allocator_instance.allocator;
1212pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
1313
14pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);
14pub var base_allocator_instance = std.mem.validationWrap(std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]));
1515var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
1616
17/// TODO https://github.com/ziglang/zig/issues/5738
18pub var log_level = std.log.Level.warn;
19
1720/// This function is intended to be used only in tests. It prints diagnostics to stderr
1821/// and then aborts when actual_error_union is not expected_error.
19pub fn expectError(expected_error: anyerror, actual_error_union: var) void {
22pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {
2023 if (actual_error_union) |actual_payload| {
2124 std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload });
2225 } else |actual_error| {
......@@ -33,7 +36,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void {
3336/// equal, prints diagnostics to stderr to show exactly how they are not equal,
3437/// then aborts.
3538/// The types must match exactly.
36pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void {
39pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
3740 switch (@typeInfo(@TypeOf(actual))) {
3841 .NoReturn,
3942 .BoundFn,
......@@ -215,7 +218,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
215218 defer preopens.deinit();
216219 preopens.populate() catch
217220 @panic("unable to make tmp dir for testing: unable to populate preopens");
218 const preopen = preopens.find(".") orelse
221 const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse
219222 @panic("unable to make tmp dir for testing: didn't find '.' in the preopens");
220223
221224 return std.fs.Dir{ .fd = preopen.fd };
lib/std/testing/failing_allocator.zig+18-23
......@@ -39,43 +39,38 @@ pub const FailingAllocator = struct {
3939 .allocations = 0,
4040 .deallocations = 0,
4141 .allocator = mem.Allocator{
42 .reallocFn = realloc,
43 .shrinkFn = shrink,
42 .allocFn = alloc,
43 .resizeFn = resize,
4444 },
4545 };
4646 }
4747
48 fn realloc(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
48 fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
4949 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
5050 if (self.index == self.fail_index) {
5151 return error.OutOfMemory;
5252 }
53 const result = try self.internal_allocator.reallocFn(
54 self.internal_allocator,
55 old_mem,
56 old_align,
57 new_size,
58 new_align,
59 );
60 if (new_size < old_mem.len) {
61 self.freed_bytes += old_mem.len - new_size;
62 if (new_size == 0)
63 self.deallocations += 1;
64 } else if (new_size > old_mem.len) {
65 self.allocated_bytes += new_size - old_mem.len;
66 if (old_mem.len == 0)
67 self.allocations += 1;
68 }
53 const result = try self.internal_allocator.callAllocFn(len, ptr_align, len_align);
54 self.allocated_bytes += result.len;
55 self.allocations += 1;
6956 self.index += 1;
7057 return result;
7158 }
7259
73 fn shrink(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
60 fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
7461 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
75 const r = self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
76 self.freed_bytes += old_mem.len - r.len;
77 if (new_size == 0)
62 const r = self.internal_allocator.callResizeFn(old_mem, new_len, len_align) catch |e| {
63 std.debug.assert(new_len > old_mem.len);
64 return e;
65 };
66 if (new_len == 0) {
7867 self.deallocations += 1;
68 self.freed_bytes += old_mem.len;
69 } else if (r < old_mem.len) {
70 self.freed_bytes += old_mem.len - r;
71 } else {
72 self.allocated_bytes += r - old_mem.len;
73 }
7974 return r;
8075 }
8176};
lib/std/testing/leak_count_allocator.zig+11-10
......@@ -14,23 +14,21 @@ pub const LeakCountAllocator = struct {
1414 return .{
1515 .count = 0,
1616 .allocator = .{
17 .reallocFn = realloc,
18 .shrinkFn = shrink,
17 .allocFn = alloc,
18 .resizeFn = resize,
1919 },
2020 .internal_allocator = allocator,
2121 };
2222 }
2323
24 fn realloc(allocator: *std.mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
24 fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
2525 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);
26 var data = try self.internal_allocator.reallocFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
27 if (old_mem.len == 0) {
28 self.count += 1;
29 }
30 return data;
26 const ptr = try self.internal_allocator.callAllocFn(len, ptr_align, len_align);
27 self.count += 1;
28 return ptr;
3129 }
3230
33 fn shrink(allocator: *std.mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
31 fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize {
3432 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);
3533 if (new_size == 0) {
3634 if (self.count == 0) {
......@@ -38,7 +36,10 @@ pub const LeakCountAllocator = struct {
3836 }
3937 self.count -= 1;
4038 }
41 return self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
39 return self.internal_allocator.callResizeFn(old_mem, new_size, len_align) catch |e| {
40 std.debug.assert(new_size > old_mem.len);
41 return e;
42 };
4243 }
4344
4445 pub fn validate(self: LeakCountAllocator) !void {
lib/std/thread.zig+1-1
......@@ -143,7 +143,7 @@ pub const Thread = struct {
143143 /// fn startFn(@TypeOf(context)) T
144144 /// where T is u8, noreturn, void, or !void
145145 /// caller must call wait on the returned thread
146 pub fn spawn(context: var, comptime startFn: var) SpawnError!*Thread {
146 pub fn spawn(context: anytype, comptime startFn: anytype) SpawnError!*Thread {
147147 if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
148148 // TODO compile-time call graph analysis to determine stack upper bound
149149 // https://github.com/ziglang/zig/issues/157
lib/std/unicode.zig+41
......@@ -235,6 +235,22 @@ pub const Utf8Iterator = struct {
235235 else => unreachable,
236236 }
237237 }
238
239 /// Look ahead at the next n codepoints without advancing the iterator.
240 /// If fewer than n codepoints are available, then return the remainder of the string.
241 pub fn peek(it: *Utf8Iterator, n: usize) []const u8 {
242 const original_i = it.i;
243 defer it.i = original_i;
244
245 var end_ix = original_i;
246 var found: usize = 0;
247 while (found < n) : (found += 1) {
248 const next_codepoint = it.nextCodepointSlice() orelse return it.bytes[original_i..];
249 end_ix += next_codepoint.len;
250 }
251
252 return it.bytes[original_i..end_ix];
253 }
238254};
239255
240256pub const Utf16LeIterator = struct {
......@@ -451,6 +467,31 @@ fn testMiscInvalidUtf8() void {
451467 testValid("\xee\x80\x80", 0xe000);
452468}
453469
470test "utf8 iterator peeking" {
471 comptime testUtf8Peeking();
472 testUtf8Peeking();
473}
474
475fn testUtf8Peeking() void {
476 const s = Utf8View.initComptime("noël");
477 var it = s.iterator();
478
479 testing.expect(std.mem.eql(u8, "n", it.nextCodepointSlice().?));
480
481 testing.expect(std.mem.eql(u8, "o", it.peek(1)));
482 testing.expect(std.mem.eql(u8, "oë", it.peek(2)));
483 testing.expect(std.mem.eql(u8, "oël", it.peek(3)));
484 testing.expect(std.mem.eql(u8, "oël", it.peek(4)));
485 testing.expect(std.mem.eql(u8, "oël", it.peek(10)));
486
487 testing.expect(std.mem.eql(u8, "o", it.nextCodepointSlice().?));
488 testing.expect(std.mem.eql(u8, "ë", it.nextCodepointSlice().?));
489 testing.expect(std.mem.eql(u8, "l", it.nextCodepointSlice().?));
490 testing.expect(it.nextCodepointSlice() == null);
491
492 testing.expect(std.mem.eql(u8, &[_]u8{}, it.peek(1)));
493}
494
454495fn testError(bytes: []const u8, expected_err: anyerror) void {
455496 testing.expectError(expected_err, testDecode(bytes));
456497}
lib/std/zig.zig+38
......@@ -1,4 +1,6 @@
1const std = @import("std.zig");
12const tokenizer = @import("zig/tokenizer.zig");
3
24pub const Token = tokenizer.Token;
35pub const Tokenizer = tokenizer.Tokenizer;
46pub const parse = @import("zig/parse.zig").parse;
......@@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig");
911pub const system = @import("zig/system.zig");
1012pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
1113
14pub const SrcHash = [16]u8;
15
16/// If the source is small enough, it is used directly as the hash.
17/// If it is long, blake3 hash is computed.
18pub fn hashSrc(src: []const u8) SrcHash {
19 var out: SrcHash = undefined;
20 if (src.len <= SrcHash.len) {
21 std.mem.copy(u8, &out, src);
22 std.mem.set(u8, out[src.len..], 0);
23 } else {
24 std.crypto.Blake3.hash(src, &out);
25 }
26 return out;
27}
28
1229pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
1330 var line: usize = 0;
1431 var column: usize = 0;
......@@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
2643 return .{ .line = line, .column = column };
2744}
2845
46/// Returns the standard file system basename of a binary generated by the Zig compiler.
47pub fn binNameAlloc(
48 allocator: *std.mem.Allocator,
49 root_name: []const u8,
50 target: std.Target,
51 output_mode: std.builtin.OutputMode,
52 link_mode: ?std.builtin.LinkMode,
53) error{OutOfMemory}![]u8 {
54 switch (output_mode) {
55 .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }),
56 .Lib => {
57 const suffix = switch (link_mode orelse .Static) {
58 .Static => target.staticLibSuffix(),
59 .Dynamic => target.dynamicLibSuffix(),
60 };
61 return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
62 },
63 .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),
64 }
65}
66
2967test "" {
3068 @import("std").meta.refAllDecls(@This());
3169}
lib/std/zig/ast.zig+639-342
......@@ -29,7 +29,7 @@ pub const Tree = struct {
2929 self.arena.promote(self.gpa).deinit();
3030 }
3131
32 pub fn renderError(self: *Tree, parse_error: *const Error, stream: var) !void {
32 pub fn renderError(self: *Tree, parse_error: *const Error, stream: anytype) !void {
3333 return parse_error.render(self.token_ids, stream);
3434 }
3535
......@@ -167,7 +167,7 @@ pub const Error = union(enum) {
167167 DeclBetweenFields: DeclBetweenFields,
168168 InvalidAnd: InvalidAnd,
169169
170 pub fn render(self: *const Error, tokens: []const Token.Id, stream: var) !void {
170 pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void {
171171 switch (self.*) {
172172 .InvalidToken => |*x| return x.render(tokens, stream),
173173 .ExpectedContainerMembers => |*x| return x.render(tokens, stream),
......@@ -322,9 +322,9 @@ pub const Error = union(enum) {
322322 pub const ExpectedCall = struct {
323323 node: *Node,
324324
325 pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: var) !void {
326 return stream.print("expected " ++ @tagName(Node.Id.Call) ++ ", found {}", .{
327 @tagName(self.node.id),
325 pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void {
326 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {}", .{
327 @tagName(self.node.tag),
328328 });
329329 }
330330 };
......@@ -332,9 +332,9 @@ pub const Error = union(enum) {
332332 pub const ExpectedCallOrFnProto = struct {
333333 node: *Node,
334334
335 pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: var) !void {
336 return stream.print("expected " ++ @tagName(Node.Id.Call) ++ " or " ++
337 @tagName(Node.Id.FnProto) ++ ", found {}", .{@tagName(self.node.id)});
335 pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void {
336 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++
337 @tagName(Node.Tag.FnProto) ++ ", found {}", .{@tagName(self.node.tag)});
338338 }
339339 };
340340
......@@ -342,7 +342,7 @@ pub const Error = union(enum) {
342342 token: TokenIndex,
343343 expected_id: Token.Id,
344344
345 pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: var) !void {
345 pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: anytype) !void {
346346 const found_token = tokens[self.token];
347347 switch (found_token) {
348348 .Invalid => {
......@@ -360,7 +360,7 @@ pub const Error = union(enum) {
360360 token: TokenIndex,
361361 end_id: Token.Id,
362362
363 pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: var) !void {
363 pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void {
364364 const actual_token = tokens[self.token];
365365 return stream.print("expected ',' or '{}', found '{}'", .{
366366 self.end_id.symbol(),
......@@ -375,7 +375,7 @@ pub const Error = union(enum) {
375375
376376 token: TokenIndex,
377377
378 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: var) !void {
378 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
379379 const actual_token = tokens[self.token];
380380 return stream.print(msg, .{actual_token.symbol()});
381381 }
......@@ -388,7 +388,7 @@ pub const Error = union(enum) {
388388
389389 token: TokenIndex,
390390
391 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: var) !void {
391 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
392392 return stream.writeAll(msg);
393393 }
394394 };
......@@ -396,9 +396,9 @@ pub const Error = union(enum) {
396396};
397397
398398pub const Node = struct {
399 id: Id,
399 tag: Tag,
400400
401 pub const Id = enum {
401 pub const Tag = enum {
402402 // Top level
403403 Root,
404404 Use,
......@@ -408,9 +408,69 @@ pub const Node = struct {
408408 VarDecl,
409409 Defer,
410410
411 // Operators
412 InfixOp,
413 PrefixOp,
411 // Infix operators
412 Catch,
413
414 // SimpleInfixOp
415 Add,
416 AddWrap,
417 ArrayCat,
418 ArrayMult,
419 Assign,
420 AssignBitAnd,
421 AssignBitOr,
422 AssignBitShiftLeft,
423 AssignBitShiftRight,
424 AssignBitXor,
425 AssignDiv,
426 AssignSub,
427 AssignSubWrap,
428 AssignMod,
429 AssignAdd,
430 AssignAddWrap,
431 AssignMul,
432 AssignMulWrap,
433 BangEqual,
434 BitAnd,
435 BitOr,
436 BitShiftLeft,
437 BitShiftRight,
438 BitXor,
439 BoolAnd,
440 BoolOr,
441 Div,
442 EqualEqual,
443 ErrorUnion,
444 GreaterOrEqual,
445 GreaterThan,
446 LessOrEqual,
447 LessThan,
448 MergeErrorSets,
449 Mod,
450 Mul,
451 MulWrap,
452 Period,
453 Range,
454 Sub,
455 SubWrap,
456 UnwrapOptional,
457
458 // SimplePrefixOp
459 AddressOf,
460 Await,
461 BitNot,
462 BoolNot,
463 OptionalType,
464 Negation,
465 NegationWrap,
466 Resume,
467 Try,
468
469 ArrayType,
470 /// ArrayType but has a sentinel node.
471 ArrayTypeSentinel,
472 PtrType,
473 SliceType,
414474 /// Not all suffix operations are under this tag. To save memory, some
415475 /// suffix operations have dedicated Node tags.
416476 SuffixOp,
......@@ -434,7 +494,7 @@ pub const Node = struct {
434494 Suspend,
435495
436496 // Type expressions
437 VarType,
497 AnyType,
438498 ErrorType,
439499 FnProto,
440500 AnyFrameType,
......@@ -471,49 +531,177 @@ pub const Node = struct {
471531 ContainerField,
472532 ErrorTag,
473533 FieldInitializer,
534
535 pub fn Type(tag: Tag) type {
536 return switch (tag) {
537 .Root => Root,
538 .Use => Use,
539 .TestDecl => TestDecl,
540 .VarDecl => VarDecl,
541 .Defer => Defer,
542 .Catch => Catch,
543
544 .Add,
545 .AddWrap,
546 .ArrayCat,
547 .ArrayMult,
548 .Assign,
549 .AssignBitAnd,
550 .AssignBitOr,
551 .AssignBitShiftLeft,
552 .AssignBitShiftRight,
553 .AssignBitXor,
554 .AssignDiv,
555 .AssignSub,
556 .AssignSubWrap,
557 .AssignMod,
558 .AssignAdd,
559 .AssignAddWrap,
560 .AssignMul,
561 .AssignMulWrap,
562 .BangEqual,
563 .BitAnd,
564 .BitOr,
565 .BitShiftLeft,
566 .BitShiftRight,
567 .BitXor,
568 .BoolAnd,
569 .BoolOr,
570 .Div,
571 .EqualEqual,
572 .ErrorUnion,
573 .GreaterOrEqual,
574 .GreaterThan,
575 .LessOrEqual,
576 .LessThan,
577 .MergeErrorSets,
578 .Mod,
579 .Mul,
580 .MulWrap,
581 .Period,
582 .Range,
583 .Sub,
584 .SubWrap,
585 .UnwrapOptional,
586 => SimpleInfixOp,
587
588 .AddressOf,
589 .Await,
590 .BitNot,
591 .BoolNot,
592 .OptionalType,
593 .Negation,
594 .NegationWrap,
595 .Resume,
596 .Try,
597 => SimplePrefixOp,
598
599 .ArrayType => ArrayType,
600 .ArrayTypeSentinel => ArrayTypeSentinel,
601
602 .PtrType => PtrType,
603 .SliceType => SliceType,
604 .SuffixOp => SuffixOp,
605
606 .ArrayInitializer => ArrayInitializer,
607 .ArrayInitializerDot => ArrayInitializerDot,
608
609 .StructInitializer => StructInitializer,
610 .StructInitializerDot => StructInitializerDot,
611
612 .Call => Call,
613 .Switch => Switch,
614 .While => While,
615 .For => For,
616 .If => If,
617 .ControlFlowExpression => ControlFlowExpression,
618 .Suspend => Suspend,
619 .AnyType => AnyType,
620 .ErrorType => ErrorType,
621 .FnProto => FnProto,
622 .AnyFrameType => AnyFrameType,
623 .IntegerLiteral => IntegerLiteral,
624 .FloatLiteral => FloatLiteral,
625 .EnumLiteral => EnumLiteral,
626 .StringLiteral => StringLiteral,
627 .MultilineStringLiteral => MultilineStringLiteral,
628 .CharLiteral => CharLiteral,
629 .BoolLiteral => BoolLiteral,
630 .NullLiteral => NullLiteral,
631 .UndefinedLiteral => UndefinedLiteral,
632 .Unreachable => Unreachable,
633 .Identifier => Identifier,
634 .GroupedExpression => GroupedExpression,
635 .BuiltinCall => BuiltinCall,
636 .ErrorSetDecl => ErrorSetDecl,
637 .ContainerDecl => ContainerDecl,
638 .Asm => Asm,
639 .Comptime => Comptime,
640 .Nosuspend => Nosuspend,
641 .Block => Block,
642 .DocComment => DocComment,
643 .SwitchCase => SwitchCase,
644 .SwitchElse => SwitchElse,
645 .Else => Else,
646 .Payload => Payload,
647 .PointerPayload => PointerPayload,
648 .PointerIndexPayload => PointerIndexPayload,
649 .ContainerField => ContainerField,
650 .ErrorTag => ErrorTag,
651 .FieldInitializer => FieldInitializer,
652 };
653 }
474654 };
475655
656 /// Prefer `castTag` to this.
476657 pub fn cast(base: *Node, comptime T: type) ?*T {
477 if (base.id == comptime typeToId(T)) {
478 return @fieldParentPtr(T, "base", base);
658 if (std.meta.fieldInfo(T, "base").default_value) |default_base| {
659 return base.castTag(default_base.tag);
660 }
661 inline for (@typeInfo(Tag).Enum.fields) |field| {
662 const tag = @intToEnum(Tag, field.value);
663 if (base.tag == tag) {
664 if (T == tag.Type()) {
665 return @fieldParentPtr(T, "base", base);
666 }
667 return null;
668 }
669 }
670 unreachable;
671 }
672
673 pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() {
674 if (base.tag == tag) {
675 return @fieldParentPtr(tag.Type(), "base", base);
479676 }
480677 return null;
481678 }
482679
483680 pub fn iterate(base: *Node, index: usize) ?*Node {
484 inline for (@typeInfo(Id).Enum.fields) |f| {
485 if (base.id == @field(Id, f.name)) {
486 const T = @field(Node, f.name);
487 return @fieldParentPtr(T, "base", base).iterate(index);
681 inline for (@typeInfo(Tag).Enum.fields) |field| {
682 const tag = @intToEnum(Tag, field.value);
683 if (base.tag == tag) {
684 return @fieldParentPtr(tag.Type(), "base", base).iterate(index);
488685 }
489686 }
490687 unreachable;
491688 }
492689
493690 pub fn firstToken(base: *const Node) TokenIndex {
494 inline for (@typeInfo(Id).Enum.fields) |f| {
495 if (base.id == @field(Id, f.name)) {
496 const T = @field(Node, f.name);
497 return @fieldParentPtr(T, "base", base).firstToken();
691 inline for (@typeInfo(Tag).Enum.fields) |field| {
692 const tag = @intToEnum(Tag, field.value);
693 if (base.tag == tag) {
694 return @fieldParentPtr(tag.Type(), "base", base).firstToken();
498695 }
499696 }
500697 unreachable;
501698 }
502699
503700 pub fn lastToken(base: *const Node) TokenIndex {
504 inline for (@typeInfo(Id).Enum.fields) |f| {
505 if (base.id == @field(Id, f.name)) {
506 const T = @field(Node, f.name);
507 return @fieldParentPtr(T, "base", base).lastToken();
508 }
509 }
510 unreachable;
511 }
512
513 pub fn typeToId(comptime T: type) Id {
514 inline for (@typeInfo(Id).Enum.fields) |f| {
515 if (T == @field(Node, f.name)) {
516 return @field(Id, f.name);
701 inline for (@typeInfo(Tag).Enum.fields) |field| {
702 const tag = @intToEnum(Tag, field.value);
703 if (base.tag == tag) {
704 return @fieldParentPtr(tag.Type(), "base", base).lastToken();
517705 }
518706 }
519707 unreachable;
......@@ -522,7 +710,7 @@ pub const Node = struct {
522710 pub fn requireSemiColon(base: *const Node) bool {
523711 var n = base;
524712 while (true) {
525 switch (n.id) {
713 switch (n.tag) {
526714 .Root,
527715 .ContainerField,
528716 .Block,
......@@ -543,7 +731,7 @@ pub const Node = struct {
543731 continue;
544732 }
545733
546 return while_node.body.id != .Block;
734 return while_node.body.tag != .Block;
547735 },
548736 .For => {
549737 const for_node = @fieldParentPtr(For, "base", n);
......@@ -552,7 +740,7 @@ pub const Node = struct {
552740 continue;
553741 }
554742
555 return for_node.body.id != .Block;
743 return for_node.body.tag != .Block;
556744 },
557745 .If => {
558746 const if_node = @fieldParentPtr(If, "base", n);
......@@ -561,7 +749,7 @@ pub const Node = struct {
561749 continue;
562750 }
563751
564 return if_node.body.id != .Block;
752 return if_node.body.tag != .Block;
565753 },
566754 .Else => {
567755 const else_node = @fieldParentPtr(Else, "base", n);
......@@ -570,23 +758,23 @@ pub const Node = struct {
570758 },
571759 .Defer => {
572760 const defer_node = @fieldParentPtr(Defer, "base", n);
573 return defer_node.expr.id != .Block;
761 return defer_node.expr.tag != .Block;
574762 },
575763 .Comptime => {
576764 const comptime_node = @fieldParentPtr(Comptime, "base", n);
577 return comptime_node.expr.id != .Block;
765 return comptime_node.expr.tag != .Block;
578766 },
579767 .Suspend => {
580768 const suspend_node = @fieldParentPtr(Suspend, "base", n);
581769 if (suspend_node.body) |body| {
582 return body.id != .Block;
770 return body.tag != .Block;
583771 }
584772
585773 return true;
586774 },
587775 .Nosuspend => {
588776 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
589 return nosuspend_node.expr.id != .Block;
777 return nosuspend_node.expr.tag != .Block;
590778 },
591779 else => return true,
592780 }
......@@ -600,7 +788,7 @@ pub const Node = struct {
600788 std.debug.warn(" ", .{});
601789 }
602790 }
603 std.debug.warn("{}\n", .{@tagName(self.id)});
791 std.debug.warn("{}\n", .{@tagName(self.tag)});
604792
605793 var child_i: usize = 0;
606794 while (self.iterate(child_i)) |child| : (child_i += 1) {
......@@ -610,7 +798,7 @@ pub const Node = struct {
610798
611799 /// The decls data follows this struct in memory as an array of Node pointers.
612800 pub const Root = struct {
613 base: Node = Node{ .id = .Root },
801 base: Node = Node{ .tag = .Root },
614802 eof_token: TokenIndex,
615803 decls_len: NodeIndex,
616804
......@@ -662,42 +850,84 @@ pub const Node = struct {
662850 }
663851 };
664852
853 /// Trailed in memory by possibly many things, with each optional thing
854 /// determined by a bit in `trailer_flags`.
665855 pub const VarDecl = struct {
666 base: Node = Node{ .id = .VarDecl },
667 doc_comments: ?*DocComment,
668 visib_token: ?TokenIndex,
669 thread_local_token: ?TokenIndex,
670 name_token: TokenIndex,
671 eq_token: ?TokenIndex,
856 base: Node = Node{ .tag = .VarDecl },
857 trailer_flags: TrailerFlags,
672858 mut_token: TokenIndex,
673 comptime_token: ?TokenIndex,
674 extern_export_token: ?TokenIndex,
675 lib_name: ?*Node,
676 type_node: ?*Node,
677 align_node: ?*Node,
678 section_node: ?*Node,
679 init_node: ?*Node,
859 name_token: TokenIndex,
680860 semicolon_token: TokenIndex,
681861
862 pub const TrailerFlags = std.meta.TrailerFlags(struct {
863 doc_comments: *DocComment,
864 visib_token: TokenIndex,
865 thread_local_token: TokenIndex,
866 eq_token: TokenIndex,
867 comptime_token: TokenIndex,
868 extern_export_token: TokenIndex,
869 lib_name: *Node,
870 type_node: *Node,
871 align_node: *Node,
872 section_node: *Node,
873 init_node: *Node,
874 });
875
876 pub const RequiredFields = struct {
877 mut_token: TokenIndex,
878 name_token: TokenIndex,
879 semicolon_token: TokenIndex,
880 };
881
882 pub fn getTrailer(self: *const VarDecl, comptime name: []const u8) ?TrailerFlags.Field(name) {
883 const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(VarDecl);
884 return self.trailer_flags.get(trailers_start, name);
885 }
886
887 pub fn setTrailer(self: *VarDecl, comptime name: []const u8, value: TrailerFlags.Field(name)) void {
888 const trailers_start = @ptrCast([*]u8, self) + @sizeOf(VarDecl);
889 self.trailer_flags.set(trailers_start, name, value);
890 }
891
892 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: anytype) !*VarDecl {
893 const trailer_flags = TrailerFlags.init(trailers);
894 const bytes = try allocator.alignedAlloc(u8, @alignOf(VarDecl), sizeInBytes(trailer_flags));
895 const var_decl = @ptrCast(*VarDecl, bytes.ptr);
896 var_decl.* = .{
897 .trailer_flags = trailer_flags,
898 .mut_token = required.mut_token,
899 .name_token = required.name_token,
900 .semicolon_token = required.semicolon_token,
901 };
902 const trailers_start = bytes.ptr + @sizeOf(VarDecl);
903 trailer_flags.setMany(trailers_start, trailers);
904 return var_decl;
905 }
906
907 pub fn destroy(self: *VarDecl, allocator: *mem.Allocator) void {
908 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
909 allocator.free(bytes);
910 }
911
682912 pub fn iterate(self: *const VarDecl, index: usize) ?*Node {
683913 var i = index;
684914
685 if (self.type_node) |type_node| {
915 if (self.getTrailer("type_node")) |type_node| {
686916 if (i < 1) return type_node;
687917 i -= 1;
688918 }
689919
690 if (self.align_node) |align_node| {
920 if (self.getTrailer("align_node")) |align_node| {
691921 if (i < 1) return align_node;
692922 i -= 1;
693923 }
694924
695 if (self.section_node) |section_node| {
925 if (self.getTrailer("section_node")) |section_node| {
696926 if (i < 1) return section_node;
697927 i -= 1;
698928 }
699929
700 if (self.init_node) |init_node| {
930 if (self.getTrailer("init_node")) |init_node| {
701931 if (i < 1) return init_node;
702932 i -= 1;
703933 }
......@@ -706,21 +936,25 @@ pub const Node = struct {
706936 }
707937
708938 pub fn firstToken(self: *const VarDecl) TokenIndex {
709 if (self.visib_token) |visib_token| return visib_token;
710 if (self.thread_local_token) |thread_local_token| return thread_local_token;
711 if (self.comptime_token) |comptime_token| return comptime_token;
712 if (self.extern_export_token) |extern_export_token| return extern_export_token;
713 assert(self.lib_name == null);
939 if (self.getTrailer("visib_token")) |visib_token| return visib_token;
940 if (self.getTrailer("thread_local_token")) |thread_local_token| return thread_local_token;
941 if (self.getTrailer("comptime_token")) |comptime_token| return comptime_token;
942 if (self.getTrailer("extern_export_token")) |extern_export_token| return extern_export_token;
943 assert(self.getTrailer("lib_name") == null);
714944 return self.mut_token;
715945 }
716946
717947 pub fn lastToken(self: *const VarDecl) TokenIndex {
718948 return self.semicolon_token;
719949 }
950
951 fn sizeInBytes(trailer_flags: TrailerFlags) usize {
952 return @sizeOf(VarDecl) + trailer_flags.sizeInBytes();
953 }
720954 };
721955
722956 pub const Use = struct {
723 base: Node = Node{ .id = .Use },
957 base: Node = Node{ .tag = .Use },
724958 doc_comments: ?*DocComment,
725959 visib_token: ?TokenIndex,
726960 use_token: TokenIndex,
......@@ -747,7 +981,7 @@ pub const Node = struct {
747981 };
748982
749983 pub const ErrorSetDecl = struct {
750 base: Node = Node{ .id = .ErrorSetDecl },
984 base: Node = Node{ .tag = .ErrorSetDecl },
751985 error_token: TokenIndex,
752986 rbrace_token: TokenIndex,
753987 decls_len: NodeIndex,
......@@ -797,7 +1031,7 @@ pub const Node = struct {
7971031
7981032 /// The fields and decls Node pointers directly follow this struct in memory.
7991033 pub const ContainerDecl = struct {
800 base: Node = Node{ .id = .ContainerDecl },
1034 base: Node = Node{ .tag = .ContainerDecl },
8011035 kind_token: TokenIndex,
8021036 layout_token: ?TokenIndex,
8031037 lbrace_token: TokenIndex,
......@@ -866,7 +1100,7 @@ pub const Node = struct {
8661100 };
8671101
8681102 pub const ContainerField = struct {
869 base: Node = Node{ .id = .ContainerField },
1103 base: Node = Node{ .tag = .ContainerField },
8701104 doc_comments: ?*DocComment,
8711105 comptime_token: ?TokenIndex,
8721106 name_token: TokenIndex,
......@@ -917,7 +1151,7 @@ pub const Node = struct {
9171151 };
9181152
9191153 pub const ErrorTag = struct {
920 base: Node = Node{ .id = .ErrorTag },
1154 base: Node = Node{ .tag = .ErrorTag },
9211155 doc_comments: ?*DocComment,
9221156 name_token: TokenIndex,
9231157
......@@ -942,7 +1176,7 @@ pub const Node = struct {
9421176 };
9431177
9441178 pub const Identifier = struct {
945 base: Node = Node{ .id = .Identifier },
1179 base: Node = Node{ .tag = .Identifier },
9461180 token: TokenIndex,
9471181
9481182 pub fn iterate(self: *const Identifier, index: usize) ?*Node {
......@@ -959,23 +1193,34 @@ pub const Node = struct {
9591193 };
9601194
9611195 /// The params are directly after the FnProto in memory.
1196 /// Next, each optional thing determined by a bit in `trailer_flags`.
9621197 pub const FnProto = struct {
963 base: Node = Node{ .id = .FnProto },
964 doc_comments: ?*DocComment,
965 visib_token: ?TokenIndex,
1198 base: Node = Node{ .tag = .FnProto },
1199 trailer_flags: TrailerFlags,
9661200 fn_token: TokenIndex,
967 name_token: ?TokenIndex,
9681201 params_len: NodeIndex,
9691202 return_type: ReturnType,
970 var_args_token: ?TokenIndex,
971 extern_export_inline_token: ?TokenIndex,
972 body_node: ?*Node,
973 lib_name: ?*Node, // populated if this is an extern declaration
974 align_expr: ?*Node, // populated if align(A) is present
975 section_expr: ?*Node, // populated if linksection(A) is present
976 callconv_expr: ?*Node, // populated if callconv(A) is present
977 is_extern_prototype: bool = false, // TODO: Remove once extern fn rewriting is
978 is_async: bool = false, // TODO: remove once async fn rewriting is
1203
1204 pub const TrailerFlags = std.meta.TrailerFlags(struct {
1205 doc_comments: *DocComment,
1206 body_node: *Node,
1207 lib_name: *Node, // populated if this is an extern declaration
1208 align_expr: *Node, // populated if align(A) is present
1209 section_expr: *Node, // populated if linksection(A) is present
1210 callconv_expr: *Node, // populated if callconv(A) is present
1211 visib_token: TokenIndex,
1212 name_token: TokenIndex,
1213 var_args_token: TokenIndex,
1214 extern_export_inline_token: TokenIndex,
1215 is_extern_prototype: void, // TODO: Remove once extern fn rewriting is
1216 is_async: void, // TODO: remove once async fn rewriting is
1217 });
1218
1219 pub const RequiredFields = struct {
1220 fn_token: TokenIndex,
1221 params_len: NodeIndex,
1222 return_type: ReturnType,
1223 };
9791224
9801225 pub const ReturnType = union(enum) {
9811226 Explicit: *Node,
......@@ -991,8 +1236,7 @@ pub const Node = struct {
9911236 param_type: ParamType,
9921237
9931238 pub const ParamType = union(enum) {
994 var_type: *Node,
995 var_args: TokenIndex,
1239 any_type: *Node,
9961240 type_expr: *Node,
9971241 };
9981242
......@@ -1001,8 +1245,7 @@ pub const Node = struct {
10011245
10021246 if (i < 1) {
10031247 switch (self.param_type) {
1004 .var_args => return null,
1005 .var_type, .type_expr => |node| return node,
1248 .any_type, .type_expr => |node| return node,
10061249 }
10071250 }
10081251 i -= 1;
......@@ -1015,34 +1258,79 @@ pub const Node = struct {
10151258 if (self.noalias_token) |noalias_token| return noalias_token;
10161259 if (self.name_token) |name_token| return name_token;
10171260 switch (self.param_type) {
1018 .var_args => |tok| return tok,
1019 .var_type, .type_expr => |node| return node.firstToken(),
1261 .any_type, .type_expr => |node| return node.firstToken(),
10201262 }
10211263 }
10221264
10231265 pub fn lastToken(self: *const ParamDecl) TokenIndex {
10241266 switch (self.param_type) {
1025 .var_args => |tok| return tok,
1026 .var_type, .type_expr => |node| return node.lastToken(),
1267 .any_type, .type_expr => |node| return node.lastToken(),
10271268 }
10281269 }
10291270 };
10301271
1272 /// For debugging purposes.
1273 pub fn dump(self: *const FnProto) void {
1274 const trailers_start = @alignCast(
1275 @alignOf(ParamDecl),
1276 @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1277 );
1278 std.debug.print("{*} flags: {b} name_token: {} {*} params_len: {}\n", .{
1279 self,
1280 self.trailer_flags.bits,
1281 self.getTrailer("name_token"),
1282 self.trailer_flags.ptrConst(trailers_start, "name_token"),
1283 self.params_len,
1284 });
1285 }
1286
1287 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
1288 const trailers_start = @alignCast(
1289 @alignOf(ParamDecl),
1290 @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1291 );
1292 return self.trailer_flags.get(trailers_start, name);
1293 }
1294
1295 pub fn setTrailer(self: *FnProto, comptime name: []const u8, value: TrailerFlags.Field(name)) void {
1296 const trailers_start = @alignCast(
1297 @alignOf(ParamDecl),
1298 @ptrCast([*]u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1299 );
1300 self.trailer_flags.set(trailers_start, name, value);
1301 }
1302
10311303 /// After this the caller must initialize the params list.
1032 pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*FnProto {
1033 const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(params_len));
1034 return @ptrCast(*FnProto, bytes.ptr);
1304 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: anytype) !*FnProto {
1305 const trailer_flags = TrailerFlags.init(trailers);
1306 const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(
1307 required.params_len,
1308 trailer_flags,
1309 ));
1310 const fn_proto = @ptrCast(*FnProto, bytes.ptr);
1311 fn_proto.* = .{
1312 .trailer_flags = trailer_flags,
1313 .fn_token = required.fn_token,
1314 .params_len = required.params_len,
1315 .return_type = required.return_type,
1316 };
1317 const trailers_start = @alignCast(
1318 @alignOf(ParamDecl),
1319 bytes.ptr + @sizeOf(FnProto) + @sizeOf(ParamDecl) * required.params_len,
1320 );
1321 trailer_flags.setMany(trailers_start, trailers);
1322 return fn_proto;
10351323 }
10361324
1037 pub fn free(self: *FnProto, allocator: *mem.Allocator) void {
1038 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];
1325 pub fn destroy(self: *FnProto, allocator: *mem.Allocator) void {
1326 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len, self.trailer_flags)];
10391327 allocator.free(bytes);
10401328 }
10411329
10421330 pub fn iterate(self: *const FnProto, index: usize) ?*Node {
10431331 var i = index;
10441332
1045 if (self.lib_name) |lib_name| {
1333 if (self.getTrailer("lib_name")) |lib_name| {
10461334 if (i < 1) return lib_name;
10471335 i -= 1;
10481336 }
......@@ -1050,24 +1338,22 @@ pub const Node = struct {
10501338 const params_len: usize = if (self.params_len == 0)
10511339 0
10521340 else switch (self.paramsConst()[self.params_len - 1].param_type) {
1053 .var_type, .type_expr => self.params_len,
1054 .var_args => self.params_len - 1,
1341 .any_type, .type_expr => self.params_len,
10551342 };
10561343 if (i < params_len) {
10571344 switch (self.paramsConst()[i].param_type) {
1058 .var_type => |n| return n,
1059 .var_args => unreachable,
1345 .any_type => |n| return n,
10601346 .type_expr => |n| return n,
10611347 }
10621348 }
10631349 i -= params_len;
10641350
1065 if (self.align_expr) |align_expr| {
1351 if (self.getTrailer("align_expr")) |align_expr| {
10661352 if (i < 1) return align_expr;
10671353 i -= 1;
10681354 }
10691355
1070 if (self.section_expr) |section_expr| {
1356 if (self.getTrailer("section_expr")) |section_expr| {
10711357 if (i < 1) return section_expr;
10721358 i -= 1;
10731359 }
......@@ -1080,7 +1366,7 @@ pub const Node = struct {
10801366 .Invalid => {},
10811367 }
10821368
1083 if (self.body_node) |body_node| {
1369 if (self.getTrailer("body_node")) |body_node| {
10841370 if (i < 1) return body_node;
10851371 i -= 1;
10861372 }
......@@ -1089,14 +1375,14 @@ pub const Node = struct {
10891375 }
10901376
10911377 pub fn firstToken(self: *const FnProto) TokenIndex {
1092 if (self.visib_token) |visib_token| return visib_token;
1093 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
1094 assert(self.lib_name == null);
1378 if (self.getTrailer("visib_token")) |visib_token| return visib_token;
1379 if (self.getTrailer("extern_export_inline_token")) |extern_export_inline_token| return extern_export_inline_token;
1380 assert(self.getTrailer("lib_name") == null);
10951381 return self.fn_token;
10961382 }
10971383
10981384 pub fn lastToken(self: *const FnProto) TokenIndex {
1099 if (self.body_node) |body_node| return body_node.lastToken();
1385 if (self.getTrailer("body_node")) |body_node| return body_node.lastToken();
11001386 switch (self.return_type) {
11011387 .Explicit, .InferErrorSet => |node| return node.lastToken(),
11021388 .Invalid => |tok| return tok,
......@@ -1104,22 +1390,22 @@ pub const Node = struct {
11041390 }
11051391
11061392 pub fn params(self: *FnProto) []ParamDecl {
1107 const decls_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);
1108 return @ptrCast([*]ParamDecl, decls_start)[0..self.params_len];
1393 const params_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);
1394 return @ptrCast([*]ParamDecl, params_start)[0..self.params_len];
11091395 }
11101396
11111397 pub fn paramsConst(self: *const FnProto) []const ParamDecl {
1112 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);
1113 return @ptrCast([*]const ParamDecl, decls_start)[0..self.params_len];
1398 const params_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);
1399 return @ptrCast([*]const ParamDecl, params_start)[0..self.params_len];
11141400 }
11151401
1116 fn sizeInBytes(params_len: NodeIndex) usize {
1117 return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len);
1402 fn sizeInBytes(params_len: NodeIndex, trailer_flags: TrailerFlags) usize {
1403 return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len) + trailer_flags.sizeInBytes();
11181404 }
11191405 };
11201406
11211407 pub const AnyFrameType = struct {
1122 base: Node = Node{ .id = .AnyFrameType },
1408 base: Node = Node{ .tag = .AnyFrameType },
11231409 anyframe_token: TokenIndex,
11241410 result: ?Result,
11251411
......@@ -1151,7 +1437,7 @@ pub const Node = struct {
11511437
11521438 /// The statements of the block follow Block directly in memory.
11531439 pub const Block = struct {
1154 base: Node = Node{ .id = .Block },
1440 base: Node = Node{ .tag = .Block },
11551441 statements_len: NodeIndex,
11561442 lbrace: TokenIndex,
11571443 rbrace: TokenIndex,
......@@ -1205,7 +1491,7 @@ pub const Node = struct {
12051491 };
12061492
12071493 pub const Defer = struct {
1208 base: Node = Node{ .id = .Defer },
1494 base: Node = Node{ .tag = .Defer },
12091495 defer_token: TokenIndex,
12101496 payload: ?*Node,
12111497 expr: *Node,
......@@ -1229,7 +1515,7 @@ pub const Node = struct {
12291515 };
12301516
12311517 pub const Comptime = struct {
1232 base: Node = Node{ .id = .Comptime },
1518 base: Node = Node{ .tag = .Comptime },
12331519 doc_comments: ?*DocComment,
12341520 comptime_token: TokenIndex,
12351521 expr: *Node,
......@@ -1253,7 +1539,7 @@ pub const Node = struct {
12531539 };
12541540
12551541 pub const Nosuspend = struct {
1256 base: Node = Node{ .id = .Nosuspend },
1542 base: Node = Node{ .tag = .Nosuspend },
12571543 nosuspend_token: TokenIndex,
12581544 expr: *Node,
12591545
......@@ -1276,7 +1562,7 @@ pub const Node = struct {
12761562 };
12771563
12781564 pub const Payload = struct {
1279 base: Node = Node{ .id = .Payload },
1565 base: Node = Node{ .tag = .Payload },
12801566 lpipe: TokenIndex,
12811567 error_symbol: *Node,
12821568 rpipe: TokenIndex,
......@@ -1300,7 +1586,7 @@ pub const Node = struct {
13001586 };
13011587
13021588 pub const PointerPayload = struct {
1303 base: Node = Node{ .id = .PointerPayload },
1589 base: Node = Node{ .tag = .PointerPayload },
13041590 lpipe: TokenIndex,
13051591 ptr_token: ?TokenIndex,
13061592 value_symbol: *Node,
......@@ -1325,7 +1611,7 @@ pub const Node = struct {
13251611 };
13261612
13271613 pub const PointerIndexPayload = struct {
1328 base: Node = Node{ .id = .PointerIndexPayload },
1614 base: Node = Node{ .tag = .PointerIndexPayload },
13291615 lpipe: TokenIndex,
13301616 ptr_token: ?TokenIndex,
13311617 value_symbol: *Node,
......@@ -1356,7 +1642,7 @@ pub const Node = struct {
13561642 };
13571643
13581644 pub const Else = struct {
1359 base: Node = Node{ .id = .Else },
1645 base: Node = Node{ .tag = .Else },
13601646 else_token: TokenIndex,
13611647 payload: ?*Node,
13621648 body: *Node,
......@@ -1387,7 +1673,7 @@ pub const Node = struct {
13871673 /// The cases node pointers are found in memory after Switch.
13881674 /// They must be SwitchCase or SwitchElse nodes.
13891675 pub const Switch = struct {
1390 base: Node = Node{ .id = .Switch },
1676 base: Node = Node{ .tag = .Switch },
13911677 switch_token: TokenIndex,
13921678 rbrace: TokenIndex,
13931679 cases_len: NodeIndex,
......@@ -1441,7 +1727,7 @@ pub const Node = struct {
14411727
14421728 /// Items sub-nodes appear in memory directly following SwitchCase.
14431729 pub const SwitchCase = struct {
1444 base: Node = Node{ .id = .SwitchCase },
1730 base: Node = Node{ .tag = .SwitchCase },
14451731 arrow_token: TokenIndex,
14461732 payload: ?*Node,
14471733 expr: *Node,
......@@ -1499,7 +1785,7 @@ pub const Node = struct {
14991785 };
15001786
15011787 pub const SwitchElse = struct {
1502 base: Node = Node{ .id = .SwitchElse },
1788 base: Node = Node{ .tag = .SwitchElse },
15031789 token: TokenIndex,
15041790
15051791 pub fn iterate(self: *const SwitchElse, index: usize) ?*Node {
......@@ -1516,7 +1802,7 @@ pub const Node = struct {
15161802 };
15171803
15181804 pub const While = struct {
1519 base: Node = Node{ .id = .While },
1805 base: Node = Node{ .tag = .While },
15201806 label: ?TokenIndex,
15211807 inline_token: ?TokenIndex,
15221808 while_token: TokenIndex,
......@@ -1575,7 +1861,7 @@ pub const Node = struct {
15751861 };
15761862
15771863 pub const For = struct {
1578 base: Node = Node{ .id = .For },
1864 base: Node = Node{ .tag = .For },
15791865 label: ?TokenIndex,
15801866 inline_token: ?TokenIndex,
15811867 for_token: TokenIndex,
......@@ -1626,7 +1912,7 @@ pub const Node = struct {
16261912 };
16271913
16281914 pub const If = struct {
1629 base: Node = Node{ .id = .If },
1915 base: Node = Node{ .tag = .If },
16301916 if_token: TokenIndex,
16311917 condition: *Node,
16321918 payload: ?*Node,
......@@ -1668,116 +1954,22 @@ pub const Node = struct {
16681954 }
16691955 };
16701956
1671 pub const InfixOp = struct {
1672 base: Node = Node{ .id = .InfixOp },
1957 pub const Catch = struct {
1958 base: Node = Node{ .tag = .Catch },
16731959 op_token: TokenIndex,
16741960 lhs: *Node,
1675 op: Op,
16761961 rhs: *Node,
1962 payload: ?*Node,
16771963
1678 pub const Op = union(enum) {
1679 Add,
1680 AddWrap,
1681 ArrayCat,
1682 ArrayMult,
1683 Assign,
1684 AssignBitAnd,
1685 AssignBitOr,
1686 AssignBitShiftLeft,
1687 AssignBitShiftRight,
1688 AssignBitXor,
1689 AssignDiv,
1690 AssignSub,
1691 AssignSubWrap,
1692 AssignMod,
1693 AssignAdd,
1694 AssignAddWrap,
1695 AssignMul,
1696 AssignMulWrap,
1697 BangEqual,
1698 BitAnd,
1699 BitOr,
1700 BitShiftLeft,
1701 BitShiftRight,
1702 BitXor,
1703 BoolAnd,
1704 BoolOr,
1705 Catch: ?*Node,
1706 Div,
1707 EqualEqual,
1708 ErrorUnion,
1709 GreaterOrEqual,
1710 GreaterThan,
1711 LessOrEqual,
1712 LessThan,
1713 MergeErrorSets,
1714 Mod,
1715 Mul,
1716 MulWrap,
1717 Period,
1718 Range,
1719 Sub,
1720 SubWrap,
1721 UnwrapOptional,
1722 };
1723
1724 pub fn iterate(self: *const InfixOp, index: usize) ?*Node {
1964 pub fn iterate(self: *const Catch, index: usize) ?*Node {
17251965 var i = index;
17261966
17271967 if (i < 1) return self.lhs;
17281968 i -= 1;
17291969
1730 switch (self.op) {
1731 .Catch => |maybe_payload| {
1732 if (maybe_payload) |payload| {
1733 if (i < 1) return payload;
1734 i -= 1;
1735 }
1736 },
1737
1738 .Add,
1739 .AddWrap,
1740 .ArrayCat,
1741 .ArrayMult,
1742 .Assign,
1743 .AssignBitAnd,
1744 .AssignBitOr,
1745 .AssignBitShiftLeft,
1746 .AssignBitShiftRight,
1747 .AssignBitXor,
1748 .AssignDiv,
1749 .AssignSub,
1750 .AssignSubWrap,
1751 .AssignMod,
1752 .AssignAdd,
1753 .AssignAddWrap,
1754 .AssignMul,
1755 .AssignMulWrap,
1756 .BangEqual,
1757 .BitAnd,
1758 .BitOr,
1759 .BitShiftLeft,
1760 .BitShiftRight,
1761 .BitXor,
1762 .BoolAnd,
1763 .BoolOr,
1764 .Div,
1765 .EqualEqual,
1766 .ErrorUnion,
1767 .GreaterOrEqual,
1768 .GreaterThan,
1769 .LessOrEqual,
1770 .LessThan,
1771 .MergeErrorSets,
1772 .Mod,
1773 .Mul,
1774 .MulWrap,
1775 .Period,
1776 .Range,
1777 .Sub,
1778 .SubWrap,
1779 .UnwrapOptional,
1780 => {},
1970 if (self.payload) |payload| {
1971 if (i < 1) return payload;
1972 i -= 1;
17811973 }
17821974
17831975 if (i < 1) return self.rhs;
......@@ -1786,94 +1978,140 @@ pub const Node = struct {
17861978 return null;
17871979 }
17881980
1789 pub fn firstToken(self: *const InfixOp) TokenIndex {
1981 pub fn firstToken(self: *const Catch) TokenIndex {
17901982 return self.lhs.firstToken();
17911983 }
17921984
1793 pub fn lastToken(self: *const InfixOp) TokenIndex {
1985 pub fn lastToken(self: *const Catch) TokenIndex {
17941986 return self.rhs.lastToken();
17951987 }
17961988 };
17971989
1798 pub const PrefixOp = struct {
1799 base: Node = Node{ .id = .PrefixOp },
1990 pub const SimpleInfixOp = struct {
1991 base: Node,
18001992 op_token: TokenIndex,
1801 op: Op,
1993 lhs: *Node,
18021994 rhs: *Node,
18031995
1804 pub const Op = union(enum) {
1805 AddressOf,
1806 ArrayType: ArrayInfo,
1807 Await,
1808 BitNot,
1809 BoolNot,
1810 OptionalType,
1811 Negation,
1812 NegationWrap,
1813 Resume,
1814 PtrType: PtrInfo,
1815 SliceType: PtrInfo,
1816 Try,
1817 };
1996 pub fn iterate(self: *const SimpleInfixOp, index: usize) ?*Node {
1997 var i = index;
18181998
1819 pub const ArrayInfo = struct {
1820 len_expr: *Node,
1821 sentinel: ?*Node,
1822 };
1999 if (i < 1) return self.lhs;
2000 i -= 1;
18232001
1824 pub const PtrInfo = struct {
1825 allowzero_token: ?TokenIndex = null,
1826 align_info: ?Align = null,
1827 const_token: ?TokenIndex = null,
1828 volatile_token: ?TokenIndex = null,
1829 sentinel: ?*Node = null,
1830
1831 pub const Align = struct {
1832 node: *Node,
1833 bit_range: ?BitRange,
1834
1835 pub const BitRange = struct {
1836 start: *Node,
1837 end: *Node,
1838 };
1839 };
1840 };
2002 if (i < 1) return self.rhs;
2003 i -= 1;
2004
2005 return null;
2006 }
2007
2008 pub fn firstToken(self: *const SimpleInfixOp) TokenIndex {
2009 return self.lhs.firstToken();
2010 }
2011
2012 pub fn lastToken(self: *const SimpleInfixOp) TokenIndex {
2013 return self.rhs.lastToken();
2014 }
2015 };
2016
2017 pub const SimplePrefixOp = struct {
2018 base: Node,
2019 op_token: TokenIndex,
2020 rhs: *Node,
2021
2022 const Self = @This();
2023
2024 pub fn iterate(self: *const Self, index: usize) ?*Node {
2025 if (index == 0) return self.rhs;
2026 return null;
2027 }
18412028
1842 pub fn iterate(self: *const PrefixOp, index: usize) ?*Node {
2029 pub fn firstToken(self: *const Self) TokenIndex {
2030 return self.op_token;
2031 }
2032
2033 pub fn lastToken(self: *const Self) TokenIndex {
2034 return self.rhs.lastToken();
2035 }
2036 };
2037
2038 pub const ArrayType = struct {
2039 base: Node = Node{ .tag = .ArrayType },
2040 op_token: TokenIndex,
2041 rhs: *Node,
2042 len_expr: *Node,
2043
2044 pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
18432045 var i = index;
18442046
1845 switch (self.op) {
1846 .PtrType, .SliceType => |addr_of_info| {
1847 if (addr_of_info.sentinel) |sentinel| {
1848 if (i < 1) return sentinel;
1849 i -= 1;
1850 }
2047 if (i < 1) return self.len_expr;
2048 i -= 1;
18512049
1852 if (addr_of_info.align_info) |align_info| {
1853 if (i < 1) return align_info.node;
1854 i -= 1;
1855 }
1856 },
2050 if (i < 1) return self.rhs;
2051 i -= 1;
18572052
1858 .ArrayType => |array_info| {
1859 if (i < 1) return array_info.len_expr;
1860 i -= 1;
1861 if (array_info.sentinel) |sentinel| {
1862 if (i < 1) return sentinel;
1863 i -= 1;
1864 }
1865 },
2053 return null;
2054 }
18662055
1867 .AddressOf,
1868 .Await,
1869 .BitNot,
1870 .BoolNot,
1871 .OptionalType,
1872 .Negation,
1873 .NegationWrap,
1874 .Try,
1875 .Resume,
1876 => {},
2056 pub fn firstToken(self: *const ArrayType) TokenIndex {
2057 return self.op_token;
2058 }
2059
2060 pub fn lastToken(self: *const ArrayType) TokenIndex {
2061 return self.rhs.lastToken();
2062 }
2063 };
2064
2065 pub const ArrayTypeSentinel = struct {
2066 base: Node = Node{ .tag = .ArrayTypeSentinel },
2067 op_token: TokenIndex,
2068 rhs: *Node,
2069 len_expr: *Node,
2070 sentinel: *Node,
2071
2072 pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node {
2073 var i = index;
2074
2075 if (i < 1) return self.len_expr;
2076 i -= 1;
2077
2078 if (i < 1) return self.sentinel;
2079 i -= 1;
2080
2081 if (i < 1) return self.rhs;
2082 i -= 1;
2083
2084 return null;
2085 }
2086
2087 pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
2088 return self.op_token;
2089 }
2090
2091 pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
2092 return self.rhs.lastToken();
2093 }
2094 };
2095
2096 pub const PtrType = struct {
2097 base: Node = Node{ .tag = .PtrType },
2098 op_token: TokenIndex,
2099 rhs: *Node,
2100 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
2101 /// one of these possibly-null things. Then we have them directly follow the PtrType in memory.
2102 ptr_info: PtrInfo = .{},
2103
2104 pub fn iterate(self: *const PtrType, index: usize) ?*Node {
2105 var i = index;
2106
2107 if (self.ptr_info.sentinel) |sentinel| {
2108 if (i < 1) return sentinel;
2109 i -= 1;
2110 }
2111
2112 if (self.ptr_info.align_info) |align_info| {
2113 if (i < 1) return align_info.node;
2114 i -= 1;
18772115 }
18782116
18792117 if (i < 1) return self.rhs;
......@@ -1882,17 +2120,53 @@ pub const Node = struct {
18822120 return null;
18832121 }
18842122
1885 pub fn firstToken(self: *const PrefixOp) TokenIndex {
2123 pub fn firstToken(self: *const PtrType) TokenIndex {
18862124 return self.op_token;
18872125 }
18882126
1889 pub fn lastToken(self: *const PrefixOp) TokenIndex {
2127 pub fn lastToken(self: *const PtrType) TokenIndex {
2128 return self.rhs.lastToken();
2129 }
2130 };
2131
2132 pub const SliceType = struct {
2133 base: Node = Node{ .tag = .SliceType },
2134 op_token: TokenIndex,
2135 rhs: *Node,
2136 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
2137 /// one of these possibly-null things. Then we have them directly follow the SliceType in memory.
2138 ptr_info: PtrInfo = .{},
2139
2140 pub fn iterate(self: *const SliceType, index: usize) ?*Node {
2141 var i = index;
2142
2143 if (self.ptr_info.sentinel) |sentinel| {
2144 if (i < 1) return sentinel;
2145 i -= 1;
2146 }
2147
2148 if (self.ptr_info.align_info) |align_info| {
2149 if (i < 1) return align_info.node;
2150 i -= 1;
2151 }
2152
2153 if (i < 1) return self.rhs;
2154 i -= 1;
2155
2156 return null;
2157 }
2158
2159 pub fn firstToken(self: *const SliceType) TokenIndex {
2160 return self.op_token;
2161 }
2162
2163 pub fn lastToken(self: *const SliceType) TokenIndex {
18902164 return self.rhs.lastToken();
18912165 }
18922166 };
18932167
18942168 pub const FieldInitializer = struct {
1895 base: Node = Node{ .id = .FieldInitializer },
2169 base: Node = Node{ .tag = .FieldInitializer },
18962170 period_token: TokenIndex,
18972171 name_token: TokenIndex,
18982172 expr: *Node,
......@@ -1917,7 +2191,7 @@ pub const Node = struct {
19172191
19182192 /// Elements occur directly in memory after ArrayInitializer.
19192193 pub const ArrayInitializer = struct {
1920 base: Node = Node{ .id = .ArrayInitializer },
2194 base: Node = Node{ .tag = .ArrayInitializer },
19212195 rtoken: TokenIndex,
19222196 list_len: NodeIndex,
19232197 lhs: *Node,
......@@ -1970,7 +2244,7 @@ pub const Node = struct {
19702244
19712245 /// Elements occur directly in memory after ArrayInitializerDot.
19722246 pub const ArrayInitializerDot = struct {
1973 base: Node = Node{ .id = .ArrayInitializerDot },
2247 base: Node = Node{ .tag = .ArrayInitializerDot },
19742248 dot: TokenIndex,
19752249 rtoken: TokenIndex,
19762250 list_len: NodeIndex,
......@@ -2020,7 +2294,7 @@ pub const Node = struct {
20202294
20212295 /// Elements occur directly in memory after StructInitializer.
20222296 pub const StructInitializer = struct {
2023 base: Node = Node{ .id = .StructInitializer },
2297 base: Node = Node{ .tag = .StructInitializer },
20242298 rtoken: TokenIndex,
20252299 list_len: NodeIndex,
20262300 lhs: *Node,
......@@ -2073,7 +2347,7 @@ pub const Node = struct {
20732347
20742348 /// Elements occur directly in memory after StructInitializerDot.
20752349 pub const StructInitializerDot = struct {
2076 base: Node = Node{ .id = .StructInitializerDot },
2350 base: Node = Node{ .tag = .StructInitializerDot },
20772351 dot: TokenIndex,
20782352 rtoken: TokenIndex,
20792353 list_len: NodeIndex,
......@@ -2123,7 +2397,7 @@ pub const Node = struct {
21232397
21242398 /// Parameter nodes directly follow Call in memory.
21252399 pub const Call = struct {
2126 base: Node = Node{ .id = .Call },
2400 base: Node = Node{ .tag = .Call },
21272401 lhs: *Node,
21282402 rtoken: TokenIndex,
21292403 params_len: NodeIndex,
......@@ -2177,7 +2451,7 @@ pub const Node = struct {
21772451 };
21782452
21792453 pub const SuffixOp = struct {
2180 base: Node = Node{ .id = .SuffixOp },
2454 base: Node = Node{ .tag = .SuffixOp },
21812455 op: Op,
21822456 lhs: *Node,
21832457 rtoken: TokenIndex,
......@@ -2237,7 +2511,7 @@ pub const Node = struct {
22372511 };
22382512
22392513 pub const GroupedExpression = struct {
2240 base: Node = Node{ .id = .GroupedExpression },
2514 base: Node = Node{ .tag = .GroupedExpression },
22412515 lparen: TokenIndex,
22422516 expr: *Node,
22432517 rparen: TokenIndex,
......@@ -2260,8 +2534,10 @@ pub const Node = struct {
22602534 }
22612535 };
22622536
2537 /// TODO break this into separate Break, Continue, Return AST Nodes to save memory.
2538 /// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more.
22632539 pub const ControlFlowExpression = struct {
2264 base: Node = Node{ .id = .ControlFlowExpression },
2540 base: Node = Node{ .tag = .ControlFlowExpression },
22652541 ltoken: TokenIndex,
22662542 kind: Kind,
22672543 rhs: ?*Node,
......@@ -2316,7 +2592,7 @@ pub const Node = struct {
23162592 };
23172593
23182594 pub const Suspend = struct {
2319 base: Node = Node{ .id = .Suspend },
2595 base: Node = Node{ .tag = .Suspend },
23202596 suspend_token: TokenIndex,
23212597 body: ?*Node,
23222598
......@@ -2345,7 +2621,7 @@ pub const Node = struct {
23452621 };
23462622
23472623 pub const IntegerLiteral = struct {
2348 base: Node = Node{ .id = .IntegerLiteral },
2624 base: Node = Node{ .tag = .IntegerLiteral },
23492625 token: TokenIndex,
23502626
23512627 pub fn iterate(self: *const IntegerLiteral, index: usize) ?*Node {
......@@ -2362,7 +2638,7 @@ pub const Node = struct {
23622638 };
23632639
23642640 pub const EnumLiteral = struct {
2365 base: Node = Node{ .id = .EnumLiteral },
2641 base: Node = Node{ .tag = .EnumLiteral },
23662642 dot: TokenIndex,
23672643 name: TokenIndex,
23682644
......@@ -2380,7 +2656,7 @@ pub const Node = struct {
23802656 };
23812657
23822658 pub const FloatLiteral = struct {
2383 base: Node = Node{ .id = .FloatLiteral },
2659 base: Node = Node{ .tag = .FloatLiteral },
23842660 token: TokenIndex,
23852661
23862662 pub fn iterate(self: *const FloatLiteral, index: usize) ?*Node {
......@@ -2398,7 +2674,7 @@ pub const Node = struct {
23982674
23992675 /// Parameters are in memory following BuiltinCall.
24002676 pub const BuiltinCall = struct {
2401 base: Node = Node{ .id = .BuiltinCall },
2677 base: Node = Node{ .tag = .BuiltinCall },
24022678 params_len: NodeIndex,
24032679 builtin_token: TokenIndex,
24042680 rparen_token: TokenIndex,
......@@ -2447,7 +2723,7 @@ pub const Node = struct {
24472723 };
24482724
24492725 pub const StringLiteral = struct {
2450 base: Node = Node{ .id = .StringLiteral },
2726 base: Node = Node{ .tag = .StringLiteral },
24512727 token: TokenIndex,
24522728
24532729 pub fn iterate(self: *const StringLiteral, index: usize) ?*Node {
......@@ -2465,7 +2741,7 @@ pub const Node = struct {
24652741
24662742 /// The string literal tokens appear directly in memory after MultilineStringLiteral.
24672743 pub const MultilineStringLiteral = struct {
2468 base: Node = Node{ .id = .MultilineStringLiteral },
2744 base: Node = Node{ .tag = .MultilineStringLiteral },
24692745 lines_len: TokenIndex,
24702746
24712747 /// After this the caller must initialize the lines list.
......@@ -2507,7 +2783,7 @@ pub const Node = struct {
25072783 };
25082784
25092785 pub const CharLiteral = struct {
2510 base: Node = Node{ .id = .CharLiteral },
2786 base: Node = Node{ .tag = .CharLiteral },
25112787 token: TokenIndex,
25122788
25132789 pub fn iterate(self: *const CharLiteral, index: usize) ?*Node {
......@@ -2524,7 +2800,7 @@ pub const Node = struct {
25242800 };
25252801
25262802 pub const BoolLiteral = struct {
2527 base: Node = Node{ .id = .BoolLiteral },
2803 base: Node = Node{ .tag = .BoolLiteral },
25282804 token: TokenIndex,
25292805
25302806 pub fn iterate(self: *const BoolLiteral, index: usize) ?*Node {
......@@ -2541,7 +2817,7 @@ pub const Node = struct {
25412817 };
25422818
25432819 pub const NullLiteral = struct {
2544 base: Node = Node{ .id = .NullLiteral },
2820 base: Node = Node{ .tag = .NullLiteral },
25452821 token: TokenIndex,
25462822
25472823 pub fn iterate(self: *const NullLiteral, index: usize) ?*Node {
......@@ -2558,7 +2834,7 @@ pub const Node = struct {
25582834 };
25592835
25602836 pub const UndefinedLiteral = struct {
2561 base: Node = Node{ .id = .UndefinedLiteral },
2837 base: Node = Node{ .tag = .UndefinedLiteral },
25622838 token: TokenIndex,
25632839
25642840 pub fn iterate(self: *const UndefinedLiteral, index: usize) ?*Node {
......@@ -2575,7 +2851,7 @@ pub const Node = struct {
25752851 };
25762852
25772853 pub const Asm = struct {
2578 base: Node = Node{ .id = .Asm },
2854 base: Node = Node{ .tag = .Asm },
25792855 asm_token: TokenIndex,
25802856 rparen: TokenIndex,
25812857 volatile_token: ?TokenIndex,
......@@ -2695,7 +2971,7 @@ pub const Node = struct {
26952971 };
26962972
26972973 pub const Unreachable = struct {
2698 base: Node = Node{ .id = .Unreachable },
2974 base: Node = Node{ .tag = .Unreachable },
26992975 token: TokenIndex,
27002976
27012977 pub fn iterate(self: *const Unreachable, index: usize) ?*Node {
......@@ -2712,7 +2988,7 @@ pub const Node = struct {
27122988 };
27132989
27142990 pub const ErrorType = struct {
2715 base: Node = Node{ .id = .ErrorType },
2991 base: Node = Node{ .tag = .ErrorType },
27162992 token: TokenIndex,
27172993
27182994 pub fn iterate(self: *const ErrorType, index: usize) ?*Node {
......@@ -2728,25 +3004,28 @@ pub const Node = struct {
27283004 }
27293005 };
27303006
2731 pub const VarType = struct {
2732 base: Node = Node{ .id = .VarType },
3007 pub const AnyType = struct {
3008 base: Node = Node{ .tag = .AnyType },
27333009 token: TokenIndex,
27343010
2735 pub fn iterate(self: *const VarType, index: usize) ?*Node {
3011 pub fn iterate(self: *const AnyType, index: usize) ?*Node {
27363012 return null;
27373013 }
27383014
2739 pub fn firstToken(self: *const VarType) TokenIndex {
3015 pub fn firstToken(self: *const AnyType) TokenIndex {
27403016 return self.token;
27413017 }
27423018
2743 pub fn lastToken(self: *const VarType) TokenIndex {
3019 pub fn lastToken(self: *const AnyType) TokenIndex {
27443020 return self.token;
27453021 }
27463022 };
27473023
3024 /// TODO remove from the Node base struct
3025 /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()
3026 /// and forwards to find same-line doc comments.
27483027 pub const DocComment = struct {
2749 base: Node = Node{ .id = .DocComment },
3028 base: Node = Node{ .tag = .DocComment },
27503029 /// Points to the first doc comment token. API users are expected to iterate over the
27513030 /// tokens array, looking for more doc comments, ignoring line comments, and stopping
27523031 /// at the first other token.
......@@ -2768,7 +3047,7 @@ pub const Node = struct {
27683047 };
27693048
27703049 pub const TestDecl = struct {
2771 base: Node = Node{ .id = .TestDecl },
3050 base: Node = Node{ .tag = .TestDecl },
27723051 doc_comments: ?*DocComment,
27733052 test_token: TokenIndex,
27743053 name: *Node,
......@@ -2793,9 +3072,27 @@ pub const Node = struct {
27933072 };
27943073};
27953074
3075pub const PtrInfo = struct {
3076 allowzero_token: ?TokenIndex = null,
3077 align_info: ?Align = null,
3078 const_token: ?TokenIndex = null,
3079 volatile_token: ?TokenIndex = null,
3080 sentinel: ?*Node = null,
3081
3082 pub const Align = struct {
3083 node: *Node,
3084 bit_range: ?BitRange = null,
3085
3086 pub const BitRange = struct {
3087 start: *Node,
3088 end: *Node,
3089 };
3090 };
3091};
3092
27963093test "iterate" {
27973094 var root = Node.Root{
2798 .base = Node{ .id = Node.Id.Root },
3095 .base = Node{ .tag = Node.Tag.Root },
27993096 .decls_len = 0,
28003097 .eof_token = 0,
28013098 };
lib/std/zig/cross_target.zig+3-3
......@@ -497,7 +497,7 @@ pub const CrossTarget = struct {
497497
498498 pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![]u8 {
499499 if (self.isNative()) {
500 return mem.dupe(allocator, u8, "native");
500 return allocator.dupe(u8, "native");
501501 }
502502
503503 const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
......@@ -514,14 +514,14 @@ pub const CrossTarget = struct {
514514 switch (self.getOsVersionMin()) {
515515 .none => {},
516516 .semver => |v| try result.outStream().print(".{}", .{v}),
517 .windows => |v| try result.outStream().print(".{}", .{@tagName(v)}),
517 .windows => |v| try result.outStream().print("{s}", .{v}),
518518 }
519519 }
520520 if (self.os_version_max) |max| {
521521 switch (max) {
522522 .none => {},
523523 .semver => |v| try result.outStream().print("...{}", .{v}),
524 .windows => |v| try result.outStream().print("...{}", .{@tagName(v)}),
524 .windows => |v| try result.outStream().print("..{s}", .{v}),
525525 }
526526 }
527527
lib/std/zig/parse.zig+415-255
......@@ -150,7 +150,7 @@ const Parser = struct {
150150
151151 const visib_token = p.eatToken(.Keyword_pub);
152152
153 if (p.parseTopLevelDecl() catch |err| switch (err) {
153 if (p.parseTopLevelDecl(doc_comments, visib_token) catch |err| switch (err) {
154154 error.OutOfMemory => return error.OutOfMemory,
155155 error.ParseError => {
156156 p.findNextContainerMember();
......@@ -160,30 +160,7 @@ const Parser = struct {
160160 if (field_state == .seen) {
161161 field_state = .{ .end = visib_token orelse node.firstToken() };
162162 }
163 switch (node.id) {
164 .FnProto => {
165 node.cast(Node.FnProto).?.doc_comments = doc_comments;
166 node.cast(Node.FnProto).?.visib_token = visib_token;
167 },
168 .VarDecl => {
169 node.cast(Node.VarDecl).?.doc_comments = doc_comments;
170 node.cast(Node.VarDecl).?.visib_token = visib_token;
171 },
172 .Use => {
173 node.cast(Node.Use).?.doc_comments = doc_comments;
174 node.cast(Node.Use).?.visib_token = visib_token;
175 },
176 else => unreachable,
177 }
178163 try list.append(node);
179 if (try p.parseAppendedDocComment(node.lastToken())) |appended_comment| {
180 switch (node.id) {
181 .FnProto => {},
182 .VarDecl => node.cast(Node.VarDecl).?.doc_comments = appended_comment,
183 .Use => node.cast(Node.Use).?.doc_comments = appended_comment,
184 else => unreachable,
185 }
186 }
187164 continue;
188165 }
189166
......@@ -417,7 +394,7 @@ const Parser = struct {
417394 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
418395 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
419396 /// / KEYWORD_usingnamespace Expr SEMICOLON
420 fn parseTopLevelDecl(p: *Parser) !?*Node {
397 fn parseTopLevelDecl(p: *Parser, doc_comments: ?*Node.DocComment, visib_token: ?TokenIndex) !?*Node {
421398 var lib_name: ?*Node = null;
422399 const extern_export_inline_token = blk: {
423400 if (p.eatToken(.Keyword_export)) |token| break :blk token;
......@@ -430,20 +407,12 @@ const Parser = struct {
430407 break :blk null;
431408 };
432409
433 if (try p.parseFnProto()) |node| {
434 const fn_node = node.cast(Node.FnProto).?;
435 fn_node.*.extern_export_inline_token = extern_export_inline_token;
436 fn_node.*.lib_name = lib_name;
437 if (p.eatToken(.Semicolon)) |_| return node;
438
439 if (try p.expectNodeRecoverable(parseBlock, .{
440 // since parseBlock only return error.ParseError on
441 // a missing '}' we can assume this function was
442 // supposed to end here.
443 .ExpectedSemiOrLBrace = .{ .token = p.tok_i },
444 })) |body_node| {
445 fn_node.body_node = body_node;
446 }
410 if (try p.parseFnProto(.top_level, .{
411 .doc_comments = doc_comments,
412 .visib_token = visib_token,
413 .extern_export_inline_token = extern_export_inline_token,
414 .lib_name = lib_name,
415 })) |node| {
447416 return node;
448417 }
449418
......@@ -460,12 +429,13 @@ const Parser = struct {
460429
461430 const thread_local_token = p.eatToken(.Keyword_threadlocal);
462431
463 if (try p.parseVarDecl()) |node| {
464 var var_decl = node.cast(Node.VarDecl).?;
465 var_decl.*.thread_local_token = thread_local_token;
466 var_decl.*.comptime_token = null;
467 var_decl.*.extern_export_token = extern_export_inline_token;
468 var_decl.*.lib_name = lib_name;
432 if (try p.parseVarDecl(.{
433 .doc_comments = doc_comments,
434 .visib_token = visib_token,
435 .thread_local_token = thread_local_token,
436 .extern_export_token = extern_export_inline_token,
437 .lib_name = lib_name,
438 })) |node| {
469439 return node;
470440 }
471441
......@@ -485,21 +455,41 @@ const Parser = struct {
485455 return error.ParseError;
486456 }
487457
488 return p.parseUse();
458 const use_token = p.eatToken(.Keyword_usingnamespace) orelse return null;
459 const expr = try p.expectNode(parseExpr, .{
460 .ExpectedExpr = .{ .token = p.tok_i },
461 });
462 const semicolon_token = try p.expectToken(.Semicolon);
463
464 const node = try p.arena.allocator.create(Node.Use);
465 node.* = .{
466 .doc_comments = doc_comments orelse try p.parseAppendedDocComment(semicolon_token),
467 .visib_token = visib_token,
468 .use_token = use_token,
469 .expr = expr,
470 .semicolon_token = semicolon_token,
471 };
472
473 return &node.base;
489474 }
490475
491 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
492 fn parseFnProto(p: *Parser) !?*Node {
476 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
477 fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct {
478 doc_comments: ?*Node.DocComment = null,
479 visib_token: ?TokenIndex = null,
480 extern_export_inline_token: ?TokenIndex = null,
481 lib_name: ?*Node = null,
482 }) !?*Node {
493483 // TODO: Remove once extern/async fn rewriting is
494 var is_async = false;
495 var is_extern = false;
484 var is_async: ?void = null;
485 var is_extern_prototype: ?void = null;
496486 const cc_token: ?TokenIndex = blk: {
497487 if (p.eatToken(.Keyword_extern)) |token| {
498 is_extern = true;
488 is_extern_prototype = {};
499489 break :blk token;
500490 }
501491 if (p.eatToken(.Keyword_async)) |token| {
502 is_async = true;
492 is_async = {};
503493 break :blk token;
504494 }
505495 break :blk null;
......@@ -513,13 +503,14 @@ const Parser = struct {
513503 const lparen = try p.expectToken(.LParen);
514504 const params = try p.parseParamDeclList();
515505 defer p.gpa.free(params);
506 const var_args_token = p.eatToken(.Ellipsis3);
516507 const rparen = try p.expectToken(.RParen);
517508 const align_expr = try p.parseByteAlign();
518509 const section_expr = try p.parseLinkSection();
519510 const callconv_expr = try p.parseCallconv();
520511 const exclamation_token = p.eatToken(.Bang);
521512
522 const return_type_expr = (try p.parseVarType()) orelse
513 const return_type_expr = (try p.parseAnyType()) orelse
523514 try p.expectNodeRecoverable(parseTypeExpr, .{
524515 // most likely the user forgot to specify the return type.
525516 // Mark return type as invalid and try to continue.
......@@ -535,37 +526,53 @@ const Parser = struct {
535526 else
536527 R{ .Explicit = return_type_expr.? };
537528
538 const var_args_token = if (params.len > 0) blk: {
539 const param_type = params[params.len - 1].param_type;
540 break :blk if (param_type == .var_args) param_type.var_args else null;
541 } else
542 null;
529 const body_node: ?*Node = switch (level) {
530 .top_level => blk: {
531 if (p.eatToken(.Semicolon)) |_| {
532 break :blk null;
533 }
534 break :blk try p.expectNodeRecoverable(parseBlock, .{
535 // Since parseBlock only return error.ParseError on
536 // a missing '}' we can assume this function was
537 // supposed to end here.
538 .ExpectedSemiOrLBrace = .{ .token = p.tok_i },
539 });
540 },
541 .as_type => null,
542 };
543543
544 const fn_proto_node = try Node.FnProto.alloc(&p.arena.allocator, params.len);
545 fn_proto_node.* = .{
546 .doc_comments = null,
547 .visib_token = null,
548 .fn_token = fn_token,
549 .name_token = name_token,
544 const fn_proto_node = try Node.FnProto.create(&p.arena.allocator, .{
550545 .params_len = params.len,
546 .fn_token = fn_token,
551547 .return_type = return_type,
548 }, .{
549 .doc_comments = fields.doc_comments,
550 .visib_token = fields.visib_token,
551 .name_token = name_token,
552552 .var_args_token = var_args_token,
553 .extern_export_inline_token = null,
554 .body_node = null,
555 .lib_name = null,
553 .extern_export_inline_token = fields.extern_export_inline_token,
554 .body_node = body_node,
555 .lib_name = fields.lib_name,
556556 .align_expr = align_expr,
557557 .section_expr = section_expr,
558558 .callconv_expr = callconv_expr,
559 .is_extern_prototype = is_extern,
559 .is_extern_prototype = is_extern_prototype,
560560 .is_async = is_async,
561 };
561 });
562562 std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params);
563563
564564 return &fn_proto_node.base;
565565 }
566566
567567 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
568 fn parseVarDecl(p: *Parser) !?*Node {
568 fn parseVarDecl(p: *Parser, fields: struct {
569 doc_comments: ?*Node.DocComment = null,
570 visib_token: ?TokenIndex = null,
571 thread_local_token: ?TokenIndex = null,
572 extern_export_token: ?TokenIndex = null,
573 lib_name: ?*Node = null,
574 comptime_token: ?TokenIndex = null,
575 }) !?*Node {
569576 const mut_token = p.eatToken(.Keyword_const) orelse
570577 p.eatToken(.Keyword_var) orelse
571578 return null;
......@@ -587,23 +594,25 @@ const Parser = struct {
587594 } else null;
588595 const semicolon_token = try p.expectToken(.Semicolon);
589596
590 const node = try p.arena.allocator.create(Node.VarDecl);
591 node.* = .{
592 .doc_comments = null,
593 .visib_token = null,
594 .thread_local_token = null,
597 const doc_comments = fields.doc_comments orelse try p.parseAppendedDocComment(semicolon_token);
598
599 const node = try Node.VarDecl.create(&p.arena.allocator, .{
600 .mut_token = mut_token,
595601 .name_token = name_token,
602 .semicolon_token = semicolon_token,
603 }, .{
604 .doc_comments = doc_comments,
605 .visib_token = fields.visib_token,
606 .thread_local_token = fields.thread_local_token,
596607 .eq_token = eq_token,
597 .mut_token = mut_token,
598 .comptime_token = null,
599 .extern_export_token = null,
600 .lib_name = null,
608 .comptime_token = fields.comptime_token,
609 .extern_export_token = fields.extern_export_token,
610 .lib_name = fields.lib_name,
601611 .type_node = type_node,
602612 .align_node = align_node,
603613 .section_node = section_node,
604614 .init_node = init_node,
605 .semicolon_token = semicolon_token,
606 };
615 });
607616 return &node.base;
608617 }
609618
......@@ -618,9 +627,9 @@ const Parser = struct {
618627 var align_expr: ?*Node = null;
619628 var type_expr: ?*Node = null;
620629 if (p.eatToken(.Colon)) |_| {
621 if (p.eatToken(.Keyword_var)) |var_tok| {
622 const node = try p.arena.allocator.create(Node.VarType);
623 node.* = .{ .token = var_tok };
630 if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| {
631 const node = try p.arena.allocator.create(Node.AnyType);
632 node.* = .{ .token = anytype_tok };
624633 type_expr = &node.base;
625634 } else {
626635 type_expr = try p.expectNode(parseTypeExpr, .{
......@@ -663,10 +672,9 @@ const Parser = struct {
663672 fn parseStatement(p: *Parser) Error!?*Node {
664673 const comptime_token = p.eatToken(.Keyword_comptime);
665674
666 const var_decl_node = try p.parseVarDecl();
667 if (var_decl_node) |node| {
668 const var_decl = node.cast(Node.VarDecl).?;
669 var_decl.comptime_token = comptime_token;
675 if (try p.parseVarDecl(.{
676 .comptime_token = comptime_token,
677 })) |node| {
670678 return node;
671679 }
672680
......@@ -937,7 +945,6 @@ const Parser = struct {
937945 return node;
938946 }
939947
940
941948 while_prefix.body = try p.expectNode(parseAssignExpr, .{
942949 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
943950 });
......@@ -1008,7 +1015,7 @@ const Parser = struct {
10081015 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
10091016 fn parseBoolOrExpr(p: *Parser) !?*Node {
10101017 return p.parseBinOpExpr(
1011 SimpleBinOpParseFn(.Keyword_or, Node.InfixOp.Op.BoolOr),
1018 SimpleBinOpParseFn(.Keyword_or, .BoolOr),
10121019 parseBoolAndExpr,
10131020 .Infinitely,
10141021 );
......@@ -1121,10 +1128,10 @@ const Parser = struct {
11211128 const expr_node = try p.expectNode(parseExpr, .{
11221129 .ExpectedExpr = .{ .token = p.tok_i },
11231130 });
1124 const node = try p.arena.allocator.create(Node.PrefixOp);
1131 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
11251132 node.* = .{
1133 .base = .{ .tag = .Resume },
11261134 .op_token = token,
1127 .op = .Resume,
11281135 .rhs = expr_node,
11291136 };
11301137 return &node.base;
......@@ -1398,8 +1405,8 @@ const Parser = struct {
13981405 fn parseErrorUnionExpr(p: *Parser) !?*Node {
13991406 const suffix_expr = (try p.parseSuffixExpr()) orelse return null;
14001407
1401 if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(p)) |node| {
1402 const error_union = node.cast(Node.InfixOp).?;
1408 if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| {
1409 const error_union = node.castTag(.ErrorUnion).?;
14031410 const type_expr = try p.expectNode(parseTypeExpr, .{
14041411 .ExpectedTypeExpr = .{ .token = p.tok_i },
14051412 });
......@@ -1432,10 +1439,56 @@ const Parser = struct {
14321439 .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },
14331440 });
14341441
1442 // TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
14351443 while (try p.parseSuffixOp()) |node| {
1436 switch (node.id) {
1444 switch (node.tag) {
14371445 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
1438 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,
1446 .Catch => node.castTag(.Catch).?.lhs = res,
1447
1448 .Add,
1449 .AddWrap,
1450 .ArrayCat,
1451 .ArrayMult,
1452 .Assign,
1453 .AssignBitAnd,
1454 .AssignBitOr,
1455 .AssignBitShiftLeft,
1456 .AssignBitShiftRight,
1457 .AssignBitXor,
1458 .AssignDiv,
1459 .AssignSub,
1460 .AssignSubWrap,
1461 .AssignMod,
1462 .AssignAdd,
1463 .AssignAddWrap,
1464 .AssignMul,
1465 .AssignMulWrap,
1466 .BangEqual,
1467 .BitAnd,
1468 .BitOr,
1469 .BitShiftLeft,
1470 .BitShiftRight,
1471 .BitXor,
1472 .BoolAnd,
1473 .BoolOr,
1474 .Div,
1475 .EqualEqual,
1476 .ErrorUnion,
1477 .GreaterOrEqual,
1478 .GreaterThan,
1479 .LessOrEqual,
1480 .LessThan,
1481 .MergeErrorSets,
1482 .Mod,
1483 .Mul,
1484 .MulWrap,
1485 .Period,
1486 .Range,
1487 .Sub,
1488 .SubWrap,
1489 .UnwrapOptional,
1490 => node.cast(Node.SimpleInfixOp).?.lhs = res,
1491
14391492 else => unreachable,
14401493 }
14411494 res = node;
......@@ -1463,10 +1516,55 @@ const Parser = struct {
14631516 var res = expr;
14641517
14651518 while (true) {
1519 // TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
14661520 if (try p.parseSuffixOp()) |node| {
1467 switch (node.id) {
1521 switch (node.tag) {
14681522 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
1469 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,
1523 .Catch => node.castTag(.Catch).?.lhs = res,
1524
1525 .Add,
1526 .AddWrap,
1527 .ArrayCat,
1528 .ArrayMult,
1529 .Assign,
1530 .AssignBitAnd,
1531 .AssignBitOr,
1532 .AssignBitShiftLeft,
1533 .AssignBitShiftRight,
1534 .AssignBitXor,
1535 .AssignDiv,
1536 .AssignSub,
1537 .AssignSubWrap,
1538 .AssignMod,
1539 .AssignAdd,
1540 .AssignAddWrap,
1541 .AssignMul,
1542 .AssignMulWrap,
1543 .BangEqual,
1544 .BitAnd,
1545 .BitOr,
1546 .BitShiftLeft,
1547 .BitShiftRight,
1548 .BitXor,
1549 .BoolAnd,
1550 .BoolOr,
1551 .Div,
1552 .EqualEqual,
1553 .ErrorUnion,
1554 .GreaterOrEqual,
1555 .GreaterThan,
1556 .LessOrEqual,
1557 .LessThan,
1558 .MergeErrorSets,
1559 .Mod,
1560 .Mul,
1561 .MulWrap,
1562 .Period,
1563 .Range,
1564 .Sub,
1565 .SubWrap,
1566 .UnwrapOptional,
1567 => node.cast(Node.SimpleInfixOp).?.lhs = res,
14701568 else => unreachable,
14711569 }
14721570 res = node;
......@@ -1529,7 +1627,7 @@ const Parser = struct {
15291627 if (try p.parseAnonLiteral()) |node| return node;
15301628 if (try p.parseErrorSetDecl()) |node| return node;
15311629 if (try p.parseFloatLiteral()) |node| return node;
1532 if (try p.parseFnProto()) |node| return node;
1630 if (try p.parseFnProto(.as_type, .{})) |node| return node;
15331631 if (try p.parseGroupedExpr()) |node| return node;
15341632 if (try p.parseLabeledTypeExpr()) |node| return node;
15351633 if (try p.parseIdentifier()) |node| return node;
......@@ -1553,11 +1651,11 @@ const Parser = struct {
15531651 const global_error_set = try p.createLiteral(Node.ErrorType, token);
15541652 if (period == null or identifier == null) return global_error_set;
15551653
1556 const node = try p.arena.allocator.create(Node.InfixOp);
1654 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
15571655 node.* = .{
1656 .base = Node{ .tag = .Period },
15581657 .op_token = period.?,
15591658 .lhs = global_error_set,
1560 .op = .Period,
15611659 .rhs = identifier.?,
15621660 };
15631661 return &node.base;
......@@ -1654,7 +1752,7 @@ const Parser = struct {
16541752 }
16551753
16561754 if (try p.parseLoopTypeExpr()) |node| {
1657 switch (node.id) {
1755 switch (node.tag) {
16581756 .For => node.cast(Node.For).?.label = label,
16591757 .While => node.cast(Node.While).?.label = label,
16601758 else => unreachable,
......@@ -2023,14 +2121,13 @@ const Parser = struct {
20232121 }
20242122
20252123 /// ParamType
2026 /// <- KEYWORD_var
2124 /// <- Keyword_anytype
20272125 /// / DOT3
20282126 /// / TypeExpr
20292127 fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {
20302128 // TODO cast from tuple to error union is broken
20312129 const P = Node.FnProto.ParamDecl.ParamType;
2032 if (try p.parseVarType()) |node| return P{ .var_type = node };
2033 if (p.eatToken(.Ellipsis3)) |token| return P{ .var_args = token };
2130 if (try p.parseAnyType()) |node| return P{ .any_type = node };
20342131 if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };
20352132 return null;
20362133 }
......@@ -2231,11 +2328,11 @@ const Parser = struct {
22312328 .ExpectedExpr = .{ .token = p.tok_i },
22322329 });
22332330
2234 const node = try p.arena.allocator.create(Node.InfixOp);
2331 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
22352332 node.* = .{
2333 .base = Node{ .tag = .Range },
22362334 .op_token = token,
22372335 .lhs = expr,
2238 .op = .Range,
22392336 .rhs = range_end,
22402337 };
22412338 return &node.base;
......@@ -2260,7 +2357,7 @@ const Parser = struct {
22602357 /// / EQUAL
22612358 fn parseAssignOp(p: *Parser) !?*Node {
22622359 const token = p.nextToken();
2263 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
2360 const op: Node.Tag = switch (p.token_ids[token]) {
22642361 .AsteriskEqual => .AssignMul,
22652362 .SlashEqual => .AssignDiv,
22662363 .PercentEqual => .AssignMod,
......@@ -2281,11 +2378,11 @@ const Parser = struct {
22812378 },
22822379 };
22832380
2284 const node = try p.arena.allocator.create(Node.InfixOp);
2381 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
22852382 node.* = .{
2383 .base = .{ .tag = op },
22862384 .op_token = token,
22872385 .lhs = undefined, // set by caller
2288 .op = op,
22892386 .rhs = undefined, // set by caller
22902387 };
22912388 return &node.base;
......@@ -2300,7 +2397,7 @@ const Parser = struct {
23002397 /// / RARROWEQUAL
23012398 fn parseCompareOp(p: *Parser) !?*Node {
23022399 const token = p.nextToken();
2303 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
2400 const op: Node.Tag = switch (p.token_ids[token]) {
23042401 .EqualEqual => .EqualEqual,
23052402 .BangEqual => .BangEqual,
23062403 .AngleBracketLeft => .LessThan,
......@@ -2324,12 +2421,22 @@ const Parser = struct {
23242421 /// / KEYWORD_catch Payload?
23252422 fn parseBitwiseOp(p: *Parser) !?*Node {
23262423 const token = p.nextToken();
2327 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
2424 const op: Node.Tag = switch (p.token_ids[token]) {
23282425 .Ampersand => .BitAnd,
23292426 .Caret => .BitXor,
23302427 .Pipe => .BitOr,
23312428 .Keyword_orelse => .UnwrapOptional,
2332 .Keyword_catch => .{ .Catch = try p.parsePayload() },
2429 .Keyword_catch => {
2430 const payload = try p.parsePayload();
2431 const node = try p.arena.allocator.create(Node.Catch);
2432 node.* = .{
2433 .op_token = token,
2434 .lhs = undefined, // set by caller
2435 .rhs = undefined, // set by caller
2436 .payload = payload,
2437 };
2438 return &node.base;
2439 },
23332440 else => {
23342441 p.putBackToken(token);
23352442 return null;
......@@ -2344,7 +2451,7 @@ const Parser = struct {
23442451 /// / RARROW2
23452452 fn parseBitShiftOp(p: *Parser) !?*Node {
23462453 const token = p.nextToken();
2347 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
2454 const op: Node.Tag = switch (p.token_ids[token]) {
23482455 .AngleBracketAngleBracketLeft => .BitShiftLeft,
23492456 .AngleBracketAngleBracketRight => .BitShiftRight,
23502457 else => {
......@@ -2364,7 +2471,7 @@ const Parser = struct {
23642471 /// / MINUSPERCENT
23652472 fn parseAdditionOp(p: *Parser) !?*Node {
23662473 const token = p.nextToken();
2367 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
2474 const op: Node.Tag = switch (p.token_ids[token]) {
23682475 .Plus => .Add,
23692476 .Minus => .Sub,
23702477 .PlusPlus => .ArrayCat,
......@@ -2388,7 +2495,7 @@ const Parser = struct {
23882495 /// / ASTERISKPERCENT
23892496 fn parseMultiplyOp(p: *Parser) !?*Node {
23902497 const token = p.nextToken();
2391 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
2498 const op: Node.Tag = switch (p.token_ids[token]) {
23922499 .PipePipe => .MergeErrorSets,
23932500 .Asterisk => .Mul,
23942501 .Slash => .Div,
......@@ -2414,24 +2521,26 @@ const Parser = struct {
24142521 /// / KEYWORD_await
24152522 fn parsePrefixOp(p: *Parser) !?*Node {
24162523 const token = p.nextToken();
2417 const op: Node.PrefixOp.Op = switch (p.token_ids[token]) {
2418 .Bang => .BoolNot,
2419 .Minus => .Negation,
2420 .Tilde => .BitNot,
2421 .MinusPercent => .NegationWrap,
2422 .Ampersand => .AddressOf,
2423 .Keyword_try => .Try,
2424 .Keyword_await => .Await,
2524 switch (p.token_ids[token]) {
2525 .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
2526 .Minus => return p.allocSimplePrefixOp(.Negation, token),
2527 .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
2528 .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
2529 .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
2530 .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
2531 .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
24252532 else => {
24262533 p.putBackToken(token);
24272534 return null;
24282535 },
2429 };
2536 }
2537 }
24302538
2431 const node = try p.arena.allocator.create(Node.PrefixOp);
2539 fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node {
2540 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
24322541 node.* = .{
2542 .base = .{ .tag = tag },
24332543 .op_token = token,
2434 .op = op,
24352544 .rhs = undefined, // set by caller
24362545 };
24372546 return &node.base;
......@@ -2451,19 +2560,15 @@ const Parser = struct {
24512560 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
24522561 fn parsePrefixTypeOp(p: *Parser) !?*Node {
24532562 if (p.eatToken(.QuestionMark)) |token| {
2454 const node = try p.arena.allocator.create(Node.PrefixOp);
2563 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
24552564 node.* = .{
2565 .base = .{ .tag = .OptionalType },
24562566 .op_token = token,
2457 .op = .OptionalType,
24582567 .rhs = undefined, // set by caller
24592568 };
24602569 return &node.base;
24612570 }
24622571
2463 // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or
2464 // .return_type more difficult for the caller (see parsePrefixOpExpr helper).
2465 // Consider making the AnyFrameType a member of PrefixOp and add a
2466 // PrefixOp.AnyFrameType variant?
24672572 if (p.eatToken(.Keyword_anyframe)) |token| {
24682573 const arrow = p.eatToken(.Arrow) orelse {
24692574 p.putBackToken(token);
......@@ -2483,11 +2588,15 @@ const Parser = struct {
24832588 if (try p.parsePtrTypeStart()) |node| {
24842589 // If the token encountered was **, there will be two nodes instead of one.
24852590 // The attributes should be applied to the rightmost operator.
2486 const prefix_op = node.cast(Node.PrefixOp).?;
2487 var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk)
2488 &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType
2591 var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
2592 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
2593 &ptr_type.rhs.cast(Node.PtrType).?.ptr_info
2594 else
2595 &ptr_type.ptr_info
2596 else if (node.cast(Node.SliceType)) |slice_type|
2597 &slice_type.ptr_info
24892598 else
2490 &prefix_op.op.PtrType;
2599 unreachable;
24912600
24922601 while (true) {
24932602 if (p.eatToken(.Keyword_align)) |align_token| {
......@@ -2506,7 +2615,7 @@ const Parser = struct {
25062615 .ExpectedIntegerLiteral = .{ .token = p.tok_i },
25072616 });
25082617
2509 break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{
2618 break :bit_range_value ast.PtrInfo.Align.BitRange{
25102619 .start = range_start,
25112620 .end = range_end,
25122621 };
......@@ -2520,7 +2629,7 @@ const Parser = struct {
25202629 continue;
25212630 }
25222631
2523 ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{
2632 ptr_info.align_info = ast.PtrInfo.Align{
25242633 .node = expr_node,
25252634 .bit_range = bit_range,
25262635 };
......@@ -2564,58 +2673,54 @@ const Parser = struct {
25642673 }
25652674
25662675 if (try p.parseArrayTypeStart()) |node| {
2567 switch (node.cast(Node.PrefixOp).?.op) {
2568 .ArrayType => {},
2569 .SliceType => |*slice_type| {
2570 // Collect pointer qualifiers in any order, but disallow duplicates
2571 while (true) {
2572 if (try p.parseByteAlign()) |align_expr| {
2573 if (slice_type.align_info != null) {
2574 try p.errors.append(p.gpa, .{
2575 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2576 });
2577 continue;
2578 }
2579 slice_type.align_info = Node.PrefixOp.PtrInfo.Align{
2580 .node = align_expr,
2581 .bit_range = null,
2582 };
2676 if (node.cast(Node.SliceType)) |slice_type| {
2677 // Collect pointer qualifiers in any order, but disallow duplicates
2678 while (true) {
2679 if (try p.parseByteAlign()) |align_expr| {
2680 if (slice_type.ptr_info.align_info != null) {
2681 try p.errors.append(p.gpa, .{
2682 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2683 });
25832684 continue;
25842685 }
2585 if (p.eatToken(.Keyword_const)) |const_token| {
2586 if (slice_type.const_token != null) {
2587 try p.errors.append(p.gpa, .{
2588 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2589 });
2590 continue;
2591 }
2592 slice_type.const_token = const_token;
2686 slice_type.ptr_info.align_info = ast.PtrInfo.Align{
2687 .node = align_expr,
2688 .bit_range = null,
2689 };
2690 continue;
2691 }
2692 if (p.eatToken(.Keyword_const)) |const_token| {
2693 if (slice_type.ptr_info.const_token != null) {
2694 try p.errors.append(p.gpa, .{
2695 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2696 });
25932697 continue;
25942698 }
2595 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2596 if (slice_type.volatile_token != null) {
2597 try p.errors.append(p.gpa, .{
2598 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2599 });
2600 continue;
2601 }
2602 slice_type.volatile_token = volatile_token;
2699 slice_type.ptr_info.const_token = const_token;
2700 continue;
2701 }
2702 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2703 if (slice_type.ptr_info.volatile_token != null) {
2704 try p.errors.append(p.gpa, .{
2705 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2706 });
26032707 continue;
26042708 }
2605 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2606 if (slice_type.allowzero_token != null) {
2607 try p.errors.append(p.gpa, .{
2608 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2609 });
2610 continue;
2611 }
2612 slice_type.allowzero_token = allowzero_token;
2709 slice_type.ptr_info.volatile_token = volatile_token;
2710 continue;
2711 }
2712 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2713 if (slice_type.ptr_info.allowzero_token != null) {
2714 try p.errors.append(p.gpa, .{
2715 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2716 });
26132717 continue;
26142718 }
2615 break;
2719 slice_type.ptr_info.allowzero_token = allowzero_token;
2720 continue;
26162721 }
2617 },
2618 else => unreachable,
2722 break;
2723 }
26192724 }
26202725 return node;
26212726 }
......@@ -2669,14 +2774,14 @@ const Parser = struct {
26692774
26702775 if (p.eatToken(.Period)) |period| {
26712776 if (try p.parseIdentifier()) |identifier| {
2672 // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser.
2777 // TODO: It's a bit weird to return a SimpleInfixOp from the SuffixOp parser.
26732778 // Should there be an Node.SuffixOp.FieldAccess variant? Or should
26742779 // this grammar rule be altered?
2675 const node = try p.arena.allocator.create(Node.InfixOp);
2780 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
26762781 node.* = .{
2782 .base = Node{ .tag = .Period },
26772783 .op_token = period,
26782784 .lhs = undefined, // set by caller
2679 .op = .Period,
26802785 .rhs = identifier,
26812786 };
26822787 return &node.base;
......@@ -2729,29 +2834,32 @@ const Parser = struct {
27292834 null;
27302835 const rbracket = try p.expectToken(.RBracket);
27312836
2732 const op: Node.PrefixOp.Op = if (expr) |len_expr|
2733 .{
2734 .ArrayType = .{
2837 if (expr) |len_expr| {
2838 if (sentinel) |s| {
2839 const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
2840 node.* = .{
2841 .op_token = lbracket,
2842 .rhs = undefined, // set by caller
27352843 .len_expr = len_expr,
2736 .sentinel = sentinel,
2737 },
2844 .sentinel = s,
2845 };
2846 return &node.base;
2847 } else {
2848 const node = try p.arena.allocator.create(Node.ArrayType);
2849 node.* = .{
2850 .op_token = lbracket,
2851 .rhs = undefined, // set by caller
2852 .len_expr = len_expr,
2853 };
2854 return &node.base;
27382855 }
2739 else
2740 .{
2741 .SliceType = Node.PrefixOp.PtrInfo{
2742 .allowzero_token = null,
2743 .align_info = null,
2744 .const_token = null,
2745 .volatile_token = null,
2746 .sentinel = sentinel,
2747 },
2748 };
2856 }
27492857
2750 const node = try p.arena.allocator.create(Node.PrefixOp);
2858 const node = try p.arena.allocator.create(Node.SliceType);
27512859 node.* = .{
27522860 .op_token = lbracket,
2753 .op = op,
27542861 .rhs = undefined, // set by caller
2862 .ptr_info = .{ .sentinel = sentinel },
27552863 };
27562864 return &node.base;
27572865 }
......@@ -2769,28 +2877,26 @@ const Parser = struct {
27692877 })
27702878 else
27712879 null;
2772 const node = try p.arena.allocator.create(Node.PrefixOp);
2880 const node = try p.arena.allocator.create(Node.PtrType);
27732881 node.* = .{
27742882 .op_token = asterisk,
2775 .op = .{ .PtrType = .{ .sentinel = sentinel } },
27762883 .rhs = undefined, // set by caller
2884 .ptr_info = .{ .sentinel = sentinel },
27772885 };
27782886 return &node.base;
27792887 }
27802888
27812889 if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {
2782 const node = try p.arena.allocator.create(Node.PrefixOp);
2890 const node = try p.arena.allocator.create(Node.PtrType);
27832891 node.* = .{
27842892 .op_token = double_asterisk,
2785 .op = .{ .PtrType = .{} },
27862893 .rhs = undefined, // set by caller
27872894 };
27882895
27892896 // Special case for **, which is its own token
2790 const child = try p.arena.allocator.create(Node.PrefixOp);
2897 const child = try p.arena.allocator.create(Node.PtrType);
27912898 child.* = .{
27922899 .op_token = double_asterisk,
2793 .op = .{ .PtrType = .{} },
27942900 .rhs = undefined, // set by caller
27952901 };
27962902 node.rhs = &child.base;
......@@ -2809,10 +2915,9 @@ const Parser = struct {
28092915 p.putBackToken(ident);
28102916 } else {
28112917 _ = try p.expectToken(.RBracket);
2812 const node = try p.arena.allocator.create(Node.PrefixOp);
2918 const node = try p.arena.allocator.create(Node.PtrType);
28132919 node.* = .{
28142920 .op_token = lbracket,
2815 .op = .{ .PtrType = .{} },
28162921 .rhs = undefined, // set by caller
28172922 };
28182923 return &node.base;
......@@ -2825,11 +2930,11 @@ const Parser = struct {
28252930 else
28262931 null;
28272932 _ = try p.expectToken(.RBracket);
2828 const node = try p.arena.allocator.create(Node.PrefixOp);
2933 const node = try p.arena.allocator.create(Node.PtrType);
28292934 node.* = .{
28302935 .op_token = lbracket,
2831 .op = .{ .PtrType = .{ .sentinel = sentinel } },
28322936 .rhs = undefined, // set by caller
2937 .ptr_info = .{ .sentinel = sentinel },
28332938 };
28342939 return &node.base;
28352940 }
......@@ -2956,7 +3061,7 @@ const Parser = struct {
29563061
29573062 const NodeParseFn = fn (p: *Parser) Error!?*Node;
29583063
2959 fn ListParseFn(comptime E: type, comptime nodeParseFn: var) ParseFn([]E) {
3064 fn ListParseFn(comptime E: type, comptime nodeParseFn: anytype) ParseFn([]E) {
29603065 return struct {
29613066 pub fn parse(p: *Parser) ![]E {
29623067 var list = std.ArrayList(E).init(p.gpa);
......@@ -2983,7 +3088,7 @@ const Parser = struct {
29833088 }.parse;
29843089 }
29853090
2986 fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) NodeParseFn {
3091 fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn {
29873092 return struct {
29883093 pub fn parse(p: *Parser) Error!?*Node {
29893094 const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {
......@@ -2997,11 +3102,11 @@ const Parser = struct {
29973102 else => return null,
29983103 } else p.eatToken(token) orelse return null;
29993104
3000 const node = try p.arena.allocator.create(Node.InfixOp);
3105 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
30013106 node.* = .{
3107 .base = .{ .tag = op },
30023108 .op_token = op_token,
30033109 .lhs = undefined, // set by caller
3004 .op = op,
30053110 .rhs = undefined, // set by caller
30063111 };
30073112 return &node.base;
......@@ -3058,9 +3163,10 @@ const Parser = struct {
30583163 return &node.base;
30593164 }
30603165
3061 fn parseVarType(p: *Parser) !?*Node {
3062 const token = p.eatToken(.Keyword_var) orelse return null;
3063 const node = try p.arena.allocator.create(Node.VarType);
3166 fn parseAnyType(p: *Parser) !?*Node {
3167 const token = p.eatToken(.Keyword_anytype) orelse
3168 p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle
3169 const node = try p.arena.allocator.create(Node.AnyType);
30643170 node.* = .{
30653171 .token = token,
30663172 };
......@@ -3070,7 +3176,6 @@ const Parser = struct {
30703176 fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node {
30713177 const result = try p.arena.allocator.create(T);
30723178 result.* = T{
3073 .base = Node{ .id = Node.typeToId(T) },
30743179 .token = token,
30753180 };
30763181 return &result.base;
......@@ -3146,30 +3251,15 @@ const Parser = struct {
31463251
31473252 fn parseTry(p: *Parser) !?*Node {
31483253 const token = p.eatToken(.Keyword_try) orelse return null;
3149 const node = try p.arena.allocator.create(Node.PrefixOp);
3254 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
31503255 node.* = .{
3256 .base = .{ .tag = .Try },
31513257 .op_token = token,
3152 .op = .Try,
31533258 .rhs = undefined, // set by caller
31543259 };
31553260 return &node.base;
31563261 }
31573262
3158 fn parseUse(p: *Parser) !?*Node {
3159 const token = p.eatToken(.Keyword_usingnamespace) orelse return null;
3160 const node = try p.arena.allocator.create(Node.Use);
3161 node.* = .{
3162 .doc_comments = null,
3163 .visib_token = null,
3164 .use_token = token,
3165 .expr = try p.expectNode(parseExpr, .{
3166 .ExpectedExpr = .{ .token = p.tok_i },
3167 }),
3168 .semicolon_token = try p.expectToken(.Semicolon),
3169 };
3170 return &node.base;
3171 }
3172
31733263 /// IfPrefix Body (KEYWORD_else Payload? Body)?
31743264 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node {
31753265 const node = (try p.parseIfPrefix()) orelse return null;
......@@ -3223,20 +3313,53 @@ const Parser = struct {
32233313 }
32243314
32253315 /// Op* Child
3226 fn parsePrefixOpExpr(p: *Parser, opParseFn: NodeParseFn, childParseFn: NodeParseFn) Error!?*Node {
3316 fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node {
32273317 if (try opParseFn(p)) |first_op| {
32283318 var rightmost_op = first_op;
32293319 while (true) {
3230 switch (rightmost_op.id) {
3231 .PrefixOp => {
3232 var prefix_op = rightmost_op.cast(Node.PrefixOp).?;
3320 switch (rightmost_op.tag) {
3321 .AddressOf,
3322 .Await,
3323 .BitNot,
3324 .BoolNot,
3325 .OptionalType,
3326 .Negation,
3327 .NegationWrap,
3328 .Resume,
3329 .Try,
3330 => {
3331 if (try opParseFn(p)) |rhs| {
3332 rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs;
3333 rightmost_op = rhs;
3334 } else break;
3335 },
3336 .ArrayType => {
3337 if (try opParseFn(p)) |rhs| {
3338 rightmost_op.cast(Node.ArrayType).?.rhs = rhs;
3339 rightmost_op = rhs;
3340 } else break;
3341 },
3342 .ArrayTypeSentinel => {
3343 if (try opParseFn(p)) |rhs| {
3344 rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs;
3345 rightmost_op = rhs;
3346 } else break;
3347 },
3348 .SliceType => {
3349 if (try opParseFn(p)) |rhs| {
3350 rightmost_op.cast(Node.SliceType).?.rhs = rhs;
3351 rightmost_op = rhs;
3352 } else break;
3353 },
3354 .PtrType => {
3355 var ptr_type = rightmost_op.cast(Node.PtrType).?;
32333356 // If the token encountered was **, there will be two nodes
3234 if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) {
3235 rightmost_op = prefix_op.rhs;
3236 prefix_op = rightmost_op.cast(Node.PrefixOp).?;
3357 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
3358 rightmost_op = ptr_type.rhs;
3359 ptr_type = rightmost_op.cast(Node.PtrType).?;
32373360 }
32383361 if (try opParseFn(p)) |rhs| {
3239 prefix_op.rhs = rhs;
3362 ptr_type.rhs = rhs;
32403363 rightmost_op = rhs;
32413364 } else break;
32423365 },
......@@ -3252,9 +3375,42 @@ const Parser = struct {
32523375 }
32533376
32543377 // If any prefix op existed, a child node on the RHS is required
3255 switch (rightmost_op.id) {
3256 .PrefixOp => {
3257 const prefix_op = rightmost_op.cast(Node.PrefixOp).?;
3378 switch (rightmost_op.tag) {
3379 .AddressOf,
3380 .Await,
3381 .BitNot,
3382 .BoolNot,
3383 .OptionalType,
3384 .Negation,
3385 .NegationWrap,
3386 .Resume,
3387 .Try,
3388 => {
3389 const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?;
3390 prefix_op.rhs = try p.expectNode(childParseFn, .{
3391 .InvalidToken = .{ .token = p.tok_i },
3392 });
3393 },
3394 .ArrayType => {
3395 const prefix_op = rightmost_op.cast(Node.ArrayType).?;
3396 prefix_op.rhs = try p.expectNode(childParseFn, .{
3397 .InvalidToken = .{ .token = p.tok_i },
3398 });
3399 },
3400 .ArrayTypeSentinel => {
3401 const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?;
3402 prefix_op.rhs = try p.expectNode(childParseFn, .{
3403 .InvalidToken = .{ .token = p.tok_i },
3404 });
3405 },
3406 .PtrType => {
3407 const prefix_op = rightmost_op.cast(Node.PtrType).?;
3408 prefix_op.rhs = try p.expectNode(childParseFn, .{
3409 .InvalidToken = .{ .token = p.tok_i },
3410 });
3411 },
3412 .SliceType => {
3413 const prefix_op = rightmost_op.cast(Node.SliceType).?;
32583414 prefix_op.rhs = try p.expectNode(childParseFn, .{
32593415 .InvalidToken = .{ .token = p.tok_i },
32603416 });
......@@ -3295,9 +3451,13 @@ const Parser = struct {
32953451 const left = res;
32963452 res = node;
32973453
3298 const op = node.cast(Node.InfixOp).?;
3299 op.*.lhs = left;
3300 op.*.rhs = right;
3454 if (node.castTag(.Catch)) |op| {
3455 op.lhs = left;
3456 op.rhs = right;
3457 } else if (node.cast(Node.SimpleInfixOp)) |op| {
3458 op.lhs = left;
3459 op.rhs = right;
3460 }
33013461
33023462 switch (chain) {
33033463 .Once => break,
......@@ -3308,12 +3468,12 @@ const Parser = struct {
33083468 return res;
33093469 }
33103470
3311 fn createInfixOp(p: *Parser, index: TokenIndex, op: Node.InfixOp.Op) !*Node {
3312 const node = try p.arena.allocator.create(Node.InfixOp);
3471 fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node {
3472 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
33133473 node.* = .{
3314 .op_token = index,
3474 .base = Node{ .tag = tag },
3475 .op_token = op_token,
33153476 .lhs = undefined, // set by caller
3316 .op = op,
33173477 .rhs = undefined, // set by caller
33183478 };
33193479 return &node.base;
lib/std/zig/parser_test.zig+41-20
......@@ -1,4 +1,32 @@
1const builtin = @import("builtin");
1test "zig fmt: convert var to anytype" {
2 // TODO remove in next release cycle
3 try testTransform(
4 \\pub fn main(
5 \\ a: var,
6 \\ bar: var,
7 \\) void {}
8 ,
9 \\pub fn main(
10 \\ a: anytype,
11 \\ bar: anytype,
12 \\) void {}
13 \\
14 );
15}
16
17test "zig fmt: noasync to nosuspend" {
18 // TODO: remove this
19 try testTransform(
20 \\pub fn main() void {
21 \\ noasync call();
22 \\}
23 ,
24 \\pub fn main() void {
25 \\ nosuspend call();
26 \\}
27 \\
28 );
29}
230
331test "recovery: top level" {
432 try testError(
......@@ -422,10 +450,10 @@ test "zig fmt: asm expression with comptime content" {
422450 );
423451}
424452
425test "zig fmt: var struct field" {
453test "zig fmt: anytype struct field" {
426454 try testCanonical(
427455 \\pub const Pointer = struct {
428 \\ sentinel: var,
456 \\ sentinel: anytype,
429457 \\};
430458 \\
431459 );
......@@ -1932,7 +1960,7 @@ test "zig fmt: preserve spacing" {
19321960test "zig fmt: return types" {
19331961 try testCanonical(
19341962 \\pub fn main() !void {}
1935 \\pub fn main() var {}
1963 \\pub fn main() anytype {}
19361964 \\pub fn main() i32 {}
19371965 \\
19381966 );
......@@ -2140,9 +2168,9 @@ test "zig fmt: call expression" {
21402168 );
21412169}
21422170
2143test "zig fmt: var type" {
2171test "zig fmt: anytype type" {
21442172 try testCanonical(
2145 \\fn print(args: var) var {}
2173 \\fn print(args: anytype) anytype {}
21462174 \\
21472175 );
21482176}
......@@ -3146,20 +3174,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {
31463174 );
31473175}
31483176
3149test "zig fmt: noasync to nosuspend" {
3150 // TODO: remove this
3151 try testTransform(
3152 \\pub fn main() void {
3153 \\ noasync call();
3154 \\}
3155 ,
3156 \\pub fn main() void {
3157 \\ nosuspend call();
3158 \\}
3159 \\
3160 );
3161}
3162
31633177test "zig fmt: convert async fn into callconv(.Async)" {
31643178 try testTransform(
31653179 \\async fn foo() void {}
......@@ -3180,6 +3194,13 @@ test "zig fmt: convert extern fn proto into callconv(.C)" {
31803194 );
31813195}
31823196
3197test "zig fmt: C var args" {
3198 try testCanonical(
3199 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
3200 \\
3201 );
3202}
3203
31833204const std = @import("std");
31843205const mem = std.mem;
31853206const warn = std.debug.warn;
lib/std/zig/render.zig+396-234
......@@ -12,7 +12,7 @@ pub const Error = error{
1212};
1313
1414/// Returns whether anything changed
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
15pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
1616 // cannot render an invalid tree
1717 std.debug.assert(tree.errors.len == 0);
1818
......@@ -64,7 +64,7 @@ pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(
6464
6565fn renderRoot(
6666 allocator: *mem.Allocator,
67 stream: var,
67 stream: anytype,
6868 tree: *ast.Tree,
6969) (@TypeOf(stream).Error || Error)!void {
7070 // render all the line comments at the beginning of the file
......@@ -191,13 +191,13 @@ fn renderRoot(
191191 }
192192}
193193
194fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
194fn renderExtraNewline(tree: *ast.Tree, stream: anytype, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
195195 return renderExtraNewlineToken(tree, stream, start_col, node.firstToken());
196196}
197197
198198fn renderExtraNewlineToken(
199199 tree: *ast.Tree,
200 stream: var,
200 stream: anytype,
201201 start_col: *usize,
202202 first_token: ast.TokenIndex,
203203) @TypeOf(stream).Error!void {
......@@ -218,18 +218,18 @@ fn renderExtraNewlineToken(
218218 }
219219}
220220
221fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
221fn renderTopLevelDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
222222 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
223223}
224224
225fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
226 switch (decl.id) {
225fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
226 switch (decl.tag) {
227227 .FnProto => {
228228 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
229229
230 try renderDocComments(tree, stream, fn_proto, indent, start_col);
230 try renderDocComments(tree, stream, fn_proto, fn_proto.getTrailer("doc_comments"), indent, start_col);
231231
232 if (fn_proto.body_node) |body_node| {
232 if (fn_proto.getTrailer("body_node")) |body_node| {
233233 try renderExpression(allocator, stream, tree, indent, start_col, decl, .Space);
234234 try renderExpression(allocator, stream, tree, indent, start_col, body_node, space);
235235 } else {
......@@ -252,14 +252,14 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
252252 .VarDecl => {
253253 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
254254
255 try renderDocComments(tree, stream, var_decl, indent, start_col);
255 try renderDocComments(tree, stream, var_decl, var_decl.getTrailer("doc_comments"), indent, start_col);
256256 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
257257 },
258258
259259 .TestDecl => {
260260 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
261261
262 try renderDocComments(tree, stream, test_decl, indent, start_col);
262 try renderDocComments(tree, stream, test_decl, test_decl.doc_comments, indent, start_col);
263263 try renderToken(tree, stream, test_decl.test_token, indent, start_col, .Space);
264264 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.name, .Space);
265265 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, space);
......@@ -268,7 +268,7 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
268268 .ContainerField => {
269269 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);
270270
271 try renderDocComments(tree, stream, field, indent, start_col);
271 try renderDocComments(tree, stream, field, field.doc_comments, indent, start_col);
272272 if (field.comptime_token) |t| {
273273 try renderToken(tree, stream, t, indent, start_col, .Space); // comptime
274274 }
......@@ -358,14 +358,14 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
358358
359359fn renderExpression(
360360 allocator: *mem.Allocator,
361 stream: var,
361 stream: anytype,
362362 tree: *ast.Tree,
363363 indent: usize,
364364 start_col: *usize,
365365 base: *ast.Node,
366366 space: Space,
367367) (@TypeOf(stream).Error || Error)!void {
368 switch (base.id) {
368 switch (base.tag) {
369369 .Identifier => {
370370 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
371371 return renderToken(tree, stream, identifier.token, indent, start_col, space);
......@@ -436,13 +436,10 @@ fn renderExpression(
436436 }
437437 },
438438
439 .InfixOp => {
440 const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
439 .Catch => {
440 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
441441
442 const op_space = switch (infix_op_node.op) {
443 ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None,
444 else => Space.Space,
445 };
442 const op_space = Space.Space;
446443 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
447444
448445 const after_op_space = blk: {
......@@ -458,182 +455,247 @@ fn renderExpression(
458455 start_col.* = indent + indent_delta;
459456 }
460457
461 switch (infix_op_node.op) {
462 ast.Node.InfixOp.Op.Catch => |maybe_payload| if (maybe_payload) |payload| {
463 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
464 },
465 else => {},
458 if (infix_op_node.payload) |payload| {
459 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
466460 }
467461
468462 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
469463 },
470464
471 .PrefixOp => {
472 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
473
474 switch (prefix_op_node.op) {
475 .PtrType => |ptr_info| {
476 const op_tok_id = tree.token_ids[prefix_op_node.op_token];
477 switch (op_tok_id) {
478 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
479 .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier)
480 try stream.writeAll("[*c")
481 else
482 try stream.writeAll("[*"),
483 else => unreachable,
484 }
485 if (ptr_info.sentinel) |sentinel| {
486 const colon_token = tree.prevToken(sentinel.firstToken());
487 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
488 const sentinel_space = switch (op_tok_id) {
489 .LBracket => Space.None,
490 else => Space.Space,
491 };
492 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
493 }
494 switch (op_tok_id) {
495 .Asterisk, .AsteriskAsterisk => {},
496 .LBracket => try stream.writeByte(']'),
497 else => unreachable,
498 }
499 if (ptr_info.allowzero_token) |allowzero_token| {
500 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
501 }
502 if (ptr_info.align_info) |align_info| {
503 const lparen_token = tree.prevToken(align_info.node.firstToken());
504 const align_token = tree.prevToken(lparen_token);
465 .Add,
466 .AddWrap,
467 .ArrayCat,
468 .ArrayMult,
469 .Assign,
470 .AssignBitAnd,
471 .AssignBitOr,
472 .AssignBitShiftLeft,
473 .AssignBitShiftRight,
474 .AssignBitXor,
475 .AssignDiv,
476 .AssignSub,
477 .AssignSubWrap,
478 .AssignMod,
479 .AssignAdd,
480 .AssignAddWrap,
481 .AssignMul,
482 .AssignMulWrap,
483 .BangEqual,
484 .BitAnd,
485 .BitOr,
486 .BitShiftLeft,
487 .BitShiftRight,
488 .BitXor,
489 .BoolAnd,
490 .BoolOr,
491 .Div,
492 .EqualEqual,
493 .ErrorUnion,
494 .GreaterOrEqual,
495 .GreaterThan,
496 .LessOrEqual,
497 .LessThan,
498 .MergeErrorSets,
499 .Mod,
500 .Mul,
501 .MulWrap,
502 .Period,
503 .Range,
504 .Sub,
505 .SubWrap,
506 .UnwrapOptional,
507 => {
508 const infix_op_node = @fieldParentPtr(ast.Node.SimpleInfixOp, "base", base);
509
510 const op_space = switch (base.tag) {
511 .Period, .ErrorUnion, .Range => Space.None,
512 else => Space.Space,
513 };
514 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
505515
506 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
507 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
516 const after_op_space = blk: {
517 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
518 break :blk if (loc.line == 0) op_space else Space.Newline;
519 };
508520
509 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
521 try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);
522 if (after_op_space == Space.Newline and
523 tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)
524 {
525 try stream.writeByteNTimes(' ', indent + indent_delta);
526 start_col.* = indent + indent_delta;
527 }
510528
511 if (align_info.bit_range) |bit_range| {
512 const colon1 = tree.prevToken(bit_range.start.firstToken());
513 const colon2 = tree.prevToken(bit_range.end.firstToken());
529 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
530 },
514531
515 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
516 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
517 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
518 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
532 .BitNot,
533 .BoolNot,
534 .Negation,
535 .NegationWrap,
536 .OptionalType,
537 .AddressOf,
538 => {
539 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
540 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.None);
541 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
542 },
519543
520 const rparen_token = tree.nextToken(bit_range.end.lastToken());
521 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
522 } else {
523 const rparen_token = tree.nextToken(align_info.node.lastToken());
524 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
525 }
526 }
527 if (ptr_info.const_token) |const_token| {
528 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
529 }
530 if (ptr_info.volatile_token) |volatile_token| {
531 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
532 }
533 },
544 .Try,
545 .Resume,
546 .Await,
547 => {
548 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
549 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.Space);
550 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
551 },
534552
535 .SliceType => |ptr_info| {
536 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [
537 if (ptr_info.sentinel) |sentinel| {
538 const colon_token = tree.prevToken(sentinel.firstToken());
539 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
540 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
541 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
542 } else {
543 try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ]
544 }
553 .ArrayType => {
554 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
555 return renderArrayType(
556 allocator,
557 stream,
558 tree,
559 indent,
560 start_col,
561 array_type.op_token,
562 array_type.rhs,
563 array_type.len_expr,
564 null,
565 space,
566 );
567 },
568 .ArrayTypeSentinel => {
569 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
570 return renderArrayType(
571 allocator,
572 stream,
573 tree,
574 indent,
575 start_col,
576 array_type.op_token,
577 array_type.rhs,
578 array_type.len_expr,
579 array_type.sentinel,
580 space,
581 );
582 },
545583
546 if (ptr_info.allowzero_token) |allowzero_token| {
547 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
548 }
549 if (ptr_info.align_info) |align_info| {
550 const lparen_token = tree.prevToken(align_info.node.firstToken());
551 const align_token = tree.prevToken(lparen_token);
584 .PtrType => {
585 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
586 const op_tok_id = tree.token_ids[ptr_type.op_token];
587 switch (op_tok_id) {
588 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
589 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
590 try stream.writeAll("[*c")
591 else
592 try stream.writeAll("[*"),
593 else => unreachable,
594 }
595 if (ptr_type.ptr_info.sentinel) |sentinel| {
596 const colon_token = tree.prevToken(sentinel.firstToken());
597 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
598 const sentinel_space = switch (op_tok_id) {
599 .LBracket => Space.None,
600 else => Space.Space,
601 };
602 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
603 }
604 switch (op_tok_id) {
605 .Asterisk, .AsteriskAsterisk => {},
606 .LBracket => try stream.writeByte(']'),
607 else => unreachable,
608 }
609 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
610 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
611 }
612 if (ptr_type.ptr_info.align_info) |align_info| {
613 const lparen_token = tree.prevToken(align_info.node.firstToken());
614 const align_token = tree.prevToken(lparen_token);
552615
553 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
554 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
616 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
617 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
555618
556 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
619 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
557620
558 if (align_info.bit_range) |bit_range| {
559 const colon1 = tree.prevToken(bit_range.start.firstToken());
560 const colon2 = tree.prevToken(bit_range.end.firstToken());
621 if (align_info.bit_range) |bit_range| {
622 const colon1 = tree.prevToken(bit_range.start.firstToken());
623 const colon2 = tree.prevToken(bit_range.end.firstToken());
561624
562 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
563 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
564 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
565 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
625 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
626 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
627 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
628 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
566629
567 const rparen_token = tree.nextToken(bit_range.end.lastToken());
568 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
569 } else {
570 const rparen_token = tree.nextToken(align_info.node.lastToken());
571 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
572 }
573 }
574 if (ptr_info.const_token) |const_token| {
575 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
576 }
577 if (ptr_info.volatile_token) |volatile_token| {
578 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
579 }
580 },
630 const rparen_token = tree.nextToken(bit_range.end.lastToken());
631 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
632 } else {
633 const rparen_token = tree.nextToken(align_info.node.lastToken());
634 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
635 }
636 }
637 if (ptr_type.ptr_info.const_token) |const_token| {
638 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
639 }
640 if (ptr_type.ptr_info.volatile_token) |volatile_token| {
641 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
642 }
643 return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space);
644 },
581645
582 .ArrayType => |array_info| {
583 const lbracket = prefix_op_node.op_token;
584 const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel|
585 sentinel.lastToken()
586 else
587 array_info.len_expr.lastToken());
646 .SliceType => {
647 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
648 try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [
649 if (slice_type.ptr_info.sentinel) |sentinel| {
650 const colon_token = tree.prevToken(sentinel.firstToken());
651 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
652 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
653 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
654 } else {
655 try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ]
656 }
588657
589 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
658 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
659 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
660 }
661 if (slice_type.ptr_info.align_info) |align_info| {
662 const lparen_token = tree.prevToken(align_info.node.firstToken());
663 const align_token = tree.prevToken(lparen_token);
590664
591 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
592 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
593 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
594 const new_space = if (ends_with_comment) Space.Newline else Space.None;
595 try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space);
596 if (starts_with_comment) {
597 try stream.writeByte('\n');
598 }
599 if (ends_with_comment or starts_with_comment) {
600 try stream.writeByteNTimes(' ', indent);
601 }
602 if (array_info.sentinel) |sentinel| {
603 const colon_token = tree.prevToken(sentinel.firstToken());
604 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
605 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
606 }
607 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
608 },
609 .BitNot,
610 .BoolNot,
611 .Negation,
612 .NegationWrap,
613 .OptionalType,
614 .AddressOf,
615 => {
616 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);
617 },
665 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
666 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
618667
619 .Try,
620 .Resume,
621 => {
622 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
623 },
668 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
624669
625 .Await => |await_info| {
626 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
627 },
628 }
670 if (align_info.bit_range) |bit_range| {
671 const colon1 = tree.prevToken(bit_range.start.firstToken());
672 const colon2 = tree.prevToken(bit_range.end.firstToken());
673
674 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
675 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
676 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
677 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
629678
630 return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);
679 const rparen_token = tree.nextToken(bit_range.end.lastToken());
680 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
681 } else {
682 const rparen_token = tree.nextToken(align_info.node.lastToken());
683 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
684 }
685 }
686 if (slice_type.ptr_info.const_token) |const_token| {
687 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
688 }
689 if (slice_type.ptr_info.volatile_token) |volatile_token| {
690 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
691 }
692 return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space);
631693 },
632694
633695 .ArrayInitializer, .ArrayInitializerDot => {
634696 var rtoken: ast.TokenIndex = undefined;
635697 var exprs: []*ast.Node = undefined;
636 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.id) {
698 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
637699 .ArrayInitializerDot => blk: {
638700 const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
639701 rtoken = casted.rtoken;
......@@ -767,14 +829,14 @@ fn renderExpression(
767829 }
768830
769831 try renderExtraNewline(tree, stream, start_col, next_expr);
770 if (next_expr.id != .MultilineStringLiteral) {
832 if (next_expr.tag != .MultilineStringLiteral) {
771833 try stream.writeByteNTimes(' ', new_indent);
772834 }
773835 } else {
774836 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
775837 }
776838 }
777 if (exprs[exprs.len - 1].id != .MultilineStringLiteral) {
839 if (exprs[exprs.len - 1].tag != .MultilineStringLiteral) {
778840 try stream.writeByteNTimes(' ', indent);
779841 }
780842 return renderToken(tree, stream, rtoken, indent, start_col, space);
......@@ -797,7 +859,7 @@ fn renderExpression(
797859 .StructInitializer, .StructInitializerDot => {
798860 var rtoken: ast.TokenIndex = undefined;
799861 var field_inits: []*ast.Node = undefined;
800 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.id) {
862 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
801863 .StructInitializerDot => blk: {
802864 const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);
803865 rtoken = casted.rtoken;
......@@ -851,7 +913,7 @@ fn renderExpression(
851913 if (field_inits.len == 1) blk: {
852914 const field_init = field_inits[0].cast(ast.Node.FieldInitializer).?;
853915
854 switch (field_init.expr.id) {
916 switch (field_init.expr.tag) {
855917 .StructInitializer,
856918 .StructInitializerDot,
857919 => break :blk,
......@@ -948,7 +1010,7 @@ fn renderExpression(
9481010
9491011 const params = call.params();
9501012 for (params) |param_node, i| {
951 const param_node_new_indent = if (param_node.id == .MultilineStringLiteral) blk: {
1013 const param_node_new_indent = if (param_node.tag == .MultilineStringLiteral) blk: {
9521014 break :blk indent;
9531015 } else blk: {
9541016 try stream.writeByteNTimes(' ', new_indent);
......@@ -1179,9 +1241,15 @@ fn renderExpression(
11791241 const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
11801242 return renderToken(tree, stream, error_type.token, indent, start_col, space);
11811243 },
1182 .VarType => {
1183 const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
1184 return renderToken(tree, stream, var_type.token, indent, start_col, space);
1244 .AnyType => {
1245 const any_type = @fieldParentPtr(ast.Node.AnyType, "base", base);
1246 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
1247 // TODO remove in next release cycle
1248 try stream.writeAll("anytype");
1249 if (space == .Comma) try stream.writeAll(",\n");
1250 return;
1251 }
1252 return renderToken(tree, stream, any_type.token, indent, start_col, space);
11851253 },
11861254 .ContainerDecl => {
11871255 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
......@@ -1252,7 +1320,7 @@ fn renderExpression(
12521320 // declarations inside are fields
12531321 const src_has_only_fields = blk: {
12541322 for (fields_and_decls) |decl| {
1255 if (decl.id != .ContainerField) break :blk false;
1323 if (decl.tag != .ContainerField) break :blk false;
12561324 }
12571325 break :blk true;
12581326 };
......@@ -1377,7 +1445,7 @@ fn renderExpression(
13771445 .ErrorTag => {
13781446 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);
13791447
1380 try renderDocComments(tree, stream, tag, indent, start_col);
1448 try renderDocComments(tree, stream, tag, tag.doc_comments, indent, start_col);
13811449 return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name
13821450 },
13831451
......@@ -1451,23 +1519,23 @@ fn renderExpression(
14511519 .FnProto => {
14521520 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
14531521
1454 if (fn_proto.visib_token) |visib_token_index| {
1522 if (fn_proto.getTrailer("visib_token")) |visib_token_index| {
14551523 const visib_token = tree.token_ids[visib_token_index];
14561524 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
14571525
14581526 try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub
14591527 }
14601528
1461 if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
1462 if (!fn_proto.is_extern_prototype)
1529 if (fn_proto.getTrailer("extern_export_inline_token")) |extern_export_inline_token| {
1530 if (fn_proto.getTrailer("is_extern_prototype") == null)
14631531 try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export/inline
14641532 }
14651533
1466 if (fn_proto.lib_name) |lib_name| {
1534 if (fn_proto.getTrailer("lib_name")) |lib_name| {
14671535 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space);
14681536 }
14691537
1470 const lparen = if (fn_proto.name_token) |name_token| blk: {
1538 const lparen = if (fn_proto.getTrailer("name_token")) |name_token| blk: {
14711539 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn
14721540 try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name
14731541 break :blk tree.nextToken(name_token);
......@@ -1480,11 +1548,11 @@ fn renderExpression(
14801548 const rparen = tree.prevToken(
14811549 // the first token for the annotation expressions is the left
14821550 // parenthesis, hence the need for two prevToken
1483 if (fn_proto.align_expr) |align_expr|
1551 if (fn_proto.getTrailer("align_expr")) |align_expr|
14841552 tree.prevToken(tree.prevToken(align_expr.firstToken()))
1485 else if (fn_proto.section_expr) |section_expr|
1553 else if (fn_proto.getTrailer("section_expr")) |section_expr|
14861554 tree.prevToken(tree.prevToken(section_expr.firstToken()))
1487 else if (fn_proto.callconv_expr) |callconv_expr|
1555 else if (fn_proto.getTrailer("callconv_expr")) |callconv_expr|
14881556 tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
14891557 else switch (fn_proto.return_type) {
14901558 .Explicit => |node| node.firstToken(),
......@@ -1505,11 +1573,14 @@ fn renderExpression(
15051573 for (fn_proto.params()) |param_decl, i| {
15061574 try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl, Space.None);
15071575
1508 if (i + 1 < fn_proto.params_len) {
1576 if (i + 1 < fn_proto.params_len or fn_proto.getTrailer("var_args_token") != null) {
15091577 const comma = tree.nextToken(param_decl.lastToken());
15101578 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
15111579 }
15121580 }
1581 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
1582 try renderToken(tree, stream, var_args_token, indent, start_col, Space.None);
1583 }
15131584 } else {
15141585 // one param per line
15151586 const new_indent = indent + indent_delta;
......@@ -1519,12 +1590,16 @@ fn renderExpression(
15191590 try stream.writeByteNTimes(' ', new_indent);
15201591 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl, Space.Comma);
15211592 }
1593 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
1594 try stream.writeByteNTimes(' ', new_indent);
1595 try renderToken(tree, stream, var_args_token, new_indent, start_col, Space.Comma);
1596 }
15221597 try stream.writeByteNTimes(' ', indent);
15231598 }
15241599
15251600 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
15261601
1527 if (fn_proto.align_expr) |align_expr| {
1602 if (fn_proto.getTrailer("align_expr")) |align_expr| {
15281603 const align_rparen = tree.nextToken(align_expr.lastToken());
15291604 const align_lparen = tree.prevToken(align_expr.firstToken());
15301605 const align_kw = tree.prevToken(align_lparen);
......@@ -1535,7 +1610,7 @@ fn renderExpression(
15351610 try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // )
15361611 }
15371612
1538 if (fn_proto.section_expr) |section_expr| {
1613 if (fn_proto.getTrailer("section_expr")) |section_expr| {
15391614 const section_rparen = tree.nextToken(section_expr.lastToken());
15401615 const section_lparen = tree.prevToken(section_expr.firstToken());
15411616 const section_kw = tree.prevToken(section_lparen);
......@@ -1546,7 +1621,7 @@ fn renderExpression(
15461621 try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )
15471622 }
15481623
1549 if (fn_proto.callconv_expr) |callconv_expr| {
1624 if (fn_proto.getTrailer("callconv_expr")) |callconv_expr| {
15501625 const callconv_rparen = tree.nextToken(callconv_expr.lastToken());
15511626 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
15521627 const callconv_kw = tree.prevToken(callconv_lparen);
......@@ -1555,9 +1630,9 @@ fn renderExpression(
15551630 try renderToken(tree, stream, callconv_lparen, indent, start_col, Space.None); // (
15561631 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
15571632 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
1558 } else if (fn_proto.is_extern_prototype) {
1633 } else if (fn_proto.getTrailer("is_extern_prototype") != null) {
15591634 try stream.writeAll("callconv(.C) ");
1560 } else if (fn_proto.is_async) {
1635 } else if (fn_proto.getTrailer("is_async") != null) {
15611636 try stream.writeAll("callconv(.Async) ");
15621637 }
15631638
......@@ -1792,7 +1867,7 @@ fn renderExpression(
17921867
17931868 const rparen = tree.nextToken(for_node.array_expr.lastToken());
17941869
1795 const body_is_block = for_node.body.id == .Block;
1870 const body_is_block = for_node.body.tag == .Block;
17961871 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
17971872 const body_on_same_line = body_is_block or src_one_line_to_body;
17981873
......@@ -1835,7 +1910,7 @@ fn renderExpression(
18351910
18361911 try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition
18371912
1838 const body_is_if_block = if_node.body.id == .If;
1913 const body_is_if_block = if_node.body.tag == .If;
18391914 const body_is_block = nodeIsBlock(if_node.body);
18401915
18411916 if (body_is_if_block) {
......@@ -1939,7 +2014,7 @@ fn renderExpression(
19392014
19402015 const indent_once = indent + indent_delta;
19412016
1942 if (asm_node.template.id == .MultilineStringLiteral) {
2017 if (asm_node.template.tag == .MultilineStringLiteral) {
19432018 // After rendering a multiline string literal the cursor is
19442019 // already offset by indent
19452020 try stream.writeByteNTimes(' ', indent_delta);
......@@ -2051,9 +2126,49 @@ fn renderExpression(
20512126 }
20522127}
20532128
2129fn renderArrayType(
2130 allocator: *mem.Allocator,
2131 stream: anytype,
2132 tree: *ast.Tree,
2133 indent: usize,
2134 start_col: *usize,
2135 lbracket: ast.TokenIndex,
2136 rhs: *ast.Node,
2137 len_expr: *ast.Node,
2138 opt_sentinel: ?*ast.Node,
2139 space: Space,
2140) (@TypeOf(stream).Error || Error)!void {
2141 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
2142 sentinel.lastToken()
2143 else
2144 len_expr.lastToken());
2145
2146 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
2147
2148 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
2149 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
2150 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
2151 const new_space = if (ends_with_comment) Space.Newline else Space.None;
2152 try renderExpression(allocator, stream, tree, new_indent, start_col, len_expr, new_space);
2153 if (starts_with_comment) {
2154 try stream.writeByte('\n');
2155 }
2156 if (ends_with_comment or starts_with_comment) {
2157 try stream.writeByteNTimes(' ', indent);
2158 }
2159 if (opt_sentinel) |sentinel| {
2160 const colon_token = tree.prevToken(sentinel.firstToken());
2161 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
2162 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
2163 }
2164 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
2165
2166 return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
2167}
2168
20542169fn renderAsmOutput(
20552170 allocator: *mem.Allocator,
2056 stream: var,
2171 stream: anytype,
20572172 tree: *ast.Tree,
20582173 indent: usize,
20592174 start_col: *usize,
......@@ -2081,7 +2196,7 @@ fn renderAsmOutput(
20812196
20822197fn renderAsmInput(
20832198 allocator: *mem.Allocator,
2084 stream: var,
2199 stream: anytype,
20852200 tree: *ast.Tree,
20862201 indent: usize,
20872202 start_col: *usize,
......@@ -2099,70 +2214,75 @@ fn renderAsmInput(
20992214
21002215fn renderVarDecl(
21012216 allocator: *mem.Allocator,
2102 stream: var,
2217 stream: anytype,
21032218 tree: *ast.Tree,
21042219 indent: usize,
21052220 start_col: *usize,
21062221 var_decl: *ast.Node.VarDecl,
21072222) (@TypeOf(stream).Error || Error)!void {
2108 if (var_decl.visib_token) |visib_token| {
2223 if (var_decl.getTrailer("visib_token")) |visib_token| {
21092224 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
21102225 }
21112226
2112 if (var_decl.extern_export_token) |extern_export_token| {
2227 if (var_decl.getTrailer("extern_export_token")) |extern_export_token| {
21132228 try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern
21142229
2115 if (var_decl.lib_name) |lib_name| {
2230 if (var_decl.getTrailer("lib_name")) |lib_name| {
21162231 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space); // "lib"
21172232 }
21182233 }
21192234
2120 if (var_decl.comptime_token) |comptime_token| {
2235 if (var_decl.getTrailer("comptime_token")) |comptime_token| {
21212236 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime
21222237 }
21232238
2124 if (var_decl.thread_local_token) |thread_local_token| {
2239 if (var_decl.getTrailer("thread_local_token")) |thread_local_token| {
21252240 try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal
21262241 }
21272242 try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var
21282243
2129 const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or
2130 var_decl.section_node != null or var_decl.init_node != null)) Space.Space else Space.None;
2244 const name_space = if (var_decl.getTrailer("type_node") == null and
2245 (var_decl.getTrailer("align_node") != null or
2246 var_decl.getTrailer("section_node") != null or
2247 var_decl.getTrailer("init_node") != null))
2248 Space.Space
2249 else
2250 Space.None;
21312251 try renderToken(tree, stream, var_decl.name_token, indent, start_col, name_space);
21322252
2133 if (var_decl.type_node) |type_node| {
2253 if (var_decl.getTrailer("type_node")) |type_node| {
21342254 try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, start_col, Space.Space);
2135 const s = if (var_decl.align_node != null or
2136 var_decl.section_node != null or
2137 var_decl.init_node != null) Space.Space else Space.None;
2255 const s = if (var_decl.getTrailer("align_node") != null or
2256 var_decl.getTrailer("section_node") != null or
2257 var_decl.getTrailer("init_node") != null) Space.Space else Space.None;
21382258 try renderExpression(allocator, stream, tree, indent, start_col, type_node, s);
21392259 }
21402260
2141 if (var_decl.align_node) |align_node| {
2261 if (var_decl.getTrailer("align_node")) |align_node| {
21422262 const lparen = tree.prevToken(align_node.firstToken());
21432263 const align_kw = tree.prevToken(lparen);
21442264 const rparen = tree.nextToken(align_node.lastToken());
21452265 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align
21462266 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
21472267 try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None);
2148 const s = if (var_decl.section_node != null or var_decl.init_node != null) Space.Space else Space.None;
2268 const s = if (var_decl.getTrailer("section_node") != null or var_decl.getTrailer("init_node") != null) Space.Space else Space.None;
21492269 try renderToken(tree, stream, rparen, indent, start_col, s); // )
21502270 }
21512271
2152 if (var_decl.section_node) |section_node| {
2272 if (var_decl.getTrailer("section_node")) |section_node| {
21532273 const lparen = tree.prevToken(section_node.firstToken());
21542274 const section_kw = tree.prevToken(lparen);
21552275 const rparen = tree.nextToken(section_node.lastToken());
21562276 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // linksection
21572277 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
21582278 try renderExpression(allocator, stream, tree, indent, start_col, section_node, Space.None);
2159 const s = if (var_decl.init_node != null) Space.Space else Space.None;
2279 const s = if (var_decl.getTrailer("init_node") != null) Space.Space else Space.None;
21602280 try renderToken(tree, stream, rparen, indent, start_col, s); // )
21612281 }
21622282
2163 if (var_decl.init_node) |init_node| {
2164 const s = if (init_node.id == .MultilineStringLiteral) Space.None else Space.Space;
2165 try renderToken(tree, stream, var_decl.eq_token.?, indent, start_col, s); // =
2283 if (var_decl.getTrailer("init_node")) |init_node| {
2284 const s = if (init_node.tag == .MultilineStringLiteral) Space.None else Space.Space;
2285 try renderToken(tree, stream, var_decl.getTrailer("eq_token").?, indent, start_col, s); // =
21662286 try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);
21672287 }
21682288
......@@ -2171,14 +2291,14 @@ fn renderVarDecl(
21712291
21722292fn renderParamDecl(
21732293 allocator: *mem.Allocator,
2174 stream: var,
2294 stream: anytype,
21752295 tree: *ast.Tree,
21762296 indent: usize,
21772297 start_col: *usize,
21782298 param_decl: ast.Node.FnProto.ParamDecl,
21792299 space: Space,
21802300) (@TypeOf(stream).Error || Error)!void {
2181 try renderDocComments(tree, stream, param_decl, indent, start_col);
2301 try renderDocComments(tree, stream, param_decl, param_decl.doc_comments, indent, start_col);
21822302
21832303 if (param_decl.comptime_token) |comptime_token| {
21842304 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space);
......@@ -2191,20 +2311,19 @@ fn renderParamDecl(
21912311 try renderToken(tree, stream, tree.nextToken(name_token), indent, start_col, Space.Space); // :
21922312 }
21932313 switch (param_decl.param_type) {
2194 .var_args => |token| try renderToken(tree, stream, token, indent, start_col, space),
2195 .var_type, .type_expr => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, space),
2314 .any_type, .type_expr => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, space),
21962315 }
21972316}
21982317
21992318fn renderStatement(
22002319 allocator: *mem.Allocator,
2201 stream: var,
2320 stream: anytype,
22022321 tree: *ast.Tree,
22032322 indent: usize,
22042323 start_col: *usize,
22052324 base: *ast.Node,
22062325) (@TypeOf(stream).Error || Error)!void {
2207 switch (base.id) {
2326 switch (base.tag) {
22082327 .VarDecl => {
22092328 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
22102329 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
......@@ -2236,7 +2355,7 @@ const Space = enum {
22362355
22372356fn renderTokenOffset(
22382357 tree: *ast.Tree,
2239 stream: var,
2358 stream: anytype,
22402359 token_index: ast.TokenIndex,
22412360 indent: usize,
22422361 start_col: *usize,
......@@ -2434,7 +2553,7 @@ fn renderTokenOffset(
24342553
24352554fn renderToken(
24362555 tree: *ast.Tree,
2437 stream: var,
2556 stream: anytype,
24382557 token_index: ast.TokenIndex,
24392558 indent: usize,
24402559 start_col: *usize,
......@@ -2445,18 +2564,19 @@ fn renderToken(
24452564
24462565fn renderDocComments(
24472566 tree: *ast.Tree,
2448 stream: var,
2449 node: var,
2567 stream: anytype,
2568 node: anytype,
2569 doc_comments: ?*ast.Node.DocComment,
24502570 indent: usize,
24512571 start_col: *usize,
24522572) (@TypeOf(stream).Error || Error)!void {
2453 const comment = node.doc_comments orelse return;
2573 const comment = doc_comments orelse return;
24542574 return renderDocCommentsToken(tree, stream, comment, node.firstToken(), indent, start_col);
24552575}
24562576
24572577fn renderDocCommentsToken(
24582578 tree: *ast.Tree,
2459 stream: var,
2579 stream: anytype,
24602580 comment: *ast.Node.DocComment,
24612581 first_token: ast.TokenIndex,
24622582 indent: usize,
......@@ -2482,7 +2602,7 @@ fn renderDocCommentsToken(
24822602}
24832603
24842604fn nodeIsBlock(base: *const ast.Node) bool {
2485 return switch (base.id) {
2605 return switch (base.tag) {
24862606 .Block,
24872607 .If,
24882608 .For,
......@@ -2494,10 +2614,52 @@ fn nodeIsBlock(base: *const ast.Node) bool {
24942614}
24952615
24962616fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
2497 const infix_op = base.cast(ast.Node.InfixOp) orelse return false;
2498 return switch (infix_op.op) {
2499 ast.Node.InfixOp.Op.Period => false,
2500 else => true,
2617 return switch (base.tag) {
2618 .Catch,
2619 .Add,
2620 .AddWrap,
2621 .ArrayCat,
2622 .ArrayMult,
2623 .Assign,
2624 .AssignBitAnd,
2625 .AssignBitOr,
2626 .AssignBitShiftLeft,
2627 .AssignBitShiftRight,
2628 .AssignBitXor,
2629 .AssignDiv,
2630 .AssignSub,
2631 .AssignSubWrap,
2632 .AssignMod,
2633 .AssignAdd,
2634 .AssignAddWrap,
2635 .AssignMul,
2636 .AssignMulWrap,
2637 .BangEqual,
2638 .BitAnd,
2639 .BitOr,
2640 .BitShiftLeft,
2641 .BitShiftRight,
2642 .BitXor,
2643 .BoolAnd,
2644 .BoolOr,
2645 .Div,
2646 .EqualEqual,
2647 .ErrorUnion,
2648 .GreaterOrEqual,
2649 .GreaterThan,
2650 .LessOrEqual,
2651 .LessThan,
2652 .MergeErrorSets,
2653 .Mod,
2654 .Mul,
2655 .MulWrap,
2656 .Range,
2657 .Sub,
2658 .SubWrap,
2659 .UnwrapOptional,
2660 => true,
2661
2662 else => false,
25012663 };
25022664}
25032665
......@@ -2532,7 +2694,7 @@ const FindByteOutStream = struct {
25322694 }
25332695};
25342696
2535fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void {
2697fn copyFixingWhitespace(stream: anytype, slice: []const u8) @TypeOf(stream).Error!void {
25362698 for (slice) |byte| switch (byte) {
25372699 '\t' => try stream.writeAll(" "),
25382700 '\r' => {},
lib/std/zig/string_literal.zig+1-1
......@@ -125,7 +125,7 @@ test "parse" {
125125}
126126
127127/// Writes a Zig-syntax escaped string literal to the stream. Includes the double quotes.
128pub fn render(utf8: []const u8, out_stream: var) !void {
128pub fn render(utf8: []const u8, out_stream: anytype) !void {
129129 try out_stream.writeByte('"');
130130 for (utf8) |byte| switch (byte) {
131131 '\n' => try out_stream.writeAll("\\n"),
lib/std/zig/system.zig+6-5
......@@ -130,7 +130,7 @@ pub const NativePaths = struct {
130130 return self.appendArray(&self.include_dirs, s);
131131 }
132132
133 pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void {
133 pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
134134 const item = try std.fmt.allocPrint0(self.include_dirs.allocator, fmt, args);
135135 errdefer self.include_dirs.allocator.free(item);
136136 try self.include_dirs.append(item);
......@@ -140,7 +140,7 @@ pub const NativePaths = struct {
140140 return self.appendArray(&self.lib_dirs, s);
141141 }
142142
143 pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void {
143 pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
144144 const item = try std.fmt.allocPrint0(self.lib_dirs.allocator, fmt, args);
145145 errdefer self.lib_dirs.allocator.free(item);
146146 try self.lib_dirs.append(item);
......@@ -150,7 +150,7 @@ pub const NativePaths = struct {
150150 return self.appendArray(&self.warnings, s);
151151 }
152152
153 pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void {
153 pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
154154 const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args);
155155 errdefer self.warnings.allocator.free(item);
156156 try self.warnings.append(item);
......@@ -161,7 +161,7 @@ pub const NativePaths = struct {
161161 }
162162
163163 fn appendArray(self: *NativePaths, array: *ArrayList([:0]u8), s: []const u8) !void {
164 const item = try std.mem.dupeZ(array.allocator, u8, s);
164 const item = try array.allocator.dupeZ(u8, s);
165165 errdefer array.allocator.free(item);
166166 try array.append(item);
167167 }
......@@ -859,6 +859,7 @@ pub const NativeTargetInfo = struct {
859859 error.ConnectionTimedOut => return error.UnableToReadElfFile,
860860 error.Unexpected => return error.Unexpected,
861861 error.InputOutput => return error.FileSystem,
862 error.AccessDenied => return error.Unexpected,
862863 };
863864 if (len == 0) return error.UnexpectedEndOfFile;
864865 i += len;
......@@ -886,7 +887,7 @@ pub const NativeTargetInfo = struct {
886887 abi: Target.Abi,
887888 };
888889
889 pub fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {
890 pub fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
890891 if (is_64) {
891892 if (need_bswap) {
892893 return @byteSwap(@TypeOf(int_64), int_64);
lib/std/zig/tokenizer.zig+4-1
......@@ -15,6 +15,7 @@ pub const Token = struct {
1515 .{ "allowzero", .Keyword_allowzero },
1616 .{ "and", .Keyword_and },
1717 .{ "anyframe", .Keyword_anyframe },
18 .{ "anytype", .Keyword_anytype },
1819 .{ "asm", .Keyword_asm },
1920 .{ "async", .Keyword_async },
2021 .{ "await", .Keyword_await },
......@@ -140,6 +141,8 @@ pub const Token = struct {
140141 Keyword_align,
141142 Keyword_allowzero,
142143 Keyword_and,
144 Keyword_anyframe,
145 Keyword_anytype,
143146 Keyword_asm,
144147 Keyword_async,
145148 Keyword_await,
......@@ -168,7 +171,6 @@ pub const Token = struct {
168171 Keyword_or,
169172 Keyword_orelse,
170173 Keyword_packed,
171 Keyword_anyframe,
172174 Keyword_pub,
173175 Keyword_resume,
174176 Keyword_return,
......@@ -263,6 +265,7 @@ pub const Token = struct {
263265 .Keyword_allowzero => "allowzero",
264266 .Keyword_and => "and",
265267 .Keyword_anyframe => "anyframe",
268 .Keyword_anytype => "anytype",
266269 .Keyword_asm => "asm",
267270 .Keyword_async => "async",
268271 .Keyword_await => "await",
src-self-hosted/Module.zig+1667-683
......@@ -15,30 +15,39 @@ const ir = @import("ir.zig");
1515const zir = @import("zir.zig");
1616const Module = @This();
1717const Inst = ir.Inst;
18
19/// General-purpose allocator.
20allocator: *Allocator,
18const Body = ir.Body;
19const ast = std.zig.ast;
20const trace = @import("tracy.zig").trace;
21const liveness = @import("liveness.zig");
22const astgen = @import("astgen.zig");
23
24/// General-purpose allocator. Used for both temporary and long-term storage.
25gpa: *Allocator,
2126/// Pointer to externally managed resource.
2227root_pkg: *Package,
2328/// Module owns this resource.
24root_scope: *Scope.ZIRModule,
25bin_file: link.ElfFile,
29/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
30root_scope: *Scope,
31bin_file: *link.File,
2632bin_file_dir: std.fs.Dir,
2733bin_file_path: []const u8,
2834/// It's rare for a decl to be exported, so we save memory by having a sparse map of
2935/// Decl pointers to details about them being exported.
3036/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
31decl_exports: std.AutoHashMap(*Decl, []*Export),
37decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
38/// We track which export is associated with the given symbol name for quick
39/// detection of symbol collisions.
40symbol_exports: std.StringHashMapUnmanaged(*Export) = .{},
3241/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
3342/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
3443/// is performing the export of another Decl.
3544/// This table owns the Export memory.
36export_owners: std.AutoHashMap(*Decl, []*Export),
45export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
3746/// Maps fully qualified namespaced names to the Decl struct for them.
38decl_table: std.AutoHashMap(Decl.Hash, *Decl),
47decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
3948
4049optimize_mode: std.builtin.Mode,
41link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},
50link_error_flags: link.File.ErrorFlags = .{},
4251
4352work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
4453
......@@ -47,28 +56,36 @@ work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
4756/// The ErrorMsg memory is owned by the decl, using Module's allocator.
4857/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
4958/// a Decl can have a failed_decls entry but have analysis status of success.
50failed_decls: std.AutoHashMap(*Decl, *ErrorMsg),
59failed_decls: std.AutoHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
5160/// Using a map here for consistency with the other fields here.
52/// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator.
53failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),
61/// The ErrorMsg memory is owned by the `Scope`, using Module's allocator.
62failed_files: std.AutoHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
5463/// Using a map here for consistency with the other fields here.
5564/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
56failed_exports: std.AutoHashMap(*Export, *ErrorMsg),
65failed_exports: std.AutoHashMapUnmanaged(*Export, *ErrorMsg) = .{},
5766
5867/// Incrementing integer used to compare against the corresponding Decl
5968/// field to determine whether a Decl's status applies to an ongoing update, or a
6069/// previous analysis.
6170generation: u32 = 0,
6271
72next_anon_name_index: usize = 0,
73
6374/// Candidates for deletion. After a semantic analysis update completes, this list
6475/// contains Decls that need to be deleted if they end up having no references to them.
65deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){},
76deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
77
78keep_source_files_loaded: bool,
6679
67pub const WorkItem = union(enum) {
80pub const InnerError = error{ OutOfMemory, AnalysisFail };
81
82const WorkItem = union(enum) {
6883 /// Write the machine code for a Decl to the output file.
6984 codegen_decl: *Decl,
70 /// Decl has been determined to be outdated; perform semantic analysis again.
71 re_analyze_decl: *Decl,
85 /// The Decl needs to be analyzed and possibly export itself.
86 /// It may have already be analyzed, or it may have been determined
87 /// to be outdated; in this case perform semantic analysis again.
88 analyze_decl: *Decl,
7289};
7390
7491pub const Export = struct {
......@@ -76,7 +93,7 @@ pub const Export = struct {
7693 /// Byte offset into the file that contains the export directive.
7794 src: usize,
7895 /// Represents the position of the export, if any, in the output file.
79 link: link.ElfFile.Export,
96 link: link.File.Elf.Export,
8097 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
8198 owner_decl: *Decl,
8299 /// The Decl being exported. Note this is *not* the Decl performing the export.
......@@ -99,13 +116,12 @@ pub const Decl = struct {
99116 /// mapping them to an address in the output file.
100117 /// Memory owned by this decl, using Module's allocator.
101118 name: [*:0]const u8,
102 /// The direct parent container of the Decl. This field will need to get more fleshed out when
103 /// self-hosted supports proper struct types and Zig AST => ZIR.
119 /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`.
104120 /// Reference to externally owned memory.
105 scope: *Scope.ZIRModule,
106 /// Byte offset into the source file that contains this declaration.
107 /// This is the base offset that src offsets within this Decl are relative to.
108 src: usize,
121 scope: *Scope,
122 /// The AST Node decl index or ZIR Inst index that contains this declaration.
123 /// Must be recomputed when the corresponding source file is modified.
124 src_index: usize,
109125 /// The most recent value of the Decl after a successful semantic analysis.
110126 typed_value: union(enum) {
111127 never_succeeded: void,
......@@ -116,6 +132,9 @@ pub const Decl = struct {
116132 /// analysis of the function body is performed with this value set to `success`. Functions
117133 /// have their own analysis status field.
118134 analysis: enum {
135 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
136 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
137 unreferenced,
119138 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
120139 in_progress,
121140 /// This Decl might be OK but it depends on another one which did not successfully complete
......@@ -125,6 +144,10 @@ pub const Decl = struct {
125144 /// There will be a corresponding ErrorMsg in Module.failed_decls.
126145 sema_failure,
127146 /// There will be a corresponding ErrorMsg in Module.failed_decls.
147 /// This indicates the failure was something like running out of disk space,
148 /// and attempting semantic analysis again may succeed.
149 sema_failure_retryable,
150 /// There will be a corresponding ErrorMsg in Module.failed_decls.
128151 codegen_failure,
129152 /// There will be a corresponding ErrorMsg in Module.failed_decls.
130153 /// This indicates the failure was something like running out of disk space,
......@@ -148,49 +171,54 @@ pub const Decl = struct {
148171
149172 /// Represents the position of the code in the output file.
150173 /// This is populated regardless of semantic analysis and code generation.
151 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
174 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
152175
153 contents_hash: Hash,
176 contents_hash: std.zig.SrcHash,
154177
155178 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
156179 /// typed_value is modified.
157 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
180 dependants: DepsTable = .{},
158181 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
159182 /// typed_value may need to be regenerated.
160 dependencies: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},
183 dependencies: DepsTable = .{},
184
185 /// The reason this is not `std.AutoHashMapUnmanaged` is a workaround for
186 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
187 pub const DepsTable = std.HashMapUnmanaged(*Decl, void, std.hash_map.getAutoHashFn(*Decl), std.hash_map.getAutoEqlFn(*Decl), false);
161188
162 pub fn destroy(self: *Decl, allocator: *Allocator) void {
163 allocator.free(mem.spanZ(self.name));
189 pub fn destroy(self: *Decl, gpa: *Allocator) void {
190 gpa.free(mem.spanZ(self.name));
164191 if (self.typedValueManaged()) |tvm| {
165 tvm.deinit(allocator);
192 tvm.deinit(gpa);
166193 }
167 self.dependants.deinit(allocator);
168 self.dependencies.deinit(allocator);
169 allocator.destroy(self);
170 }
171
172 pub const Hash = [16]u8;
173
174 /// If the name is small enough, it is used directly as the hash.
175 /// If it is long, blake3 hash is computed.
176 pub fn hashSimpleName(name: []const u8) Hash {
177 var out: Hash = undefined;
178 if (name.len <= Hash.len) {
179 mem.copy(u8, &out, name);
180 mem.set(u8, out[name.len..], 0);
181 } else {
182 std.crypto.Blake3.hash(name, &out);
194 self.dependants.deinit(gpa);
195 self.dependencies.deinit(gpa);
196 gpa.destroy(self);
197 }
198
199 pub fn src(self: Decl) usize {
200 switch (self.scope.tag) {
201 .file => {
202 const file = @fieldParentPtr(Scope.File, "base", self.scope);
203 const tree = file.contents.tree;
204 const decl_node = tree.root_node.decls()[self.src_index];
205 return tree.token_locs[decl_node.firstToken()].start;
206 },
207 .zir_module => {
208 const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
209 const module = zir_module.contents.module;
210 const src_decl = module.decls[self.src_index];
211 return src_decl.inst.src;
212 },
213 .block => unreachable,
214 .gen_zir => unreachable,
215 .local_var => unreachable,
216 .decl => unreachable,
183217 }
184 return out;
185218 }
186219
187 /// Must generate unique bytes with no collisions with other decls.
188 /// The point of hashing here is only to limit the number of bytes of
189 /// the unique identifier to a fixed size (16 bytes).
190 pub fn fullyQualifiedNameHash(self: Decl) Hash {
191 // Right now we only have ZIRModule as the source. So this is simply the
192 // relative name of the decl.
193 return hashSimpleName(mem.spanZ(self.name));
220 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
221 return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
194222 }
195223
196224 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
......@@ -225,34 +253,20 @@ pub const Decl = struct {
225253 }
226254
227255 fn removeDependant(self: *Decl, other: *Decl) void {
228 for (self.dependants.items) |item, i| {
229 if (item == other) {
230 _ = self.dependants.swapRemove(i);
231 return;
232 }
233 }
234 unreachable;
256 self.dependants.removeAssertDiscard(other);
235257 }
236258
237259 fn removeDependency(self: *Decl, other: *Decl) void {
238 for (self.dependencies.items) |item, i| {
239 if (item == other) {
240 _ = self.dependencies.swapRemove(i);
241 return;
242 }
243 }
244 unreachable;
260 self.dependencies.removeAssertDiscard(other);
245261 }
246262};
247263
248264/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
249265pub const Fn = struct {
250266 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
251 fn_type: Type,
252267 analysis: union(enum) {
253 /// The value is the source instruction.
254 queued: *zir.Inst.Fn,
255 in_progress: *Analysis,
268 queued: *ZIR,
269 in_progress,
256270 /// There will be a corresponding ErrorMsg in Module.failed_decls
257271 sema_failure,
258272 /// This Fn might be OK but it depends on another Decl which did not successfully complete
......@@ -266,16 +280,20 @@ pub const Fn = struct {
266280 /// of Fn analysis.
267281 pub const Analysis = struct {
268282 inner_block: Scope.Block,
269 /// TODO Performance optimization idea: instead of this inst_table,
270 /// use a field in the zir.Inst instead to track corresponding instructions
271 inst_table: std.AutoHashMap(*zir.Inst, *Inst),
272 needed_inst_capacity: usize,
283 };
284
285 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
286 pub const ZIR = struct {
287 body: zir.Module.Body,
288 arena: std.heap.ArenaAllocator.State,
273289 };
274290};
275291
276292pub const Scope = struct {
277293 tag: Tag,
278294
295 pub const NameHash = [16]u8;
296
279297 pub fn cast(base: *Scope, comptime T: type) ?*T {
280298 if (base.tag != T.base_tag)
281299 return null;
......@@ -289,30 +307,76 @@ pub const Scope = struct {
289307 switch (self.tag) {
290308 .block => return self.cast(Block).?.arena,
291309 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
310 .gen_zir => return self.cast(GenZIR).?.arena,
311 .local_var => return self.cast(LocalVar).?.gen_zir.arena,
292312 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
313 .file => unreachable,
293314 }
294315 }
295316
296 /// Asserts the scope has a parent which is a DeclAnalysis and
297 /// returns the Decl.
317 /// If the scope has a parent which is a `DeclAnalysis`,
318 /// returns the `Decl`, otherwise returns `null`.
298319 pub fn decl(self: *Scope) ?*Decl {
299320 return switch (self.tag) {
300321 .block => self.cast(Block).?.decl,
322 .gen_zir => self.cast(GenZIR).?.decl,
323 .local_var => return self.cast(LocalVar).?.gen_zir.decl,
301324 .decl => self.cast(DeclAnalysis).?.decl,
302325 .zir_module => null,
326 .file => null,
303327 };
304328 }
305329
306 /// Asserts the scope has a parent which is a ZIRModule and
330 /// Asserts the scope has a parent which is a ZIRModule or File and
307331 /// returns it.
308 pub fn namespace(self: *Scope) *ZIRModule {
332 pub fn namespace(self: *Scope) *Scope {
309333 switch (self.tag) {
310334 .block => return self.cast(Block).?.decl.scope,
335 .gen_zir => return self.cast(GenZIR).?.decl.scope,
336 .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope,
311337 .decl => return self.cast(DeclAnalysis).?.decl.scope,
312 .zir_module => return self.cast(ZIRModule).?,
338 .zir_module, .file => return self,
339 }
340 }
341
342 /// Must generate unique bytes with no collisions with other decls.
343 /// The point of hashing here is only to limit the number of bytes of
344 /// the unique identifier to a fixed size (16 bytes).
345 pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
346 switch (self.tag) {
347 .block => unreachable,
348 .gen_zir => unreachable,
349 .local_var => unreachable,
350 .decl => unreachable,
351 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
352 .file => return self.cast(File).?.fullyQualifiedNameHash(name),
313353 }
314354 }
315355
356 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
357 pub fn tree(self: *Scope) *ast.Tree {
358 switch (self.tag) {
359 .file => return self.cast(File).?.contents.tree,
360 .zir_module => unreachable,
361 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,
362 .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,
363 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,
364 .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope.cast(File).?.contents.tree,
365 }
366 }
367
368 /// Asserts the scope is a child of a `GenZIR` and returns it.
369 pub fn getGenZIR(self: *Scope) *GenZIR {
370 return switch (self.tag) {
371 .block => unreachable,
372 .gen_zir => self.cast(GenZIR).?,
373 .local_var => return self.cast(LocalVar).?.gen_zir,
374 .decl => unreachable,
375 .zir_module => unreachable,
376 .file => unreachable,
377 };
378 }
379
316380 pub fn dumpInst(self: *Scope, inst: *Inst) void {
317381 const zir_module = self.namespace();
318382 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
......@@ -325,10 +389,179 @@ pub const Scope = struct {
325389 });
326390 }
327391
392 /// Asserts the scope has a parent which is a ZIRModule or File and
393 /// returns the sub_file_path field.
394 pub fn subFilePath(base: *Scope) []const u8 {
395 switch (base.tag) {
396 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
397 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
398 .block => unreachable,
399 .gen_zir => unreachable,
400 .local_var => unreachable,
401 .decl => unreachable,
402 }
403 }
404
405 pub fn unload(base: *Scope, gpa: *Allocator) void {
406 switch (base.tag) {
407 .file => return @fieldParentPtr(File, "base", base).unload(gpa),
408 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
409 .block => unreachable,
410 .gen_zir => unreachable,
411 .local_var => unreachable,
412 .decl => unreachable,
413 }
414 }
415
416 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
417 switch (base.tag) {
418 .file => return @fieldParentPtr(File, "base", base).getSource(module),
419 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
420 .gen_zir => unreachable,
421 .local_var => unreachable,
422 .block => unreachable,
423 .decl => unreachable,
424 }
425 }
426
427 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
428 pub fn removeDecl(base: *Scope, child: *Decl) void {
429 switch (base.tag) {
430 .file => return @fieldParentPtr(File, "base", base).removeDecl(child),
431 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
432 .block => unreachable,
433 .gen_zir => unreachable,
434 .local_var => unreachable,
435 .decl => unreachable,
436 }
437 }
438
439 /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
440 pub fn destroy(base: *Scope, gpa: *Allocator) void {
441 switch (base.tag) {
442 .file => {
443 const scope_file = @fieldParentPtr(File, "base", base);
444 scope_file.deinit(gpa);
445 gpa.destroy(scope_file);
446 },
447 .zir_module => {
448 const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
449 scope_zir_module.deinit(gpa);
450 gpa.destroy(scope_zir_module);
451 },
452 .block => unreachable,
453 .gen_zir => unreachable,
454 .local_var => unreachable,
455 .decl => unreachable,
456 }
457 }
458
459 fn name_hash_hash(x: NameHash) u32 {
460 return @truncate(u32, @bitCast(u128, x));
461 }
462
463 fn name_hash_eql(a: NameHash, b: NameHash) bool {
464 return @bitCast(u128, a) == @bitCast(u128, b);
465 }
466
328467 pub const Tag = enum {
468 /// .zir source code.
329469 zir_module,
470 /// .zig source code.
471 file,
330472 block,
331473 decl,
474 gen_zir,
475 local_var,
476 };
477
478 pub const File = struct {
479 pub const base_tag: Tag = .file;
480 base: Scope = Scope{ .tag = base_tag },
481
482 /// Relative to the owning package's root_src_dir.
483 /// Reference to external memory, not owned by File.
484 sub_file_path: []const u8,
485 source: union(enum) {
486 unloaded: void,
487 bytes: [:0]const u8,
488 },
489 contents: union {
490 not_available: void,
491 tree: *ast.Tree,
492 },
493 status: enum {
494 never_loaded,
495 unloaded_success,
496 unloaded_parse_failure,
497 loaded_success,
498 },
499
500 /// Direct children of the file.
501 decls: ArrayListUnmanaged(*Decl),
502
503 pub fn unload(self: *File, gpa: *Allocator) void {
504 switch (self.status) {
505 .never_loaded,
506 .unloaded_parse_failure,
507 .unloaded_success,
508 => {},
509
510 .loaded_success => {
511 self.contents.tree.deinit();
512 self.status = .unloaded_success;
513 },
514 }
515 switch (self.source) {
516 .bytes => |bytes| {
517 gpa.free(bytes);
518 self.source = .{ .unloaded = {} };
519 },
520 .unloaded => {},
521 }
522 }
523
524 pub fn deinit(self: *File, gpa: *Allocator) void {
525 self.decls.deinit(gpa);
526 self.unload(gpa);
527 self.* = undefined;
528 }
529
530 pub fn removeDecl(self: *File, child: *Decl) void {
531 for (self.decls.items) |item, i| {
532 if (item == child) {
533 _ = self.decls.swapRemove(i);
534 return;
535 }
536 }
537 }
538
539 pub fn dumpSrc(self: *File, src: usize) void {
540 const loc = std.zig.findLineColumn(self.source.bytes, src);
541 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
542 }
543
544 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
545 switch (self.source) {
546 .unloaded => {
547 const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
548 module.gpa,
549 self.sub_file_path,
550 std.math.maxInt(u32),
551 1,
552 0,
553 );
554 self.source = .{ .bytes = source };
555 return source;
556 },
557 .bytes => |bytes| return bytes,
558 }
559 }
560
561 pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash {
562 // We don't have struct scopes yet so this is currently just a simple name hash.
563 return std.zig.hashSrc(name);
564 }
332565 };
333566
334567 pub const ZIRModule = struct {
......@@ -355,7 +588,12 @@ pub const Scope = struct {
355588 loaded_success,
356589 },
357590
358 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {
591 /// Even though .zir files only have 1 module, this set is still needed
592 /// because of anonymous Decls, which can exist in the global set, but
593 /// not this one.
594 decls: ArrayListUnmanaged(*Decl),
595
596 pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
359597 switch (self.status) {
360598 .never_loaded,
361599 .unloaded_parse_failure,
......@@ -364,34 +602,68 @@ pub const Scope = struct {
364602 => {},
365603
366604 .loaded_success => {
367 self.contents.module.deinit(allocator);
368 allocator.destroy(self.contents.module);
605 self.contents.module.deinit(gpa);
606 gpa.destroy(self.contents.module);
607 self.contents = .{ .not_available = {} };
369608 self.status = .unloaded_success;
370609 },
371610 .loaded_sema_failure => {
372 self.contents.module.deinit(allocator);
373 allocator.destroy(self.contents.module);
611 self.contents.module.deinit(gpa);
612 gpa.destroy(self.contents.module);
613 self.contents = .{ .not_available = {} };
374614 self.status = .unloaded_sema_failure;
375615 },
376616 }
377617 switch (self.source) {
378618 .bytes => |bytes| {
379 allocator.free(bytes);
619 gpa.free(bytes);
380620 self.source = .{ .unloaded = {} };
381621 },
382622 .unloaded => {},
383623 }
384624 }
385625
386 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {
387 self.unload(allocator);
626 pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
627 self.decls.deinit(gpa);
628 self.unload(gpa);
388629 self.* = undefined;
389630 }
390631
632 pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
633 for (self.decls.items) |item, i| {
634 if (item == child) {
635 _ = self.decls.swapRemove(i);
636 return;
637 }
638 }
639 }
640
391641 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
392642 const loc = std.zig.findLineColumn(self.source.bytes, src);
393643 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
394644 }
645
646 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
647 switch (self.source) {
648 .unloaded => {
649 const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
650 module.gpa,
651 self.sub_file_path,
652 std.math.maxInt(u32),
653 1,
654 0,
655 );
656 self.source = .{ .bytes = source };
657 return source;
658 },
659 .bytes => |bytes| return bytes,
660 }
661 }
662
663 pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
664 // ZIR modules only have 1 file with all decls global in the same namespace.
665 return std.zig.hashSrc(name);
666 }
395667 };
396668
397669 /// This is a temporary structure, references to it are valid only
......@@ -399,11 +671,19 @@ pub const Scope = struct {
399671 pub const Block = struct {
400672 pub const base_tag: Tag = .block;
401673 base: Scope = Scope{ .tag = base_tag },
402 func: *Fn,
674 parent: ?*Block,
675 func: ?*Fn,
403676 decl: *Decl,
404677 instructions: ArrayListUnmanaged(*Inst),
405678 /// Points to the arena allocator of DeclAnalysis
406679 arena: *Allocator,
680 label: ?Label = null,
681
682 pub const Label = struct {
683 zir_block: *zir.Inst.Block,
684 results: ArrayListUnmanaged(*Inst),
685 block_inst: *Inst.Block,
686 };
407687 };
408688
409689 /// This is a temporary structure, references to it are valid only
......@@ -414,10 +694,31 @@ pub const Scope = struct {
414694 decl: *Decl,
415695 arena: std.heap.ArenaAllocator,
416696 };
417};
418697
419pub const Body = struct {
420 instructions: []*Inst,
698 /// This is a temporary structure, references to it are valid only
699 /// during semantic analysis of the decl.
700 pub const GenZIR = struct {
701 pub const base_tag: Tag = .gen_zir;
702 base: Scope = Scope{ .tag = base_tag },
703 /// Parents can be: `GenZIR`, `ZIRModule`, `File`
704 parent: *Scope,
705 decl: *Decl,
706 arena: *Allocator,
707 /// The first N instructions in a function body ZIR are arg instructions.
708 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
709 };
710
711 /// This structure lives as long as the AST generation of the Block
712 /// node that contains the variable.
713 pub const LocalVar = struct {
714 pub const base_tag: Tag = .local_var;
715 base: Scope = Scope{ .tag = base_tag },
716 /// Parents can be: `LocalVar`, `GenZIR`.
717 parent: *Scope,
718 gen_zir: *GenZIR,
719 name: []const u8,
720 inst: *zir.Inst,
721 };
421722};
422723
423724pub const AllErrors = struct {
......@@ -432,8 +733,8 @@ pub const AllErrors = struct {
432733 msg: []const u8,
433734 };
434735
435 pub fn deinit(self: *AllErrors, allocator: *Allocator) void {
436 self.arena.promote(allocator).deinit();
736 pub fn deinit(self: *AllErrors, gpa: *Allocator) void {
737 self.arena.promote(gpa).deinit();
437738 }
438739
439740 fn add(
......@@ -463,147 +764,163 @@ pub const InitOptions = struct {
463764 link_mode: ?std.builtin.LinkMode = null,
464765 object_format: ?std.builtin.ObjectFormat = null,
465766 optimize_mode: std.builtin.Mode = .Debug,
767 keep_source_files_loaded: bool = false,
466768};
467769
468770pub fn init(gpa: *Allocator, options: InitOptions) !Module {
469 const root_scope = try gpa.create(Scope.ZIRModule);
470 errdefer gpa.destroy(root_scope);
471
472 root_scope.* = .{
473 .sub_file_path = options.root_pkg.root_src_path,
474 .source = .{ .unloaded = {} },
475 .contents = .{ .not_available = {} },
476 .status = .never_loaded,
477 };
478
479771 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
480 var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
772 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
481773 .target = options.target,
482774 .output_mode = options.output_mode,
483775 .link_mode = options.link_mode orelse .Static,
484776 .object_format = options.object_format orelse options.target.getObjectFormat(),
485777 });
486 errdefer bin_file.deinit();
778 errdefer bin_file.destroy();
779
780 const root_scope = blk: {
781 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
782 const root_scope = try gpa.create(Scope.File);
783 root_scope.* = .{
784 .sub_file_path = options.root_pkg.root_src_path,
785 .source = .{ .unloaded = {} },
786 .contents = .{ .not_available = {} },
787 .status = .never_loaded,
788 .decls = .{},
789 };
790 break :blk &root_scope.base;
791 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
792 const root_scope = try gpa.create(Scope.ZIRModule);
793 root_scope.* = .{
794 .sub_file_path = options.root_pkg.root_src_path,
795 .source = .{ .unloaded = {} },
796 .contents = .{ .not_available = {} },
797 .status = .never_loaded,
798 .decls = .{},
799 };
800 break :blk &root_scope.base;
801 } else {
802 unreachable;
803 }
804 };
487805
488806 return Module{
489 .allocator = gpa,
807 .gpa = gpa,
490808 .root_pkg = options.root_pkg,
491809 .root_scope = root_scope,
492810 .bin_file_dir = bin_file_dir,
493811 .bin_file_path = options.bin_file_path,
494812 .bin_file = bin_file,
495813 .optimize_mode = options.optimize_mode,
496 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa),
497 .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa),
498 .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa),
499 .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa),
500 .failed_files = std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg).init(gpa),
501 .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa),
502814 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
815 .keep_source_files_loaded = options.keep_source_files_loaded,
503816 };
504817}
505818
506819pub fn deinit(self: *Module) void {
507 self.bin_file.deinit();
508 const allocator = self.allocator;
509 self.deletion_set.deinit(allocator);
820 self.bin_file.destroy();
821 const gpa = self.gpa;
822 self.deletion_set.deinit(gpa);
510823 self.work_queue.deinit();
511 {
512 var it = self.decl_table.iterator();
513 while (it.next()) |kv| {
514 kv.value.destroy(allocator);
515 }
516 self.decl_table.deinit();
517 }
518 {
519 var it = self.failed_decls.iterator();
520 while (it.next()) |kv| {
521 kv.value.destroy(allocator);
522 }
523 self.failed_decls.deinit();
824
825 for (self.decl_table.items()) |entry| {
826 entry.value.destroy(gpa);
524827 }
525 {
526 var it = self.failed_files.iterator();
527 while (it.next()) |kv| {
528 kv.value.destroy(allocator);
529 }
530 self.failed_files.deinit();
828 self.decl_table.deinit(gpa);
829
830 for (self.failed_decls.items()) |entry| {
831 entry.value.destroy(gpa);
531832 }
532 {
533 var it = self.failed_exports.iterator();
534 while (it.next()) |kv| {
535 kv.value.destroy(allocator);
536 }
537 self.failed_exports.deinit();
833 self.failed_decls.deinit(gpa);
834
835 for (self.failed_files.items()) |entry| {
836 entry.value.destroy(gpa);
538837 }
539 {
540 var it = self.decl_exports.iterator();
541 while (it.next()) |kv| {
542 const export_list = kv.value;
543 allocator.free(export_list);
544 }
545 self.decl_exports.deinit();
838 self.failed_files.deinit(gpa);
839
840 for (self.failed_exports.items()) |entry| {
841 entry.value.destroy(gpa);
546842 }
547 {
548 var it = self.export_owners.iterator();
549 while (it.next()) |kv| {
550 freeExportList(allocator, kv.value);
551 }
552 self.export_owners.deinit();
843 self.failed_exports.deinit(gpa);
844
845 for (self.decl_exports.items()) |entry| {
846 const export_list = entry.value;
847 gpa.free(export_list);
553848 }
554 {
555 self.root_scope.deinit(allocator);
556 allocator.destroy(self.root_scope);
849 self.decl_exports.deinit(gpa);
850
851 for (self.export_owners.items()) |entry| {
852 freeExportList(gpa, entry.value);
557853 }
854 self.export_owners.deinit(gpa);
855
856 self.symbol_exports.deinit(gpa);
857 self.root_scope.destroy(gpa);
558858 self.* = undefined;
559859}
560860
561fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
861fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
562862 for (export_list) |exp| {
563 allocator.destroy(exp);
863 gpa.destroy(exp);
564864 }
565 allocator.free(export_list);
865 gpa.free(export_list);
566866}
567867
568868pub fn target(self: Module) std.Target {
569 return self.bin_file.options.target;
869 return self.bin_file.options().target;
570870}
571871
572872/// Detect changes to source files, perform semantic analysis, and update the output files.
573873pub fn update(self: *Module) !void {
874 const tracy = trace(@src());
875 defer tracy.end();
876
574877 self.generation += 1;
575878
576879 // TODO Use the cache hash file system to detect which source files changed.
577 // Here we simulate a full cache miss.
578 // Analyze the root source file now.
579 // Source files could have been loaded for any reason; to force a refresh we unload now.
580 self.root_scope.unload(self.allocator);
581 self.analyzeRoot(self.root_scope) catch |err| switch (err) {
582 error.AnalysisFail => {
583 assert(self.totalErrorCount() != 0);
584 },
585 else => |e| return e,
586 };
880 // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
881 // to force a refresh we unload now.
882 if (self.root_scope.cast(Scope.File)) |zig_file| {
883 zig_file.unload(self.gpa);
884 self.analyzeRootSrcFile(zig_file) catch |err| switch (err) {
885 error.AnalysisFail => {
886 assert(self.totalErrorCount() != 0);
887 },
888 else => |e| return e,
889 };
890 } else if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| {
891 zir_module.unload(self.gpa);
892 self.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
893 error.AnalysisFail => {
894 assert(self.totalErrorCount() != 0);
895 },
896 else => |e| return e,
897 };
898 }
587899
588900 try self.performAllTheWork();
589901
590902 // Process the deletion set.
591903 while (self.deletion_set.popOrNull()) |decl| {
592 if (decl.dependants.items.len != 0) {
904 if (decl.dependants.items().len != 0) {
593905 decl.deletion_flag = false;
594906 continue;
595907 }
596908 try self.deleteDecl(decl);
597909 }
598910
599 // If there are any errors, we anticipate the source files being loaded
600 // to report error messages. Otherwise we unload all source files to save memory.
601911 if (self.totalErrorCount() == 0) {
602 self.root_scope.unload(self.allocator);
912 // This is needed before reading the error flags.
913 try self.bin_file.flush();
603914 }
604915
605 try self.bin_file.flush();
606 self.link_error_flags = self.bin_file.error_flags;
916 self.link_error_flags = self.bin_file.errorFlags();
917 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
918
919 // If there are any errors, we anticipate the source files being loaded
920 // to report error messages. Otherwise we unload all source files to save memory.
921 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
922 self.root_scope.unload(self.gpa);
923 }
607924}
608925
609926/// Having the file open for writing is problematic as far as executing the
......@@ -619,48 +936,39 @@ pub fn makeBinFileWritable(self: *Module) !void {
619936}
620937
621938pub fn totalErrorCount(self: *Module) usize {
622 return self.failed_decls.size +
623 self.failed_files.size +
624 self.failed_exports.size +
625 @boolToInt(self.link_error_flags.no_entry_point_found);
939 const total = self.failed_decls.items().len +
940 self.failed_files.items().len +
941 self.failed_exports.items().len;
942 return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total;
626943}
627944
628945pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
629 var arena = std.heap.ArenaAllocator.init(self.allocator);
946 var arena = std.heap.ArenaAllocator.init(self.gpa);
630947 errdefer arena.deinit();
631948
632 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);
949 var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
633950 defer errors.deinit();
634951
635 {
636 var it = self.failed_files.iterator();
637 while (it.next()) |kv| {
638 const scope = kv.key;
639 const err_msg = kv.value;
640 const source = try self.getSource(scope);
641 try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*);
642 }
952 for (self.failed_files.items()) |entry| {
953 const scope = entry.key;
954 const err_msg = entry.value;
955 const source = try scope.getSource(self);
956 try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
643957 }
644 {
645 var it = self.failed_decls.iterator();
646 while (it.next()) |kv| {
647 const decl = kv.key;
648 const err_msg = kv.value;
649 const source = try self.getSource(decl.scope);
650 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
651 }
958 for (self.failed_decls.items()) |entry| {
959 const decl = entry.key;
960 const err_msg = entry.value;
961 const source = try decl.scope.getSource(self);
962 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
652963 }
653 {
654 var it = self.failed_exports.iterator();
655 while (it.next()) |kv| {
656 const decl = kv.key.owner_decl;
657 const err_msg = kv.value;
658 const source = try self.getSource(decl.scope);
659 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
660 }
964 for (self.failed_exports.items()) |entry| {
965 const decl = entry.key.owner_decl;
966 const err_msg = entry.value;
967 const source = try decl.scope.getSource(self);
968 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
661969 }
662970
663 if (self.link_error_flags.no_entry_point_found) {
971 if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
664972 try errors.append(.{
665973 .src_path = self.root_pkg.root_src_path,
666974 .line = 0,
......@@ -678,17 +986,17 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
678986 };
679987}
680988
681const InnerError = error{ OutOfMemory, AnalysisFail };
682
683989pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
684990 while (self.work_queue.readItem()) |work_item| switch (work_item) {
685991 .codegen_decl => |decl| switch (decl.analysis) {
992 .unreferenced => unreachable,
686993 .in_progress => unreachable,
687994 .outdated => unreachable,
688995
689996 .sema_failure,
690997 .codegen_failure,
691998 .dependency_failure,
999 .sema_failure_retryable,
6921000 => continue,
6931001
6941002 .complete, .codegen_failure_retryable => {
......@@ -696,17 +1004,21 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
6961004 switch (payload.func.analysis) {
6971005 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
6981006 error.AnalysisFail => {
699 if (payload.func.analysis == .queued) {
700 payload.func.analysis = .dependency_failure;
701 }
1007 assert(payload.func.analysis != .in_progress);
7021008 continue;
7031009 },
704 else => |e| return e,
1010 error.OutOfMemory => return error.OutOfMemory,
7051011 },
7061012 .in_progress => unreachable,
7071013 .sema_failure, .dependency_failure => continue,
7081014 .success => {},
7091015 }
1016 // Here we tack on additional allocations to the Decl's arena. The allocations are
1017 // lifetime annotations in the ZIR.
1018 var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
1019 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
1020 std.log.debug(.module, "analyze liveness of {}\n", .{decl.name});
1021 try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);
7101022 }
7111023
7121024 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
......@@ -716,108 +1028,363 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
7161028 error.AnalysisFail => {
7171029 decl.analysis = .dependency_failure;
7181030 },
1031 error.CGenFailure => {
1032 // Error is handled by CBE, don't try adding it again
1033 },
7191034 else => {
720 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
721 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
722 self.allocator,
723 decl.src,
724 "unable to codegen: {}",
725 .{@errorName(err)},
726 ));
1035 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1036 const result = self.failed_decls.getOrPutAssumeCapacity(decl);
1037 if (result.found_existing) {
1038 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });
1039 } else {
1040 result.entry.value = try ErrorMsg.create(
1041 self.gpa,
1042 decl.src(),
1043 "unable to codegen: {}",
1044 .{@errorName(err)},
1045 );
1046 }
7271047 decl.analysis = .codegen_failure_retryable;
7281048 },
7291049 };
7301050 },
7311051 },
732 .re_analyze_decl => |decl| switch (decl.analysis) {
733 .in_progress => unreachable,
1052 .analyze_decl => |decl| {
1053 self.ensureDeclAnalyzed(decl) catch |err| switch (err) {
1054 error.OutOfMemory => return error.OutOfMemory,
1055 error.AnalysisFail => continue,
1056 };
1057 },
1058 };
1059}
7341060
735 .sema_failure,
736 .codegen_failure,
737 .dependency_failure,
738 .complete,
739 .codegen_failure_retryable,
740 => continue,
1061fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1062 const tracy = trace(@src());
1063 defer tracy.end();
7411064
742 .outdated => {
743 const zir_module = self.getSrcModule(decl.scope) catch |err| switch (err) {
744 error.OutOfMemory => return error.OutOfMemory,
745 else => {
746 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
747 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
748 self.allocator,
749 decl.src,
750 "unable to load source file '{}': {}",
751 .{ decl.scope.sub_file_path, @errorName(err) },
752 ));
753 decl.analysis = .codegen_failure_retryable;
754 continue;
755 },
756 };
757 const decl_name = mem.spanZ(decl.name);
758 // We already detected deletions, so we know this will be found.
759 const src_decl = zir_module.findDecl(decl_name).?;
760 self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) {
761 error.OutOfMemory => return error.OutOfMemory,
762 error.AnalysisFail => continue,
763 };
764 },
1065 const subsequent_analysis = switch (decl.analysis) {
1066 .in_progress => unreachable,
1067
1068 .sema_failure,
1069 .sema_failure_retryable,
1070 .codegen_failure,
1071 .dependency_failure,
1072 .codegen_failure_retryable,
1073 => return error.AnalysisFail,
1074
1075 .complete, .outdated => blk: {
1076 if (decl.generation == self.generation) {
1077 assert(decl.analysis == .complete);
1078 return;
1079 }
1080 //std.debug.warn("re-analyzing {}\n", .{decl.name});
1081
1082 // The exports this Decl performs will be re-discovered, so we remove them here
1083 // prior to re-analysis.
1084 self.deleteDeclExports(decl);
1085 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
1086 for (decl.dependencies.items()) |entry| {
1087 const dep = entry.key;
1088 dep.removeDependant(decl);
1089 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
1090 // We don't perform a deletion here, because this Decl or another one
1091 // may end up referencing it before the update is complete.
1092 dep.deletion_flag = true;
1093 try self.deletion_set.append(self.gpa, dep);
1094 }
1095 }
1096 decl.dependencies.clearRetainingCapacity();
1097
1098 break :blk true;
7651099 },
1100
1101 .unreferenced => false,
7661102 };
767}
7681103
769fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
770 try depender.dependencies.ensureCapacity(self.allocator, depender.dependencies.items.len + 1);
771 try dependee.dependants.ensureCapacity(self.allocator, dependee.dependants.items.len + 1);
1104 const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
1105 try self.analyzeZirDecl(decl, zir_module.contents.module.decls[decl.src_index])
1106 else
1107 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
1108 error.OutOfMemory => return error.OutOfMemory,
1109 error.AnalysisFail => return error.AnalysisFail,
1110 else => {
1111 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1112 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1113 self.gpa,
1114 decl.src(),
1115 "unable to analyze: {}",
1116 .{@errorName(err)},
1117 ));
1118 decl.analysis = .sema_failure_retryable;
1119 return error.AnalysisFail;
1120 },
1121 };
7721122
773 for (depender.dependencies.items) |item| {
774 if (item == dependee) break; // Already in the set.
775 } else {
776 depender.dependencies.appendAssumeCapacity(dependee);
1123 if (subsequent_analysis) {
1124 // We may need to chase the dependants and re-analyze them.
1125 // However, if the decl is a function, and the type is the same, we do not need to.
1126 if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
1127 for (decl.dependants.items()) |entry| {
1128 const dep = entry.key;
1129 switch (dep.analysis) {
1130 .unreferenced => unreachable,
1131 .in_progress => unreachable,
1132 .outdated => continue, // already queued for update
1133
1134 .dependency_failure,
1135 .sema_failure,
1136 .sema_failure_retryable,
1137 .codegen_failure,
1138 .codegen_failure_retryable,
1139 .complete,
1140 => if (dep.generation != self.generation) {
1141 try self.markOutdatedDecl(dep);
1142 },
1143 }
1144 }
1145 }
7771146 }
1147}
7781148
779 for (dependee.dependants.items) |item| {
780 if (item == depender) break; // Already in the set.
781 } else {
782 dependee.dependants.appendAssumeCapacity(depender);
1149fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1150 const tracy = trace(@src());
1151 defer tracy.end();
1152
1153 const file_scope = decl.scope.cast(Scope.File).?;
1154 const tree = try self.getAstTree(file_scope);
1155 const ast_node = tree.root_node.decls()[decl.src_index];
1156 switch (ast_node.tag) {
1157 .FnProto => {
1158 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
1159
1160 decl.analysis = .in_progress;
1161
1162 // This arena allocator's memory is discarded at the end of this function. It is used
1163 // to determine the type of the function, and hence the type of the decl, which is needed
1164 // to complete the Decl analysis.
1165 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1166 defer fn_type_scope_arena.deinit();
1167 var fn_type_scope: Scope.GenZIR = .{
1168 .decl = decl,
1169 .arena = &fn_type_scope_arena.allocator,
1170 .parent = decl.scope,
1171 };
1172 defer fn_type_scope.instructions.deinit(self.gpa);
1173
1174 const body_node = fn_proto.getTrailer("body_node") orelse
1175 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
1176
1177 const param_decls = fn_proto.params();
1178 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
1179 for (param_decls) |param_decl, i| {
1180 const param_type_node = switch (param_decl.param_type) {
1181 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
1182 .type_expr => |node| node,
1183 };
1184 param_types[i] = try astgen.expr(self, &fn_type_scope.base, param_type_node);
1185 }
1186 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
1187 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
1188 }
1189 if (fn_proto.getTrailer("lib_name")) |lib_name| {
1190 return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
1191 }
1192 if (fn_proto.getTrailer("align_expr")) |align_expr| {
1193 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
1194 }
1195 if (fn_proto.getTrailer("section_expr")) |sect_expr| {
1196 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
1197 }
1198 if (fn_proto.getTrailer("callconv_expr")) |callconv_expr| {
1199 return self.failNode(
1200 &fn_type_scope.base,
1201 callconv_expr,
1202 "TODO implement function calling convention expression",
1203 .{},
1204 );
1205 }
1206 const return_type_expr = switch (fn_proto.return_type) {
1207 .Explicit => |node| node,
1208 .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
1209 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
1210 };
1211
1212 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, return_type_expr);
1213 const fn_src = tree.token_locs[fn_proto.fn_token].start;
1214 const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1215 .return_type = return_type_inst,
1216 .param_types = param_types,
1217 }, .{});
1218 _ = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.Return, .{ .operand = fn_type_inst }, .{});
1219
1220 // We need the memory for the Type to go into the arena for the Decl
1221 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1222 errdefer decl_arena.deinit();
1223 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1224
1225 var block_scope: Scope.Block = .{
1226 .parent = null,
1227 .func = null,
1228 .decl = decl,
1229 .instructions = .{},
1230 .arena = &decl_arena.allocator,
1231 };
1232 defer block_scope.instructions.deinit(self.gpa);
1233
1234 const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{
1235 .instructions = fn_type_scope.instructions.items,
1236 });
1237 const new_func = try decl_arena.allocator.create(Fn);
1238 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
1239
1240 const fn_zir = blk: {
1241 // This scope's arena memory is discarded after the ZIR generation
1242 // pass completes, and semantic analysis of it completes.
1243 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1244 errdefer gen_scope_arena.deinit();
1245 var gen_scope: Scope.GenZIR = .{
1246 .decl = decl,
1247 .arena = &gen_scope_arena.allocator,
1248 .parent = decl.scope,
1249 };
1250 defer gen_scope.instructions.deinit(self.gpa);
1251
1252 // We need an instruction for each parameter, and they must be first in the body.
1253 try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
1254 var params_scope = &gen_scope.base;
1255 for (fn_proto.params()) |param, i| {
1256 const name_token = param.name_token.?;
1257 const src = tree.token_locs[name_token].start;
1258 const param_name = tree.tokenSlice(name_token);
1259 const arg = try newZIRInst(&gen_scope_arena.allocator, src, zir.Inst.Arg, .{}, .{});
1260 gen_scope.instructions.items[i] = &arg.base;
1261 const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar);
1262 sub_scope.* = .{
1263 .parent = params_scope,
1264 .gen_zir = &gen_scope,
1265 .name = param_name,
1266 .inst = &arg.base,
1267 };
1268 params_scope = &sub_scope.base;
1269 }
1270
1271 const body_block = body_node.cast(ast.Node.Block).?;
1272
1273 try astgen.blockExpr(self, params_scope, body_block);
1274
1275 if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
1276 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
1277 {
1278 const src = tree.token_locs[body_block.rbrace].start;
1279 _ = try self.addZIRInst(&gen_scope.base, src, zir.Inst.ReturnVoid, .{}, .{});
1280 }
1281
1282 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
1283 fn_zir.* = .{
1284 .body = .{
1285 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1286 },
1287 .arena = gen_scope_arena.state,
1288 };
1289 break :blk fn_zir;
1290 };
1291
1292 new_func.* = .{
1293 .analysis = .{ .queued = fn_zir },
1294 .owner_decl = decl,
1295 };
1296 fn_payload.* = .{ .func = new_func };
1297
1298 var prev_type_has_bits = false;
1299 var type_changed = true;
1300
1301 if (decl.typedValueManaged()) |tvm| {
1302 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1303 type_changed = !tvm.typed_value.ty.eql(fn_type);
1304
1305 tvm.deinit(self.gpa);
1306 }
1307
1308 decl_arena_state.* = decl_arena.state;
1309 decl.typed_value = .{
1310 .most_recent = .{
1311 .typed_value = .{
1312 .ty = fn_type,
1313 .val = Value.initPayload(&fn_payload.base),
1314 },
1315 .arena = decl_arena_state,
1316 },
1317 };
1318 decl.analysis = .complete;
1319 decl.generation = self.generation;
1320
1321 if (fn_type.hasCodeGenBits()) {
1322 // We don't fully codegen the decl until later, but we do need to reserve a global
1323 // offset table index for it. This allows us to codegen decls out of dependency order,
1324 // increasing how many computations can be done in parallel.
1325 try self.bin_file.allocateDeclIndexes(decl);
1326 try self.work_queue.writeItem(.{ .codegen_decl = decl });
1327 } else if (prev_type_has_bits) {
1328 self.bin_file.freeDecl(decl);
1329 }
1330
1331 if (fn_proto.getTrailer("extern_export_inline_token")) |maybe_export_token| {
1332 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1333 const export_src = tree.token_locs[maybe_export_token].start;
1334 const name_loc = tree.token_locs[fn_proto.getTrailer("name_token").?];
1335 const name = tree.tokenSliceLoc(name_loc);
1336 // The scope needs to have the decl in it.
1337 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1338 }
1339 }
1340 return type_changed;
1341 },
1342 .VarDecl => @panic("TODO var decl"),
1343 .Comptime => @panic("TODO comptime decl"),
1344 .Use => @panic("TODO usingnamespace decl"),
1345 else => unreachable,
7831346 }
7841347}
7851348
786fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {
787 switch (root_scope.source) {
788 .unloaded => {
789 const source = try self.root_pkg.root_src_dir.readFileAllocOptions(
790 self.allocator,
791 root_scope.sub_file_path,
792 std.math.maxInt(u32),
793 1,
794 0,
795 );
796 root_scope.source = .{ .bytes = source };
797 return source;
798 },
799 .bytes => |bytes| return bytes,
1349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
1350 try self.analyzeBody(&block_scope.base, body);
1351 for (block_scope.instructions.items) |inst| {
1352 if (inst.cast(Inst.Ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);
1354 return val.toType();
1355 } else {
1356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
1357 }
8001358 }
1359 unreachable;
1360}
1361
1362fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
1363 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
1364 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
1365
1366 depender.dependencies.putAssumeCapacity(dependee, {});
1367 dependee.dependants.putAssumeCapacity(depender, {});
8011368}
8021369
8031370fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
8041371 switch (root_scope.status) {
8051372 .never_loaded, .unloaded_success => {
806 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
1373 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
8071374
808 const source = try self.getSource(root_scope);
1375 const source = try root_scope.getSource(self);
8091376
8101377 var keep_zir_module = false;
811 const zir_module = try self.allocator.create(zir.Module);
812 defer if (!keep_zir_module) self.allocator.destroy(zir_module);
1378 const zir_module = try self.gpa.create(zir.Module);
1379 defer if (!keep_zir_module) self.gpa.destroy(zir_module);
8131380
814 zir_module.* = try zir.parse(self.allocator, source);
815 defer if (!keep_zir_module) zir_module.deinit(self.allocator);
1381 zir_module.* = try zir.parse(self.gpa, source);
1382 defer if (!keep_zir_module) zir_module.deinit(self.gpa);
8161383
8171384 if (zir_module.error_msg) |src_err_msg| {
8181385 self.failed_files.putAssumeCapacityNoClobber(
819 root_scope,
820 try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
1386 &root_scope.base,
1387 try ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
8211388 );
8221389 root_scope.status = .unloaded_parse_failure;
8231390 return error.AnalysisFail;
......@@ -838,96 +1405,194 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
8381405 }
8391406}
8401407
841fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
1408fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1409 const tracy = trace(@src());
1410 defer tracy.end();
1411
8421412 switch (root_scope.status) {
843 .never_loaded => {
844 const src_module = try self.getSrcModule(root_scope);
1413 .never_loaded, .unloaded_success => {
1414 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
8451415
846 // Here we ensure enough queue capacity to store all the decls, so that later we can use
847 // appendAssumeCapacity.
848 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
1416 const source = try root_scope.getSource(self);
8491417
850 for (src_module.decls) |decl| {
851 if (decl.cast(zir.Inst.Export)) |export_inst| {
852 _ = try self.resolveDecl(&root_scope.base, &export_inst.base);
853 }
1418 var keep_tree = false;
1419 const tree = try std.zig.parse(self.gpa, source);
1420 defer if (!keep_tree) tree.deinit();
1421
1422 if (tree.errors.len != 0) {
1423 const parse_err = tree.errors[0];
1424
1425 var msg = std.ArrayList(u8).init(self.gpa);
1426 defer msg.deinit();
1427
1428 try parse_err.render(tree.token_ids, msg.outStream());
1429 const err_msg = try self.gpa.create(ErrorMsg);
1430 err_msg.* = .{
1431 .msg = msg.toOwnedSlice(),
1432 .byte_offset = tree.token_locs[parse_err.loc()].start,
1433 };
1434
1435 self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
1436 root_scope.status = .unloaded_parse_failure;
1437 return error.AnalysisFail;
8541438 }
1439
1440 root_scope.status = .loaded_success;
1441 root_scope.contents = .{ .tree = tree };
1442 keep_tree = true;
1443
1444 return tree;
8551445 },
8561446
857 .unloaded_parse_failure,
858 .unloaded_sema_failure,
859 .unloaded_success,
860 .loaded_sema_failure,
861 .loaded_success,
862 => {
863 const src_module = try self.getSrcModule(root_scope);
864
865 var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator);
866 defer exports_to_resolve.deinit();
867
868 // Keep track of the decls that we expect to see in this file so that
869 // we know which ones have been deleted.
870 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator);
871 defer deleted_decls.deinit();
872 try deleted_decls.ensureCapacity(self.decl_table.size);
873 {
874 var it = self.decl_table.iterator();
875 while (it.next()) |kv| {
876 deleted_decls.putAssumeCapacityNoClobber(kv.value, {});
877 }
878 }
1447 .unloaded_parse_failure => return error.AnalysisFail,
8791448
880 for (src_module.decls) |src_decl| {
881 const name_hash = Decl.hashSimpleName(src_decl.name);
882 if (self.decl_table.get(name_hash)) |kv| {
883 const decl = kv.value;
884 deleted_decls.removeAssertDiscard(decl);
885 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
886 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
887 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
888 //std.debug.warn("'{}' {x} => {x}\n", .{ src_decl.name, decl.contents_hash, new_contents_hash });
1449 .loaded_success => return root_scope.contents.tree,
1450 }
1451}
1452
1453fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1454 // We may be analyzing it for the first time, or this may be
1455 // an incremental update. This code handles both cases.
1456 const tree = try self.getAstTree(root_scope);
1457 const decls = tree.root_node.decls();
1458
1459 try self.work_queue.ensureUnusedCapacity(decls.len);
1460 try root_scope.decls.ensureCapacity(self.gpa, decls.len);
1461
1462 // Keep track of the decls that we expect to see in this file so that
1463 // we know which ones have been deleted.
1464 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.gpa);
1465 defer deleted_decls.deinit();
1466 try deleted_decls.ensureCapacity(root_scope.decls.items.len);
1467 for (root_scope.decls.items) |file_decl| {
1468 deleted_decls.putAssumeCapacityNoClobber(file_decl, {});
1469 }
1470
1471 for (decls) |src_decl, decl_i| {
1472 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
1473 // We will create a Decl for it regardless of analysis status.
1474 const name_tok = fn_proto.getTrailer("name_token") orelse {
1475 @panic("TODO missing function name");
1476 };
1477
1478 const name_loc = tree.token_locs[name_tok];
1479 const name = tree.tokenSliceLoc(name_loc);
1480 const name_hash = root_scope.fullyQualifiedNameHash(name);
1481 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1482 if (self.decl_table.get(name_hash)) |decl| {
1483 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1484 // have been re-ordered.
1485 decl.src_index = decl_i;
1486 if (deleted_decls.remove(decl) == null) {
1487 decl.analysis = .sema_failure;
1488 const err_msg = try ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
1489 errdefer err_msg.destroy(self.gpa);
1490 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
1491 } else {
1492 if (!srcHashEql(decl.contents_hash, contents_hash)) {
8891493 try self.markOutdatedDecl(decl);
890 decl.contents_hash = new_contents_hash;
1494 decl.contents_hash = contents_hash;
8911495 }
892 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
893 try exports_to_resolve.append(&export_inst.base);
8941496 }
895 }
896 {
897 // Handle explicitly deleted decls from the source code. Not to be confused
898 // with when we delete decls because they are no longer referenced.
899 var it = deleted_decls.iterator();
900 while (it.next()) |kv| {
901 //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name});
902 try self.deleteDecl(kv.key);
1497 } else {
1498 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
1499 root_scope.decls.appendAssumeCapacity(new_decl);
1500 if (fn_proto.getTrailer("extern_export_inline_token")) |maybe_export_token| {
1501 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1502 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
1503 }
9031504 }
9041505 }
905 for (exports_to_resolve.items) |export_inst| {
906 _ = try self.resolveDecl(&root_scope.base, export_inst);
1506 }
1507 // TODO also look for global variable declarations
1508 // TODO also look for comptime blocks and exported globals
1509 }
1510 // Handle explicitly deleted decls from the source code. Not to be confused
1511 // with when we delete decls because they are no longer referenced.
1512 for (deleted_decls.items()) |entry| {
1513 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});
1514 try self.deleteDecl(entry.key);
1515 }
1516}
1517
1518fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1519 // We may be analyzing it for the first time, or this may be
1520 // an incremental update. This code handles both cases.
1521 const src_module = try self.getSrcModule(root_scope);
1522
1523 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
1524 try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
1525
1526 var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
1527 defer exports_to_resolve.deinit();
1528
1529 // Keep track of the decls that we expect to see in this file so that
1530 // we know which ones have been deleted.
1531 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.gpa);
1532 defer deleted_decls.deinit();
1533 try deleted_decls.ensureCapacity(self.decl_table.items().len);
1534 for (self.decl_table.items()) |entry| {
1535 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
1536 }
1537
1538 for (src_module.decls) |src_decl, decl_i| {
1539 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
1540 if (self.decl_table.get(name_hash)) |decl| {
1541 deleted_decls.removeAssertDiscard(decl);
1542 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
1543 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
1544 try self.markOutdatedDecl(decl);
1545 decl.contents_hash = src_decl.contents_hash;
9071546 }
908 },
1547 } else {
1548 const new_decl = try self.createNewDecl(
1549 &root_scope.base,
1550 src_decl.name,
1551 decl_i,
1552 name_hash,
1553 src_decl.contents_hash,
1554 );
1555 root_scope.decls.appendAssumeCapacity(new_decl);
1556 if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
1557 try exports_to_resolve.append(src_decl);
1558 }
1559 }
1560 }
1561 for (exports_to_resolve.items) |export_decl| {
1562 _ = try self.resolveZirDecl(&root_scope.base, export_decl);
1563 }
1564 // Handle explicitly deleted decls from the source code. Not to be confused
1565 // with when we delete decls because they are no longer referenced.
1566 for (deleted_decls.items()) |entry| {
1567 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});
1568 try self.deleteDecl(entry.key);
9091569 }
9101570}
9111571
9121572fn deleteDecl(self: *Module, decl: *Decl) !void {
913 try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len);
1573 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
1574
1575 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
1576 // not be present in the set, and this does nothing.
1577 decl.scope.removeDecl(decl);
9141578
9151579 //std.debug.warn("deleting decl '{}'\n", .{decl.name});
9161580 const name_hash = decl.fullyQualifiedNameHash();
9171581 self.decl_table.removeAssertDiscard(name_hash);
9181582 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
919 for (decl.dependencies.items) |dep| {
1583 for (decl.dependencies.items()) |entry| {
1584 const dep = entry.key;
9201585 dep.removeDependant(decl);
921 if (dep.dependants.items.len == 0) {
1586 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
9221587 // We don't recursively perform a deletion here, because during the update,
9231588 // another reference to it may turn up.
924 assert(!dep.deletion_flag);
9251589 dep.deletion_flag = true;
9261590 self.deletion_set.appendAssumeCapacity(dep);
9271591 }
9281592 }
9291593 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
930 for (decl.dependants.items) |dep| {
1594 for (decl.dependants.items()) |entry| {
1595 const dep = entry.key;
9311596 dep.removeDependency(decl);
9321597 if (dep.analysis != .outdated) {
9331598 // TODO Move this failure possibility to the top of the function.
......@@ -935,11 +1600,11 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
9351600 }
9361601 }
9371602 if (self.failed_decls.remove(decl)) |entry| {
938 entry.value.destroy(self.allocator);
1603 entry.value.destroy(self.gpa);
9391604 }
9401605 self.deleteDeclExports(decl);
9411606 self.bin_file.freeDecl(decl);
942 decl.destroy(self.allocator);
1607 decl.destroy(self.gpa);
9431608}
9441609
9451610/// Delete all the Export objects that are caused by this Decl. Re-analysis of
......@@ -948,7 +1613,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
9481613 const kv = self.export_owners.remove(decl) orelse return;
9491614
9501615 for (kv.value) |exp| {
951 if (self.decl_exports.get(exp.exported_decl)) |decl_exports_kv| {
1616 if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
9521617 // Remove exports with owner_decl matching the regenerating decl.
9531618 const list = decl_exports_kv.value;
9541619 var i: usize = 0;
......@@ -961,96 +1626,108 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
9611626 i += 1;
9621627 }
9631628 }
964 decl_exports_kv.value = self.allocator.shrink(list, new_len);
1629 decl_exports_kv.value = self.gpa.shrink(list, new_len);
9651630 if (new_len == 0) {
9661631 self.decl_exports.removeAssertDiscard(exp.exported_decl);
9671632 }
9681633 }
969
970 self.bin_file.deleteExport(exp.link);
971 self.allocator.destroy(exp);
1634 if (self.bin_file.cast(link.File.Elf)) |elf| {
1635 elf.deleteExport(exp.link);
1636 }
1637 if (self.failed_exports.remove(exp)) |entry| {
1638 entry.value.destroy(self.gpa);
1639 }
1640 _ = self.symbol_exports.remove(exp.options.name);
1641 self.gpa.destroy(exp);
9721642 }
973 self.allocator.free(kv.value);
1643 self.gpa.free(kv.value);
9741644}
9751645
9761646fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1647 const tracy = trace(@src());
1648 defer tracy.end();
1649
9771650 // Use the Decl's arena for function memory.
978 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);
1651 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
9791652 defer decl.typed_value.most_recent.arena.?.* = arena.state;
980 var analysis: Fn.Analysis = .{
981 .inner_block = .{
982 .func = func,
983 .decl = decl,
984 .instructions = .{},
985 .arena = &arena.allocator,
986 },
987 .needed_inst_capacity = 0,
988 .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator),
1653 var inner_block: Scope.Block = .{
1654 .parent = null,
1655 .func = func,
1656 .decl = decl,
1657 .instructions = .{},
1658 .arena = &arena.allocator,
9891659 };
990 defer analysis.inner_block.instructions.deinit(self.allocator);
991 defer analysis.inst_table.deinit();
1660 defer inner_block.instructions.deinit(self.gpa);
9921661
993 const fn_inst = func.analysis.queued;
994 func.analysis = .{ .in_progress = &analysis };
1662 const fn_zir = func.analysis.queued;
1663 defer fn_zir.arena.promote(self.gpa).deinit();
1664 func.analysis = .{ .in_progress = {} };
1665 //std.debug.warn("set {} to in_progress\n", .{decl.name});
9951666
996 try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body);
1667 try self.analyzeBody(&inner_block.base, fn_zir.body);
9971668
998 func.analysis = .{
999 .success = .{
1000 .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items),
1001 },
1002 };
1669 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1670 func.analysis = .{ .success = .{ .instructions = instructions } };
1671 //std.debug.warn("set {} to success\n", .{decl.name});
10031672}
10041673
1005fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void {
1006 switch (decl.analysis) {
1007 .in_progress => unreachable,
1008 .dependency_failure,
1009 .sema_failure,
1010 .codegen_failure,
1011 .codegen_failure_retryable,
1012 .complete,
1013 => return,
1014
1015 .outdated => {}, // Decl re-analysis
1674fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1675 //std.debug.warn("mark {} outdated\n", .{decl.name});
1676 try self.work_queue.writeItem(.{ .analyze_decl = decl });
1677 if (self.failed_decls.remove(decl)) |entry| {
1678 entry.value.destroy(self.gpa);
10161679 }
1017 //std.debug.warn("re-analyzing {}\n", .{decl.name});
1018 decl.src = old_inst.src;
1680 decl.analysis = .outdated;
1681}
1682
1683fn allocateNewDecl(
1684 self: *Module,
1685 scope: *Scope,
1686 src_index: usize,
1687 contents_hash: std.zig.SrcHash,
1688) !*Decl {
1689 const new_decl = try self.gpa.create(Decl);
1690 new_decl.* = .{
1691 .name = "",
1692 .scope = scope.namespace(),
1693 .src_index = src_index,
1694 .typed_value = .{ .never_succeeded = {} },
1695 .analysis = .unreferenced,
1696 .deletion_flag = false,
1697 .contents_hash = contents_hash,
1698 .link = link.File.Elf.TextBlock.empty,
1699 .generation = 0,
1700 };
1701 return new_decl;
1702}
10191703
1020 // The exports this Decl performs will be re-discovered, so we remove them here
1021 // prior to re-analysis.
1022 self.deleteDeclExports(decl);
1023 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
1024 for (decl.dependencies.items) |dep| {
1025 dep.removeDependant(decl);
1026 if (dep.dependants.items.len == 0) {
1027 // We don't perform a deletion here, because this Decl or another one
1028 // may end up referencing it before the update is complete.
1029 assert(!dep.deletion_flag);
1030 dep.deletion_flag = true;
1031 try self.deletion_set.append(self.allocator, dep);
1032 }
1033 }
1034 decl.dependencies.shrink(self.allocator, 0);
1704fn createNewDecl(
1705 self: *Module,
1706 scope: *Scope,
1707 decl_name: []const u8,
1708 src_index: usize,
1709 name_hash: Scope.NameHash,
1710 contents_hash: std.zig.SrcHash,
1711) !*Decl {
1712 try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
1713 const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
1714 errdefer self.gpa.destroy(new_decl);
1715 new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
1716 self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
1717 return new_decl;
1718}
1719
1720fn analyzeZirDecl(self: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
10351721 var decl_scope: Scope.DeclAnalysis = .{
10361722 .decl = decl,
1037 .arena = std.heap.ArenaAllocator.init(self.allocator),
1723 .arena = std.heap.ArenaAllocator.init(self.gpa),
10381724 };
10391725 errdefer decl_scope.arena.deinit();
10401726
1041 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
1042 error.OutOfMemory => return error.OutOfMemory,
1043 error.AnalysisFail => {
1044 switch (decl.analysis) {
1045 .in_progress => decl.analysis = .dependency_failure,
1046 else => {},
1047 }
1048 decl.generation = self.generation;
1049 return error.AnalysisFail;
1050 },
1051 };
1727 decl.analysis = .in_progress;
1728
1729 const typed_value = try self.analyzeConstInst(&decl_scope.base, src_decl.inst);
10521730 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
1053 arena_state.* = decl_scope.arena.state;
10541731
10551732 var prev_type_has_bits = false;
10561733 var type_changed = true;
......@@ -1059,8 +1736,10 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
10591736 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
10601737 type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
10611738
1062 tvm.deinit(self.allocator);
1739 tvm.deinit(self.gpa);
10631740 }
1741
1742 arena_state.* = decl_scope.arena.state;
10641743 decl.typed_value = .{
10651744 .most_recent = .{
10661745 .typed_value = typed_value,
......@@ -1079,137 +1758,66 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
10791758 self.bin_file.freeDecl(decl);
10801759 }
10811760
1082 // If the decl is a function, and the type is the same, we do not need
1083 // to chase the dependants.
1084 if (type_changed or typed_value.val.tag() != .function) {
1085 for (decl.dependants.items) |dep| {
1086 switch (dep.analysis) {
1087 .in_progress => unreachable,
1088 .outdated => continue, // already queued for update
1089
1090 .dependency_failure,
1091 .sema_failure,
1092 .codegen_failure,
1093 .codegen_failure_retryable,
1094 .complete,
1095 => if (dep.generation != self.generation) {
1096 try self.markOutdatedDecl(dep);
1097 },
1098 }
1099 }
1100 }
1761 return type_changed;
11011762}
11021763
1103fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1104 //std.debug.warn("mark {} outdated\n", .{decl.name});
1105 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
1106 if (self.failed_decls.remove(decl)) |entry| {
1107 entry.value.destroy(self.allocator);
1108 }
1109 decl.analysis = .outdated;
1764fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
1765 const zir_module = self.root_scope.cast(Scope.ZIRModule).?;
1766 const entry = zir_module.contents.module.findDecl(src_decl.name).?;
1767 return self.resolveZirDeclHavingIndex(scope, src_decl, entry.index);
11101768}
11111769
1112fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
1113 const hash = Decl.hashSimpleName(old_inst.name);
1114 if (self.decl_table.get(hash)) |kv| {
1115 const decl = kv.value;
1116 try self.reAnalyzeDecl(decl, old_inst);
1117 return decl;
1118 } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| {
1119 // This is just a named reference to another decl.
1120 return self.analyzeDeclVal(scope, decl_val);
1121 } else {
1122 const new_decl = blk: {
1123 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
1124 const new_decl = try self.allocator.create(Decl);
1125 errdefer self.allocator.destroy(new_decl);
1126 const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
1127 errdefer self.allocator.free(name);
1128 new_decl.* = .{
1129 .name = name,
1130 .scope = scope.namespace(),
1131 .src = old_inst.src,
1132 .typed_value = .{ .never_succeeded = {} },
1133 .analysis = .in_progress,
1134 .deletion_flag = false,
1135 .contents_hash = Decl.hashSimpleName(old_inst.contents),
1136 .link = link.ElfFile.TextBlock.empty,
1137 .generation = 0,
1138 };
1139 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
1140 break :blk new_decl;
1141 };
1142
1143 var decl_scope: Scope.DeclAnalysis = .{
1144 .decl = new_decl,
1145 .arena = std.heap.ArenaAllocator.init(self.allocator),
1146 };
1147 errdefer decl_scope.arena.deinit();
1148
1149 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
1150 error.OutOfMemory => return error.OutOfMemory,
1151 error.AnalysisFail => {
1152 switch (new_decl.analysis) {
1153 .in_progress => new_decl.analysis = .dependency_failure,
1154 else => {},
1155 }
1156 new_decl.generation = self.generation;
1157 return error.AnalysisFail;
1158 },
1159 };
1160 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
1161
1162 arena_state.* = decl_scope.arena.state;
1163
1164 new_decl.typed_value = .{
1165 .most_recent = .{
1166 .typed_value = typed_value,
1167 .arena = arena_state,
1168 },
1169 };
1170 new_decl.analysis = .complete;
1171 new_decl.generation = self.generation;
1172 if (typed_value.ty.hasCodeGenBits()) {
1173 // We don't fully codegen the decl until later, but we do need to reserve a global
1174 // offset table index for it. This allows us to codegen decls out of dependency order,
1175 // increasing how many computations can be done in parallel.
1176 try self.bin_file.allocateDeclIndexes(new_decl);
1177 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
1178 }
1179 return new_decl;
1180 }
1770fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl {
1771 const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name);
1772 const decl = self.decl_table.get(name_hash).?;
1773 decl.src_index = src_index;
1774 try self.ensureDeclAnalyzed(decl);
1775 return decl;
11811776}
11821777
11831778/// Declares a dependency on the decl.
1184fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
1185 const decl = try self.resolveDecl(scope, old_inst);
1779fn resolveCompleteZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
1780 const decl = try self.resolveZirDecl(scope, src_decl);
11861781 switch (decl.analysis) {
1782 .unreferenced => unreachable,
11871783 .in_progress => unreachable,
11881784 .outdated => unreachable,
11891785
11901786 .dependency_failure,
11911787 .sema_failure,
1788 .sema_failure_retryable,
11921789 .codegen_failure,
11931790 .codegen_failure_retryable,
11941791 => return error.AnalysisFail,
11951792
11961793 .complete => {},
11971794 }
1198 if (scope.decl()) |scope_decl| {
1199 try self.declareDeclDependency(scope_decl, decl);
1200 }
12011795 return decl;
12021796}
12031797
1798/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
12041799fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
1205 if (scope.cast(Scope.Block)) |block| {
1206 if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| {
1207 return kv.value;
1208 }
1209 }
1210
1211 const decl = try self.resolveCompleteDecl(scope, old_inst);
1800 if (old_inst.analyzed_inst) |inst| return inst;
1801
1802 // If this assert trips, the instruction that was referenced did not get properly
1803 // analyzed before it was referenced.
1804 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
1805 const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
1806 const decl_name = declval.positionals.name;
1807 const entry = zir_module.contents.module.findDecl(decl_name) orelse
1808 return self.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name});
1809 break :blk entry;
1810 } else blk: {
1811 // If this assert trips, the instruction that was referenced did not get
1812 // properly analyzed by a previous instruction analysis before it was
1813 // referenced by the current one.
1814 break :blk zir_module.contents.module.findInstDecl(old_inst).?;
1815 };
1816 const decl = try self.resolveCompleteZirDecl(scope, entry.decl);
12121817 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
1818 // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
1819 // but this would prevent the analyzeDeclRef from happening, which is needed to properly
1820 // detect Decl dependencies and dependency failures on updates.
12131821 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
12141822}
12151823
......@@ -1258,29 +1866,25 @@ fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
12581866 return val.toType();
12591867}
12601868
1261fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void {
1262 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);
1263 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
1264 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
1265 const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
1869fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {
1870 try self.ensureDeclAnalyzed(exported_decl);
12661871 const typed_value = exported_decl.typed_value.most_recent.typed_value;
12671872 switch (typed_value.ty.zigTypeTag()) {
12681873 .Fn => {},
1269 else => return self.fail(
1270 scope,
1271 export_inst.positionals.value.src,
1272 "unable to export type '{}'",
1273 .{typed_value.ty},
1274 ),
1874 else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
12751875 }
1276 const new_export = try self.allocator.create(Export);
1277 errdefer self.allocator.destroy(new_export);
1876
1877 try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
1878 try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
1879
1880 const new_export = try self.gpa.create(Export);
1881 errdefer self.gpa.destroy(new_export);
12781882
12791883 const owner_decl = scope.decl().?;
12801884
12811885 new_export.* = .{
12821886 .options = .{ .name = symbol_name },
1283 .src = export_inst.base.src,
1887 .src = src,
12841888 .link = .{},
12851889 .owner_decl = owner_decl,
12861890 .exported_decl = exported_decl,
......@@ -1288,30 +1892,44 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
12881892 };
12891893
12901894 // Add to export_owners table.
1291 const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable;
1895 const eo_gop = self.export_owners.getOrPut(self.gpa, owner_decl) catch unreachable;
12921896 if (!eo_gop.found_existing) {
1293 eo_gop.kv.value = &[0]*Export{};
1897 eo_gop.entry.value = &[0]*Export{};
12941898 }
1295 eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1);
1296 eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export;
1297 errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1);
1899 eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
1900 eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
1901 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
12981902
12991903 // Add to exported_decl table.
1300 const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable;
1904 const de_gop = self.decl_exports.getOrPut(self.gpa, exported_decl) catch unreachable;
13011905 if (!de_gop.found_existing) {
1302 de_gop.kv.value = &[0]*Export{};
1303 }
1304 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);
1305 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;
1306 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);
1307
1308 self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) {
1906 de_gop.entry.value = &[0]*Export{};
1907 }
1908 de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
1909 de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
1910 errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
1911
1912 if (self.symbol_exports.get(symbol_name)) |_| {
1913 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
1914 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
1915 self.gpa,
1916 src,
1917 "exported symbol collision: {}",
1918 .{symbol_name},
1919 ));
1920 // TODO: add a note
1921 new_export.status = .failed;
1922 return;
1923 }
1924
1925 try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
1926 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
13091927 error.OutOfMemory => return error.OutOfMemory,
13101928 else => {
1311 try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);
1929 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
13121930 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
1313 self.allocator,
1314 export_inst.base.src,
1931 self.gpa,
1932 src,
13151933 "unable to export: {}",
13161934 .{@errorName(err)},
13171935 ));
......@@ -1320,7 +1938,6 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
13201938 };
13211939}
13221940
1323/// TODO should not need the cast on the last parameter at the callsites
13241941fn addNewInstArgs(
13251942 self: *Module,
13261943 block: *Scope.Block,
......@@ -1334,6 +1951,64 @@ fn addNewInstArgs(
13341951 return &inst.base;
13351952}
13361953
1954fn newZIRInst(
1955 gpa: *Allocator,
1956 src: usize,
1957 comptime T: type,
1958 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1959 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1960) !*T {
1961 const inst = try gpa.create(T);
1962 inst.* = .{
1963 .base = .{
1964 .tag = T.base_tag,
1965 .src = src,
1966 },
1967 .positionals = positionals,
1968 .kw_args = kw_args,
1969 };
1970 return inst;
1971}
1972
1973pub fn addZIRInstSpecial(
1974 self: *Module,
1975 scope: *Scope,
1976 src: usize,
1977 comptime T: type,
1978 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1979 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1980) !*T {
1981 const gen_zir = scope.getGenZIR();
1982 try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
1983 const inst = try newZIRInst(gen_zir.arena, src, T, positionals, kw_args);
1984 gen_zir.instructions.appendAssumeCapacity(&inst.base);
1985 return inst;
1986}
1987
1988pub fn addZIRInst(
1989 self: *Module,
1990 scope: *Scope,
1991 src: usize,
1992 comptime T: type,
1993 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1994 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1995) !*zir.Inst {
1996 const inst_special = try self.addZIRInstSpecial(scope, src, T, positionals, kw_args);
1997 return &inst_special.base;
1998}
1999
2000/// TODO The existence of this function is a workaround for a bug in stage1.
2001pub fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
2002 const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
2003 return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
2004}
2005
2006/// TODO The existence of this function is a workaround for a bug in stage1.
2007pub fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
2008 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
2009 return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{});
2010}
2011
13372012fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
13382013 const inst = try block.arena.create(T);
13392014 inst.* = .{
......@@ -1344,7 +2019,7 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime
13442019 },
13452020 .args = undefined,
13462021 };
1347 try block.instructions.append(self.allocator, &inst.base);
2022 try block.instructions.append(self.gpa, &inst.base);
13482023 return inst;
13492024}
13502025
......@@ -1361,19 +2036,6 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)
13612036 return &const_inst.base;
13622037}
13632038
1364fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst {
1365 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
1366 ty_payload.* = .{ .len = str.len };
1367
1368 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
1369 bytes_payload.* = .{ .data = str };
1370
1371 return self.constInst(scope, src, .{
1372 .ty = Type.initPayload(&ty_payload.base),
1373 .val = Value.initPayload(&bytes_payload.base),
1374 });
1375}
1376
13772039fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
13782040 return self.constInst(scope, src, .{
13792041 .ty = Type.initTag(.type),
......@@ -1388,6 +2050,13 @@ fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
13882050 });
13892051}
13902052
2053fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2054 return self.constInst(scope, src, .{
2055 .ty = Type.initTag(.noreturn),
2056 .val = Value.initTag(.the_one_possible_value),
2057 });
2058}
2059
13912060fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
13922061 return self.constInst(scope, src, .{
13932062 .ty = ty,
......@@ -1451,7 +2120,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI
14512120 });
14522121}
14532122
1454fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
2123fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
14552124 const new_inst = try self.analyzeInst(scope, old_inst);
14562125 return TypedValue{
14572126 .ty = new_inst.ty,
......@@ -1459,24 +2128,33 @@ fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerErro
14592128 };
14602129}
14612130
2131fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
2132 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
2133 // after analysis.
2134 const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
2135 return self.constInst(scope, const_inst.base.src, typed_value_copy);
2136}
2137
14622138fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
14632139 switch (old_inst.tag) {
2140 .arg => return self.analyzeInstArg(scope, old_inst.cast(zir.Inst.Arg).?),
2141 .block => return self.analyzeInstBlock(scope, old_inst.cast(zir.Inst.Block).?),
2142 .@"break" => return self.analyzeInstBreak(scope, old_inst.cast(zir.Inst.Break).?),
14642143 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
2144 .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.cast(zir.Inst.BreakVoid).?),
14652145 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
14662146 .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?),
2147 .@"const" => return self.analyzeInstConst(scope, old_inst.cast(zir.Inst.Const).?),
14672148 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
2149 .declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.cast(zir.Inst.DeclRefStr).?),
14682150 .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),
1469 .str => {
1470 const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;
1471 // The bytes references memory inside the ZIR module, which can get deallocated
1472 // after semantic analysis is complete. We need the memory to be in the Decl's arena.
1473 const arena_bytes = try scope.arena().dupe(u8, bytes);
1474 return self.constStr(scope, old_inst.src, arena_bytes);
1475 },
2151 .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.cast(zir.Inst.DeclValInModule).?),
2152 .str => return self.analyzeInstStr(scope, old_inst.cast(zir.Inst.Str).?),
14762153 .int => {
14772154 const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int;
14782155 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
14792156 },
2157 .inttype => return self.analyzeInstIntType(scope, old_inst.cast(zir.Inst.IntType).?),
14802158 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(zir.Inst.PtrToInt).?),
14812159 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(zir.Inst.FieldPtr).?),
14822160 .deref => return self.analyzeInstDeref(scope, old_inst.cast(zir.Inst.Deref).?),
......@@ -1484,58 +2162,220 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
14842162 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?),
14852163 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?),
14862164 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?),
2165 .returnvoid => return self.analyzeInstRetVoid(scope, old_inst.cast(zir.Inst.ReturnVoid).?),
14872166 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?),
1488 .@"export" => {
1489 try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?);
1490 return self.constVoid(scope, old_inst.src);
1491 },
2167 .@"export" => return self.analyzeInstExport(scope, old_inst.cast(zir.Inst.Export).?),
14922168 .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?),
1493 .ref => return self.analyzeInstRef(scope, old_inst.cast(zir.Inst.Ref).?),
14942169 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?),
14952170 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?),
14962171 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),
14972172 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),
14982173 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),
2174 .sub => return self.analyzeInstSub(scope, old_inst.cast(zir.Inst.Sub).?),
14992175 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),
15002176 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
15012177 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
15022178 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),
2179 .boolnot => return self.analyzeInstBoolNot(scope, old_inst.cast(zir.Inst.BoolNot).?),
2180 }
2181}
2182
2183fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
2184 // The bytes references memory inside the ZIR module, which can get deallocated
2185 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
2186 var new_decl_arena = std.heap.ArenaAllocator.init(self.gpa);
2187 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
2188
2189 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
2190 ty_payload.* = .{ .len = arena_bytes.len };
2191
2192 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
2193 bytes_payload.* = .{ .data = arena_bytes };
2194
2195 const new_decl = try self.createAnonymousDecl(scope, &new_decl_arena, .{
2196 .ty = Type.initPayload(&ty_payload.base),
2197 .val = Value.initPayload(&bytes_payload.base),
2198 });
2199 return self.analyzeDeclRef(scope, str_inst.base.src, new_decl);
2200}
2201
2202fn createAnonymousDecl(
2203 self: *Module,
2204 scope: *Scope,
2205 decl_arena: *std.heap.ArenaAllocator,
2206 typed_value: TypedValue,
2207) !*Decl {
2208 const name_index = self.getNextAnonNameIndex();
2209 const scope_decl = scope.decl().?;
2210 const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
2211 defer self.gpa.free(name);
2212 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
2213 const src_hash: std.zig.SrcHash = undefined;
2214 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
2215 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
2216
2217 decl_arena_state.* = decl_arena.state;
2218 new_decl.typed_value = .{
2219 .most_recent = .{
2220 .typed_value = typed_value,
2221 .arena = decl_arena_state,
2222 },
2223 };
2224 new_decl.analysis = .complete;
2225 new_decl.generation = self.generation;
2226
2227 // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
2228 // We should be able to further improve the compiler to not omit Decls which are only referenced at
2229 // compile-time and not runtime.
2230 if (typed_value.ty.hasCodeGenBits()) {
2231 try self.bin_file.allocateDeclIndexes(new_decl);
2232 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
15032233 }
2234
2235 return new_decl;
2236}
2237
2238fn getNextAnonNameIndex(self: *Module) usize {
2239 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
2240}
2241
2242pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
2243 const namespace = scope.namespace();
2244 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
2245 return self.decl_table.get(name_hash);
2246}
2247
2248fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
2249 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
2250 const exported_decl = self.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
2251 return self.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name});
2252 try self.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
2253 return self.constVoid(scope, export_inst.base.src);
15042254}
15052255
15062256fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
15072257 return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
15082258}
15092259
2260fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
2261 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2262 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
2263 const param_index = b.instructions.items.len;
2264 const param_count = fn_ty.fnParamLen();
2265 if (param_index >= param_count) {
2266 return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
2267 param_index,
2268 param_count,
2269 });
2270 }
2271 const param_type = fn_ty.fnParamType(param_index);
2272 return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {});
2273}
2274
2275fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
2276 const parent_block = scope.cast(Scope.Block).?;
2277
2278 // Reserve space for a Block instruction so that generated Break instructions can
2279 // point to it, even if it doesn't end up getting used because the code ends up being
2280 // comptime evaluated.
2281 const block_inst = try parent_block.arena.create(Inst.Block);
2282 block_inst.* = .{
2283 .base = .{
2284 .tag = Inst.Block.base_tag,
2285 .ty = undefined, // Set after analysis.
2286 .src = inst.base.src,
2287 },
2288 .args = undefined,
2289 };
2290
2291 var child_block: Scope.Block = .{
2292 .parent = parent_block,
2293 .func = parent_block.func,
2294 .decl = parent_block.decl,
2295 .instructions = .{},
2296 .arena = parent_block.arena,
2297 // TODO @as here is working around a miscompilation compiler bug :(
2298 .label = @as(?Scope.Block.Label, Scope.Block.Label{
2299 .zir_block = inst,
2300 .results = .{},
2301 .block_inst = block_inst,
2302 }),
2303 };
2304 const label = &child_block.label.?;
2305
2306 defer child_block.instructions.deinit(self.gpa);
2307 defer label.results.deinit(self.gpa);
2308
2309 try self.analyzeBody(&child_block.base, inst.positionals.body);
2310
2311 // Blocks must terminate with noreturn instruction.
2312 assert(child_block.instructions.items.len != 0);
2313 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
2314
2315 // Need to set the type and emit the Block instruction. This allows machine code generation
2316 // to emit a jump instruction to after the block when it encounters the break.
2317 try parent_block.instructions.append(self.gpa, &block_inst.base);
2318 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);
2319 block_inst.args.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
2320 return &block_inst.base;
2321}
2322
15102323fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
15112324 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1512 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
2325 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {});
15132326}
15142327
1515fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst {
1516 const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand);
1517 return self.analyzeDeclRef(scope, inst.base.src, decl);
2328fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
2329 const operand = try self.resolveInst(scope, inst.positionals.operand);
2330 const block = inst.positionals.block;
2331 return self.analyzeBreak(scope, inst.base.src, block, operand);
15182332}
15192333
1520fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
2334fn analyzeInstBreakVoid(self: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
2335 const block = inst.positionals.block;
2336 const void_inst = try self.constVoid(scope, inst.base.src);
2337 return self.analyzeBreak(scope, inst.base.src, block, void_inst);
2338}
2339
2340fn analyzeBreak(
2341 self: *Module,
2342 scope: *Scope,
2343 src: usize,
2344 zir_block: *zir.Inst.Block,
2345 operand: *Inst,
2346) InnerError!*Inst {
2347 var opt_block = scope.cast(Scope.Block);
2348 while (opt_block) |block| {
2349 if (block.label) |*label| {
2350 if (label.zir_block == zir_block) {
2351 try label.results.append(self.gpa, operand);
2352 const b = try self.requireRuntimeBlock(scope, src);
2353 return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{
2354 .block = label.block_inst,
2355 .operand = operand,
2356 });
2357 }
2358 }
2359 opt_block = block.parent;
2360 } else unreachable;
2361}
2362
2363fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
15212364 const decl_name = try self.resolveConstString(scope, inst.positionals.name);
1522 // This will need to get more fleshed out when there are proper structs & namespaces.
1523 const zir_module = scope.namespace();
1524 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
1525 return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name});
2365 return self.analyzeDeclRefByName(scope, inst.base.src, decl_name);
2366}
15262367
1527 const decl = try self.resolveCompleteDecl(scope, src_decl);
1528 return self.analyzeDeclRef(scope, inst.base.src, decl);
2368fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
2369 return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
15292370}
15302371
15312372fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
15322373 const decl_name = inst.positionals.name;
1533 // This will need to get more fleshed out when there are proper structs & namespaces.
1534 const zir_module = scope.namespace();
2374 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
15352375 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
15362376 return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
15372377
1538 const decl = try self.resolveCompleteDecl(scope, src_decl);
2378 const decl = try self.resolveCompleteZirDecl(scope, src_decl.decl);
15392379
15402380 return decl;
15412381}
......@@ -1546,18 +2386,46 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn
15462386 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
15472387}
15482388
2389fn analyzeInstDeclValInModule(self: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
2390 const decl = inst.positionals.decl;
2391 const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
2392 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
2393}
2394
15492395fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2396 const scope_decl = scope.decl().?;
2397 try self.declareDeclDependency(scope_decl, decl);
2398 self.ensureDeclAnalyzed(decl) catch |err| {
2399 if (scope.cast(Scope.Block)) |block| {
2400 if (block.func) |func| {
2401 func.analysis = .dependency_failure;
2402 } else {
2403 block.decl.analysis = .dependency_failure;
2404 }
2405 } else {
2406 scope_decl.analysis = .dependency_failure;
2407 }
2408 return err;
2409 };
2410
15502411 const decl_tv = try decl.typedValue();
15512412 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
15522413 ty_payload.* = .{ .pointee_type = decl_tv.ty };
15532414 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
15542415 val_payload.* = .{ .decl = decl };
2416
15552417 return self.constInst(scope, src, .{
15562418 .ty = Type.initPayload(&ty_payload.base),
15572419 .val = Value.initPayload(&val_payload.base),
15582420 });
15592421}
15602422
2423fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
2424 const decl = self.lookupDeclName(scope, decl_name) orelse
2425 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
2426 return self.analyzeDeclRef(scope, src, decl);
2427}
2428
15612429fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
15622430 const func = try self.resolveInst(scope, inst.positionals.func);
15632431 if (func.ty.zigTypeTag() != .Fn)
......@@ -1605,8 +2473,8 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
16052473
16062474 // TODO handle function calls of generic functions
16072475
1608 const fn_param_types = try self.allocator.alloc(Type, fn_params_len);
1609 defer self.allocator.free(fn_param_types);
2476 const fn_param_types = try self.gpa.alloc(Type, fn_params_len);
2477 defer self.gpa.free(fn_param_types);
16102478 func.ty.fnParamTypes(fn_param_types);
16112479
16122480 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
......@@ -1616,7 +2484,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
16162484 }
16172485
16182486 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1619 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){
2487 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{
16202488 .func = func,
16212489 .args = casted_args,
16222490 });
......@@ -1624,10 +2492,22 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
16242492
16252493fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
16262494 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
2495 const fn_zir = blk: {
2496 var fn_arena = std.heap.ArenaAllocator.init(self.gpa);
2497 errdefer fn_arena.deinit();
2498
2499 const fn_zir = try scope.arena().create(Fn.ZIR);
2500 fn_zir.* = .{
2501 .body = .{
2502 .instructions = fn_inst.positionals.body.instructions,
2503 },
2504 .arena = fn_arena.state,
2505 };
2506 break :blk fn_zir;
2507 };
16272508 const new_func = try scope.arena().create(Fn);
16282509 new_func.* = .{
1629 .fn_type = fn_type,
1630 .analysis = .{ .queued = fn_inst },
2510 .analysis = .{ .queued = fn_zir },
16312511 .owner_decl = scope.decl().?,
16322512 };
16332513 const fn_payload = try scope.arena().create(Value.Payload.Function);
......@@ -1638,31 +2518,45 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError
16382518 });
16392519}
16402520
2521fn analyzeInstIntType(self: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
2522 return self.fail(scope, inttype.base.src, "TODO implement inttype", .{});
2523}
2524
16412525fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
16422526 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
16432527
1644 if (return_type.zigTypeTag() == .NoReturn and
1645 fntype.positionals.param_types.len == 0 and
1646 fntype.kw_args.cc == .Unspecified)
1647 {
1648 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
1649 }
2528 // Hot path for some common function types.
2529 if (fntype.positionals.param_types.len == 0) {
2530 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) {
2531 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
2532 }
16502533
1651 if (return_type.zigTypeTag() == .NoReturn and
1652 fntype.positionals.param_types.len == 0 and
1653 fntype.kw_args.cc == .Naked)
1654 {
1655 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
2534 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) {
2535 return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
2536 }
2537
2538 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) {
2539 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
2540 }
2541
2542 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) {
2543 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
2544 }
16562545 }
16572546
1658 if (return_type.zigTypeTag() == .Void and
1659 fntype.positionals.param_types.len == 0 and
1660 fntype.kw_args.cc == .C)
1661 {
1662 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
2547 const arena = scope.arena();
2548 const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
2549 for (fntype.positionals.param_types) |param_type, i| {
2550 param_types[i] = try self.resolveType(scope, param_type);
16632551 }
16642552
1665 return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{});
2553 const payload = try arena.create(Type.Payload.Function);
2554 payload.* = .{
2555 .cc = fntype.kw_args.cc,
2556 .return_type = return_type,
2557 .param_types = param_types,
2558 };
2559 return self.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
16662560}
16672561
16682562fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
......@@ -1683,7 +2577,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn
16832577 // TODO handle known-pointer-address
16842578 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
16852579 const ty = Type.initTag(.usize);
1686 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });
2580 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr });
16872581}
16882582
16892583fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
......@@ -1788,11 +2682,24 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn
17882682 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
17892683}
17902684
2685fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.Sub) InnerError!*Inst {
2686 return self.fail(scope, inst.base.src, "TODO implement analysis of sub", .{});
2687}
2688
17912689fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {
2690 const tracy = trace(@src());
2691 defer tracy.end();
2692
17922693 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
17932694 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
17942695
1795 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {
2696 if ((lhs.ty.zigTypeTag() == .Int or lhs.ty.zigTypeTag() == .ComptimeInt) and
2697 (rhs.ty.zigTypeTag() == .Int or rhs.ty.zigTypeTag() == .ComptimeInt))
2698 {
2699 if (!lhs.ty.eql(rhs.ty)) {
2700 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
2701 }
2702
17962703 if (lhs.value()) |lhs_val| {
17972704 if (rhs.value()) |rhs_val| {
17982705 // TODO is this a performance issue? maybe we should try the operation without
......@@ -1809,10 +2716,6 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
18092716 result_bigint.add(lhs_bigint, rhs_bigint);
18102717 const result_limbs = result_bigint.limbs[0..result_bigint.len];
18112718
1812 if (!lhs.ty.eql(rhs.ty)) {
1813 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
1814 }
1815
18162719 const val_payload = if (result_bigint.positive) blk: {
18172720 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);
18182721 val_payload.* = .{ .limbs = result_limbs };
......@@ -1829,9 +2732,14 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
18292732 });
18302733 }
18312734 }
1832 }
18332735
1834 return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{});
2736 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2737 return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{
2738 .lhs = lhs,
2739 .rhs = rhs,
2740 });
2741 }
2742 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });
18352743}
18362744
18372745fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.Deref) InnerError!*Inst {
......@@ -1875,7 +2783,7 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
18752783 }
18762784
18772785 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
1878 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){
2786 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{
18792787 .asm_source = asm_source,
18802788 .is_volatile = assembly.kw_args.@"volatile",
18812789 .output = output,
......@@ -1911,20 +2819,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
19112819 }
19122820 const b = try self.requireRuntimeBlock(scope, inst.base.src);
19132821 switch (op) {
1914 .eq => return self.addNewInstArgs(
1915 b,
1916 inst.base.src,
1917 Type.initTag(.bool),
1918 Inst.IsNull,
1919 Inst.Args(Inst.IsNull){ .operand = opt_operand },
1920 ),
1921 .neq => return self.addNewInstArgs(
1922 b,
1923 inst.base.src,
1924 Type.initTag(.bool),
1925 Inst.IsNonNull,
1926 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
1927 ),
2822 .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{
2823 .operand = opt_operand,
2824 }),
2825 .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
2826 .operand = opt_operand,
2827 }),
19282828 else => unreachable,
19292829 }
19302830 } else if (is_equality_cmp and
......@@ -1953,6 +2853,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
19532853 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
19542854}
19552855
2856fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) InnerError!*Inst {
2857 const uncasted_operand = try self.resolveInst(scope, inst.positionals.operand);
2858 const bool_type = Type.initTag(.bool);
2859 const operand = try self.coerce(scope, bool_type, uncasted_operand);
2860 if (try self.resolveDefinedValue(scope, operand)) |val| {
2861 return self.constBool(scope, inst.base.src, !val.toBool());
2862 }
2863 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2864 return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand });
2865}
2866
19562867fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
19572868 const operand = try self.resolveInst(scope, inst.positionals.operand);
19582869 return self.analyzeIsNull(scope, inst.base.src, operand, true);
......@@ -1976,24 +2887,26 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
19762887 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
19772888
19782889 var true_block: Scope.Block = .{
2890 .parent = parent_block,
19792891 .func = parent_block.func,
19802892 .decl = parent_block.decl,
19812893 .instructions = .{},
19822894 .arena = parent_block.arena,
19832895 };
1984 defer true_block.instructions.deinit(self.allocator);
2896 defer true_block.instructions.deinit(self.gpa);
19852897 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
19862898
19872899 var false_block: Scope.Block = .{
2900 .parent = parent_block,
19882901 .func = parent_block.func,
19892902 .decl = parent_block.decl,
19902903 .instructions = .{},
19912904 .arena = parent_block.arena,
19922905 };
1993 defer false_block.instructions.deinit(self.allocator);
2906 defer false_block.instructions.deinit(self.gpa);
19942907 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
19952908
1996 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){
2909 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.noreturn), Inst.CondBr, Inst.Args(Inst.CondBr){
19972910 .condition = cond,
19982911 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
19992912 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
......@@ -2019,23 +2932,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
20192932}
20202933
20212934fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
2935 const operand = try self.resolveInst(scope, inst.positionals.operand);
2936 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2937 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand });
2938}
2939
2940fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
20222941 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2023 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});
2942 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {});
20242943}
20252944
20262945fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
2027 if (scope.cast(Scope.Block)) |b| {
2028 const analysis = b.func.analysis.in_progress;
2029 analysis.needed_inst_capacity += body.instructions.len;
2030 try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity);
2031 for (body.instructions) |src_inst| {
2032 const new_inst = try self.analyzeInst(scope, src_inst);
2033 analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst);
2034 }
2035 } else {
2036 for (body.instructions) |src_inst| {
2037 _ = try self.analyzeInst(scope, src_inst);
2038 }
2946 for (body.instructions) |src_inst| {
2947 src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst);
20392948 }
20402949}
20412950
......@@ -2118,7 +3027,7 @@ fn cmpNumeric(
21183027 };
21193028 const casted_lhs = try self.coerce(scope, dest_type, lhs);
21203029 const casted_rhs = try self.coerce(scope, dest_type, rhs);
2121 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
3030 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{
21223031 .lhs = casted_lhs,
21233032 .rhs = casted_rhs,
21243033 .op = op,
......@@ -2148,7 +3057,7 @@ fn cmpNumeric(
21483057 return self.constUndef(scope, src, Type.initTag(.bool));
21493058 const is_unsigned = if (lhs_is_float) x: {
21503059 var bigint_space: Value.BigIntSpace = undefined;
2151 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
3060 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
21523061 defer bigint.deinit();
21533062 const zcmp = lhs_val.orderAgainstZero();
21543063 if (lhs_val.floatHasFraction()) {
......@@ -2183,7 +3092,7 @@ fn cmpNumeric(
21833092 return self.constUndef(scope, src, Type.initTag(.bool));
21843093 const is_unsigned = if (rhs_is_float) x: {
21853094 var bigint_space: Value.BigIntSpace = undefined;
2186 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);
3095 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
21873096 defer bigint.deinit();
21883097 const zcmp = rhs_val.orderAgainstZero();
21893098 if (rhs_val.floatHasFraction()) {
......@@ -2220,9 +3129,9 @@ fn cmpNumeric(
22203129 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
22213130 };
22223131 const casted_lhs = try self.coerce(scope, dest_type, lhs);
2223 const casted_rhs = try self.coerce(scope, dest_type, lhs);
3132 const casted_rhs = try self.coerce(scope, dest_type, rhs);
22243133
2225 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){
3134 return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{
22263135 .lhs = casted_lhs,
22273136 .rhs = casted_rhs,
22283137 .op = op,
......@@ -2241,6 +3150,31 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
22413150 }
22423151}
22433152
3153fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3154 if (instructions.len == 0)
3155 return Type.initTag(.noreturn);
3156
3157 if (instructions.len == 1)
3158 return instructions[0].ty;
3159
3160 var prev_inst = instructions[0];
3161 for (instructions[1..]) |next_inst| {
3162 if (next_inst.ty.eql(prev_inst.ty))
3163 continue;
3164 if (next_inst.ty.zigTypeTag() == .NoReturn)
3165 continue;
3166 if (prev_inst.ty.zigTypeTag() == .NoReturn) {
3167 prev_inst = next_inst;
3168 continue;
3169 }
3170
3171 // TODO error notes pointing out each type
3172 return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
3173 }
3174
3175 return prev_inst.ty;
3176}
3177
22443178fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
22453179 // If the types are the same, we can return the operand.
22463180 if (dest_type.eql(inst.ty))
......@@ -2282,7 +3216,10 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
22823216 if (inst.value()) |val| {
22833217 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
22843218 } else {
2285 return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{});
3219 return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{
3220 inst.ty,
3221 dest_type,
3222 });
22863223 }
22873224 } else {
22883225 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
......@@ -2299,7 +3236,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
22993236 }
23003237 // TODO validate the type size and other compile errors
23013238 const b = try self.requireRuntimeBlock(scope, inst.src);
2302 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
3239 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst });
23033240}
23043241
23053242fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
......@@ -2310,34 +3247,77 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
23103247 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
23113248}
23123249
2313fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError {
3250pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
23143251 @setCold(true);
2315 const err_msg = try ErrorMsg.create(self.allocator, src, format, args);
3252 const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
23163253 return self.failWithOwnedErrorMsg(scope, src, err_msg);
23173254}
23183255
3256pub fn failTok(
3257 self: *Module,
3258 scope: *Scope,
3259 token_index: ast.TokenIndex,
3260 comptime format: []const u8,
3261 args: anytype,
3262) InnerError {
3263 @setCold(true);
3264 const src = scope.tree().token_locs[token_index].start;
3265 return self.fail(scope, src, format, args);
3266}
3267
3268pub fn failNode(
3269 self: *Module,
3270 scope: *Scope,
3271 ast_node: *ast.Node,
3272 comptime format: []const u8,
3273 args: anytype,
3274) InnerError {
3275 @setCold(true);
3276 const src = scope.tree().token_locs[ast_node.firstToken()].start;
3277 return self.fail(scope, src, format, args);
3278}
3279
23193280fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {
23203281 {
2321 errdefer err_msg.destroy(self.allocator);
2322 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
2323 try self.failed_files.ensureCapacity(self.failed_files.size + 1);
3282 errdefer err_msg.destroy(self.gpa);
3283 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
3284 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
23243285 }
23253286 switch (scope.tag) {
23263287 .decl => {
23273288 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
23283289 decl.analysis = .sema_failure;
3290 decl.generation = self.generation;
23293291 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
23303292 },
23313293 .block => {
23323294 const block = scope.cast(Scope.Block).?;
2333 block.func.analysis = .sema_failure;
3295 if (block.func) |func| {
3296 func.analysis = .sema_failure;
3297 } else {
3298 block.decl.analysis = .sema_failure;
3299 block.decl.generation = self.generation;
3300 }
23343301 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
23353302 },
3303 .gen_zir => {
3304 const gen_zir = scope.cast(Scope.GenZIR).?;
3305 gen_zir.decl.analysis = .sema_failure;
3306 gen_zir.decl.generation = self.generation;
3307 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
3308 },
3309 .local_var => {
3310 const gen_zir = scope.cast(Scope.LocalVar).?.gen_zir;
3311 gen_zir.decl.analysis = .sema_failure;
3312 gen_zir.decl.generation = self.generation;
3313 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
3314 },
23363315 .zir_module => {
23373316 const zir_module = scope.cast(Scope.ZIRModule).?;
23383317 zir_module.status = .loaded_sema_failure;
2339 self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg);
3318 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
23403319 },
3320 .file => unreachable,
23413321 }
23423322 return error.AnalysisFail;
23433323}
......@@ -2360,28 +3340,32 @@ pub const ErrorMsg = struct {
23603340 byte_offset: usize,
23613341 msg: []const u8,
23623342
2363 pub fn create(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !*ErrorMsg {
2364 const self = try allocator.create(ErrorMsg);
2365 errdefer allocator.destroy(self);
2366 self.* = try init(allocator, byte_offset, format, args);
3343 pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
3344 const self = try gpa.create(ErrorMsg);
3345 errdefer gpa.destroy(self);
3346 self.* = try init(gpa, byte_offset, format, args);
23673347 return self;
23683348 }
23693349
23703350 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
2371 pub fn destroy(self: *ErrorMsg, allocator: *Allocator) void {
2372 self.deinit(allocator);
2373 allocator.destroy(self);
3351 pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
3352 self.deinit(gpa);
3353 gpa.destroy(self);
23743354 }
23753355
2376 pub fn init(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !ErrorMsg {
3356 pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
23773357 return ErrorMsg{
23783358 .byte_offset = byte_offset,
2379 .msg = try std.fmt.allocPrint(allocator, format, args),
3359 .msg = try std.fmt.allocPrint(gpa, format, args),
23803360 };
23813361 }
23823362
2383 pub fn deinit(self: *ErrorMsg, allocator: *Allocator) void {
2384 allocator.free(self.msg);
3363 pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
3364 gpa.free(self.msg);
23853365 self.* = undefined;
23863366 }
23873367};
3368
3369fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
3370 return @bitCast(u128, a) == @bitCast(u128, b);
3371}
src-self-hosted/TypedValue.zig+8
......@@ -21,3 +21,11 @@ pub const Managed = struct {
2121 self.* = undefined;
2222 }
2323};
24
25/// Assumes arena allocation. Does a recursive copy.
26pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
27 return TypedValue{
28 .ty = try self.ty.copy(allocator),
29 .val = try self.val.copy(allocator),
30 };
31}
src-self-hosted/astgen.zig created+643
......@@ -0,0 +1,643 @@
1const std = @import("std");
2const mem = std.mem;
3const Value = @import("value.zig").Value;
4const Type = @import("type.zig").Type;
5const TypedValue = @import("TypedValue.zig");
6const assert = std.debug.assert;
7const zir = @import("zir.zig");
8const Module = @import("Module.zig");
9const ast = std.zig.ast;
10const trace = @import("tracy.zig").trace;
11const Scope = Module.Scope;
12const InnerError = Module.InnerError;
13
14/// Turn Zig AST into untyped ZIR istructions.
15pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
16 switch (node.tag) {
17 .VarDecl => unreachable, // Handled in `blockExpr`.
18
19 .Identifier => return identifier(mod, scope, node.castTag(.Identifier).?),
20 .Asm => return assembly(mod, scope, node.castTag(.Asm).?),
21 .StringLiteral => return stringLiteral(mod, scope, node.castTag(.StringLiteral).?),
22 .IntegerLiteral => return integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?),
23 .BuiltinCall => return builtinCall(mod, scope, node.castTag(.BuiltinCall).?),
24 .Call => return callExpr(mod, scope, node.castTag(.Call).?),
25 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
26 .ControlFlowExpression => return controlFlowExpr(mod, scope, node.castTag(.ControlFlowExpression).?),
27 .If => return ifExpr(mod, scope, node.castTag(.If).?),
28 .Assign => return assign(mod, scope, node.castTag(.Assign).?),
29 .Add => return add(mod, scope, node.castTag(.Add).?),
30 .BangEqual => return cmp(mod, scope, node.castTag(.BangEqual).?, .neq),
31 .EqualEqual => return cmp(mod, scope, node.castTag(.EqualEqual).?, .eq),
32 .GreaterThan => return cmp(mod, scope, node.castTag(.GreaterThan).?, .gt),
33 .GreaterOrEqual => return cmp(mod, scope, node.castTag(.GreaterOrEqual).?, .gte),
34 .LessThan => return cmp(mod, scope, node.castTag(.LessThan).?, .lt),
35 .LessOrEqual => return cmp(mod, scope, node.castTag(.LessOrEqual).?, .lte),
36 .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
37 else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
38 }
39}
40
41pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) !void {
42 const tracy = trace(@src());
43 defer tracy.end();
44
45 if (block_node.label) |label| {
46 return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{});
47 }
48
49 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
50 defer block_arena.deinit();
51
52 var scope = parent_scope;
53 for (block_node.statements()) |statement| {
54 switch (statement.tag) {
55 .VarDecl => {
56 const sub_scope = try block_arena.allocator.create(Scope.LocalVar);
57 const var_decl_node = @fieldParentPtr(ast.Node.VarDecl, "base", statement);
58 sub_scope.* = try varDecl(mod, scope, var_decl_node);
59 scope = &sub_scope.base;
60 },
61 else => _ = try expr(mod, scope, statement),
62 }
63 }
64}
65
66fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scope.LocalVar {
67 // TODO implement detection of shadowing
68 if (node.getTrailer("comptime_token")) |comptime_token| {
69 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
70 }
71 if (node.getTrailer("align_node")) |align_node| {
72 return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
73 }
74 if (node.getTrailer("type_node")) |type_node| {
75 return mod.failNode(scope, type_node, "TODO implement typed locals", .{});
76 }
77 const tree = scope.tree();
78 switch (tree.token_ids[node.mut_token]) {
79 .Keyword_const => {},
80 .Keyword_var => {
81 return mod.failTok(scope, node.mut_token, "TODO implement mutable locals", .{});
82 },
83 else => unreachable,
84 }
85 // Depending on the type of AST the initialization expression is, we may need an lvalue
86 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
87 // the variable, no memory location needed.
88 const init_node = node.getTrailer("init_node").?;
89 if (nodeNeedsMemoryLocation(init_node)) {
90 return mod.failNode(scope, init_node, "TODO implement result locations", .{});
91 }
92 const init_inst = try expr(mod, scope, init_node);
93 const ident_name = tree.tokenSlice(node.name_token); // TODO support @"aoeu" identifiers
94 return Scope.LocalVar{
95 .parent = scope,
96 .gen_zir = scope.getGenZIR(),
97 .name = ident_name,
98 .inst = init_inst,
99 };
100}
101
102fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
103 const operand = try expr(mod, scope, node.rhs);
104 const tree = scope.tree();
105 const src = tree.token_locs[node.op_token].start;
106 return mod.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
107}
108
109fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
110 if (infix_node.lhs.tag == .Identifier) {
111 const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
112 const tree = scope.tree();
113 const ident_name = tree.tokenSlice(ident.token);
114 if (std.mem.eql(u8, ident_name, "_")) {
115 return expr(mod, scope, infix_node.rhs);
116 } else {
117 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
118 }
119 } else {
120 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
121 }
122}
123
124fn add(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
125 const lhs = try expr(mod, scope, infix_node.lhs);
126 const rhs = try expr(mod, scope, infix_node.rhs);
127
128 const tree = scope.tree();
129 const src = tree.token_locs[infix_node.op_token].start;
130
131 return mod.addZIRInst(scope, src, zir.Inst.Add, .{ .lhs = lhs, .rhs = rhs }, .{});
132}
133
134fn cmp(
135 mod: *Module,
136 scope: *Scope,
137 infix_node: *ast.Node.SimpleInfixOp,
138 op: std.math.CompareOperator,
139) InnerError!*zir.Inst {
140 const lhs = try expr(mod, scope, infix_node.lhs);
141 const rhs = try expr(mod, scope, infix_node.rhs);
142
143 const tree = scope.tree();
144 const src = tree.token_locs[infix_node.op_token].start;
145
146 return mod.addZIRInst(scope, src, zir.Inst.Cmp, .{
147 .lhs = lhs,
148 .op = op,
149 .rhs = rhs,
150 }, .{});
151}
152
153fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
154 if (if_node.payload) |payload| {
155 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
156 }
157 if (if_node.@"else") |else_node| {
158 if (else_node.payload) |payload| {
159 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{});
160 }
161 }
162 var block_scope: Scope.GenZIR = .{
163 .parent = scope,
164 .decl = scope.decl().?,
165 .arena = scope.arena(),
166 .instructions = .{},
167 };
168 defer block_scope.instructions.deinit(mod.gpa);
169
170 const cond = try expr(mod, &block_scope.base, if_node.condition);
171
172 const tree = scope.tree();
173 const if_src = tree.token_locs[if_node.if_token].start;
174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
175 .condition = cond,
176 .true_body = undefined, // populated below
177 .false_body = undefined, // populated below
178 }, .{});
179
180 const block = try mod.addZIRInstBlock(scope, if_src, .{
181 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
182 });
183 var then_scope: Scope.GenZIR = .{
184 .parent = scope,
185 .decl = block_scope.decl,
186 .arena = block_scope.arena,
187 .instructions = .{},
188 };
189 defer then_scope.instructions.deinit(mod.gpa);
190
191 const then_result = try expr(mod, &then_scope.base, if_node.body);
192 if (!then_result.tag.isNoReturn()) {
193 const then_src = tree.token_locs[if_node.body.lastToken()].start;
194 _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
195 .block = block,
196 .operand = then_result,
197 }, .{});
198 }
199 condbr.positionals.true_body = .{
200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
201 };
202
203 var else_scope: Scope.GenZIR = .{
204 .parent = scope,
205 .decl = block_scope.decl,
206 .arena = block_scope.arena,
207 .instructions = .{},
208 };
209 defer else_scope.instructions.deinit(mod.gpa);
210
211 if (if_node.@"else") |else_node| {
212 const else_result = try expr(mod, &else_scope.base, else_node.body);
213 if (!else_result.tag.isNoReturn()) {
214 const else_src = tree.token_locs[else_node.body.lastToken()].start;
215 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
216 .block = block,
217 .operand = else_result,
218 }, .{});
219 }
220 } else {
221 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
222 // by directly allocating the body for this one instruction.
223 const else_src = tree.token_locs[if_node.lastToken()].start;
224 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
225 .block = block,
226 }, .{});
227 }
228 condbr.positionals.false_body = .{
229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
230 };
231
232 return &block.base;
233}
234
235fn controlFlowExpr(
236 mod: *Module,
237 scope: *Scope,
238 cfe: *ast.Node.ControlFlowExpression,
239) InnerError!*zir.Inst {
240 switch (cfe.kind) {
241 .Break => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Break", .{}),
242 .Continue => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Continue", .{}),
243 .Return => {},
244 }
245 const tree = scope.tree();
246 const src = tree.token_locs[cfe.ltoken].start;
247 if (cfe.rhs) |rhs_node| {
248 const operand = try expr(mod, scope, rhs_node);
249 return mod.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
250 } else {
251 return mod.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
252 }
253}
254
255fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
256 const tracy = trace(@src());
257 defer tracy.end();
258
259 const tree = scope.tree();
260 // TODO implement @"aoeu" identifiers
261 const ident_name = tree.tokenSlice(ident.token);
262 const src = tree.token_locs[ident.token].start;
263 if (mem.eql(u8, ident_name, "_")) {
264 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
265 }
266
267 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
268 return mod.addZIRInstConst(scope, src, typed_value);
269 }
270
271 if (ident_name.len >= 2) integer: {
272 const first_c = ident_name[0];
273 if (first_c == 'i' or first_c == 'u') {
274 const is_signed = first_c == 'i';
275 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
276 error.Overflow => return mod.failNode(
277 scope,
278 &ident.base,
279 "primitive integer type '{}' exceeds maximum bit width of 65535",
280 .{ident_name},
281 ),
282 error.InvalidCharacter => break :integer,
283 };
284 const val = switch (bit_count) {
285 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
286 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
287 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
288 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
289 else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
290 };
291 return mod.addZIRInstConst(scope, src, .{
292 .ty = Type.initTag(.type),
293 .val = val,
294 });
295 }
296 }
297
298 // Local variables, including function parameters.
299 {
300 var s = scope;
301 while (true) switch (s.tag) {
302 .local_var => {
303 const local_var = s.cast(Scope.LocalVar).?;
304 if (mem.eql(u8, local_var.name, ident_name)) {
305 return local_var.inst;
306 }
307 s = local_var.parent;
308 },
309 .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
310 else => break,
311 };
312 }
313
314 if (mod.lookupDeclName(scope, ident_name)) |decl| {
315 return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
316 }
317
318 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
319}
320
321fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
322 const tree = scope.tree();
323 const unparsed_bytes = tree.tokenSlice(str_lit.token);
324 const arena = scope.arena();
325
326 var bad_index: usize = undefined;
327 const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
328 error.InvalidCharacter => {
329 const bad_byte = unparsed_bytes[bad_index];
330 const src = tree.token_locs[str_lit.token].start;
331 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
332 },
333 else => |e| return e,
334 };
335
336 const src = tree.token_locs[str_lit.token].start;
337 return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
338}
339
340fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
341 const arena = scope.arena();
342 const tree = scope.tree();
343 const prefixed_bytes = tree.tokenSlice(int_lit.token);
344 const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
345 16
346 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
347 8
348 else if (mem.startsWith(u8, prefixed_bytes, "0b"))
349 2
350 else
351 @as(u8, 10);
352
353 const bytes = if (base == 10)
354 prefixed_bytes
355 else
356 prefixed_bytes[2..];
357
358 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
359 const int_payload = try arena.create(Value.Payload.Int_u64);
360 int_payload.* = .{ .int = small_int };
361 const src = tree.token_locs[int_lit.token].start;
362 return mod.addZIRInstConst(scope, src, .{
363 .ty = Type.initTag(.comptime_int),
364 .val = Value.initPayload(&int_payload.base),
365 });
366 } else |err| {
367 return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
368 }
369}
370
371fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
372 if (asm_node.outputs.len != 0) {
373 return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
374 }
375 const arena = scope.arena();
376 const tree = scope.tree();
377
378 const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
379 const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
380
381 for (asm_node.inputs) |input, i| {
382 // TODO semantically analyze constraints
383 inputs[i] = try expr(mod, scope, input.constraint);
384 args[i] = try expr(mod, scope, input.expr);
385 }
386
387 const src = tree.token_locs[asm_node.asm_token].start;
388 const return_type = try mod.addZIRInstConst(scope, src, .{
389 .ty = Type.initTag(.type),
390 .val = Value.initTag(.void_type),
391 });
392 const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
393 .asm_source = try expr(mod, scope, asm_node.template),
394 .return_type = return_type,
395 }, .{
396 .@"volatile" = asm_node.volatile_token != null,
397 //.clobbers = TODO handle clobbers
398 .inputs = inputs,
399 .args = args,
400 });
401 return asm_inst;
402}
403
404fn builtinCall(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
405 const tree = scope.tree();
406 const builtin_name = tree.tokenSlice(call.builtin_token);
407 const src = tree.token_locs[call.builtin_token].start;
408
409 inline for (std.meta.declarations(zir.Inst)) |inst| {
410 if (inst.data != .Type) continue;
411 const T = inst.data.Type;
412 if (!@hasDecl(T, "builtin_name")) continue;
413 if (std.mem.eql(u8, builtin_name, T.builtin_name)) {
414 var value: T = undefined;
415 const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct;
416 if (positionals.fields.len == 0) {
417 return mod.addZIRInst(scope, src, T, value.positionals, value.kw_args);
418 }
419 const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len;
420 if (arg_count) |some| {
421 if (call.params_len != some) {
422 return mod.failTok(
423 scope,
424 call.builtin_token,
425 "expected {} parameter{}, found {}",
426 .{ some, if (some == 1) "" else "s", call.params_len },
427 );
428 }
429 const params = call.params();
430 inline for (positionals.fields) |p, i| {
431 @field(value.positionals, p.name) = try expr(mod, scope, params[i]);
432 }
433 } else {
434 return mod.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name});
435 }
436
437 return mod.addZIRInst(scope, src, T, value.positionals, .{});
438 }
439 }
440 return mod.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
441}
442
443fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.Inst {
444 const tree = scope.tree();
445 const lhs = try expr(mod, scope, node.lhs);
446
447 const param_nodes = node.params();
448 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
449 for (param_nodes) |param_node, i| {
450 args[i] = try expr(mod, scope, param_node);
451 }
452
453 const src = tree.token_locs[node.lhs.firstToken()].start;
454 return mod.addZIRInst(scope, src, zir.Inst.Call, .{
455 .func = lhs,
456 .args = args,
457 }, .{});
458}
459
460fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
461 const tree = scope.tree();
462 const src = tree.token_locs[unreach_node.token].start;
463 return mod.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
464}
465
466fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
467 const simple_types = std.ComptimeStringMap(Value.Tag, .{
468 .{ "u8", .u8_type },
469 .{ "i8", .i8_type },
470 .{ "isize", .isize_type },
471 .{ "usize", .usize_type },
472 .{ "c_short", .c_short_type },
473 .{ "c_ushort", .c_ushort_type },
474 .{ "c_int", .c_int_type },
475 .{ "c_uint", .c_uint_type },
476 .{ "c_long", .c_long_type },
477 .{ "c_ulong", .c_ulong_type },
478 .{ "c_longlong", .c_longlong_type },
479 .{ "c_ulonglong", .c_ulonglong_type },
480 .{ "c_longdouble", .c_longdouble_type },
481 .{ "f16", .f16_type },
482 .{ "f32", .f32_type },
483 .{ "f64", .f64_type },
484 .{ "f128", .f128_type },
485 .{ "c_void", .c_void_type },
486 .{ "bool", .bool_type },
487 .{ "void", .void_type },
488 .{ "type", .type_type },
489 .{ "anyerror", .anyerror_type },
490 .{ "comptime_int", .comptime_int_type },
491 .{ "comptime_float", .comptime_float_type },
492 .{ "noreturn", .noreturn_type },
493 });
494 if (simple_types.get(name)) |tag| {
495 return TypedValue{
496 .ty = Type.initTag(.type),
497 .val = Value.initTag(tag),
498 };
499 }
500 if (mem.eql(u8, name, "null")) {
501 return TypedValue{
502 .ty = Type.initTag(.@"null"),
503 .val = Value.initTag(.null_value),
504 };
505 }
506 if (mem.eql(u8, name, "undefined")) {
507 return TypedValue{
508 .ty = Type.initTag(.@"undefined"),
509 .val = Value.initTag(.undef),
510 };
511 }
512 if (mem.eql(u8, name, "true")) {
513 return TypedValue{
514 .ty = Type.initTag(.bool),
515 .val = Value.initTag(.bool_true),
516 };
517 }
518 if (mem.eql(u8, name, "false")) {
519 return TypedValue{
520 .ty = Type.initTag(.bool),
521 .val = Value.initTag(.bool_false),
522 };
523 }
524 return null;
525}
526
527fn nodeNeedsMemoryLocation(node: *ast.Node) bool {
528 return switch (node.tag) {
529 .Root,
530 .Use,
531 .TestDecl,
532 .DocComment,
533 .SwitchCase,
534 .SwitchElse,
535 .Else,
536 .Payload,
537 .PointerPayload,
538 .PointerIndexPayload,
539 .ContainerField,
540 .ErrorTag,
541 .FieldInitializer,
542 => unreachable,
543
544 .ControlFlowExpression,
545 .BitNot,
546 .BoolNot,
547 .VarDecl,
548 .Defer,
549 .AddressOf,
550 .OptionalType,
551 .Negation,
552 .NegationWrap,
553 .Resume,
554 .ArrayType,
555 .ArrayTypeSentinel,
556 .PtrType,
557 .SliceType,
558 .Suspend,
559 .AnyType,
560 .ErrorType,
561 .FnProto,
562 .AnyFrameType,
563 .IntegerLiteral,
564 .FloatLiteral,
565 .EnumLiteral,
566 .StringLiteral,
567 .MultilineStringLiteral,
568 .CharLiteral,
569 .BoolLiteral,
570 .NullLiteral,
571 .UndefinedLiteral,
572 .Unreachable,
573 .Identifier,
574 .ErrorSetDecl,
575 .ContainerDecl,
576 .Asm,
577 .Add,
578 .AddWrap,
579 .ArrayCat,
580 .ArrayMult,
581 .Assign,
582 .AssignBitAnd,
583 .AssignBitOr,
584 .AssignBitShiftLeft,
585 .AssignBitShiftRight,
586 .AssignBitXor,
587 .AssignDiv,
588 .AssignSub,
589 .AssignSubWrap,
590 .AssignMod,
591 .AssignAdd,
592 .AssignAddWrap,
593 .AssignMul,
594 .AssignMulWrap,
595 .BangEqual,
596 .BitAnd,
597 .BitOr,
598 .BitShiftLeft,
599 .BitShiftRight,
600 .BitXor,
601 .BoolAnd,
602 .BoolOr,
603 .Div,
604 .EqualEqual,
605 .ErrorUnion,
606 .GreaterOrEqual,
607 .GreaterThan,
608 .LessOrEqual,
609 .LessThan,
610 .MergeErrorSets,
611 .Mod,
612 .Mul,
613 .MulWrap,
614 .Range,
615 .Period,
616 .Sub,
617 .SubWrap,
618 => false,
619
620 .ArrayInitializer,
621 .ArrayInitializerDot,
622 .StructInitializer,
623 .StructInitializerDot,
624 => true,
625
626 .GroupedExpression => nodeNeedsMemoryLocation(node.castTag(.GroupedExpression).?.expr),
627
628 .UnwrapOptional => @panic("TODO nodeNeedsMemoryLocation for UnwrapOptional"),
629 .Catch => @panic("TODO nodeNeedsMemoryLocation for Catch"),
630 .Await => @panic("TODO nodeNeedsMemoryLocation for Await"),
631 .Try => @panic("TODO nodeNeedsMemoryLocation for Try"),
632 .If => @panic("TODO nodeNeedsMemoryLocation for If"),
633 .SuffixOp => @panic("TODO nodeNeedsMemoryLocation for SuffixOp"),
634 .Call => @panic("TODO nodeNeedsMemoryLocation for Call"),
635 .Switch => @panic("TODO nodeNeedsMemoryLocation for Switch"),
636 .While => @panic("TODO nodeNeedsMemoryLocation for While"),
637 .For => @panic("TODO nodeNeedsMemoryLocation for For"),
638 .BuiltinCall => @panic("TODO nodeNeedsMemoryLocation for BuiltinCall"),
639 .Comptime => @panic("TODO nodeNeedsMemoryLocation for Comptime"),
640 .Nosuspend => @panic("TODO nodeNeedsMemoryLocation for Nosuspend"),
641 .Block => @panic("TODO nodeNeedsMemoryLocation for Block"),
642 };
643}
src-self-hosted/cbe.h created+8
......@@ -0,0 +1,8 @@
1#if __STDC_VERSION__ >= 201112L
2#define noreturn _Noreturn
3#elif __GNUC__ && !__STRICT_ANSI__
4#define noreturn __attribute__ ((noreturn))
5#else
6#define noreturn
7#endif
8
src-self-hosted/codegen.zig+820-178
......@@ -10,6 +10,19 @@ const Module = @import("Module.zig");
1010const ErrorMsg = Module.ErrorMsg;
1111const Target = std.Target;
1212const Allocator = mem.Allocator;
13const trace = @import("tracy.zig").trace;
14
15/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
16pub const BlockData = struct {
17 relocs: std.ArrayListUnmanaged(Reloc) = .{},
18};
19
20pub const Reloc = union(enum) {
21 /// The value is an offset into the `Function` `code` from the beginning.
22 /// To perform the reloc, write 32-bit signed little-endian integer
23 /// which is a relative jump, based on the address following the reloc.
24 rel32: usize,
25};
1326
1427pub const Result = union(enum) {
1528 /// The `code` parameter passed to `generateSymbol` has the value appended.
......@@ -20,7 +33,7 @@ pub const Result = union(enum) {
2033};
2134
2235pub fn generateSymbol(
23 bin_file: *link.ElfFile,
36 bin_file: *link.File.Elf,
2437 src: usize,
2538 typed_value: TypedValue,
2639 code: *std.ArrayList(u8),
......@@ -29,27 +42,52 @@ pub fn generateSymbol(
2942 /// A Decl that this symbol depends on had a semantic analysis failure.
3043 AnalysisFail,
3144}!Result {
45 const tracy = trace(@src());
46 defer tracy.end();
47
3248 switch (typed_value.ty.zigTypeTag()) {
3349 .Fn => {
3450 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
3551
52 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
53 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
54 defer bin_file.allocator.free(param_types);
55 fn_type.fnParamTypes(param_types);
56 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
57 defer bin_file.allocator.free(mc_args);
58
59 var branch_stack = std.ArrayList(Function.Branch).init(bin_file.allocator);
60 defer {
61 assert(branch_stack.items.len == 1);
62 branch_stack.items[0].deinit(bin_file.allocator);
63 branch_stack.deinit();
64 }
65 const branch = try branch_stack.addOne();
66 branch.* = .{};
67
3668 var function = Function{
69 .gpa = bin_file.allocator,
3770 .target = &bin_file.options.target,
3871 .bin_file = bin_file,
3972 .mod_fn = module_fn,
4073 .code = code,
41 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator),
4274 .err_msg = null,
75 .args = mc_args,
76 .arg_index = 0,
77 .branch_stack = &branch_stack,
78 .src = src,
4379 };
44 defer function.inst_table.deinit();
4580
46 for (module_fn.analysis.success.instructions) |inst| {
47 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
48 error.CodegenFail => return Result{ .fail = function.err_msg.? },
49 else => |e| return e,
50 };
51 try function.inst_table.putNoClobber(inst, new_inst);
52 }
81 const cc = fn_type.fnCallingConvention();
82 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
83 error.CodegenFail => return Result{ .fail = function.err_msg.? },
84 else => |e| return e,
85 };
86
87 function.gen() catch |err| switch (err) {
88 error.CodegenFail => return Result{ .fail = function.err_msg.? },
89 else => |e| return e,
90 };
5391
5492 if (function.err_msg) |em| {
5593 return Result{ .fail = em };
......@@ -146,47 +184,434 @@ pub fn generateSymbol(
146184 }
147185}
148186
187const InnerError = error{
188 OutOfMemory,
189 CodegenFail,
190};
191
192const MCValue = union(enum) {
193 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
194 none,
195 /// Control flow will not allow this value to be observed.
196 unreach,
197 /// No more references to this value remain.
198 dead,
199 /// A pointer-sized integer that fits in a register.
200 immediate: u64,
201 /// The constant was emitted into the code, at this offset.
202 embedded_in_code: usize,
203 /// The value is in a target-specific register. The value can
204 /// be @intToEnum casted to the respective Reg enum.
205 register: usize,
206 /// The value is in memory at a hard-coded address.
207 memory: u64,
208 /// The value is one of the stack variables.
209 stack_offset: u64,
210 /// The value is in the compare flags assuming an unsigned operation,
211 /// with this operator applied on top of it.
212 compare_flags_unsigned: std.math.CompareOperator,
213 /// The value is in the compare flags assuming a signed operation,
214 /// with this operator applied on top of it.
215 compare_flags_signed: std.math.CompareOperator,
216
217 fn isMemory(mcv: MCValue) bool {
218 return switch (mcv) {
219 .embedded_in_code, .memory, .stack_offset => true,
220 else => false,
221 };
222 }
223
224 fn isImmediate(mcv: MCValue) bool {
225 return switch (mcv) {
226 .immediate => true,
227 else => false,
228 };
229 }
230
231 fn isMutable(mcv: MCValue) bool {
232 return switch (mcv) {
233 .none => unreachable,
234 .unreach => unreachable,
235 .dead => unreachable,
236
237 .immediate,
238 .embedded_in_code,
239 .memory,
240 .compare_flags_unsigned,
241 .compare_flags_signed,
242 => false,
243
244 .register,
245 .stack_offset,
246 => true,
247 };
248 }
249};
250
149251const Function = struct {
150 bin_file: *link.ElfFile,
252 gpa: *Allocator,
253 bin_file: *link.File.Elf,
151254 target: *const std.Target,
152255 mod_fn: *const Module.Fn,
153256 code: *std.ArrayList(u8),
154 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
155257 err_msg: ?*ErrorMsg,
258 args: []MCValue,
259 arg_index: usize,
260 src: usize,
156261
157 const MCValue = union(enum) {
158 none,
159 unreach,
160 /// A pointer-sized integer that fits in a register.
161 immediate: u64,
162 /// The constant was emitted into the code, at this offset.
163 embedded_in_code: usize,
164 /// The value is in a target-specific register. The value can
165 /// be @intToEnum casted to the respective Reg enum.
166 register: usize,
167 /// The value is in memory at a hard-coded address.
168 memory: u64,
262 /// Whenever there is a runtime branch, we push a Branch onto this stack,
263 /// and pop it off when the runtime branch joins. This provides an "overlay"
264 /// of the table of mappings from instructions to `MCValue` from within the branch.
265 /// This way we can modify the `MCValue` for an instruction in different ways
266 /// within different branches. Special consideration is needed when a branch
267 /// joins with its parent, to make sure all instructions have the same MCValue
268 /// across each runtime branch upon joining.
269 branch_stack: *std.ArrayList(Branch),
270
271 const Branch = struct {
272 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
273
274 /// The key is an enum value of an arch-specific register.
275 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
276
277 /// Maps offset to what is stored there.
278 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
279 /// Offset from the stack base, representing the end of the stack frame.
280 max_end_stack: u32 = 0,
281 /// Represents the current end stack offset. If there is no existing slot
282 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
283 next_stack_offset: u32 = 0,
284
285 fn deinit(self: *Branch, gpa: *Allocator) void {
286 self.inst_table.deinit(gpa);
287 self.registers.deinit(gpa);
288 self.stack.deinit(gpa);
289 self.* = undefined;
290 }
291 };
292
293 const RegisterAllocation = struct {
294 inst: *ir.Inst,
169295 };
170296
171 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
297 const StackAllocation = struct {
298 inst: *ir.Inst,
299 size: u32,
300 };
301
302 fn gen(self: *Function) !void {
303 switch (self.target.cpu.arch) {
304 .arm => return self.genArch(.arm),
305 .armeb => return self.genArch(.armeb),
306 .aarch64 => return self.genArch(.aarch64),
307 .aarch64_be => return self.genArch(.aarch64_be),
308 .aarch64_32 => return self.genArch(.aarch64_32),
309 .arc => return self.genArch(.arc),
310 .avr => return self.genArch(.avr),
311 .bpfel => return self.genArch(.bpfel),
312 .bpfeb => return self.genArch(.bpfeb),
313 .hexagon => return self.genArch(.hexagon),
314 .mips => return self.genArch(.mips),
315 .mipsel => return self.genArch(.mipsel),
316 .mips64 => return self.genArch(.mips64),
317 .mips64el => return self.genArch(.mips64el),
318 .msp430 => return self.genArch(.msp430),
319 .powerpc => return self.genArch(.powerpc),
320 .powerpc64 => return self.genArch(.powerpc64),
321 .powerpc64le => return self.genArch(.powerpc64le),
322 .r600 => return self.genArch(.r600),
323 .amdgcn => return self.genArch(.amdgcn),
324 .riscv32 => return self.genArch(.riscv32),
325 .riscv64 => return self.genArch(.riscv64),
326 .sparc => return self.genArch(.sparc),
327 .sparcv9 => return self.genArch(.sparcv9),
328 .sparcel => return self.genArch(.sparcel),
329 .s390x => return self.genArch(.s390x),
330 .tce => return self.genArch(.tce),
331 .tcele => return self.genArch(.tcele),
332 .thumb => return self.genArch(.thumb),
333 .thumbeb => return self.genArch(.thumbeb),
334 .i386 => return self.genArch(.i386),
335 .x86_64 => return self.genArch(.x86_64),
336 .xcore => return self.genArch(.xcore),
337 .nvptx => return self.genArch(.nvptx),
338 .nvptx64 => return self.genArch(.nvptx64),
339 .le32 => return self.genArch(.le32),
340 .le64 => return self.genArch(.le64),
341 .amdil => return self.genArch(.amdil),
342 .amdil64 => return self.genArch(.amdil64),
343 .hsail => return self.genArch(.hsail),
344 .hsail64 => return self.genArch(.hsail64),
345 .spir => return self.genArch(.spir),
346 .spir64 => return self.genArch(.spir64),
347 .kalimba => return self.genArch(.kalimba),
348 .shave => return self.genArch(.shave),
349 .lanai => return self.genArch(.lanai),
350 .wasm32 => return self.genArch(.wasm32),
351 .wasm64 => return self.genArch(.wasm64),
352 .renderscript32 => return self.genArch(.renderscript32),
353 .renderscript64 => return self.genArch(.renderscript64),
354 .ve => return self.genArch(.ve),
355 }
356 }
357
358 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {
359 try self.code.ensureCapacity(self.code.items.len + 11);
360
361 // push rbp
362 // mov rbp, rsp
363 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
364
365 // sub rsp, x
366 const stack_end = self.branch_stack.items[0].max_end_stack;
367 if (stack_end > std.math.maxInt(i32)) {
368 return self.fail(self.src, "too much stack used in call parameters", .{});
369 } else if (stack_end > std.math.maxInt(i8)) {
370 // 48 83 ec xx sub rsp,0x10
371 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
372 const x = @intCast(u32, stack_end);
373 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
374 } else if (stack_end != 0) {
375 // 48 81 ec xx xx xx xx sub rsp,0x80
376 const x = @intCast(u8, stack_end);
377 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
378 }
379
380 try self.genBody(self.mod_fn.analysis.success, arch);
381 }
382
383 fn genBody(self: *Function, body: ir.Body, comptime arch: std.Target.Cpu.Arch) InnerError!void {
384 const inst_table = &self.branch_stack.items[0].inst_table;
385 for (body.instructions) |inst| {
386 const new_inst = try self.genFuncInst(inst, arch);
387 try inst_table.putNoClobber(self.gpa, inst, new_inst);
388 }
389 }
390
391 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {
172392 switch (inst.tag) {
173 .breakpoint => return self.genBreakpoint(inst.src),
174 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
175 .unreach => return MCValue{ .unreach = {} },
393 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
394 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
395 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
396 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
397 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
398 .br => return self.genBr(inst.cast(ir.Inst.Br).?, arch),
399 .breakpoint => return self.genBreakpoint(inst.src, arch),
400 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?, arch),
401 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
402 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
403 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
176404 .constant => unreachable, // excluded from function bodies
177 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
405 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
406 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
178407 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
179 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
180 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
181 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
182 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
183 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
184 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
408 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
409 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
410 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
411 .unreach => return MCValue{ .unreach = {} },
412 .not => return self.genNot(inst.cast(ir.Inst.Not).?, arch),
185413 }
186414 }
187415
188 fn genBreakpoint(self: *Function, src: usize) !MCValue {
189 switch (self.target.cpu.arch) {
416 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {
417 // No side effects, so if it's unreferenced, do nothing.
418 if (inst.base.isUnused())
419 return MCValue.dead;
420 const operand = try self.resolveInst(inst.args.operand);
421 switch (operand) {
422 .dead => unreachable,
423 .unreach => unreachable,
424 .compare_flags_unsigned => |op| return MCValue{
425 .compare_flags_unsigned = switch (op) {
426 .gte => .lt,
427 .gt => .lte,
428 .neq => .eq,
429 .lt => .gte,
430 .lte => .gt,
431 .eq => .neq,
432 },
433 },
434 .compare_flags_signed => |op| return MCValue{
435 .compare_flags_signed = switch (op) {
436 .gte => .lt,
437 .gt => .lte,
438 .neq => .eq,
439 .lt => .gte,
440 .lte => .gt,
441 .eq => .neq,
442 },
443 },
444 else => {},
445 }
446
447 switch (arch) {
448 .x86_64 => {
449 var imm = ir.Inst.Constant{
450 .base = .{
451 .tag = .constant,
452 .deaths = 0,
453 .ty = inst.args.operand.ty,
454 .src = inst.args.operand.src,
455 },
456 .val = Value.initTag(.bool_true),
457 };
458 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
459 },
460 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
461 }
462 }
463
464 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {
465 // No side effects, so if it's unreferenced, do nothing.
466 if (inst.base.isUnused())
467 return MCValue.dead;
468 switch (arch) {
469 .x86_64 => {
470 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
471 },
472 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
473 }
474 }
475
476 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {
477 // No side effects, so if it's unreferenced, do nothing.
478 if (inst.base.isUnused())
479 return MCValue.dead;
480 switch (arch) {
481 .x86_64 => {
482 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
483 },
484 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
485 }
486 }
487
488 /// ADD, SUB, XOR, OR, AND
489 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
490 try self.code.ensureCapacity(self.code.items.len + 8);
491
492 const lhs = try self.resolveInst(op_lhs);
493 const rhs = try self.resolveInst(op_rhs);
494
495 // There are 2 operands, destination and source.
496 // Either one, but not both, can be a memory operand.
497 // Source operand can be an immediate, 8 bits or 32 bits.
498 // So, if either one of the operands dies with this instruction, we can use it
499 // as the result MCValue.
500 var dst_mcv: MCValue = undefined;
501 var src_mcv: MCValue = undefined;
502 var src_inst: *ir.Inst = undefined;
503 if (inst.operandDies(0) and lhs.isMutable()) {
504 // LHS dies; use it as the destination.
505 // Both operands cannot be memory.
506 src_inst = op_rhs;
507 if (lhs.isMemory() and rhs.isMemory()) {
508 dst_mcv = try self.copyToNewRegister(op_lhs);
509 src_mcv = rhs;
510 } else {
511 dst_mcv = lhs;
512 src_mcv = rhs;
513 }
514 } else if (inst.operandDies(1) and rhs.isMutable()) {
515 // RHS dies; use it as the destination.
516 // Both operands cannot be memory.
517 src_inst = op_lhs;
518 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.copyToNewRegister(op_rhs);
520 src_mcv = lhs;
521 } else {
522 dst_mcv = rhs;
523 src_mcv = lhs;
524 }
525 } else {
526 if (lhs.isMemory()) {
527 dst_mcv = try self.copyToNewRegister(op_lhs);
528 src_mcv = rhs;
529 src_inst = op_rhs;
530 } else {
531 dst_mcv = try self.copyToNewRegister(op_rhs);
532 src_mcv = lhs;
533 src_inst = op_lhs;
534 }
535 }
536 // This instruction supports only signed 32-bit immediates at most. If the immediate
537 // value is larger than this, we put it in a register.
538 // A potential opportunity for future optimization here would be keeping track
539 // of the fact that the instruction is available both as an immediate
540 // and as a register.
541 switch (src_mcv) {
542 .immediate => |imm| {
543 if (imm > std.math.maxInt(u31)) {
544 src_mcv = try self.copyToNewRegister(src_inst);
545 }
546 },
547 else => {},
548 }
549
550 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);
551
552 return dst_mcv;
553 }
554
555 fn genX8664BinMathCode(self: *Function, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
556 switch (dst_mcv) {
557 .none => unreachable,
558 .dead, .unreach, .immediate => unreachable,
559 .compare_flags_unsigned => unreachable,
560 .compare_flags_signed => unreachable,
561 .register => |dst_reg_usize| {
562 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));
563 switch (src_mcv) {
564 .none => unreachable,
565 .dead, .unreach => unreachable,
566 .register => |src_reg_usize| {
567 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));
568 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
569 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
570 },
571 .immediate => |imm| {
572 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
573 // 81 /opx id
574 if (imm32 <= std.math.maxInt(u7)) {
575 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
576 self.code.appendSliceAssumeCapacity(&[_]u8{
577 0x83,
578 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
579 @intCast(u8, imm32),
580 });
581 } else {
582 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
583 self.code.appendSliceAssumeCapacity(&[_]u8{
584 0x81,
585 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
586 });
587 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
588 }
589 },
590 .embedded_in_code, .memory, .stack_offset => {
591 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
592 },
593 .compare_flags_unsigned => {
594 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
595 },
596 .compare_flags_signed => {
597 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
598 },
599 }
600 },
601 .embedded_in_code, .memory, .stack_offset => {
602 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
603 },
604 }
605 }
606
607 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
608 const i = self.arg_index;
609 self.arg_index += 1;
610 return self.args[i];
611 }
612
613 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
614 switch (arch) {
190615 .i386, .x86_64 => {
191616 try self.code.append(0xcc); // int3
192617 },
......@@ -195,14 +620,43 @@ const Function = struct {
195620 return .none;
196621 }
197622
198 fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue {
199 switch (self.target.cpu.arch) {
200 .x86_64, .i386 => {
201 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
202 if (inst.args.args.len != 0) {
203 return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});
623 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {
624 const fn_ty = inst.args.func.ty;
625 const cc = fn_ty.fnCallingConvention();
626 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
627 defer self.gpa.free(param_types);
628 fn_ty.fnParamTypes(param_types);
629 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
630 defer self.gpa.free(mc_args);
631 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
632
633 switch (arch) {
634 .x86_64 => {
635 for (mc_args) |mc_arg, arg_i| {
636 const arg = inst.args.args[arg_i];
637 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
638 switch (mc_arg) {
639 .none => continue,
640 .register => |reg| {
641 try self.genSetReg(arg.src, arch, @intToEnum(Reg(arch), @intCast(u8, reg)), arg_mcv);
642 // TODO interact with the register allocator to mark the instruction as moved.
643 },
644 .stack_offset => {
645 // Here we need to emit instructions like this:
646 // mov qword ptr [rsp + stack_offset], x
647 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
648 },
649 .immediate => unreachable,
650 .unreach => unreachable,
651 .dead => unreachable,
652 .embedded_in_code => unreachable,
653 .memory => unreachable,
654 .compare_flags_signed => unreachable,
655 .compare_flags_unsigned => unreachable,
204656 }
657 }
205658
659 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
206660 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
207661 const func = func_val.func;
208662 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
......@@ -210,17 +664,11 @@ const Function = struct {
210664 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
211665 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
212666 // ff 14 25 xx xx xx xx call [addr]
213 try self.code.resize(self.code.items.len + 7);
214 self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };
215 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);
216 const return_type = func.fn_type.fnReturnType();
217 switch (return_type.zigTypeTag()) {
218 .Void => return MCValue{ .none = {} },
219 .NoReturn => return MCValue{ .unreach = {} },
220 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
221 }
667 try self.code.ensureCapacity(self.code.items.len + 7);
668 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
669 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
222670 } else {
223 return self.fail(inst.base.src, "TODO implement calling weird function values", .{});
671 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
224672 }
225673 } else {
226674 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
......@@ -228,121 +676,210 @@ const Function = struct {
228676 },
229677 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
230678 }
679
680 const return_type = fn_ty.fnReturnType();
681 switch (return_type.zigTypeTag()) {
682 .Void => return MCValue{ .none = {} },
683 .NoReturn => return MCValue{ .unreach = {} },
684 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
685 }
231686 }
232687
233 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
234 switch (self.target.cpu.arch) {
235 .i386, .x86_64 => {
688 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {
689 if (mcv != .none) {
690 return self.fail(src, "TODO implement return with non-void operand", .{});
691 }
692 switch (arch) {
693 .i386 => {
236694 try self.code.append(0xc3); // ret
237695 },
238 else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}),
696 .x86_64 => {
697 try self.code.appendSlice(&[_]u8{
698 0x5d, // pop rbp
699 0xc3, // ret
700 });
701 },
702 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
239703 }
240704 return .unreach;
241705 }
242706
243 fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
244 switch (self.target.cpu.arch) {
707 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {
708 const operand = try self.resolveInst(inst.args.operand);
709 return self.ret(inst.base.src, arch, operand);
710 }
711
712 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
713 return self.ret(inst.base.src, arch, .none);
714 }
715
716 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {
717 // No side effects, so if it's unreferenced, do nothing.
718 if (inst.base.isUnused())
719 return MCValue.dead;
720 switch (arch) {
721 .x86_64 => {
722 try self.code.ensureCapacity(self.code.items.len + 8);
723
724 const lhs = try self.resolveInst(inst.args.lhs);
725 const rhs = try self.resolveInst(inst.args.rhs);
726
727 // There are 2 operands, destination and source.
728 // Either one, but not both, can be a memory operand.
729 // Source operand can be an immediate, 8 bits or 32 bits.
730 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
731 try self.copyToNewRegister(inst.args.lhs)
732 else
733 lhs;
734 // This instruction supports only signed 32-bit immediates at most.
735 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
736
737 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
738 const info = inst.args.lhs.ty.intInfo(self.target.*);
739 if (info.signed) {
740 return MCValue{ .compare_flags_signed = inst.args.op };
741 } else {
742 return MCValue{ .compare_flags_unsigned = inst.args.op };
743 }
744 },
245745 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
246746 }
247747 }
248748
249 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {
250 switch (self.target.cpu.arch) {
749 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
750 switch (arch) {
751 .x86_64 => {
752 try self.code.ensureCapacity(self.code.items.len + 6);
753
754 const cond = try self.resolveInst(inst.args.condition);
755 switch (cond) {
756 .compare_flags_signed => |cmp_op| {
757 // Here we map to the opposite opcode because the jump is to the false branch.
758 const opcode: u8 = switch (cmp_op) {
759 .gte => 0x8c,
760 .gt => 0x8e,
761 .neq => 0x84,
762 .lt => 0x8d,
763 .lte => 0x8f,
764 .eq => 0x85,
765 };
766 return self.genX86CondBr(inst, opcode, arch);
767 },
768 .compare_flags_unsigned => |cmp_op| {
769 // Here we map to the opposite opcode because the jump is to the false branch.
770 const opcode: u8 = switch (cmp_op) {
771 .gte => 0x82,
772 .gt => 0x86,
773 .neq => 0x84,
774 .lt => 0x83,
775 .lte => 0x87,
776 .eq => 0x85,
777 };
778 return self.genX86CondBr(inst, opcode, arch);
779 },
780 .register => |reg_usize| {
781 const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
782 // test reg, 1
783 // TODO detect al, ax, eax
784 try self.code.ensureCapacity(self.code.items.len + 4);
785 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
786 self.code.appendSliceAssumeCapacity(&[_]u8{
787 0xf6,
788 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
789 0x01,
790 });
791 return self.genX86CondBr(inst, 0x84, arch);
792 },
793 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
794 }
795 },
251796 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
252797 }
253798 }
254799
255 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {
256 switch (self.target.cpu.arch) {
800 fn genX86CondBr(self: *Function, inst: *ir.Inst.CondBr, opcode: u8, comptime arch: std.Target.Cpu.Arch) !MCValue {
801 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
802 const reloc = Reloc{ .rel32 = self.code.items.len };
803 self.code.items.len += 4;
804 try self.genBody(inst.args.true_body, arch);
805 try self.performReloc(inst.base.src, reloc);
806 try self.genBody(inst.args.false_body, arch);
807 return MCValue.unreach;
808 }
809
810 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
811 switch (arch) {
257812 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
258813 }
259814 }
260815
261 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {
816 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
262817 // Here you can specialize this instruction if it makes sense to, otherwise the default
263818 // will call genIsNull and invert the result.
264 switch (self.target.cpu.arch) {
819 switch (arch) {
265820 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
266821 }
267822 }
268823
269 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {
270 switch (self.target.cpu.arch) {
824 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
825 if (inst.base.ty.hasCodeGenBits()) {
826 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
827 }
828 // A block is nothing but a setup to be able to jump to the end.
829 defer inst.codegen.relocs.deinit(self.gpa);
830 try self.genBody(inst.args.body, arch);
831
832 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
833
834 return MCValue.none;
835 }
836
837 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {
838 switch (reloc) {
839 .rel32 => |pos| {
840 const amt = self.code.items.len - (pos + 4);
841 const s32_amt = std.math.cast(i32, amt) catch
842 return self.fail(src, "unable to perform relocation: jump too far", .{});
843 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
844 },
845 }
846 }
847
848 fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue {
849 if (!inst.args.operand.ty.hasCodeGenBits())
850 return self.brVoid(inst.base.src, inst.args.block, arch);
851
852 const operand = try self.resolveInst(inst.args.operand);
853 switch (arch) {
854 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
855 }
856 }
857
858 fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
859 return self.brVoid(inst.base.src, inst.args.block, arch);
860 }
861
862 fn brVoid(self: *Function, src: usize, block: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
863 // Emit a jump with a relocation. It will be patched up after the block ends.
864 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
865
866 switch (arch) {
271867 .i386, .x86_64 => {
272 // TODO x86 treats the operands as signed
273 if (amount <= std.math.maxInt(u8)) {
274 try self.code.resize(self.code.items.len + 2);
275 self.code.items[self.code.items.len - 2] = 0xeb;
276 self.code.items[self.code.items.len - 1] = @intCast(u8, amount);
277 } else {
278 try self.code.resize(self.code.items.len + 5);
279 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
280 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
281 mem.writeIntLittle(u32, imm_ptr, amount);
282 }
868 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
869 // which is available if the jump is 127 bytes or less forward.
870 try self.code.resize(self.code.items.len + 5);
871 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
872 // Leave the jump offset undefined
873 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
283874 },
284 else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.target.cpu.arch}),
875 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
285876 }
877 return .none;
286878 }
287879
288 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {
289 // TODO convert to inline function
290 switch (self.target.cpu.arch) {
291 .arm => return self.genAsmArch(.arm, inst),
292 .armeb => return self.genAsmArch(.armeb, inst),
293 .aarch64 => return self.genAsmArch(.aarch64, inst),
294 .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
295 .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
296 .arc => return self.genAsmArch(.arc, inst),
297 .avr => return self.genAsmArch(.avr, inst),
298 .bpfel => return self.genAsmArch(.bpfel, inst),
299 .bpfeb => return self.genAsmArch(.bpfeb, inst),
300 .hexagon => return self.genAsmArch(.hexagon, inst),
301 .mips => return self.genAsmArch(.mips, inst),
302 .mipsel => return self.genAsmArch(.mipsel, inst),
303 .mips64 => return self.genAsmArch(.mips64, inst),
304 .mips64el => return self.genAsmArch(.mips64el, inst),
305 .msp430 => return self.genAsmArch(.msp430, inst),
306 .powerpc => return self.genAsmArch(.powerpc, inst),
307 .powerpc64 => return self.genAsmArch(.powerpc64, inst),
308 .powerpc64le => return self.genAsmArch(.powerpc64le, inst),
309 .r600 => return self.genAsmArch(.r600, inst),
310 .amdgcn => return self.genAsmArch(.amdgcn, inst),
311 .riscv32 => return self.genAsmArch(.riscv32, inst),
312 .riscv64 => return self.genAsmArch(.riscv64, inst),
313 .sparc => return self.genAsmArch(.sparc, inst),
314 .sparcv9 => return self.genAsmArch(.sparcv9, inst),
315 .sparcel => return self.genAsmArch(.sparcel, inst),
316 .s390x => return self.genAsmArch(.s390x, inst),
317 .tce => return self.genAsmArch(.tce, inst),
318 .tcele => return self.genAsmArch(.tcele, inst),
319 .thumb => return self.genAsmArch(.thumb, inst),
320 .thumbeb => return self.genAsmArch(.thumbeb, inst),
321 .i386 => return self.genAsmArch(.i386, inst),
322 .x86_64 => return self.genAsmArch(.x86_64, inst),
323 .xcore => return self.genAsmArch(.xcore, inst),
324 .nvptx => return self.genAsmArch(.nvptx, inst),
325 .nvptx64 => return self.genAsmArch(.nvptx64, inst),
326 .le32 => return self.genAsmArch(.le32, inst),
327 .le64 => return self.genAsmArch(.le64, inst),
328 .amdil => return self.genAsmArch(.amdil, inst),
329 .amdil64 => return self.genAsmArch(.amdil64, inst),
330 .hsail => return self.genAsmArch(.hsail, inst),
331 .hsail64 => return self.genAsmArch(.hsail64, inst),
332 .spir => return self.genAsmArch(.spir, inst),
333 .spir64 => return self.genAsmArch(.spir64, inst),
334 .kalimba => return self.genAsmArch(.kalimba, inst),
335 .shave => return self.genAsmArch(.shave, inst),
336 .lanai => return self.genAsmArch(.lanai, inst),
337 .wasm32 => return self.genAsmArch(.wasm32, inst),
338 .wasm64 => return self.genAsmArch(.wasm64, inst),
339 .renderscript32 => return self.genAsmArch(.renderscript32, inst),
340 .renderscript64 => return self.genAsmArch(.renderscript64, inst),
341 .ve => return self.genAsmArch(.ve, inst),
342 }
343 }
344
345 fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
880 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
881 if (!inst.args.is_volatile and inst.base.isUnused())
882 return MCValue.dead;
346883 if (arch != .x86_64 and arch != .i386) {
347884 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
348885 }
......@@ -384,30 +921,49 @@ const Function = struct {
384921 /// resulting REX is meaningful, but will remain the same if it is not.
385922 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
386923 /// 0x40, and cannot be done via this function.
387 fn REX(self: *Function, arg: struct { B: bool = false, W: bool = false, X: bool = false, R: bool = false }) !void {
924 fn rex(self: *Function, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
388925 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
389926 var value: u8 = 0x40;
390 if (arg.B) {
927 if (arg.b) {
391928 value |= 0x1;
392929 }
393 if (arg.X) {
930 if (arg.x) {
394931 value |= 0x2;
395932 }
396 if (arg.R) {
933 if (arg.r) {
397934 value |= 0x4;
398935 }
399 if (arg.W) {
936 if (arg.w) {
400937 value |= 0x8;
401938 }
402939 if (value != 0x40) {
403 try self.code.append(value);
940 self.code.appendAssumeCapacity(value);
404941 }
405942 }
406943
407944 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
408945 switch (arch) {
409946 .x86_64 => switch (mcv) {
410 .none, .unreach => unreachable,
947 .dead => unreachable,
948 .none => unreachable,
949 .unreach => unreachable,
950 .compare_flags_unsigned => |op| {
951 try self.code.ensureCapacity(self.code.items.len + 3);
952 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
953 const opcode: u8 = switch (op) {
954 .gte => 0x93,
955 .gt => 0x97,
956 .neq => 0x95,
957 .lt => 0x92,
958 .lte => 0x96,
959 .eq => 0x94,
960 };
961 const id = @as(u8, reg.id() & 0b111);
962 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
963 },
964 .compare_flags_signed => |op| {
965 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
966 },
411967 .immediate => |x| {
412968 if (reg.size() != 64) {
413969 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
......@@ -426,11 +982,11 @@ const Function = struct {
426982 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
427983 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
428984 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
429 try self.REX(.{ .R = reg.isExtended(), .B = reg.isExtended() });
985 try self.code.ensureCapacity(self.code.items.len + 3);
986 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
430987 const id = @as(u8, reg.id() & 0b111);
431 return self.code.appendSlice(&[_]u8{
432 0x31, 0xC0 | id << 3 | id,
433 });
988 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
989 return;
434990 }
435991 if (x <= std.math.maxInt(u32)) {
436992 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
......@@ -463,9 +1019,9 @@ const Function = struct {
4631019 // Since we always need a REX here, let's just check if we also need to set REX.B.
4641020 //
4651021 // In this case, the encoding of the REX byte is 0b0100100B
466
467 try self.REX(.{ .W = true, .B = reg.isExtended() });
468 try self.code.resize(self.code.items.len + 9);
1022 try self.code.ensureCapacity(self.code.items.len + 10);
1023 self.rex(.{ .w = true, .b = reg.isExtended() });
1024 self.code.items.len += 9;
4691025 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
4701026 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
4711027 mem.writeIntLittle(u64, imm_ptr, x);
......@@ -476,13 +1032,13 @@ const Function = struct {
4761032 }
4771033 // We need the offset from RIP in a signed i32 twos complement.
4781034 // The instruction is 7 bytes long and RIP points to the next instruction.
479 //
1035 try self.code.ensureCapacity(self.code.items.len + 7);
4801036 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
4811037 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
4821038 // bits as five.
4831039 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
484 try self.REX(.{ .W = true, .B = reg.isExtended() });
485 try self.code.resize(self.code.items.len + 6);
1040 self.rex(.{ .w = true, .b = reg.isExtended() });
1041 self.code.items.len += 6;
4861042 const rip = self.code.items.len;
4871043 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
4881044 const offset = @intCast(i32, big_offset);
......@@ -502,9 +1058,10 @@ const Function = struct {
5021058 // If the *source* is extended, the B field must be 1.
5031059 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
5041060 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
505 try self.REX(.{ .W = true, .R = reg.isExtended(), .B = src_reg.isExtended() });
1061 try self.code.ensureCapacity(self.code.items.len + 3);
1062 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });
5061063 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
507 try self.code.appendSlice(&[_]u8{ 0x8B, R });
1064 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
5081065 },
5091066 .memory => |x| {
5101067 if (reg.size() != 64) {
......@@ -518,14 +1075,14 @@ const Function = struct {
5181075 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
5191076 // 0b00RRR100, where RRR is the lower three bits of the register ID.
5201077 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
521 try self.REX(.{ .W = true, .B = reg.isExtended() });
522 try self.code.resize(self.code.items.len + 7);
523 const r = 0x04 | (@as(u8, reg.id() & 0b111) << 3);
524 self.code.items[self.code.items.len - 7] = 0x8B;
525 self.code.items[self.code.items.len - 6] = r;
526 self.code.items[self.code.items.len - 5] = 0x25;
527 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
528 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
1078 try self.code.ensureCapacity(self.code.items.len + 8);
1079 self.rex(.{ .w = true, .b = reg.isExtended() });
1080 self.code.appendSliceAssumeCapacity(&[_]u8{
1081 0x8B,
1082 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
1083 0x25,
1084 });
1085 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
5291086 } else {
5301087 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
5311088 // the value.
......@@ -556,18 +1113,21 @@ const Function = struct {
5561113 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
5571114 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
5581115 // This operation requires three bytes: REX 0x8B R/M
559 //
1116 try self.code.ensureCapacity(self.code.items.len + 3);
5601117 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
5611118 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
5621119 //
5631120 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
5641121 // register operands need to be marked as extended.
565 try self.REX(.{ .W = true, .B = reg.isExtended(), .R = reg.isExtended() });
1122 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });
5661123 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
567 try self.code.appendSlice(&[_]u8{ 0x8B, RM });
1124 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
5681125 }
5691126 }
5701127 },
1128 .stack_offset => |off| {
1129 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1130 },
5711131 },
5721132 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
5731133 }
......@@ -584,22 +1144,59 @@ const Function = struct {
5841144 }
5851145
5861146 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {
587 if (self.inst_table.getValue(inst)) |mcv| {
588 return mcv;
589 }
1147 // Constants have static lifetimes, so they are always memoized in the outer most table.
5901148 if (inst.cast(ir.Inst.Constant)) |const_inst| {
591 const mcvalue = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
592 try self.inst_table.putNoClobber(inst, mcvalue);
593 return mcvalue;
594 } else {
595 return self.inst_table.getValue(inst).?;
1149 const branch = &self.branch_stack.items[0];
1150 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1151 if (!gop.found_existing) {
1152 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1153 }
1154 return gop.entry.value;
1155 }
1156
1157 // Treat each stack item as a "layer" on top of the previous one.
1158 var i: usize = self.branch_stack.items.len;
1159 while (true) {
1160 i -= 1;
1161 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1162 return mcv;
1163 }
5961164 }
5971165 }
5981166
1167 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {
1168 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});
1169 }
1170
1171 /// If the MCValue is an immediate, and it does not fit within this type,
1172 /// we put it in a register.
1173 /// A potential opportunity for future optimization here would be keeping track
1174 /// of the fact that the instruction is available both as an immediate
1175 /// and as a register.
1176 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {
1177 const mcv = try self.resolveInst(inst);
1178 const ti = @typeInfo(T).Int;
1179 switch (mcv) {
1180 .immediate => |imm| {
1181 // This immediate is unsigned.
1182 const U = @Type(.{
1183 .Int = .{
1184 .bits = ti.bits - @boolToInt(ti.is_signed),
1185 .is_signed = false,
1186 },
1187 });
1188 if (imm >= std.math.maxInt(U)) {
1189 return self.copyToNewRegister(inst);
1190 }
1191 },
1192 else => {},
1193 }
1194 return mcv;
1195 }
1196
5991197 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
6001198 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
6011199 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
602 const allocator = self.code.allocator;
6031200 switch (typed_value.ty.zigTypeTag()) {
6041201 .Pointer => {
6051202 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
......@@ -617,16 +1214,61 @@ const Function = struct {
6171214 }
6181215 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
6191216 },
1217 .Bool => {
1218 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1219 },
6201220 .ComptimeInt => unreachable, // semantic analysis prevents this
6211221 .ComptimeFloat => unreachable, // semantic analysis prevents this
6221222 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
6231223 }
6241224 }
6251225
626 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
1226 fn resolveParameters(
1227 self: *Function,
1228 src: usize,
1229 cc: std.builtin.CallingConvention,
1230 param_types: []const Type,
1231 results: []MCValue,
1232 ) !u32 {
1233 switch (self.target.cpu.arch) {
1234 .x86_64 => {
1235 switch (cc) {
1236 .Naked => {
1237 assert(results.len == 0);
1238 return 0;
1239 },
1240 .Unspecified, .C => {
1241 var next_int_reg: usize = 0;
1242 var next_stack_offset: u32 = 0;
1243
1244 const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1245 for (param_types) |ty, i| {
1246 switch (ty.zigTypeTag()) {
1247 .Bool, .Int => {
1248 if (next_int_reg >= integer_registers.len) {
1249 results[i] = .{ .stack_offset = next_stack_offset };
1250 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1251 } else {
1252 results[i] = .{ .register = @enumToInt(integer_registers[next_int_reg]) };
1253 next_int_reg += 1;
1254 }
1255 },
1256 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1257 }
1258 }
1259 return next_stack_offset;
1260 },
1261 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1262 }
1263 },
1264 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1265 }
1266 }
1267
1268 fn fail(self: *Function, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
6271269 @setCold(true);
6281270 assert(self.err_msg == null);
629 self.err_msg = try ErrorMsg.create(self.code.allocator, src, format, args);
1271 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
6301272 return error.CodegenFail;
6311273 }
6321274};
src-self-hosted/codegen/c.zig created+206
......@@ -0,0 +1,206 @@
1const std = @import("std");
2
3const link = @import("../link.zig");
4const Module = @import("../Module.zig");
5
6const Inst = @import("../ir.zig").Inst;
7const Value = @import("../value.zig").Value;
8const Type = @import("../type.zig").Type;
9
10const C = link.File.C;
11const Decl = Module.Decl;
12const mem = std.mem;
13
14/// Maps a name from Zig source to C. This will always give the same output for
15/// any given input.
16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
17 return allocator.dupe(u8, name);
18}
19
20fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
21 if (T.tag() == .usize) {
22 file.need_stddef = true;
23 try writer.writeAll("size_t");
24 } else {
25 switch (T.zigTypeTag()) {
26 .NoReturn => {
27 file.need_noreturn = true;
28 try writer.writeAll("noreturn void");
29 },
30 .Void => try writer.writeAll("void"),
31 .Int => {
32 if (T.tag() == .u8) {
33 file.need_stdint = true;
34 try writer.writeAll("uint8_t");
35 } else {
36 return file.fail(src, "TODO implement int types", .{});
37 }
38 },
39 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
40 }
41 }
42}
43
44fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
45 const tv = decl.typed_value.most_recent.typed_value;
46 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
47 const name = try map(file.allocator, mem.spanZ(decl.name));
48 defer file.allocator.free(name);
49 try writer.print(" {}(", .{name});
50 if (tv.ty.fnParamLen() == 0)
51 try writer.writeAll("void)")
52 else
53 return file.fail(decl.src(), "TODO implement parameters", .{});
54}
55
56pub fn generate(file: *C, decl: *Decl) !void {
57 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
58 .Fn => try genFn(file, decl),
59 .Array => try genArray(file, decl),
60 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
61 }
62}
63
64fn genArray(file: *C, decl: *Decl) !void {
65 const tv = decl.typed_value.most_recent.typed_value;
66 // TODO: prevent inline asm constants from being emitted
67 const name = try map(file.allocator, mem.span(decl.name));
68 defer file.allocator.free(name);
69 if (tv.val.cast(Value.Payload.Bytes)) |payload|
70 if (tv.ty.arraySentinel()) |sentinel|
71 if (sentinel.toUnsignedInt() == 0)
72 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
73 else
74 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
75 else
76 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
77 else
78 return file.fail(decl.src(), "TODO non-byte arrays", .{});
79}
80
81fn genFn(file: *C, decl: *Decl) !void {
82 const writer = file.main.writer();
83 const tv = decl.typed_value.most_recent.typed_value;
84
85 try renderFunctionSignature(file, writer, decl);
86
87 try writer.writeAll(" {");
88
89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
90 const instructions = func.analysis.success.instructions;
91 if (instructions.len > 0) {
92 for (instructions) |inst| {
93 try writer.writeAll("\n\t");
94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print("return;", .{}),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }
101 }
102 try writer.writeAll("\n");
103 }
104
105 try writer.writeAll("}\n\n");
106}
107
108fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {
109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;
111 const value = ret_value.value().?;
112 if (expected_return_type.eql(ret_value.ty))
113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
114 else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int)
115 if (value.intFitsInType(expected_return_type, file.options.target))
116 if (expected_return_type.intInfo(file.options.target).bits <= 64)
117 try writer.print("return {};", .{value.toUnsignedInt()})
118 else
119 return file.fail(decl.src(), "TODO return ints > 64 bits", .{})
120 else
121 return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type })
122 else
123 return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty });
124}
125
126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127 const writer = file.main.writer();
128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {
130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131 const target = func_val.func.owner_decl;
132 const target_ty = target.typed_value.most_recent.typed_value.ty;
133 const ret_ty = target_ty.fnReturnType().tag();
134 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
135 try writer.print("(void)", .{});
136 }
137 const tname = mem.spanZ(target.name);
138 if (file.called.get(tname) == null) {
139 try file.called.put(tname, void{});
140 try renderFunctionSignature(file, header, target);
141 try header.writeAll(";\n");
142 }
143 try writer.print("{}();", .{tname});
144 } else {
145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146 }
147 if (inst.args.args.len != 0) {
148 return file.fail(decl.src(), "TODO function arguments", .{});
149 }
150 } else {
151 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
152 }
153}
154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
157 const writer = file.main.writer();
158 for (as.inputs) |i, index| {
159 if (i[0] == '{' and i[i.len - 1] == '}') {
160 const reg = i[1 .. i.len - 1];
161 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {
163 if (c.val.tag() == .int_u64) {
164 try writer.writeAll("register ");
165 try renderType(file, writer, arg.ty, decl.src());
166 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
167 } else {
168 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
169 }
170 } else {
171 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
172 }
173 } else {
174 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
175 }
176 }
177 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
178 if (as.output) |o| {
179 return file.fail(decl.src(), "TODO inline asm output", .{});
180 }
181 if (as.inputs.len > 0) {
182 if (as.output == null) {
183 try writer.writeAll(" :");
184 }
185 try writer.writeAll(": ");
186 for (as.inputs) |i, index| {
187 if (i[0] == '{' and i[i.len - 1] == '}') {
188 const reg = i[1 .. i.len - 1];
189 const arg = as.args[index];
190 if (index > 0) {
191 try writer.writeAll(", ");
192 }
193 if (arg.cast(Inst.Constant)) |c| {
194 try writer.print("\"\"({}_constant)", .{reg});
195 } else {
196 // This is blocked by the earlier test
197 unreachable;
198 }
199 } else {
200 // This is blocked by the earlier test
201 unreachable;
202 }
203 }
204 }
205 try writer.writeAll(");");
206}
src-self-hosted/codegen/x86_64.zig+6-5
......@@ -1,20 +1,21 @@
1const Type = @import("../Type.zig");
2
13// zig fmt: off
24
3/// Definitions of all of the x64 registers. The order is very, very important.
5/// Definitions of all of the x64 registers. The order is semantically meaningful.
46/// The registers are defined such that IDs go in descending order of 64-bit,
57/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
6/// registers. This results in some very, very useful properties:
8/// registers. This results in some useful properties:
79///
810/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
911/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
10/// works for all except for sp, bp, si, and di, which don't *have* an 8-bit
12/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
1113/// form.
1214///
1315/// If (register & 8) is set, the register is extended.
1416///
1517/// The ID can be easily determined by figuring out what range the register is
1618/// in, and then subtracting the base.
17///
1819pub const Register = enum(u8) {
1920 // 0 through 15, 64-bit registers. 8-15 are extended.
2021 // id is just the int value.
......@@ -66,4 +67,4 @@ pub const Register = enum(u8) {
6667 }
6768};
6869
69// zig fmt: on
70// zig fmt: on
\ No newline at end of file
src-self-hosted/dep_tokenizer.zig+13-13
......@@ -299,12 +299,12 @@ pub const Tokenizer = struct {
299299 return null;
300300 }
301301
302 fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: var) Error {
302 fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: anytype) Error {
303303 self.error_text = try std.fmt.allocPrintZ(&self.arena.allocator, fmt, args);
304304 return Error.InvalidInput;
305305 }
306306
307 fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: var) Error {
307 fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: anytype) Error {
308308 var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
309309 try buffer.outStream().print(fmt, args);
310310 try buffer.appendSlice(" '");
......@@ -316,7 +316,7 @@ pub const Tokenizer = struct {
316316 return Error.InvalidInput;
317317 }
318318
319 fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: var) Error {
319 fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: anytype) Error {
320320 var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
321321 try buffer.appendSlice("illegal char ");
322322 try printUnderstandableChar(&buffer, char);
......@@ -883,7 +883,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
883883 testing.expect(false);
884884}
885885
886fn printSection(out: var, label: []const u8, bytes: []const u8) !void {
886fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
887887 try printLabel(out, label, bytes);
888888 try hexDump(out, bytes);
889889 try printRuler(out);
......@@ -891,7 +891,7 @@ fn printSection(out: var, label: []const u8, bytes: []const u8) !void {
891891 try out.write("\n");
892892}
893893
894fn printLabel(out: var, label: []const u8, bytes: []const u8) !void {
894fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
895895 var buf: [80]u8 = undefined;
896896 var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len });
897897 try out.write(text);
......@@ -903,7 +903,7 @@ fn printLabel(out: var, label: []const u8, bytes: []const u8) !void {
903903 try out.write("\n");
904904}
905905
906fn printRuler(out: var) !void {
906fn printRuler(out: anytype) !void {
907907 var i: usize = 0;
908908 const end = 79;
909909 while (i < 79) : (i += 1) {
......@@ -912,7 +912,7 @@ fn printRuler(out: var) !void {
912912 try out.write("\n");
913913}
914914
915fn hexDump(out: var, bytes: []const u8) !void {
915fn hexDump(out: anytype, bytes: []const u8) !void {
916916 const n16 = bytes.len >> 4;
917917 var line: usize = 0;
918918 var offset: usize = 0;
......@@ -959,7 +959,7 @@ fn hexDump(out: var, bytes: []const u8) !void {
959959 try out.write("\n");
960960}
961961
962fn hexDump16(out: var, offset: usize, bytes: []const u8) !void {
962fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
963963 try printDecValue(out, offset, 8);
964964 try out.write(":");
965965 try out.write(" ");
......@@ -977,19 +977,19 @@ fn hexDump16(out: var, offset: usize, bytes: []const u8) !void {
977977 try out.write("|\n");
978978}
979979
980fn printDecValue(out: var, value: u64, width: u8) !void {
980fn printDecValue(out: anytype, value: u64, width: u8) !void {
981981 var buffer: [20]u8 = undefined;
982982 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width);
983983 try out.write(buffer[0..len]);
984984}
985985
986fn printHexValue(out: var, value: u64, width: u8) !void {
986fn printHexValue(out: anytype, value: u64, width: u8) !void {
987987 var buffer: [16]u8 = undefined;
988988 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width);
989989 try out.write(buffer[0..len]);
990990}
991991
992fn printCharValues(out: var, bytes: []const u8) !void {
992fn printCharValues(out: anytype, bytes: []const u8) !void {
993993 for (bytes) |b| {
994994 try out.write(&[_]u8{printable_char_tab[b]});
995995 }
......@@ -1020,13 +1020,13 @@ comptime {
10201020// output: must be a function that takes a `self` idiom parameter
10211021// and a bytes parameter
10221022// context: must be that self
1023fn makeOutput(comptime output: var, context: var) Output(output, @TypeOf(context)) {
1023fn makeOutput(comptime output: anytype, context: anytype) Output(output, @TypeOf(context)) {
10241024 return Output(output, @TypeOf(context)){
10251025 .context = context,
10261026 };
10271027}
10281028
1029fn Output(comptime output_func: var, comptime Context: type) type {
1029fn Output(comptime output_func: anytype, comptime Context: type) type {
10301030 return struct {
10311031 context: Context,
10321032
src-self-hosted/ir.zig+121-2
......@@ -2,6 +2,8 @@ const std = @import("std");
22const Value = @import("value.zig").Value;
33const Type = @import("type.zig").Type;
44const Module = @import("Module.zig");
5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");
57
68/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
79/// of instructions that correspond to the ZIR text format.
......@@ -10,14 +12,48 @@ const Module = @import("Module.zig");
1012/// a memory location for the value to survive after a const instruction.
1113pub const Inst = struct {
1214 tag: Tag,
15 /// Each bit represents the index of an `Inst` parameter in the `args` field.
16 /// If a bit is set, it marks the end of the lifetime of the corresponding
17 /// instruction parameter. For example, 0b101 means that the first and
18 /// third `Inst` parameters' lifetimes end after this instruction, and will
19 /// not have any more following references.
20 /// The most significant bit being set means that the instruction itself is
21 /// never referenced, in other words its lifetime ends as soon as it finishes.
22 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
23 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
24 /// lifetimes of operands are encoded elsewhere.
25 deaths: DeathsInt = undefined,
1326 ty: Type,
1427 /// Byte offset into the source.
1528 src: usize,
1629
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
35 pub fn isUnused(self: Inst) bool {
36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
37 }
38
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths << index) != 0;
42 }
43
44 pub fn specialOperandDeaths(self: Inst) bool {
45 return (self.deaths & (1 << deaths_bits)) != 0;
46 }
47
1748 pub const Tag = enum {
49 add,
50 arg,
1851 assembly,
1952 bitcast,
53 block,
54 br,
2055 breakpoint,
56 brvoid,
2157 call,
2258 cmp,
2359 condbr,
......@@ -26,7 +62,10 @@ pub const Inst = struct {
2662 isnull,
2763 ptrtoint,
2864 ret,
65 retvoid,
66 sub,
2967 unreach,
68 not,
3069 };
3170
3271 pub fn cast(base: *Inst, comptime T: type) ?*T {
......@@ -49,6 +88,22 @@ pub const Inst = struct {
4988 return inst.val;
5089 }
5190
91 pub const Add = struct {
92 pub const base_tag = Tag.add;
93 base: Inst,
94
95 args: struct {
96 lhs: *Inst,
97 rhs: *Inst,
98 },
99 };
100
101 pub const Arg = struct {
102 pub const base_tag = Tag.arg;
103 base: Inst,
104 args: void,
105 };
106
52107 pub const Assembly = struct {
53108 pub const base_tag = Tag.assembly;
54109 base: Inst,
......@@ -72,12 +127,39 @@ pub const Inst = struct {
72127 },
73128 };
74129
130 pub const Block = struct {
131 pub const base_tag = Tag.block;
132 base: Inst,
133 args: struct {
134 body: Body,
135 },
136 /// This memory is reserved for codegen code to do whatever it needs to here.
137 codegen: codegen.BlockData = .{},
138 };
139
140 pub const Br = struct {
141 pub const base_tag = Tag.br;
142 base: Inst,
143 args: struct {
144 block: *Block,
145 operand: *Inst,
146 },
147 };
148
75149 pub const Breakpoint = struct {
76150 pub const base_tag = Tag.breakpoint;
77151 base: Inst,
78152 args: void,
79153 };
80154
155 pub const BrVoid = struct {
156 pub const base_tag = Tag.brvoid;
157 base: Inst,
158 args: struct {
159 block: *Block,
160 },
161 };
162
81163 pub const Call = struct {
82164 pub const base_tag = Tag.call;
83165 base: Inst,
......@@ -104,8 +186,23 @@ pub const Inst = struct {
104186 base: Inst,
105187 args: struct {
106188 condition: *Inst,
107 true_body: Module.Body,
108 false_body: Module.Body,
189 true_body: Body,
190 false_body: Body,
191 },
192 /// Set of instructions whose lifetimes end at the start of one of the branches.
193 /// The `true` branch is first: `deaths[0..true_death_count]`.
194 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
195 deaths: [*]*Inst = undefined,
196 true_death_count: u32 = 0,
197 false_death_count: u32 = 0,
198 };
199
200 pub const Not = struct {
201 pub const base_tag = Tag.not;
202
203 base: Inst,
204 args: struct {
205 operand: *Inst,
109206 },
110207 };
111208
......@@ -146,12 +243,34 @@ pub const Inst = struct {
146243 pub const Ret = struct {
147244 pub const base_tag = Tag.ret;
148245 base: Inst,
246 args: struct {
247 operand: *Inst,
248 },
249 };
250
251 pub const RetVoid = struct {
252 pub const base_tag = Tag.retvoid;
253 base: Inst,
149254 args: void,
150255 };
151256
257 pub const Sub = struct {
258 pub const base_tag = Tag.sub;
259 base: Inst,
260
261 args: struct {
262 lhs: *Inst,
263 rhs: *Inst,
264 },
265 };
266
152267 pub const Unreach = struct {
153268 pub const base_tag = Tag.unreach;
154269 base: Inst,
155270 args: void,
156271 };
157272};
273
274pub const Body = struct {
275 instructions: []*Inst,
276};
src-self-hosted/libc_installation.zig+2-2
......@@ -37,7 +37,7 @@ pub const LibCInstallation = struct {
3737 pub fn parse(
3838 allocator: *Allocator,
3939 libc_file: []const u8,
40 stderr: var,
40 stderr: anytype,
4141 ) !LibCInstallation {
4242 var self: LibCInstallation = .{};
4343
......@@ -115,7 +115,7 @@ pub const LibCInstallation = struct {
115115 return self;
116116 }
117117
118 pub fn render(self: LibCInstallation, out: var) !void {
118 pub fn render(self: LibCInstallation, out: anytype) !void {
119119 @setEvalBranchQuota(4000);
120120 const include_dir = self.include_dir orelse "";
121121 const sys_include_dir = self.sys_include_dir orelse "";
src-self-hosted/link.zig+1321-1108
......@@ -7,6 +7,7 @@ const Module = @import("Module.zig");
77const fs = std.fs;
88const elf = std.elf;
99const codegen = @import("codegen.zig");
10const c_codegen = @import("codegen/c.zig");
1011
1112const default_entry_addr = 0x8000000;
1213
......@@ -32,13 +33,23 @@ pub fn openBinFilePath(
3233 dir: fs.Dir,
3334 sub_path: []const u8,
3435 options: Options,
35) !ElfFile {
36 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });
36) !*File {
37 const cbe = options.object_format == .c;
38 const file = try dir.createFile(sub_path, .{ .truncate = cbe, .read = true, .mode = determineMode(options) });
3739 errdefer file.close();
3840
39 var bin_file = try openBinFile(allocator, file, options);
40 bin_file.owns_file_handle = true;
41 return bin_file;
41 if (cbe) {
42 var bin_file = try allocator.create(File.C);
43 errdefer allocator.destroy(bin_file);
44 bin_file.* = try openCFile(allocator, file, options);
45 return &bin_file.base;
46 } else {
47 var bin_file = try allocator.create(File.Elf);
48 errdefer allocator.destroy(bin_file);
49 bin_file.* = try openBinFile(allocator, file, options);
50 bin_file.owns_file_handle = true;
51 return &bin_file.base;
52 }
4253}
4354
4455/// Atomically overwrites the old file, if present.
......@@ -75,12 +86,24 @@ pub fn writeFilePath(
7586 return result;
7687}
7788
89fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
90 return File.C{
91 .allocator = allocator,
92 .file = file,
93 .options = options,
94 .main = std.ArrayList(u8).init(allocator),
95 .header = std.ArrayList(u8).init(allocator),
96 .constants = std.ArrayList(u8).init(allocator),
97 .called = std.StringHashMap(void).init(allocator),
98 };
99}
100
78101/// Attempts incremental linking, if the file already exists.
79102/// If incremental linking fails, falls back to truncating the file and rewriting it.
80103/// Returns an error if `file` is not already open with +read +write +seek abilities.
81104/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
82105/// This operation is not atomic.
83pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
106pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
84107 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
85108 error.IncrFailed => {
86109 return createElfFile(allocator, file, options);
......@@ -89,514 +112,592 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
89112 };
90113}
91114
92pub const ElfFile = struct {
93 allocator: *Allocator,
94 file: ?fs.File,
95 owns_file_handle: bool,
96 options: Options,
97 ptr_width: enum { p32, p64 },
98
99 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
100 /// Same order as in the file.
101 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
102 shdr_table_offset: ?u64 = null,
103
104 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
105 /// Same order as in the file.
106 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
107 phdr_table_offset: ?u64 = null,
108 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
109 phdr_load_re_index: ?u16 = null,
110 /// The index into the program headers of the global offset table.
111 /// It needs PT_LOAD and Read flags.
112 phdr_got_index: ?u16 = null,
113 entry_addr: ?u64 = null,
114
115 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
116 shstrtab_index: ?u16 = null,
117
118 text_section_index: ?u16 = null,
119 symtab_section_index: ?u16 = null,
120 got_section_index: ?u16 = null,
121
122 /// The same order as in the file. ELF requires global symbols to all be after the
123 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
124 /// write them at the end. These are only the local symbols. The length of this array
125 /// is the value used for sh_info in the .symtab section.
126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128
129 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
130 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
131 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
132
133 /// Same order as in the file. The value is the absolute vaddr value.
134 /// If the vaddr of the executable program header changes, the entire
135 /// offset table needs to be rewritten.
136 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
137
138 phdr_table_dirty: bool = false,
139 shdr_table_dirty: bool = false,
140 shstrtab_dirty: bool = false,
141 offset_table_count_dirty: bool = false,
142
143 error_flags: ErrorFlags = ErrorFlags{},
144
145 /// A list of text blocks that have surplus capacity. This list can have false
146 /// positives, as functions grow and shrink over time, only sometimes being added
147 /// or removed from the freelist.
148 ///
149 /// A text block has surplus capacity when its overcapacity value is greater than
150 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
151 /// much extra capacity, that we could fit a small new symbol in it, itself with
152 /// ideal_capacity or more.
153 ///
154 /// Ideal capacity is defined by size * alloc_num / alloc_den.
155 ///
156 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
157 /// overcapacity can be negative. A simple way to have negative overcapacity is to
158 /// allocate a fresh text block, which will have ideal capacity, and then grow it
159 /// by 1 byte. It will then have -1 overcapacity.
160 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
161 last_text_block: ?*TextBlock = null,
162
163 /// `alloc_num / alloc_den` is the factor of padding when allocating.
164 const alloc_num = 4;
165 const alloc_den = 3;
166
167 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
168 /// it as a possible place to put new symbols, it must have enough room for this many bytes
169 /// (plus extra for reserved capacity).
170 const minimum_text_block_size = 64;
171 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
172
173 pub const ErrorFlags = struct {
174 no_entry_point_found: bool = false,
175 };
176
177 pub const TextBlock = struct {
178 /// Each decl always gets a local symbol with the fully qualified name.
179 /// The vaddr and size are found here directly.
180 /// The file offset is found by computing the vaddr offset from the section vaddr
181 /// the symbol references, and adding that to the file offset of the section.
182 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
183 /// offset table entry.
184 local_sym_index: u32,
185 /// This field is undefined for symbols with size = 0.
186 offset_table_index: u32,
187 /// Points to the previous and next neighbors, based on the `text_offset`.
188 /// This can be used to find, for example, the capacity of this `TextBlock`.
189 prev: ?*TextBlock,
190 next: ?*TextBlock,
191
192 pub const empty = TextBlock{
193 .local_sym_index = 0,
194 .offset_table_index = undefined,
195 .prev = null,
196 .next = null,
197 };
198
199 /// Returns how much room there is to grow in virtual address space.
200 /// File offset relocation happens transparently, so it is not included in
201 /// this calculation.
202 fn capacity(self: TextBlock, elf_file: ElfFile) u64 {
203 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
204 if (self.next) |next| {
205 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
206 return next_sym.st_value - self_sym.st_value;
207 } else {
208 // We are the last block. The capacity is limited only by virtual address space.
209 return std.math.maxInt(u32) - self_sym.st_value;
210 }
211 }
115pub const File = struct {
116 tag: Tag,
117 pub fn cast(base: *File, comptime T: type) ?*T {
118 if (base.tag != T.base_tag)
119 return null;
212120
213 fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool {
214 // No need to keep a free list node for the last block.
215 const next = self.next orelse return false;
216 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
217 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
218 const cap = next_sym.st_value - self_sym.st_value;
219 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
220 if (cap <= ideal_cap) return false;
221 const surplus = cap - ideal_cap;
222 return surplus >= min_text_capacity;
223 }
224 };
225
226 pub const Export = struct {
227 sym_index: ?u32 = null,
228 };
121 return @fieldParentPtr(T, "base", base);
122 }
229123
230 pub fn deinit(self: *ElfFile) void {
231 self.sections.deinit(self.allocator);
232 self.program_headers.deinit(self.allocator);
233 self.shstrtab.deinit(self.allocator);
234 self.local_symbols.deinit(self.allocator);
235 self.global_symbols.deinit(self.allocator);
236 self.global_symbol_free_list.deinit(self.allocator);
237 self.local_symbol_free_list.deinit(self.allocator);
238 self.offset_table_free_list.deinit(self.allocator);
239 self.text_block_free_list.deinit(self.allocator);
240 self.offset_table.deinit(self.allocator);
241 if (self.owns_file_handle) {
242 if (self.file) |f| f.close();
124 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
125 switch (base.tag) {
126 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
127 .C => {},
128 else => unreachable,
243129 }
244130 }
245131
246 pub fn makeExecutable(self: *ElfFile) !void {
247 assert(self.owns_file_handle);
248 if (self.file) |f| {
249 f.close();
250 self.file = null;
132 pub fn makeExecutable(base: *File) !void {
133 switch (base.tag) {
134 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
135 else => unreachable,
251136 }
252137 }
253138
254 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {
255 assert(self.owns_file_handle);
256 if (self.file != null) return;
257 self.file = try dir.createFile(sub_path, .{
258 .truncate = false,
259 .read = true,
260 .mode = determineMode(self.options),
261 });
139 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
140 switch (base.tag) {
141 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
142 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
143 else => unreachable,
144 }
262145 }
263146
264 /// Returns end pos of collision, if any.
265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {
266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
267 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
268 if (start < ehdr_size)
269 return ehdr_size;
270
271 const end = start + satMul(size, alloc_num) / alloc_den;
272
273 if (self.shdr_table_offset) |off| {
274 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
275 const tight_size = self.sections.items.len * shdr_size;
276 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
277 const test_end = off + increased_size;
278 if (end > off and start < test_end) {
279 return test_end;
280 }
147 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
148 switch (base.tag) {
149 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
150 .C => {},
151 else => unreachable,
281152 }
153 }
282154
283 if (self.phdr_table_offset) |off| {
284 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
285 const tight_size = self.sections.items.len * phdr_size;
286 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
287 const test_end = off + increased_size;
288 if (end > off and start < test_end) {
289 return test_end;
290 }
155 pub fn deinit(base: *File) void {
156 switch (base.tag) {
157 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
158 .C => @fieldParentPtr(C, "base", base).deinit(),
159 else => unreachable,
291160 }
161 }
292162
293 for (self.sections.items) |section| {
294 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
295 const test_end = section.sh_offset + increased_size;
296 if (end > section.sh_offset and start < test_end) {
297 return test_end;
298 }
299 }
300 for (self.program_headers.items) |program_header| {
301 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
302 const test_end = program_header.p_offset + increased_size;
303 if (end > program_header.p_offset and start < test_end) {
304 return test_end;
305 }
163 pub fn destroy(base: *File) void {
164 switch (base.tag) {
165 .Elf => {
166 const parent = @fieldParentPtr(Elf, "base", base);
167 parent.deinit();
168 parent.allocator.destroy(parent);
169 },
170 .C => {
171 const parent = @fieldParentPtr(C, "base", base);
172 parent.deinit();
173 parent.allocator.destroy(parent);
174 },
175 else => unreachable,
306176 }
307 return null;
308177 }
309178
310 fn allocatedSize(self: *ElfFile, start: u64) u64 {
311 var min_pos: u64 = std.math.maxInt(u64);
312 if (self.shdr_table_offset) |off| {
313 if (off > start and off < min_pos) min_pos = off;
314 }
315 if (self.phdr_table_offset) |off| {
316 if (off > start and off < min_pos) min_pos = off;
317 }
318 for (self.sections.items) |section| {
319 if (section.sh_offset <= start) continue;
320 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
321 }
322 for (self.program_headers.items) |program_header| {
323 if (program_header.p_offset <= start) continue;
324 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
325 }
326 return min_pos - start;
179 pub fn flush(base: *File) !void {
180 try switch (base.tag) {
181 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
182 .C => @fieldParentPtr(C, "base", base).flush(),
183 else => unreachable,
184 };
327185 }
328186
329 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {
330 var start: u64 = 0;
331 while (self.detectAllocCollision(start, object_size)) |item_end| {
332 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
188 switch (base.tag) {
189 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
190 else => unreachable,
333191 }
334 return start;
335192 }
336193
337 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {
338 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
339 const result = self.shstrtab.items.len;
340 self.shstrtab.appendSliceAssumeCapacity(bytes);
341 self.shstrtab.appendAssumeCapacity(0);
342 return @intCast(u32, result);
194 pub fn errorFlags(base: *File) ErrorFlags {
195 return switch (base.tag) {
196 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
197 .C => return .{ .no_entry_point_found = false },
198 else => unreachable,
199 };
343200 }
344201
345 fn getString(self: *ElfFile, str_off: u32) []const u8 {
346 assert(str_off < self.shstrtab.items.len);
347 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
202 pub fn options(base: *File) Options {
203 return switch (base.tag) {
204 .Elf => @fieldParentPtr(Elf, "base", base).options,
205 .C => @fieldParentPtr(C, "base", base).options,
206 };
348207 }
349208
350 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {
351 const existing_name = self.getString(old_str_off);
352 if (mem.eql(u8, existing_name, new_name)) {
353 return old_str_off;
209 /// Must be called only after a successful call to `updateDecl`.
210 pub fn updateDeclExports(
211 base: *File,
212 module: *Module,
213 decl: *const Module.Decl,
214 exports: []const *Module.Export,
215 ) !void {
216 switch (base.tag) {
217 .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
218 .C => return {},
354219 }
355 return self.makeString(new_name);
356220 }
357221
358 pub fn populateMissingMetadata(self: *ElfFile) !void {
359 const small_ptr = switch (self.ptr_width) {
360 .p32 => true,
361 .p64 => false,
362 };
363 const ptr_size: u8 = switch (self.ptr_width) {
364 .p32 => 4,
365 .p64 => 8,
366 };
367 if (self.phdr_load_re_index == null) {
368 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
369 const file_size = self.options.program_code_size_hint;
370 const p_align = 0x1000;
371 const off = self.findFreeSpace(file_size, p_align);
372 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
373 try self.program_headers.append(self.allocator, .{
374 .p_type = elf.PT_LOAD,
375 .p_offset = off,
376 .p_filesz = file_size,
377 .p_vaddr = default_entry_addr,
378 .p_paddr = default_entry_addr,
379 .p_memsz = file_size,
380 .p_align = p_align,
381 .p_flags = elf.PF_X | elf.PF_R,
382 });
383 self.entry_addr = null;
384 self.phdr_table_dirty = true;
385 }
386 if (self.phdr_got_index == null) {
387 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
388 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
389 // We really only need ptr alignment but since we are using PROGBITS, linux requires
390 // page align.
391 const p_align = 0x1000;
392 const off = self.findFreeSpace(file_size, p_align);
393 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
394 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
395 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
396 // else in virtual memory.
397 const default_got_addr = 0x4000000;
398 try self.program_headers.append(self.allocator, .{
399 .p_type = elf.PT_LOAD,
400 .p_offset = off,
401 .p_filesz = file_size,
402 .p_vaddr = default_got_addr,
403 .p_paddr = default_got_addr,
404 .p_memsz = file_size,
405 .p_align = p_align,
406 .p_flags = elf.PF_R,
407 });
408 self.phdr_table_dirty = true;
409 }
410 if (self.shstrtab_index == null) {
411 self.shstrtab_index = @intCast(u16, self.sections.items.len);
412 assert(self.shstrtab.items.len == 0);
413 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
414 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
415 //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
416 try self.sections.append(self.allocator, .{
417 .sh_name = try self.makeString(".shstrtab"),
418 .sh_type = elf.SHT_STRTAB,
419 .sh_flags = 0,
420 .sh_addr = 0,
421 .sh_offset = off,
422 .sh_size = self.shstrtab.items.len,
423 .sh_link = 0,
424 .sh_info = 0,
425 .sh_addralign = 1,
426 .sh_entsize = 0,
427 });
428 self.shstrtab_dirty = true;
429 self.shdr_table_dirty = true;
430 }
431 if (self.text_section_index == null) {
432 self.text_section_index = @intCast(u16, self.sections.items.len);
433 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
222 pub const Tag = enum {
223 Elf,
224 C,
225 };
434226
435 try self.sections.append(self.allocator, .{
436 .sh_name = try self.makeString(".text"),
437 .sh_type = elf.SHT_PROGBITS,
438 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
439 .sh_addr = phdr.p_vaddr,
440 .sh_offset = phdr.p_offset,
441 .sh_size = phdr.p_filesz,
442 .sh_link = 0,
443 .sh_info = 0,
444 .sh_addralign = phdr.p_align,
445 .sh_entsize = 0,
446 });
447 self.shdr_table_dirty = true;
227 pub const ErrorFlags = struct {
228 no_entry_point_found: bool = false,
229 };
230
231 pub const C = struct {
232 pub const base_tag: Tag = .C;
233 base: File = File{ .tag = base_tag },
234
235 allocator: *Allocator,
236 header: std.ArrayList(u8),
237 constants: std.ArrayList(u8),
238 main: std.ArrayList(u8),
239 file: ?fs.File,
240 options: Options,
241 called: std.StringHashMap(void),
242 need_stddef: bool = false,
243 need_stdint: bool = false,
244 need_noreturn: bool = false,
245 error_msg: *Module.ErrorMsg = undefined,
246
247 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
248 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
249 return error.CGenFailure;
448250 }
449 if (self.got_section_index == null) {
450 self.got_section_index = @intCast(u16, self.sections.items.len);
451 const phdr = &self.program_headers.items[self.phdr_got_index.?];
452251
453 try self.sections.append(self.allocator, .{
454 .sh_name = try self.makeString(".got"),
455 .sh_type = elf.SHT_PROGBITS,
456 .sh_flags = elf.SHF_ALLOC,
457 .sh_addr = phdr.p_vaddr,
458 .sh_offset = phdr.p_offset,
459 .sh_size = phdr.p_filesz,
460 .sh_link = 0,
461 .sh_info = 0,
462 .sh_addralign = phdr.p_align,
463 .sh_entsize = 0,
464 });
465 self.shdr_table_dirty = true;
252 pub fn deinit(self: *File.C) void {
253 self.main.deinit();
254 self.header.deinit();
255 self.constants.deinit();
256 self.called.deinit();
257 if (self.file) |f|
258 f.close();
466259 }
467 if (self.symtab_section_index == null) {
468 self.symtab_section_index = @intCast(u16, self.sections.items.len);
469 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
470 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
471 const file_size = self.options.symbol_count_hint * each_size;
472 const off = self.findFreeSpace(file_size, min_align);
473 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
474
475 try self.sections.append(self.allocator, .{
476 .sh_name = try self.makeString(".symtab"),
477 .sh_type = elf.SHT_SYMTAB,
478 .sh_flags = 0,
479 .sh_addr = 0,
480 .sh_offset = off,
481 .sh_size = file_size,
482 // The section header index of the associated string table.
483 .sh_link = self.shstrtab_index.?,
484 .sh_info = @intCast(u32, self.local_symbols.items.len),
485 .sh_addralign = min_align,
486 .sh_entsize = each_size,
487 });
488 self.shdr_table_dirty = true;
489 try self.writeSymbol(0);
260
261 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
262 c_codegen.generate(self, decl) catch |err| {
263 if (err == error.CGenFailure) {
264 try module.failed_decls.put(module.gpa, decl, self.error_msg);
265 }
266 return err;
267 };
490268 }
491 const shsize: u64 = switch (self.ptr_width) {
492 .p32 => @sizeOf(elf.Elf32_Shdr),
493 .p64 => @sizeOf(elf.Elf64_Shdr),
494 };
495 const shalign: u16 = switch (self.ptr_width) {
496 .p32 => @alignOf(elf.Elf32_Shdr),
497 .p64 => @alignOf(elf.Elf64_Shdr),
498 };
499 if (self.shdr_table_offset == null) {
500 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
501 self.shdr_table_dirty = true;
269
270 pub fn flush(self: *File.C) !void {
271 const writer = self.file.?.writer();
272 try writer.writeAll(@embedFile("cbe.h"));
273 var includes = false;
274 if (self.need_stddef) {
275 try writer.writeAll("#include <stddef.h>\n");
276 includes = true;
277 }
278 if (self.need_stdint) {
279 try writer.writeAll("#include <stdint.h>\n");
280 includes = true;
281 }
282 if (includes) {
283 try writer.writeByte('\n');
284 }
285 if (self.header.items.len > 0) {
286 try writer.print("{}\n", .{self.header.items});
287 }
288 if (self.constants.items.len > 0) {
289 try writer.print("{}\n", .{self.constants.items});
290 }
291 if (self.main.items.len > 1) {
292 const last_two = self.main.items[self.main.items.len - 2 ..];
293 if (std.mem.eql(u8, last_two, "\n\n")) {
294 self.main.items.len -= 1;
295 }
296 }
297 try writer.writeAll(self.main.items);
298 self.file.?.close();
299 self.file = null;
502300 }
503 const phsize: u64 = switch (self.ptr_width) {
504 .p32 => @sizeOf(elf.Elf32_Phdr),
505 .p64 => @sizeOf(elf.Elf64_Phdr),
301 };
302
303 pub const Elf = struct {
304 pub const base_tag: Tag = .Elf;
305 base: File = File{ .tag = base_tag },
306
307 allocator: *Allocator,
308 file: ?fs.File,
309 owns_file_handle: bool,
310 options: Options,
311 ptr_width: enum { p32, p64 },
312
313 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
314 /// Same order as in the file.
315 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
316 shdr_table_offset: ?u64 = null,
317
318 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
319 /// Same order as in the file.
320 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
321 phdr_table_offset: ?u64 = null,
322 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
323 phdr_load_re_index: ?u16 = null,
324 /// The index into the program headers of the global offset table.
325 /// It needs PT_LOAD and Read flags.
326 phdr_got_index: ?u16 = null,
327 entry_addr: ?u64 = null,
328
329 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
330 shstrtab_index: ?u16 = null,
331
332 text_section_index: ?u16 = null,
333 symtab_section_index: ?u16 = null,
334 got_section_index: ?u16 = null,
335
336 /// The same order as in the file. ELF requires global symbols to all be after the
337 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
338 /// write them at the end. These are only the local symbols. The length of this array
339 /// is the value used for sh_info in the .symtab section.
340 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
341 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
342
343 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
344 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
345 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
346
347 /// Same order as in the file. The value is the absolute vaddr value.
348 /// If the vaddr of the executable program header changes, the entire
349 /// offset table needs to be rewritten.
350 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
351
352 phdr_table_dirty: bool = false,
353 shdr_table_dirty: bool = false,
354 shstrtab_dirty: bool = false,
355 offset_table_count_dirty: bool = false,
356
357 error_flags: ErrorFlags = ErrorFlags{},
358
359 /// A list of text blocks that have surplus capacity. This list can have false
360 /// positives, as functions grow and shrink over time, only sometimes being added
361 /// or removed from the freelist.
362 ///
363 /// A text block has surplus capacity when its overcapacity value is greater than
364 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
365 /// much extra capacity, that we could fit a small new symbol in it, itself with
366 /// ideal_capacity or more.
367 ///
368 /// Ideal capacity is defined by size * alloc_num / alloc_den.
369 ///
370 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
371 /// overcapacity can be negative. A simple way to have negative overcapacity is to
372 /// allocate a fresh text block, which will have ideal capacity, and then grow it
373 /// by 1 byte. It will then have -1 overcapacity.
374 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
375 last_text_block: ?*TextBlock = null,
376
377 /// `alloc_num / alloc_den` is the factor of padding when allocating.
378 const alloc_num = 4;
379 const alloc_den = 3;
380
381 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
382 /// it as a possible place to put new symbols, it must have enough room for this many bytes
383 /// (plus extra for reserved capacity).
384 const minimum_text_block_size = 64;
385 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
386
387 pub const TextBlock = struct {
388 /// Each decl always gets a local symbol with the fully qualified name.
389 /// The vaddr and size are found here directly.
390 /// The file offset is found by computing the vaddr offset from the section vaddr
391 /// the symbol references, and adding that to the file offset of the section.
392 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
393 /// offset table entry.
394 local_sym_index: u32,
395 /// This field is undefined for symbols with size = 0.
396 offset_table_index: u32,
397 /// Points to the previous and next neighbors, based on the `text_offset`.
398 /// This can be used to find, for example, the capacity of this `TextBlock`.
399 prev: ?*TextBlock,
400 next: ?*TextBlock,
401
402 pub const empty = TextBlock{
403 .local_sym_index = 0,
404 .offset_table_index = undefined,
405 .prev = null,
406 .next = null,
407 };
408
409 /// Returns how much room there is to grow in virtual address space.
410 /// File offset relocation happens transparently, so it is not included in
411 /// this calculation.
412 fn capacity(self: TextBlock, elf_file: Elf) u64 {
413 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
414 if (self.next) |next| {
415 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
416 return next_sym.st_value - self_sym.st_value;
417 } else {
418 // We are the last block. The capacity is limited only by virtual address space.
419 return std.math.maxInt(u32) - self_sym.st_value;
420 }
421 }
422
423 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
424 // No need to keep a free list node for the last block.
425 const next = self.next orelse return false;
426 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
427 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
428 const cap = next_sym.st_value - self_sym.st_value;
429 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
430 if (cap <= ideal_cap) return false;
431 const surplus = cap - ideal_cap;
432 return surplus >= min_text_capacity;
433 }
506434 };
507 const phalign: u16 = switch (self.ptr_width) {
508 .p32 => @alignOf(elf.Elf32_Phdr),
509 .p64 => @alignOf(elf.Elf64_Phdr),
435
436 pub const Export = struct {
437 sym_index: ?u32 = null,
510438 };
511 if (self.phdr_table_offset == null) {
512 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
513 self.phdr_table_dirty = true;
514 }
515 {
516 // Iterate over symbols, populating free_list and last_text_block.
517 if (self.local_symbols.items.len != 1) {
518 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
439
440 pub fn deinit(self: *Elf) void {
441 self.sections.deinit(self.allocator);
442 self.program_headers.deinit(self.allocator);
443 self.shstrtab.deinit(self.allocator);
444 self.local_symbols.deinit(self.allocator);
445 self.global_symbols.deinit(self.allocator);
446 self.global_symbol_free_list.deinit(self.allocator);
447 self.local_symbol_free_list.deinit(self.allocator);
448 self.offset_table_free_list.deinit(self.allocator);
449 self.text_block_free_list.deinit(self.allocator);
450 self.offset_table.deinit(self.allocator);
451 if (self.owns_file_handle) {
452 if (self.file) |f| f.close();
519453 }
520 // We are starting with an empty file. The default values are correct, null and empty list.
521454 }
522 }
523455
524 /// Commit pending changes and write headers.
525 pub fn flush(self: *ElfFile) !void {
526 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
456 pub fn makeExecutable(self: *Elf) !void {
457 assert(self.owns_file_handle);
458 if (self.file) |f| {
459 f.close();
460 self.file = null;
461 }
462 }
527463
528 // Unfortunately these have to be buffered and done at the end because ELF does not allow
529 // mixing local and global symbols within a symbol table.
530 try self.writeAllGlobalSymbols();
464 pub fn makeWritable(self: *Elf, dir: fs.Dir, sub_path: []const u8) !void {
465 assert(self.owns_file_handle);
466 if (self.file != null) return;
467 self.file = try dir.createFile(sub_path, .{
468 .truncate = false,
469 .read = true,
470 .mode = determineMode(self.options),
471 });
472 }
531473
532 if (self.phdr_table_dirty) {
533 const phsize: u64 = switch (self.ptr_width) {
534 .p32 => @sizeOf(elf.Elf32_Phdr),
535 .p64 => @sizeOf(elf.Elf64_Phdr),
536 };
537 const phalign: u16 = switch (self.ptr_width) {
538 .p32 => @alignOf(elf.Elf32_Phdr),
539 .p64 => @alignOf(elf.Elf64_Phdr),
540 };
541 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
542 const needed_size = self.program_headers.items.len * phsize;
474 /// Returns end pos of collision, if any.
475 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
476 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
477 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
478 if (start < ehdr_size)
479 return ehdr_size;
480
481 const end = start + satMul(size, alloc_num) / alloc_den;
482
483 if (self.shdr_table_offset) |off| {
484 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
485 const tight_size = self.sections.items.len * shdr_size;
486 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
487 const test_end = off + increased_size;
488 if (end > off and start < test_end) {
489 return test_end;
490 }
491 }
543492
544 if (needed_size > allocated_size) {
545 self.phdr_table_offset = null; // free the space
546 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
493 if (self.phdr_table_offset) |off| {
494 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
495 const tight_size = self.sections.items.len * phdr_size;
496 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
497 const test_end = off + increased_size;
498 if (end > off and start < test_end) {
499 return test_end;
500 }
547501 }
548502
549 switch (self.ptr_width) {
550 .p32 => {
551 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
552 defer self.allocator.free(buf);
503 for (self.sections.items) |section| {
504 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
505 const test_end = section.sh_offset + increased_size;
506 if (end > section.sh_offset and start < test_end) {
507 return test_end;
508 }
509 }
510 for (self.program_headers.items) |program_header| {
511 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
512 const test_end = program_header.p_offset + increased_size;
513 if (end > program_header.p_offset and start < test_end) {
514 return test_end;
515 }
516 }
517 return null;
518 }
553519
554 for (buf) |*phdr, i| {
555 phdr.* = progHeaderTo32(self.program_headers.items[i]);
556 if (foreign_endian) {
557 bswapAllFields(elf.Elf32_Phdr, phdr);
558 }
559 }
560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
561 },
562 .p64 => {
563 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
564 defer self.allocator.free(buf);
520 fn allocatedSize(self: *Elf, start: u64) u64 {
521 var min_pos: u64 = std.math.maxInt(u64);
522 if (self.shdr_table_offset) |off| {
523 if (off > start and off < min_pos) min_pos = off;
524 }
525 if (self.phdr_table_offset) |off| {
526 if (off > start and off < min_pos) min_pos = off;
527 }
528 for (self.sections.items) |section| {
529 if (section.sh_offset <= start) continue;
530 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
531 }
532 for (self.program_headers.items) |program_header| {
533 if (program_header.p_offset <= start) continue;
534 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
535 }
536 return min_pos - start;
537 }
565538
566 for (buf) |*phdr, i| {
567 phdr.* = self.program_headers.items[i];
568 if (foreign_endian) {
569 bswapAllFields(elf.Elf64_Phdr, phdr);
570 }
571 }
572 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
573 },
539 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
540 var start: u64 = 0;
541 while (self.detectAllocCollision(start, object_size)) |item_end| {
542 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
574543 }
575 self.phdr_table_dirty = false;
544 return start;
576545 }
577546
578 {
579 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
580 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
581 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
582 const needed_size = self.shstrtab.items.len;
547 fn makeString(self: *Elf, bytes: []const u8) !u32 {
548 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
549 const result = self.shstrtab.items.len;
550 self.shstrtab.appendSliceAssumeCapacity(bytes);
551 self.shstrtab.appendAssumeCapacity(0);
552 return @intCast(u32, result);
553 }
583554
584 if (needed_size > allocated_size) {
585 shstrtab_sect.sh_size = 0; // free the space
586 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
587 }
588 shstrtab_sect.sh_size = needed_size;
589 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
555 fn getString(self: *Elf, str_off: u32) []const u8 {
556 assert(str_off < self.shstrtab.items.len);
557 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
558 }
590559
591 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
592 if (!self.shdr_table_dirty) {
593 // Then it won't get written with the others and we need to do it.
594 try self.writeSectHeader(self.shstrtab_index.?);
595 }
596 self.shstrtab_dirty = false;
560 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
561 const existing_name = self.getString(old_str_off);
562 if (mem.eql(u8, existing_name, new_name)) {
563 return old_str_off;
597564 }
565 return self.makeString(new_name);
598566 }
599 if (self.shdr_table_dirty) {
567
568 pub fn populateMissingMetadata(self: *Elf) !void {
569 const small_ptr = switch (self.ptr_width) {
570 .p32 => true,
571 .p64 => false,
572 };
573 const ptr_size: u8 = switch (self.ptr_width) {
574 .p32 => 4,
575 .p64 => 8,
576 };
577 if (self.phdr_load_re_index == null) {
578 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
579 const file_size = self.options.program_code_size_hint;
580 const p_align = 0x1000;
581 const off = self.findFreeSpace(file_size, p_align);
582 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
583 try self.program_headers.append(self.allocator, .{
584 .p_type = elf.PT_LOAD,
585 .p_offset = off,
586 .p_filesz = file_size,
587 .p_vaddr = default_entry_addr,
588 .p_paddr = default_entry_addr,
589 .p_memsz = file_size,
590 .p_align = p_align,
591 .p_flags = elf.PF_X | elf.PF_R,
592 });
593 self.entry_addr = null;
594 self.phdr_table_dirty = true;
595 }
596 if (self.phdr_got_index == null) {
597 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
598 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
599 // We really only need ptr alignment but since we are using PROGBITS, linux requires
600 // page align.
601 const p_align = 0x1000;
602 const off = self.findFreeSpace(file_size, p_align);
603 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
604 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
605 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
606 // else in virtual memory.
607 const default_got_addr = 0x4000000;
608 try self.program_headers.append(self.allocator, .{
609 .p_type = elf.PT_LOAD,
610 .p_offset = off,
611 .p_filesz = file_size,
612 .p_vaddr = default_got_addr,
613 .p_paddr = default_got_addr,
614 .p_memsz = file_size,
615 .p_align = p_align,
616 .p_flags = elf.PF_R,
617 });
618 self.phdr_table_dirty = true;
619 }
620 if (self.shstrtab_index == null) {
621 self.shstrtab_index = @intCast(u16, self.sections.items.len);
622 assert(self.shstrtab.items.len == 0);
623 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
624 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
625 std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
626 try self.sections.append(self.allocator, .{
627 .sh_name = try self.makeString(".shstrtab"),
628 .sh_type = elf.SHT_STRTAB,
629 .sh_flags = 0,
630 .sh_addr = 0,
631 .sh_offset = off,
632 .sh_size = self.shstrtab.items.len,
633 .sh_link = 0,
634 .sh_info = 0,
635 .sh_addralign = 1,
636 .sh_entsize = 0,
637 });
638 self.shstrtab_dirty = true;
639 self.shdr_table_dirty = true;
640 }
641 if (self.text_section_index == null) {
642 self.text_section_index = @intCast(u16, self.sections.items.len);
643 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
644
645 try self.sections.append(self.allocator, .{
646 .sh_name = try self.makeString(".text"),
647 .sh_type = elf.SHT_PROGBITS,
648 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
649 .sh_addr = phdr.p_vaddr,
650 .sh_offset = phdr.p_offset,
651 .sh_size = phdr.p_filesz,
652 .sh_link = 0,
653 .sh_info = 0,
654 .sh_addralign = phdr.p_align,
655 .sh_entsize = 0,
656 });
657 self.shdr_table_dirty = true;
658 }
659 if (self.got_section_index == null) {
660 self.got_section_index = @intCast(u16, self.sections.items.len);
661 const phdr = &self.program_headers.items[self.phdr_got_index.?];
662
663 try self.sections.append(self.allocator, .{
664 .sh_name = try self.makeString(".got"),
665 .sh_type = elf.SHT_PROGBITS,
666 .sh_flags = elf.SHF_ALLOC,
667 .sh_addr = phdr.p_vaddr,
668 .sh_offset = phdr.p_offset,
669 .sh_size = phdr.p_filesz,
670 .sh_link = 0,
671 .sh_info = 0,
672 .sh_addralign = phdr.p_align,
673 .sh_entsize = 0,
674 });
675 self.shdr_table_dirty = true;
676 }
677 if (self.symtab_section_index == null) {
678 self.symtab_section_index = @intCast(u16, self.sections.items.len);
679 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
680 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
681 const file_size = self.options.symbol_count_hint * each_size;
682 const off = self.findFreeSpace(file_size, min_align);
683 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
684
685 try self.sections.append(self.allocator, .{
686 .sh_name = try self.makeString(".symtab"),
687 .sh_type = elf.SHT_SYMTAB,
688 .sh_flags = 0,
689 .sh_addr = 0,
690 .sh_offset = off,
691 .sh_size = file_size,
692 // The section header index of the associated string table.
693 .sh_link = self.shstrtab_index.?,
694 .sh_info = @intCast(u32, self.local_symbols.items.len),
695 .sh_addralign = min_align,
696 .sh_entsize = each_size,
697 });
698 self.shdr_table_dirty = true;
699 try self.writeSymbol(0);
700 }
600701 const shsize: u64 = switch (self.ptr_width) {
601702 .p32 => @sizeOf(elf.Elf32_Shdr),
602703 .p64 => @sizeOf(elf.Elf64_Shdr),
......@@ -605,763 +706,874 @@ pub const ElfFile = struct {
605706 .p32 => @alignOf(elf.Elf32_Shdr),
606707 .p64 => @alignOf(elf.Elf64_Shdr),
607708 };
608 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
609 const needed_size = self.sections.items.len * shsize;
610
611 if (needed_size > allocated_size) {
612 self.shdr_table_offset = null; // free the space
613 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
709 if (self.shdr_table_offset == null) {
710 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
711 self.shdr_table_dirty = true;
712 }
713 const phsize: u64 = switch (self.ptr_width) {
714 .p32 => @sizeOf(elf.Elf32_Phdr),
715 .p64 => @sizeOf(elf.Elf64_Phdr),
716 };
717 const phalign: u16 = switch (self.ptr_width) {
718 .p32 => @alignOf(elf.Elf32_Phdr),
719 .p64 => @alignOf(elf.Elf64_Phdr),
720 };
721 if (self.phdr_table_offset == null) {
722 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
723 self.phdr_table_dirty = true;
724 }
725 {
726 // Iterate over symbols, populating free_list and last_text_block.
727 if (self.local_symbols.items.len != 1) {
728 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
729 }
730 // We are starting with an empty file. The default values are correct, null and empty list.
614731 }
732 }
615733
616 switch (self.ptr_width) {
617 .p32 => {
618 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
619 defer self.allocator.free(buf);
734 /// Commit pending changes and write headers.
735 pub fn flush(self: *Elf) !void {
736 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
620737
621 for (buf) |*shdr, i| {
622 shdr.* = sectHeaderTo32(self.sections.items[i]);
623 if (foreign_endian) {
624 bswapAllFields(elf.Elf32_Shdr, shdr);
738 // Unfortunately these have to be buffered and done at the end because ELF does not allow
739 // mixing local and global symbols within a symbol table.
740 try self.writeAllGlobalSymbols();
741
742 if (self.phdr_table_dirty) {
743 const phsize: u64 = switch (self.ptr_width) {
744 .p32 => @sizeOf(elf.Elf32_Phdr),
745 .p64 => @sizeOf(elf.Elf64_Phdr),
746 };
747 const phalign: u16 = switch (self.ptr_width) {
748 .p32 => @alignOf(elf.Elf32_Phdr),
749 .p64 => @alignOf(elf.Elf64_Phdr),
750 };
751 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
752 const needed_size = self.program_headers.items.len * phsize;
753
754 if (needed_size > allocated_size) {
755 self.phdr_table_offset = null; // free the space
756 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
757 }
758
759 switch (self.ptr_width) {
760 .p32 => {
761 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
762 defer self.allocator.free(buf);
763
764 for (buf) |*phdr, i| {
765 phdr.* = progHeaderTo32(self.program_headers.items[i]);
766 if (foreign_endian) {
767 bswapAllFields(elf.Elf32_Phdr, phdr);
768 }
625769 }
770 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
771 },
772 .p64 => {
773 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
774 defer self.allocator.free(buf);
775
776 for (buf) |*phdr, i| {
777 phdr.* = self.program_headers.items[i];
778 if (foreign_endian) {
779 bswapAllFields(elf.Elf64_Phdr, phdr);
780 }
781 }
782 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
783 },
784 }
785 self.phdr_table_dirty = false;
786 }
787
788 {
789 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
790 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
791 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
792 const needed_size = self.shstrtab.items.len;
793
794 if (needed_size > allocated_size) {
795 shstrtab_sect.sh_size = 0; // free the space
796 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
626797 }
627 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
628 },
629 .p64 => {
630 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
631 defer self.allocator.free(buf);
798 shstrtab_sect.sh_size = needed_size;
799 std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
632800
633 for (buf) |*shdr, i| {
634 shdr.* = self.sections.items[i];
635 //std.debug.warn("writing section {}\n", .{shdr.*});
636 if (foreign_endian) {
637 bswapAllFields(elf.Elf64_Shdr, shdr);
638 }
801 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
802 if (!self.shdr_table_dirty) {
803 // Then it won't get written with the others and we need to do it.
804 try self.writeSectHeader(self.shstrtab_index.?);
639805 }
640 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
641 },
806 self.shstrtab_dirty = false;
807 }
642808 }
643 self.shdr_table_dirty = false;
644 }
645 if (self.entry_addr == null and self.options.output_mode == .Exe) {
646 self.error_flags.no_entry_point_found = true;
647 } else {
648 self.error_flags.no_entry_point_found = false;
649 try self.writeElfHeader();
650 }
809 if (self.shdr_table_dirty) {
810 const shsize: u64 = switch (self.ptr_width) {
811 .p32 => @sizeOf(elf.Elf32_Shdr),
812 .p64 => @sizeOf(elf.Elf64_Shdr),
813 };
814 const shalign: u16 = switch (self.ptr_width) {
815 .p32 => @alignOf(elf.Elf32_Shdr),
816 .p64 => @alignOf(elf.Elf64_Shdr),
817 };
818 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
819 const needed_size = self.sections.items.len * shsize;
651820
652 // The point of flush() is to commit changes, so nothing should be dirty after this.
653 assert(!self.phdr_table_dirty);
654 assert(!self.shdr_table_dirty);
655 assert(!self.shstrtab_dirty);
656 assert(!self.offset_table_count_dirty);
657 const syms_sect = &self.sections.items[self.symtab_section_index.?];
658 assert(syms_sect.sh_info == self.local_symbols.items.len);
659 }
821 if (needed_size > allocated_size) {
822 self.shdr_table_offset = null; // free the space
823 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
824 }
660825
661 fn writeElfHeader(self: *ElfFile) !void {
662 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
826 switch (self.ptr_width) {
827 .p32 => {
828 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
829 defer self.allocator.free(buf);
663830
664 var index: usize = 0;
665 hdr_buf[0..4].* = "\x7fELF".*;
666 index += 4;
831 for (buf) |*shdr, i| {
832 shdr.* = sectHeaderTo32(self.sections.items[i]);
833 if (foreign_endian) {
834 bswapAllFields(elf.Elf32_Shdr, shdr);
835 }
836 }
837 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
838 },
839 .p64 => {
840 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
841 defer self.allocator.free(buf);
842
843 for (buf) |*shdr, i| {
844 shdr.* = self.sections.items[i];
845 std.log.debug(.link, "writing section {}\n", .{shdr.*});
846 if (foreign_endian) {
847 bswapAllFields(elf.Elf64_Shdr, shdr);
848 }
849 }
850 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
851 },
852 }
853 self.shdr_table_dirty = false;
854 }
855 if (self.entry_addr == null and self.options.output_mode == .Exe) {
856 std.log.debug(.link, "no_entry_point_found = true\n", .{});
857 self.error_flags.no_entry_point_found = true;
858 } else {
859 self.error_flags.no_entry_point_found = false;
860 try self.writeElfHeader();
861 }
667862
668 hdr_buf[index] = switch (self.ptr_width) {
669 .p32 => elf.ELFCLASS32,
670 .p64 => elf.ELFCLASS64,
671 };
672 index += 1;
863 // The point of flush() is to commit changes, so nothing should be dirty after this.
864 assert(!self.phdr_table_dirty);
865 assert(!self.shdr_table_dirty);
866 assert(!self.shstrtab_dirty);
867 assert(!self.offset_table_count_dirty);
868 const syms_sect = &self.sections.items[self.symtab_section_index.?];
869 assert(syms_sect.sh_info == self.local_symbols.items.len);
870 }
673871
674 const endian = self.options.target.cpu.arch.endian();
675 hdr_buf[index] = switch (endian) {
676 .Little => elf.ELFDATA2LSB,
677 .Big => elf.ELFDATA2MSB,
678 };
679 index += 1;
872 fn writeElfHeader(self: *Elf) !void {
873 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
680874
681 hdr_buf[index] = 1; // ELF version
682 index += 1;
875 var index: usize = 0;
876 hdr_buf[0..4].* = "\x7fELF".*;
877 index += 4;
683878
684 // OS ABI, often set to 0 regardless of target platform
685 // ABI Version, possibly used by glibc but not by static executables
686 // padding
687 mem.set(u8, hdr_buf[index..][0..9], 0);
688 index += 9;
879 hdr_buf[index] = switch (self.ptr_width) {
880 .p32 => elf.ELFCLASS32,
881 .p64 => elf.ELFCLASS64,
882 };
883 index += 1;
689884
690 assert(index == 16);
885 const endian = self.options.target.cpu.arch.endian();
886 hdr_buf[index] = switch (endian) {
887 .Little => elf.ELFDATA2LSB,
888 .Big => elf.ELFDATA2MSB,
889 };
890 index += 1;
691891
692 const elf_type = switch (self.options.output_mode) {
693 .Exe => elf.ET.EXEC,
694 .Obj => elf.ET.REL,
695 .Lib => switch (self.options.link_mode) {
696 .Static => elf.ET.REL,
697 .Dynamic => elf.ET.DYN,
698 },
699 };
700 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
701 index += 2;
892 hdr_buf[index] = 1; // ELF version
893 index += 1;
702894
703 const machine = self.options.target.cpu.arch.toElfMachine();
704 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
705 index += 2;
895 // OS ABI, often set to 0 regardless of target platform
896 // ABI Version, possibly used by glibc but not by static executables
897 // padding
898 mem.set(u8, hdr_buf[index..][0..9], 0);
899 index += 9;
706900
707 // ELF Version, again
708 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
709 index += 4;
901 assert(index == 16);
710902
711 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
903 const elf_type = switch (self.options.output_mode) {
904 .Exe => elf.ET.EXEC,
905 .Obj => elf.ET.REL,
906 .Lib => switch (self.options.link_mode) {
907 .Static => elf.ET.REL,
908 .Dynamic => elf.ET.DYN,
909 },
910 };
911 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
912 index += 2;
712913
713 switch (self.ptr_width) {
714 .p32 => {
715 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
716 index += 4;
914 const machine = self.options.target.cpu.arch.toElfMachine();
915 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
916 index += 2;
717917
718 // e_phoff
719 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
720 index += 4;
918 // ELF Version, again
919 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
920 index += 4;
721921
722 // e_shoff
723 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
724 index += 4;
725 },
726 .p64 => {
727 // e_entry
728 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
729 index += 8;
730
731 // e_phoff
732 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
733 index += 8;
734
735 // e_shoff
736 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
737 index += 8;
738 },
739 }
922 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
740923
741 const e_flags = 0;
742 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
743 index += 4;
924 switch (self.ptr_width) {
925 .p32 => {
926 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
927 index += 4;
744928
745 const e_ehsize: u16 = switch (self.ptr_width) {
746 .p32 => @sizeOf(elf.Elf32_Ehdr),
747 .p64 => @sizeOf(elf.Elf64_Ehdr),
748 };
749 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
750 index += 2;
929 // e_phoff
930 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
931 index += 4;
751932
752 const e_phentsize: u16 = switch (self.ptr_width) {
753 .p32 => @sizeOf(elf.Elf32_Phdr),
754 .p64 => @sizeOf(elf.Elf64_Phdr),
755 };
756 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
757 index += 2;
933 // e_shoff
934 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
935 index += 4;
936 },
937 .p64 => {
938 // e_entry
939 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
940 index += 8;
758941
759 const e_phnum = @intCast(u16, self.program_headers.items.len);
760 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
761 index += 2;
942 // e_phoff
943 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
944 index += 8;
762945
763 const e_shentsize: u16 = switch (self.ptr_width) {
764 .p32 => @sizeOf(elf.Elf32_Shdr),
765 .p64 => @sizeOf(elf.Elf64_Shdr),
766 };
767 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
768 index += 2;
946 // e_shoff
947 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
948 index += 8;
949 },
950 }
769951
770 const e_shnum = @intCast(u16, self.sections.items.len);
771 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
772 index += 2;
952 const e_flags = 0;
953 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
954 index += 4;
773955
774 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
775 index += 2;
956 const e_ehsize: u16 = switch (self.ptr_width) {
957 .p32 => @sizeOf(elf.Elf32_Ehdr),
958 .p64 => @sizeOf(elf.Elf64_Ehdr),
959 };
960 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
961 index += 2;
776962
777 assert(index == e_ehsize);
963 const e_phentsize: u16 = switch (self.ptr_width) {
964 .p32 => @sizeOf(elf.Elf32_Phdr),
965 .p64 => @sizeOf(elf.Elf64_Phdr),
966 };
967 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
968 index += 2;
778969
779 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
780 }
970 const e_phnum = @intCast(u16, self.program_headers.items.len);
971 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
972 index += 2;
781973
782 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {
783 var already_have_free_list_node = false;
784 {
785 var i: usize = 0;
786 while (i < self.text_block_free_list.items.len) {
787 if (self.text_block_free_list.items[i] == text_block) {
788 _ = self.text_block_free_list.swapRemove(i);
789 continue;
790 }
791 if (self.text_block_free_list.items[i] == text_block.prev) {
792 already_have_free_list_node = true;
793 }
794 i += 1;
795 }
796 }
974 const e_shentsize: u16 = switch (self.ptr_width) {
975 .p32 => @sizeOf(elf.Elf32_Shdr),
976 .p64 => @sizeOf(elf.Elf64_Shdr),
977 };
978 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
979 index += 2;
797980
798 if (self.last_text_block == text_block) {
799 // TODO shrink the .text section size here
800 self.last_text_block = text_block.prev;
801 }
981 const e_shnum = @intCast(u16, self.sections.items.len);
982 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
983 index += 2;
802984
803 if (text_block.prev) |prev| {
804 prev.next = text_block.next;
985 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
986 index += 2;
805987
806 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
807 // The free list is heuristics, it doesn't have to be perfect, so we can
808 // ignore the OOM here.
809 self.text_block_free_list.append(self.allocator, prev) catch {};
810 }
811 } else {
812 text_block.prev = null;
813 }
988 assert(index == e_ehsize);
814989
815 if (text_block.next) |next| {
816 next.prev = text_block.prev;
817 } else {
818 text_block.next = null;
990 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
819991 }
820 }
821992
822 fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void {
823 // TODO check the new capacity, and if it crosses the size threshold into a big enough
824 // capacity, insert a free list node for it.
825 }
826
827 fn growTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
828 const sym = self.local_symbols.items[text_block.local_sym_index];
829 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
830 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
831 if (!need_realloc) return sym.st_value;
832 return self.allocateTextBlock(text_block, new_block_size, alignment);
833 }
834
835 fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
836 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
837 const shdr = &self.sections.items[self.text_section_index.?];
838 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
839
840 // We use these to indicate our intention to update metadata, placing the new block,
841 // and possibly removing a free list node.
842 // It would be simpler to do it inside the for loop below, but that would cause a
843 // problem if an error was returned later in the function. So this action
844 // is actually carried out at the end of the function, when errors are no longer possible.
845 var block_placement: ?*TextBlock = null;
846 var free_list_removal: ?usize = null;
847
848 // First we look for an appropriately sized free list node.
849 // The list is unordered. We'll just take the first thing that works.
850 const vaddr = blk: {
851 var i: usize = 0;
852 while (i < self.text_block_free_list.items.len) {
853 const big_block = self.text_block_free_list.items[i];
854 // We now have a pointer to a live text block that has too much capacity.
855 // Is it enough that we could fit this new text block?
856 const sym = self.local_symbols.items[big_block.local_sym_index];
857 const capacity = big_block.capacity(self.*);
858 const ideal_capacity = capacity * alloc_num / alloc_den;
859 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
860 const capacity_end_vaddr = sym.st_value + capacity;
861 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
862 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
863 if (new_start_vaddr < ideal_capacity_end_vaddr) {
864 // Additional bookkeeping here to notice if this free list node
865 // should be deleted because the block that it points to has grown to take up
866 // more of the extra capacity.
867 if (!big_block.freeListEligible(self.*)) {
993 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
994 var already_have_free_list_node = false;
995 {
996 var i: usize = 0;
997 while (i < self.text_block_free_list.items.len) {
998 if (self.text_block_free_list.items[i] == text_block) {
868999 _ = self.text_block_free_list.swapRemove(i);
869 } else {
870 i += 1;
1000 continue;
8711001 }
872 continue;
873 }
874 // At this point we know that we will place the new block here. But the
875 // remaining question is whether there is still yet enough capacity left
876 // over for there to still be a free list node.
877 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
878 const keep_free_list_node = remaining_capacity >= min_text_capacity;
879
880 // Set up the metadata to be updated, after errors are no longer possible.
881 block_placement = big_block;
882 if (!keep_free_list_node) {
883 free_list_removal = i;
1002 if (self.text_block_free_list.items[i] == text_block.prev) {
1003 already_have_free_list_node = true;
1004 }
1005 i += 1;
8841006 }
885 break :blk new_start_vaddr;
886 } else if (self.last_text_block) |last| {
887 const sym = self.local_symbols.items[last.local_sym_index];
888 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
889 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
890 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
891 // Set up the metadata to be updated, after errors are no longer possible.
892 block_placement = last;
893 break :blk new_start_vaddr;
894 } else {
895 break :blk phdr.p_vaddr;
8961007 }
897 };
8981008
899 const expand_text_section = block_placement == null or block_placement.?.next == null;
900 if (expand_text_section) {
901 const text_capacity = self.allocatedSize(shdr.sh_offset);
902 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
903 if (needed_size > text_capacity) {
904 // Must move the entire text section.
905 const new_offset = self.findFreeSpace(needed_size, 0x1000);
906 const text_size = if (self.last_text_block) |last| blk: {
907 const sym = self.local_symbols.items[last.local_sym_index];
908 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
909 } else 0;
910 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
911 if (amt != text_size) return error.InputOutput;
912 shdr.sh_offset = new_offset;
913 phdr.p_offset = new_offset;
1009 if (self.last_text_block == text_block) {
1010 // TODO shrink the .text section size here
1011 self.last_text_block = text_block.prev;
9141012 }
915 self.last_text_block = text_block;
9161013
917 shdr.sh_size = needed_size;
918 phdr.p_memsz = needed_size;
919 phdr.p_filesz = needed_size;
1014 if (text_block.prev) |prev| {
1015 prev.next = text_block.next;
9201016
921 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
922 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
923 }
1017 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
1018 // The free list is heuristics, it doesn't have to be perfect, so we can
1019 // ignore the OOM here.
1020 self.text_block_free_list.append(self.allocator, prev) catch {};
1021 }
1022 } else {
1023 text_block.prev = null;
1024 }
9241025
925 // This function can also reallocate a text block.
926 // In this case we need to "unplug" it from its previous location before
927 // plugging it in to its new location.
928 if (text_block.prev) |prev| {
929 prev.next = text_block.next;
930 }
931 if (text_block.next) |next| {
932 next.prev = text_block.prev;
1026 if (text_block.next) |next| {
1027 next.prev = text_block.prev;
1028 } else {
1029 text_block.next = null;
1030 }
9331031 }
9341032
935 if (block_placement) |big_block| {
936 text_block.prev = big_block;
937 text_block.next = big_block.next;
938 big_block.next = text_block;
939 } else {
940 text_block.prev = null;
941 text_block.next = null;
1033 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
1034 // TODO check the new capacity, and if it crosses the size threshold into a big enough
1035 // capacity, insert a free list node for it.
9421036 }
943 if (free_list_removal) |i| {
944 _ = self.text_block_free_list.swapRemove(i);
945 }
946 return vaddr;
947 }
9481037
949 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
950 if (decl.link.local_sym_index != 0) return;
951
952 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
953 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
954 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
955 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
956 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
957
958 if (self.local_symbol_free_list.popOrNull()) |i| {
959 //std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
960 decl.link.local_sym_index = i;
961 } else {
962 //std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
963 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
964 _ = self.local_symbols.addOneAssumeCapacity();
1038 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1039 const sym = self.local_symbols.items[text_block.local_sym_index];
1040 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
1041 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
1042 if (!need_realloc) return sym.st_value;
1043 return self.allocateTextBlock(text_block, new_block_size, alignment);
9651044 }
9661045
967 if (self.offset_table_free_list.popOrNull()) |i| {
968 decl.link.offset_table_index = i;
969 } else {
970 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
971 _ = self.offset_table.addOneAssumeCapacity();
972 self.offset_table_count_dirty = true;
973 }
1046 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1047 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1048 const shdr = &self.sections.items[self.text_section_index.?];
1049 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
1050
1051 // We use these to indicate our intention to update metadata, placing the new block,
1052 // and possibly removing a free list node.
1053 // It would be simpler to do it inside the for loop below, but that would cause a
1054 // problem if an error was returned later in the function. So this action
1055 // is actually carried out at the end of the function, when errors are no longer possible.
1056 var block_placement: ?*TextBlock = null;
1057 var free_list_removal: ?usize = null;
1058
1059 // First we look for an appropriately sized free list node.
1060 // The list is unordered. We'll just take the first thing that works.
1061 const vaddr = blk: {
1062 var i: usize = 0;
1063 while (i < self.text_block_free_list.items.len) {
1064 const big_block = self.text_block_free_list.items[i];
1065 // We now have a pointer to a live text block that has too much capacity.
1066 // Is it enough that we could fit this new text block?
1067 const sym = self.local_symbols.items[big_block.local_sym_index];
1068 const capacity = big_block.capacity(self.*);
1069 const ideal_capacity = capacity * alloc_num / alloc_den;
1070 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1071 const capacity_end_vaddr = sym.st_value + capacity;
1072 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1073 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1074 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1075 // Additional bookkeeping here to notice if this free list node
1076 // should be deleted because the block that it points to has grown to take up
1077 // more of the extra capacity.
1078 if (!big_block.freeListEligible(self.*)) {
1079 _ = self.text_block_free_list.swapRemove(i);
1080 } else {
1081 i += 1;
1082 }
1083 continue;
1084 }
1085 // At this point we know that we will place the new block here. But the
1086 // remaining question is whether there is still yet enough capacity left
1087 // over for there to still be a free list node.
1088 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1089 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1090
1091 // Set up the metadata to be updated, after errors are no longer possible.
1092 block_placement = big_block;
1093 if (!keep_free_list_node) {
1094 free_list_removal = i;
1095 }
1096 break :blk new_start_vaddr;
1097 } else if (self.last_text_block) |last| {
1098 const sym = self.local_symbols.items[last.local_sym_index];
1099 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
1100 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1101 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
1102 // Set up the metadata to be updated, after errors are no longer possible.
1103 block_placement = last;
1104 break :blk new_start_vaddr;
1105 } else {
1106 break :blk phdr.p_vaddr;
1107 }
1108 };
9741109
975 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1110 const expand_text_section = block_placement == null or block_placement.?.next == null;
1111 if (expand_text_section) {
1112 const text_capacity = self.allocatedSize(shdr.sh_offset);
1113 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
1114 if (needed_size > text_capacity) {
1115 // Must move the entire text section.
1116 const new_offset = self.findFreeSpace(needed_size, 0x1000);
1117 const text_size = if (self.last_text_block) |last| blk: {
1118 const sym = self.local_symbols.items[last.local_sym_index];
1119 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1120 } else 0;
1121 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
1122 if (amt != text_size) return error.InputOutput;
1123 shdr.sh_offset = new_offset;
1124 phdr.p_offset = new_offset;
1125 }
1126 self.last_text_block = text_block;
9761127
977 self.local_symbols.items[decl.link.local_sym_index] = .{
978 .st_name = 0,
979 .st_info = 0,
980 .st_other = 0,
981 .st_shndx = 0,
982 .st_value = phdr.p_vaddr,
983 .st_size = 0,
984 };
985 self.offset_table.items[decl.link.offset_table_index] = 0;
986 }
1128 shdr.sh_size = needed_size;
1129 phdr.p_memsz = needed_size;
1130 phdr.p_filesz = needed_size;
9871131
988 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {
989 self.freeTextBlock(&decl.link);
990 if (decl.link.local_sym_index != 0) {
991 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
992 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
1132 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1133 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1134 }
9931135
994 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1136 // This function can also reallocate a text block.
1137 // In this case we need to "unplug" it from its previous location before
1138 // plugging it in to its new location.
1139 if (text_block.prev) |prev| {
1140 prev.next = text_block.next;
1141 }
1142 if (text_block.next) |next| {
1143 next.prev = text_block.prev;
1144 }
9951145
996 decl.link.local_sym_index = 0;
1146 if (block_placement) |big_block| {
1147 text_block.prev = big_block;
1148 text_block.next = big_block.next;
1149 big_block.next = text_block;
1150 } else {
1151 text_block.prev = null;
1152 text_block.next = null;
1153 }
1154 if (free_list_removal) |i| {
1155 _ = self.text_block_free_list.swapRemove(i);
1156 }
1157 return vaddr;
9971158 }
998 }
9991159
1000 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {
1001 var code_buffer = std.ArrayList(u8).init(self.allocator);
1002 defer code_buffer.deinit();
1003
1004 const typed_value = decl.typed_value.most_recent.typed_value;
1005 const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
1006 .externally_managed => |x| x,
1007 .appended => code_buffer.items,
1008 .fail => |em| {
1009 decl.analysis = .codegen_failure;
1010 _ = try module.failed_decls.put(decl, em);
1011 return;
1012 },
1013 };
1160 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1161 if (decl.link.local_sym_index != 0) return;
10141162
1015 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1163 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1164 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1165 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1166 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1167 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
10161168
1017 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1018 .Fn => elf.STT_FUNC,
1019 else => elf.STT_OBJECT,
1020 };
1169 if (self.local_symbol_free_list.popOrNull()) |i| {
1170 std.log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
1171 decl.link.local_sym_index = i;
1172 } else {
1173 std.log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1174 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1175 _ = self.local_symbols.addOneAssumeCapacity();
1176 }
10211177
1022 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1023 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1024 if (local_sym.st_size != 0) {
1025 const capacity = decl.link.capacity(self.*);
1026 const need_realloc = code.len > capacity or
1027 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1028 if (need_realloc) {
1029 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1030 //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1031 if (vaddr != local_sym.st_value) {
1032 local_sym.st_value = vaddr;
1033
1034 //std.debug.warn(" (writing new offset table entry)\n", .{});
1035 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1036 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1037 }
1038 } else if (code.len < local_sym.st_size) {
1039 self.shrinkTextBlock(&decl.link, code.len);
1178 if (self.offset_table_free_list.popOrNull()) |i| {
1179 decl.link.offset_table_index = i;
1180 } else {
1181 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1182 _ = self.offset_table.addOneAssumeCapacity();
1183 self.offset_table_count_dirty = true;
10401184 }
1041 local_sym.st_size = code.len;
1042 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1043 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1044 local_sym.st_other = 0;
1045 local_sym.st_shndx = self.text_section_index.?;
1046 // TODO this write could be avoided if no fields of the symbol were changed.
1047 try self.writeSymbol(decl.link.local_sym_index);
1048 } else {
1049 const decl_name = mem.spanZ(decl.name);
1050 const name_str_index = try self.makeString(decl_name);
1051 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1052 //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1053 errdefer self.freeTextBlock(&decl.link);
1054
1055 local_sym.* = .{
1056 .st_name = name_str_index,
1057 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1185
1186 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1187
1188 self.local_symbols.items[decl.link.local_sym_index] = .{
1189 .st_name = 0,
1190 .st_info = 0,
10581191 .st_other = 0,
1059 .st_shndx = self.text_section_index.?,
1060 .st_value = vaddr,
1061 .st_size = code.len,
1192 .st_shndx = 0,
1193 .st_value = phdr.p_vaddr,
1194 .st_size = 0,
10621195 };
1063 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1064
1065 try self.writeSymbol(decl.link.local_sym_index);
1066 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1196 self.offset_table.items[decl.link.offset_table_index] = 0;
10671197 }
10681198
1069 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1070 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1071 try self.file.?.pwriteAll(code, file_offset);
1199 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1200 self.freeTextBlock(&decl.link);
1201 if (decl.link.local_sym_index != 0) {
1202 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1203 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
10721204
1073 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1074 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};
1075 return self.updateDeclExports(module, decl, decl_exports);
1076 }
1205 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
10771206
1078 /// Must be called only after a successful call to `updateDecl`.
1079 pub fn updateDeclExports(
1080 self: *ElfFile,
1081 module: *Module,
1082 decl: *const Module.Decl,
1083 exports: []const *Module.Export,
1084 ) !void {
1085 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1086 // them, so that deleting exports is guaranteed to succeed.
1087 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1088 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1089 const typed_value = decl.typed_value.most_recent.typed_value;
1090 if (decl.link.local_sym_index == 0) return;
1091 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1092
1093 for (exports) |exp| {
1094 if (exp.options.section) |section_name| {
1095 if (!mem.eql(u8, section_name, ".text")) {
1096 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
1097 module.failed_exports.putAssumeCapacityNoClobber(
1098 exp,
1099 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1100 );
1101 continue;
1102 }
1207 decl.link.local_sym_index = 0;
11031208 }
1104 const stb_bits: u8 = switch (exp.options.linkage) {
1105 .Internal => elf.STB_LOCAL,
1106 .Strong => blk: {
1107 if (mem.eql(u8, exp.options.name, "_start")) {
1108 self.entry_addr = decl_sym.st_value;
1109 }
1110 break :blk elf.STB_GLOBAL;
1111 },
1112 .Weak => elf.STB_WEAK,
1113 .LinkOnce => {
1114 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
1115 module.failed_exports.putAssumeCapacityNoClobber(
1116 exp,
1117 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1118 );
1119 continue;
1209 }
1210
1211 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1212 var code_buffer = std.ArrayList(u8).init(self.allocator);
1213 defer code_buffer.deinit();
1214
1215 const typed_value = decl.typed_value.most_recent.typed_value;
1216 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1217 .externally_managed => |x| x,
1218 .appended => code_buffer.items,
1219 .fail => |em| {
1220 decl.analysis = .codegen_failure;
1221 try module.failed_decls.put(module.gpa, decl, em);
1222 return;
11201223 },
11211224 };
1122 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1123 if (exp.link.sym_index) |i| {
1124 const sym = &self.global_symbols.items[i];
1125 sym.* = .{
1126 .st_name = try self.updateString(sym.st_name, exp.options.name),
1127 .st_info = (stb_bits << 4) | stt_bits,
1128 .st_other = 0,
1129 .st_shndx = self.text_section_index.?,
1130 .st_value = decl_sym.st_value,
1131 .st_size = decl_sym.st_size,
1132 };
1225
1226 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1227
1228 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1229 .Fn => elf.STT_FUNC,
1230 else => elf.STT_OBJECT,
1231 };
1232
1233 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1234 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1235 if (local_sym.st_size != 0) {
1236 const capacity = decl.link.capacity(self.*);
1237 const need_realloc = code.len > capacity or
1238 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1239 if (need_realloc) {
1240 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1241 std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1242 if (vaddr != local_sym.st_value) {
1243 local_sym.st_value = vaddr;
1244
1245 std.log.debug(.link, " (writing new offset table entry)\n", .{});
1246 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1247 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1248 }
1249 } else if (code.len < local_sym.st_size) {
1250 self.shrinkTextBlock(&decl.link, code.len);
1251 }
1252 local_sym.st_size = code.len;
1253 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1254 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1255 local_sym.st_other = 0;
1256 local_sym.st_shndx = self.text_section_index.?;
1257 // TODO this write could be avoided if no fields of the symbol were changed.
1258 try self.writeSymbol(decl.link.local_sym_index);
11331259 } else {
1134 const name = try self.makeString(exp.options.name);
1135 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1136 _ = self.global_symbols.addOneAssumeCapacity();
1137 break :blk self.global_symbols.items.len - 1;
1138 };
1139 self.global_symbols.items[i] = .{
1140 .st_name = name,
1141 .st_info = (stb_bits << 4) | stt_bits,
1260 const decl_name = mem.spanZ(decl.name);
1261 const name_str_index = try self.makeString(decl_name);
1262 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1263 std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1264 errdefer self.freeTextBlock(&decl.link);
1265
1266 local_sym.* = .{
1267 .st_name = name_str_index,
1268 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
11421269 .st_other = 0,
11431270 .st_shndx = self.text_section_index.?,
1144 .st_value = decl_sym.st_value,
1145 .st_size = decl_sym.st_size,
1271 .st_value = vaddr,
1272 .st_size = code.len,
11461273 };
1274 self.offset_table.items[decl.link.offset_table_index] = vaddr;
11471275
1148 exp.link.sym_index = @intCast(u32, i);
1276 try self.writeSymbol(decl.link.local_sym_index);
1277 try self.writeOffsetTableEntry(decl.link.offset_table_index);
11491278 }
1150 }
1151 }
11521279
1153 pub fn deleteExport(self: *ElfFile, exp: Export) void {
1154 const sym_index = exp.sym_index orelse return;
1155 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1156 self.global_symbols.items[sym_index].st_info = 0;
1157 }
1280 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1281 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1282 try self.file.?.pwriteAll(code, file_offset);
11581283
1159 fn writeProgHeader(self: *ElfFile, index: usize) !void {
1160 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1161 const offset = self.program_headers.items[index].p_offset;
1162 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1163 32 => {
1164 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1165 if (foreign_endian) {
1166 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1167 }
1168 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1169 },
1170 64 => {
1171 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1172 if (foreign_endian) {
1173 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1174 }
1175 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1176 },
1177 else => return error.UnsupportedArchitecture,
1284 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1285 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1286 return self.updateDeclExports(module, decl, decl_exports);
11781287 }
1179 }
11801288
1181 fn writeSectHeader(self: *ElfFile, index: usize) !void {
1182 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1183 const offset = self.sections.items[index].sh_offset;
1184 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1185 32 => {
1186 var shdr: [1]elf.Elf32_Shdr = undefined;
1187 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1188 if (foreign_endian) {
1189 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1190 }
1191 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1192 },
1193 64 => {
1194 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1195 if (foreign_endian) {
1196 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1289 /// Must be called only after a successful call to `updateDecl`.
1290 pub fn updateDeclExports(
1291 self: *Elf,
1292 module: *Module,
1293 decl: *const Module.Decl,
1294 exports: []const *Module.Export,
1295 ) !void {
1296 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1297 // them, so that deleting exports is guaranteed to succeed.
1298 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1299 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1300 const typed_value = decl.typed_value.most_recent.typed_value;
1301 if (decl.link.local_sym_index == 0) return;
1302 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1303
1304 for (exports) |exp| {
1305 if (exp.options.section) |section_name| {
1306 if (!mem.eql(u8, section_name, ".text")) {
1307 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1308 module.failed_exports.putAssumeCapacityNoClobber(
1309 exp,
1310 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1311 );
1312 continue;
1313 }
11971314 }
1198 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1199 },
1200 else => return error.UnsupportedArchitecture,
1201 }
1202 }
1315 const stb_bits: u8 = switch (exp.options.linkage) {
1316 .Internal => elf.STB_LOCAL,
1317 .Strong => blk: {
1318 if (mem.eql(u8, exp.options.name, "_start")) {
1319 self.entry_addr = decl_sym.st_value;
1320 }
1321 break :blk elf.STB_GLOBAL;
1322 },
1323 .Weak => elf.STB_WEAK,
1324 .LinkOnce => {
1325 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1326 module.failed_exports.putAssumeCapacityNoClobber(
1327 exp,
1328 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1329 );
1330 continue;
1331 },
1332 };
1333 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1334 if (exp.link.sym_index) |i| {
1335 const sym = &self.global_symbols.items[i];
1336 sym.* = .{
1337 .st_name = try self.updateString(sym.st_name, exp.options.name),
1338 .st_info = (stb_bits << 4) | stt_bits,
1339 .st_other = 0,
1340 .st_shndx = self.text_section_index.?,
1341 .st_value = decl_sym.st_value,
1342 .st_size = decl_sym.st_size,
1343 };
1344 } else {
1345 const name = try self.makeString(exp.options.name);
1346 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1347 _ = self.global_symbols.addOneAssumeCapacity();
1348 break :blk self.global_symbols.items.len - 1;
1349 };
1350 self.global_symbols.items[i] = .{
1351 .st_name = name,
1352 .st_info = (stb_bits << 4) | stt_bits,
1353 .st_other = 0,
1354 .st_shndx = self.text_section_index.?,
1355 .st_value = decl_sym.st_value,
1356 .st_size = decl_sym.st_size,
1357 };
12031358
1204 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
1205 const shdr = &self.sections.items[self.got_section_index.?];
1206 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1207 const entry_size: u16 = switch (self.ptr_width) {
1208 .p32 => 4,
1209 .p64 => 8,
1210 };
1211 if (self.offset_table_count_dirty) {
1212 // TODO Also detect virtual address collisions.
1213 const allocated_size = self.allocatedSize(shdr.sh_offset);
1214 const needed_size = self.local_symbols.items.len * entry_size;
1215 if (needed_size > allocated_size) {
1216 // Must move the entire got section.
1217 const new_offset = self.findFreeSpace(needed_size, entry_size);
1218 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1219 if (amt != shdr.sh_size) return error.InputOutput;
1220 shdr.sh_offset = new_offset;
1221 phdr.p_offset = new_offset;
1359 exp.link.sym_index = @intCast(u32, i);
1360 }
12221361 }
1223 shdr.sh_size = needed_size;
1224 phdr.p_memsz = needed_size;
1225 phdr.p_filesz = needed_size;
1362 }
12261363
1227 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1228 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1364 pub fn deleteExport(self: *Elf, exp: Export) void {
1365 const sym_index = exp.sym_index orelse return;
1366 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1367 self.global_symbols.items[sym_index].st_info = 0;
1368 }
12291369
1230 self.offset_table_count_dirty = false;
1370 fn writeProgHeader(self: *Elf, index: usize) !void {
1371 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1372 const offset = self.program_headers.items[index].p_offset;
1373 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1374 32 => {
1375 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1376 if (foreign_endian) {
1377 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1378 }
1379 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1380 },
1381 64 => {
1382 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1383 if (foreign_endian) {
1384 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1385 }
1386 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1387 },
1388 else => return error.UnsupportedArchitecture,
1389 }
12311390 }
1232 const endian = self.options.target.cpu.arch.endian();
1233 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1234 switch (self.ptr_width) {
1235 .p32 => {
1236 var buf: [4]u8 = undefined;
1237 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1238 try self.file.?.pwriteAll(&buf, off);
1239 },
1240 .p64 => {
1241 var buf: [8]u8 = undefined;
1242 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1243 try self.file.?.pwriteAll(&buf, off);
1244 },
1391
1392 fn writeSectHeader(self: *Elf, index: usize) !void {
1393 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1394 const offset = self.sections.items[index].sh_offset;
1395 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1396 32 => {
1397 var shdr: [1]elf.Elf32_Shdr = undefined;
1398 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1399 if (foreign_endian) {
1400 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1401 }
1402 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1403 },
1404 64 => {
1405 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1406 if (foreign_endian) {
1407 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1408 }
1409 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1410 },
1411 else => return error.UnsupportedArchitecture,
1412 }
12451413 }
1246 }
12471414
1248 fn writeSymbol(self: *ElfFile, index: usize) !void {
1249 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1250 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1251 // due to running out of space.
1252 if (self.local_symbols.items.len != syms_sect.sh_info) {
1253 const sym_size: u64 = switch (self.ptr_width) {
1254 .p32 => @sizeOf(elf.Elf32_Sym),
1255 .p64 => @sizeOf(elf.Elf64_Sym),
1256 };
1257 const sym_align: u16 = switch (self.ptr_width) {
1258 .p32 => @alignOf(elf.Elf32_Sym),
1259 .p64 => @alignOf(elf.Elf64_Sym),
1415 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
1416 const shdr = &self.sections.items[self.got_section_index.?];
1417 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1418 const entry_size: u16 = switch (self.ptr_width) {
1419 .p32 => 4,
1420 .p64 => 8,
12601421 };
1261 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1262 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1263 // Move all the symbols to a new file location.
1264 const new_offset = self.findFreeSpace(needed_size, sym_align);
1265 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1266 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1267 if (amt != existing_size) return error.InputOutput;
1268 syms_sect.sh_offset = new_offset;
1422 if (self.offset_table_count_dirty) {
1423 // TODO Also detect virtual address collisions.
1424 const allocated_size = self.allocatedSize(shdr.sh_offset);
1425 const needed_size = self.local_symbols.items.len * entry_size;
1426 if (needed_size > allocated_size) {
1427 // Must move the entire got section.
1428 const new_offset = self.findFreeSpace(needed_size, entry_size);
1429 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1430 if (amt != shdr.sh_size) return error.InputOutput;
1431 shdr.sh_offset = new_offset;
1432 phdr.p_offset = new_offset;
1433 }
1434 shdr.sh_size = needed_size;
1435 phdr.p_memsz = needed_size;
1436 phdr.p_filesz = needed_size;
1437
1438 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1439 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1440
1441 self.offset_table_count_dirty = false;
1442 }
1443 const endian = self.options.target.cpu.arch.endian();
1444 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1445 switch (self.ptr_width) {
1446 .p32 => {
1447 var buf: [4]u8 = undefined;
1448 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1449 try self.file.?.pwriteAll(&buf, off);
1450 },
1451 .p64 => {
1452 var buf: [8]u8 = undefined;
1453 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1454 try self.file.?.pwriteAll(&buf, off);
1455 },
12691456 }
1270 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1271 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1272 self.shdr_table_dirty = true; // TODO look into only writing one section
12731457 }
1274 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1275 switch (self.ptr_width) {
1276 .p32 => {
1277 var sym = [1]elf.Elf32_Sym{
1278 .{
1279 .st_name = self.local_symbols.items[index].st_name,
1280 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1281 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1282 .st_info = self.local_symbols.items[index].st_info,
1283 .st_other = self.local_symbols.items[index].st_other,
1284 .st_shndx = self.local_symbols.items[index].st_shndx,
1285 },
1458
1459 fn writeSymbol(self: *Elf, index: usize) !void {
1460 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1461 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1462 // due to running out of space.
1463 if (self.local_symbols.items.len != syms_sect.sh_info) {
1464 const sym_size: u64 = switch (self.ptr_width) {
1465 .p32 => @sizeOf(elf.Elf32_Sym),
1466 .p64 => @sizeOf(elf.Elf64_Sym),
12861467 };
1287 if (foreign_endian) {
1288 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1289 }
1290 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1291 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1292 },
1293 .p64 => {
1294 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1295 if (foreign_endian) {
1296 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1468 const sym_align: u16 = switch (self.ptr_width) {
1469 .p32 => @alignOf(elf.Elf32_Sym),
1470 .p64 => @alignOf(elf.Elf64_Sym),
1471 };
1472 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1473 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1474 // Move all the symbols to a new file location.
1475 const new_offset = self.findFreeSpace(needed_size, sym_align);
1476 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1477 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1478 if (amt != existing_size) return error.InputOutput;
1479 syms_sect.sh_offset = new_offset;
12971480 }
1298 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1299 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1300 },
1301 }
1302 }
1303
1304 fn writeAllGlobalSymbols(self: *ElfFile) !void {
1305 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1306 const sym_size: u64 = switch (self.ptr_width) {
1307 .p32 => @sizeOf(elf.Elf32_Sym),
1308 .p64 => @sizeOf(elf.Elf64_Sym),
1309 };
1310 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
1311 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1312 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1313 switch (self.ptr_width) {
1314 .p32 => {
1315 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1316 defer self.allocator.free(buf);
1317
1318 for (buf) |*sym, i| {
1319 sym.* = .{
1320 .st_name = self.global_symbols.items[i].st_name,
1321 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1322 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1323 .st_info = self.global_symbols.items[i].st_info,
1324 .st_other = self.global_symbols.items[i].st_other,
1325 .st_shndx = self.global_symbols.items[i].st_shndx,
1481 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1482 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1483 self.shdr_table_dirty = true; // TODO look into only writing one section
1484 }
1485 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1486 switch (self.ptr_width) {
1487 .p32 => {
1488 var sym = [1]elf.Elf32_Sym{
1489 .{
1490 .st_name = self.local_symbols.items[index].st_name,
1491 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1492 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1493 .st_info = self.local_symbols.items[index].st_info,
1494 .st_other = self.local_symbols.items[index].st_other,
1495 .st_shndx = self.local_symbols.items[index].st_shndx,
1496 },
13261497 };
13271498 if (foreign_endian) {
1328 bswapAllFields(elf.Elf32_Sym, sym);
1499 bswapAllFields(elf.Elf32_Sym, &sym[0]);
13291500 }
1330 }
1331 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1332 },
1333 .p64 => {
1334 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1335 defer self.allocator.free(buf);
1336
1337 for (buf) |*sym, i| {
1338 sym.* = .{
1339 .st_name = self.global_symbols.items[i].st_name,
1340 .st_value = self.global_symbols.items[i].st_value,
1341 .st_size = self.global_symbols.items[i].st_size,
1342 .st_info = self.global_symbols.items[i].st_info,
1343 .st_other = self.global_symbols.items[i].st_other,
1344 .st_shndx = self.global_symbols.items[i].st_shndx,
1345 };
1501 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1502 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1503 },
1504 .p64 => {
1505 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
13461506 if (foreign_endian) {
1347 bswapAllFields(elf.Elf64_Sym, sym);
1507 bswapAllFields(elf.Elf64_Sym, &sym[0]);
13481508 }
1349 }
1350 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1351 },
1509 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1510 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1511 },
1512 }
13521513 }
1353 }
1514
1515 fn writeAllGlobalSymbols(self: *Elf) !void {
1516 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1517 const sym_size: u64 = switch (self.ptr_width) {
1518 .p32 => @sizeOf(elf.Elf32_Sym),
1519 .p64 => @sizeOf(elf.Elf64_Sym),
1520 };
1521 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1522 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1523 switch (self.ptr_width) {
1524 .p32 => {
1525 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1526 defer self.allocator.free(buf);
1527
1528 for (buf) |*sym, i| {
1529 sym.* = .{
1530 .st_name = self.global_symbols.items[i].st_name,
1531 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1532 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1533 .st_info = self.global_symbols.items[i].st_info,
1534 .st_other = self.global_symbols.items[i].st_other,
1535 .st_shndx = self.global_symbols.items[i].st_shndx,
1536 };
1537 if (foreign_endian) {
1538 bswapAllFields(elf.Elf32_Sym, sym);
1539 }
1540 }
1541 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1542 },
1543 .p64 => {
1544 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1545 defer self.allocator.free(buf);
1546
1547 for (buf) |*sym, i| {
1548 sym.* = .{
1549 .st_name = self.global_symbols.items[i].st_name,
1550 .st_value = self.global_symbols.items[i].st_value,
1551 .st_size = self.global_symbols.items[i].st_size,
1552 .st_info = self.global_symbols.items[i].st_info,
1553 .st_other = self.global_symbols.items[i].st_other,
1554 .st_shndx = self.global_symbols.items[i].st_shndx,
1555 };
1556 if (foreign_endian) {
1557 bswapAllFields(elf.Elf64_Sym, sym);
1558 }
1559 }
1560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1561 },
1562 }
1563 }
1564 };
13541565};
13551566
13561567/// Truncates the existing file contents and overwrites the contents.
13571568/// Returns an error if `file` is not already open with +read +write +seek abilities.
1358pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1569pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
13591570 switch (options.output_mode) {
13601571 .Exe => {},
13611572 .Obj => {},
13621573 .Lib => return error.TODOImplementWritingLibFiles,
13631574 }
13641575 switch (options.object_format) {
1576 .c => unreachable,
13651577 .unknown => unreachable, // TODO remove this tag from the enum
13661578 .coff => return error.TODOImplementWritingCOFF,
13671579 .elf => {},
......@@ -1369,7 +1581,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
13691581 .wasm => return error.TODOImplementWritingWasmObjects,
13701582 }
13711583
1372 var self: ElfFile = .{
1584 var self: File.Elf = .{
13731585 .allocator = allocator,
13741586 .file = file,
13751587 .options = options,
......@@ -1413,7 +1625,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
14131625}
14141626
14151627/// Returns error.IncrFailed if incremental update could not be performed.
1416fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {
1628fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
14171629 switch (options.output_mode) {
14181630 .Exe => {},
14191631 .Obj => {},
......@@ -1421,12 +1633,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
14211633 }
14221634 switch (options.object_format) {
14231635 .unknown => unreachable, // TODO remove this tag from the enum
1636 .c => unreachable,
14241637 .coff => return error.IncrFailed,
14251638 .elf => {},
14261639 .macho => return error.IncrFailed,
14271640 .wasm => return error.IncrFailed,
14281641 }
1429 var self: ElfFile = .{
1642 var self: File.Elf = .{
14301643 .allocator = allocator,
14311644 .file = file,
14321645 .owns_file_handle = false,
......@@ -1446,7 +1659,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
14461659}
14471660
14481661/// Saturating multiplication
1449fn satMul(a: var, b: var) @TypeOf(a, b) {
1662fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
14501663 const T = @TypeOf(a, b);
14511664 return std.math.mul(T, a, b) catch std.math.maxInt(T);
14521665}
src-self-hosted/liveness.zig created+158
......@@ -0,0 +1,158 @@
1const std = @import("std");
2const ir = @import("ir.zig");
3const trace = @import("tracy.zig").trace;
4
5/// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated.
6pub fn analyze(
7 /// Used for temporary storage during the analysis.
8 gpa: *std.mem.Allocator,
9 /// Used to tack on extra allocations in the same lifetime as the existing instructions.
10 arena: *std.mem.Allocator,
11 body: ir.Body,
12) error{OutOfMemory}!void {
13 const tracy = trace(@src());
14 defer tracy.end();
15
16 var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
17 defer table.deinit();
18 try table.ensureCapacity(body.instructions.len);
19 try analyzeWithTable(arena, &table, body);
20}
21
22fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), body: ir.Body) error{OutOfMemory}!void {
23 var i: usize = body.instructions.len;
24
25 while (i != 0) {
26 i -= 1;
27 const base = body.instructions[i];
28 try analyzeInstGeneric(arena, table, base);
29 }
30}
31
32fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 // Obtain the corresponding instruction type based on the tag type.
34 inline for (std.meta.declarations(ir.Inst)) |decl| {
35 switch (decl.data) {
36 .Type => |T| {
37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
38 if (T.base_tag == base.tag) {
39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
40 }
41 }
42 },
43 else => {},
44 }
45 }
46 unreachable;
47}
48
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
50 if (table.contains(&inst.base)) {
51 inst.base.deaths = 0;
52 } else {
53 // No tombstone for this instruction means it is never referenced,
54 // and its birth marks its own death. Very metal 🤘
55 inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;
56 }
57
58 switch (T) {
59 ir.Inst.Constant => return,
60 ir.Inst.Block => {
61 try analyzeWithTable(arena, table, inst.args.body);
62 // We let this continue so that it can possibly mark the block as
63 // unreferenced below.
64 },
65 ir.Inst.CondBr => {
66 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
67 defer true_table.deinit();
68 try true_table.ensureCapacity(inst.args.true_body.instructions.len);
69 try analyzeWithTable(arena, &true_table, inst.args.true_body);
70
71 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
72 defer false_table.deinit();
73 try false_table.ensureCapacity(inst.args.false_body.instructions.len);
74 try analyzeWithTable(arena, &false_table, inst.args.false_body);
75
76 // Each death that occurs inside one branch, but not the other, needs
77 // to be added as a death immediately upon entering the other branch.
78 // During the iteration of the table, we additionally propagate the
79 // deaths to the parent table.
80 var true_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
81 defer true_entry_deaths.deinit();
82 var false_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
83 defer false_entry_deaths.deinit();
84 {
85 var it = false_table.iterator();
86 while (it.next()) |entry| {
87 const false_death = entry.key;
88 if (!true_table.contains(false_death)) {
89 try true_entry_deaths.append(false_death);
90 // Here we are only adding to the parent table if the following iteration
91 // would miss it.
92 try table.putNoClobber(false_death, {});
93 }
94 }
95 }
96 {
97 var it = true_table.iterator();
98 while (it.next()) |entry| {
99 const true_death = entry.key;
100 try table.putNoClobber(true_death, {});
101 if (!false_table.contains(true_death)) {
102 try false_entry_deaths.append(true_death);
103 }
104 }
105 }
106 inst.true_death_count = std.math.cast(@TypeOf(inst.true_death_count), true_entry_deaths.items.len) catch return error.OutOfMemory;
107 inst.false_death_count = std.math.cast(@TypeOf(inst.false_death_count), false_entry_deaths.items.len) catch return error.OutOfMemory;
108 const allocated_slice = try arena.alloc(*ir.Inst, true_entry_deaths.items.len + false_entry_deaths.items.len);
109 inst.deaths = allocated_slice.ptr;
110
111 // Continue on with the instruction analysis. The following code will find the condition
112 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
113 // condition's lifetime ends immediately before entering any branch.
114 },
115 ir.Inst.Call => {
116 // Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
117 const needed_bits = 1 + inst.args.args.len;
118 if (needed_bits <= ir.Inst.deaths_bits) {
119 var bit_i: ir.Inst.DeathsBitIndex = 0;
120 {
121 const prev = try table.fetchPut(inst.args.func, {});
122 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
123 bit_i += 1;
124 }
125 for (inst.args.args) |arg| {
126 const prev = try table.fetchPut(arg, {});
127 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
128 bit_i += 1;
129 }
130 } else {
131 @panic("Handle liveness analysis for function calls with many parameters");
132 }
133 },
134 else => {},
135 }
136
137 const Args = ir.Inst.Args(T);
138 if (Args == void) {
139 return;
140 }
141
142 comptime var arg_index: usize = 0;
143 inline for (std.meta.fields(Args)) |field| {
144 if (field.field_type == *ir.Inst) {
145 if (arg_index >= 6) {
146 @compileError("out of bits to mark deaths of operands");
147 }
148 const prev = try table.fetchPut(@field(inst.args, field.name), {});
149 if (prev == null) {
150 // Death.
151 inst.base.deaths |= 1 << arg_index;
152 }
153 arg_index += 1;
154 }
155 }
156
157 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ inst.base.tag, inst.base.deaths });
158}
src-self-hosted/main.zig+176-99
......@@ -38,6 +38,32 @@ const usage =
3838 \\
3939;
4040
41pub fn log(
42 comptime level: std.log.Level,
43 comptime scope: @TypeOf(.EnumLiteral),
44 comptime format: []const u8,
45 args: anytype,
46) void {
47 if (@enumToInt(level) > @enumToInt(std.log.level))
48 return;
49
50 const scope_prefix = "(" ++ switch (scope) {
51 // Uncomment to hide logs
52 //.compiler,
53 .module,
54 .liveness,
55 .link,
56 => return,
57
58 else => @tagName(scope),
59 } ++ "): ";
60
61 const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
62
63 // Print the message to stderr, silently ignoring any errors
64 std.debug.print(prefix ++ format, args);
65}
66
4167pub fn main() !void {
4268 // TODO general purpose allocator in the zig std lib
4369 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
......@@ -48,7 +74,7 @@ pub fn main() !void {
4874 const args = try process.argsAlloc(arena);
4975
5076 if (args.len <= 1) {
51 std.debug.warn("expected command argument\n\n{}", .{usage});
77 std.debug.print("expected command argument\n\n{}", .{usage});
5278 process.exit(1);
5379 }
5480
......@@ -68,14 +94,14 @@ pub fn main() !void {
6894 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
6995 } else if (mem.eql(u8, cmd, "version")) {
7096 // Need to set up the build script to give the version as a comptime value.
71 std.debug.warn("TODO version command not implemented yet\n", .{});
97 std.debug.print("TODO version command not implemented yet\n", .{});
7298 return error.Unimplemented;
7399 } else if (mem.eql(u8, cmd, "zen")) {
74100 try io.getStdOut().writeAll(info_zen);
75101 } else if (mem.eql(u8, cmd, "help")) {
76102 try io.getStdOut().writeAll(usage);
77103 } else {
78 std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });
104 std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
79105 process.exit(1);
80106 }
81107}
......@@ -86,7 +112,7 @@ const usage_build_generic =
86112 \\ zig build-obj <options> [files]
87113 \\
88114 \\Supported file types:
89 \\ (planned) .zig Zig source code
115 \\ .zig Zig source code
90116 \\ .zir Zig Intermediate Representation code
91117 \\ (planned) .o ELF object file
92118 \\ (planned) .o MACH-O (macOS) object file
......@@ -169,6 +195,7 @@ fn buildOutputType(
169195 var target_arch_os_abi: []const u8 = "native";
170196 var target_mcpu: ?[]const u8 = null;
171197 var target_dynamic_linker: ?[]const u8 = null;
198 var object_format: ?std.builtin.ObjectFormat = null;
172199
173200 var system_libs = std.ArrayList([]const u8).init(gpa);
174201 defer system_libs.deinit();
......@@ -183,7 +210,7 @@ fn buildOutputType(
183210 process.exit(0);
184211 } else if (mem.eql(u8, arg, "--color")) {
185212 if (i + 1 >= args.len) {
186 std.debug.warn("expected [auto|on|off] after --color\n", .{});
213 std.debug.print("expected [auto|on|off] after --color\n", .{});
187214 process.exit(1);
188215 }
189216 i += 1;
......@@ -195,12 +222,12 @@ fn buildOutputType(
195222 } else if (mem.eql(u8, next_arg, "off")) {
196223 color = .Off;
197224 } else {
198 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
225 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
199226 process.exit(1);
200227 }
201228 } else if (mem.eql(u8, arg, "--mode")) {
202229 if (i + 1 >= args.len) {
203 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
230 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
204231 process.exit(1);
205232 }
206233 i += 1;
......@@ -214,52 +241,58 @@ fn buildOutputType(
214241 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
215242 build_mode = .ReleaseSmall;
216243 } else {
217 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
244 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
218245 process.exit(1);
219246 }
220247 } else if (mem.eql(u8, arg, "--name")) {
221248 if (i + 1 >= args.len) {
222 std.debug.warn("expected parameter after --name\n", .{});
249 std.debug.print("expected parameter after --name\n", .{});
223250 process.exit(1);
224251 }
225252 i += 1;
226253 provided_name = args[i];
227254 } else if (mem.eql(u8, arg, "--library")) {
228255 if (i + 1 >= args.len) {
229 std.debug.warn("expected parameter after --library\n", .{});
256 std.debug.print("expected parameter after --library\n", .{});
230257 process.exit(1);
231258 }
232259 i += 1;
233260 try system_libs.append(args[i]);
234261 } else if (mem.eql(u8, arg, "--version")) {
235262 if (i + 1 >= args.len) {
236 std.debug.warn("expected parameter after --version\n", .{});
263 std.debug.print("expected parameter after --version\n", .{});
237264 process.exit(1);
238265 }
239266 i += 1;
240267 version = std.builtin.Version.parse(args[i]) catch |err| {
241 std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
268 std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
242269 process.exit(1);
243270 };
244271 } else if (mem.eql(u8, arg, "-target")) {
245272 if (i + 1 >= args.len) {
246 std.debug.warn("expected parameter after -target\n", .{});
273 std.debug.print("expected parameter after -target\n", .{});
247274 process.exit(1);
248275 }
249276 i += 1;
250277 target_arch_os_abi = args[i];
251278 } else if (mem.eql(u8, arg, "-mcpu")) {
252279 if (i + 1 >= args.len) {
253 std.debug.warn("expected parameter after -mcpu\n", .{});
280 std.debug.print("expected parameter after -mcpu\n", .{});
254281 process.exit(1);
255282 }
256283 i += 1;
257284 target_mcpu = args[i];
285 } else if (mem.eql(u8, arg, "--c")) {
286 if (object_format) |old| {
287 std.debug.print("attempted to override object format {} with C\n", .{old});
288 process.exit(1);
289 }
290 object_format = .c;
258291 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
259292 target_mcpu = arg["-mcpu=".len..];
260293 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
261294 if (i + 1 >= args.len) {
262 std.debug.warn("expected parameter after --dynamic-linker\n", .{});
295 std.debug.print("expected parameter after --dynamic-linker\n", .{});
263296 process.exit(1);
264297 }
265298 i += 1;
......@@ -301,39 +334,39 @@ fn buildOutputType(
301334 } else if (mem.startsWith(u8, arg, "-l")) {
302335 try system_libs.append(arg[2..]);
303336 } else {
304 std.debug.warn("unrecognized parameter: '{}'", .{arg});
337 std.debug.print("unrecognized parameter: '{}'", .{arg});
305338 process.exit(1);
306339 }
307340 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
308 std.debug.warn("assembly files not supported yet", .{});
341 std.debug.print("assembly files not supported yet", .{});
309342 process.exit(1);
310343 } else if (mem.endsWith(u8, arg, ".o") or
311344 mem.endsWith(u8, arg, ".obj") or
312345 mem.endsWith(u8, arg, ".a") or
313346 mem.endsWith(u8, arg, ".lib"))
314347 {
315 std.debug.warn("object files and static libraries not supported yet", .{});
348 std.debug.print("object files and static libraries not supported yet", .{});
316349 process.exit(1);
317350 } else if (mem.endsWith(u8, arg, ".c") or
318351 mem.endsWith(u8, arg, ".cpp"))
319352 {
320 std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
353 std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
321354 process.exit(1);
322355 } else if (mem.endsWith(u8, arg, ".so") or
323356 mem.endsWith(u8, arg, ".dylib") or
324357 mem.endsWith(u8, arg, ".dll"))
325358 {
326 std.debug.warn("linking against dynamic libraries not yet supported", .{});
359 std.debug.print("linking against dynamic libraries not yet supported", .{});
327360 process.exit(1);
328361 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
329362 if (root_src_file) |other| {
330 std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });
363 std.debug.print("found another zig file '{}' after root source file '{}'", .{ arg, other });
331364 process.exit(1);
332365 } else {
333366 root_src_file = arg;
334367 }
335368 } else {
336 std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});
369 std.debug.print("unrecognized file extension of parameter '{}'", .{arg});
337370 }
338371 }
339372 }
......@@ -344,13 +377,13 @@ fn buildOutputType(
344377 var it = mem.split(basename, ".");
345378 break :blk it.next() orelse basename;
346379 } else {
347 std.debug.warn("--name [name] not provided and unable to infer\n", .{});
380 std.debug.print("--name [name] not provided and unable to infer\n", .{});
348381 process.exit(1);
349382 }
350383 };
351384
352385 if (system_libs.items.len != 0) {
353 std.debug.warn("linking against system libraries not yet supported", .{});
386 std.debug.print("linking against system libraries not yet supported", .{});
354387 process.exit(1);
355388 }
356389
......@@ -362,17 +395,17 @@ fn buildOutputType(
362395 .diagnostics = &diags,
363396 }) catch |err| switch (err) {
364397 error.UnknownCpuModel => {
365 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
398 std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
366399 diags.cpu_name.?,
367400 @tagName(diags.arch.?),
368401 });
369402 for (diags.arch.?.allCpuModels()) |cpu| {
370 std.debug.warn(" {}\n", .{cpu.name});
403 std.debug.print(" {}\n", .{cpu.name});
371404 }
372405 process.exit(1);
373406 },
374407 error.UnknownCpuFeature => {
375 std.debug.warn(
408 std.debug.print(
376409 \\Unknown CPU feature: '{}'
377410 \\Available CPU features for architecture '{}':
378411 \\
......@@ -381,47 +414,36 @@ fn buildOutputType(
381414 @tagName(diags.arch.?),
382415 });
383416 for (diags.arch.?.allFeaturesList()) |feature| {
384 std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
417 std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
385418 }
386419 process.exit(1);
387420 },
388421 else => |e| return e,
389422 };
390423
391 const object_format: ?std.builtin.ObjectFormat = null;
392424 var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
393425 if (target_info.cpu_detection_unimplemented) {
394426 // TODO We want to just use detected_info.target but implementing
395427 // CPU model & feature detection is todo so here we rely on LLVM.
396 std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
428 std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
397429 process.exit(1);
398430 }
399431
400432 const src_path = root_src_file orelse {
401 std.debug.warn("expected at least one file argument", .{});
433 std.debug.print("expected at least one file argument", .{});
402434 process.exit(1);
403435 };
404436
405437 const bin_path = switch (emit_bin) {
406438 .no => {
407 std.debug.warn("-fno-emit-bin not supported yet", .{});
439 std.debug.print("-fno-emit-bin not supported yet", .{});
408440 process.exit(1);
409441 },
410 .yes_default_path => switch (output_mode) {
411 .Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
412 .Lib => blk: {
413 const suffix = switch (link_mode orelse .Static) {
414 .Static => target_info.target.staticLibSuffix(),
415 .Dynamic => target_info.target.dynamicLibSuffix(),
416 };
417 break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{
418 target_info.target.libPrefix(),
419 root_name,
420 suffix,
421 });
422 },
423 .Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }),
424 },
442 .yes_default_path => if (object_format != null and object_format.? == .c)
443 try std.fmt.allocPrint(arena, "{}.c", .{root_name})
444 else
445 try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
446
425447 .yes => |p| p,
426448 };
427449
......@@ -450,6 +472,7 @@ fn buildOutputType(
450472 .link_mode = link_mode,
451473 .object_format = object_format,
452474 .optimize_mode = build_mode,
475 .keep_source_files_loaded = zir_out_path != null,
453476 });
454477 defer module.deinit();
455478
......@@ -487,20 +510,24 @@ fn buildOutputType(
487510}
488511
489512fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
513 var timer = try std.time.Timer.start();
490514 try module.update();
515 const update_nanos = timer.read();
491516
492517 var errors = try module.getAllErrorsAlloc();
493 defer errors.deinit(module.allocator);
518 defer errors.deinit(module.gpa);
494519
495520 if (errors.list.len != 0) {
496521 for (errors.list) |full_err_msg| {
497 std.debug.warn("{}:{}:{}: error: {}\n", .{
522 std.debug.print("{}:{}:{}: error: {}\n", .{
498523 full_err_msg.src_path,
499524 full_err_msg.line + 1,
500525 full_err_msg.column + 1,
501526 full_err_msg.msg,
502527 });
503528 }
529 } else {
530 std.log.info(.compiler, "Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
504531 }
505532
506533 if (zir_out_path) |zop| {
......@@ -546,8 +573,9 @@ const Fmt = struct {
546573 any_error: bool,
547574 color: Color,
548575 gpa: *Allocator,
576 out_buffer: std.ArrayList(u8),
549577
550 const SeenMap = std.BufSet;
578 const SeenMap = std.AutoHashMap(fs.File.INode, void);
551579};
552580
553581pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
......@@ -568,7 +596,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
568596 process.exit(0);
569597 } else if (mem.eql(u8, arg, "--color")) {
570598 if (i + 1 >= args.len) {
571 std.debug.warn("expected [auto|on|off] after --color\n", .{});
599 std.debug.print("expected [auto|on|off] after --color\n", .{});
572600 process.exit(1);
573601 }
574602 i += 1;
......@@ -580,7 +608,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
580608 } else if (mem.eql(u8, next_arg, "off")) {
581609 color = .Off;
582610 } else {
583 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
611 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
584612 process.exit(1);
585613 }
586614 } else if (mem.eql(u8, arg, "--stdin")) {
......@@ -588,7 +616,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
588616 } else if (mem.eql(u8, arg, "--check")) {
589617 check_flag = true;
590618 } else {
591 std.debug.warn("unrecognized parameter: '{}'", .{arg});
619 std.debug.print("unrecognized parameter: '{}'", .{arg});
592620 process.exit(1);
593621 }
594622 } else {
......@@ -599,7 +627,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
599627
600628 if (stdin_flag) {
601629 if (input_files.items.len != 0) {
602 std.debug.warn("cannot use --stdin with positional arguments\n", .{});
630 std.debug.print("cannot use --stdin with positional arguments\n", .{});
603631 process.exit(1);
604632 }
605633
......@@ -609,7 +637,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
609637 defer gpa.free(source_code);
610638
611639 const tree = std.zig.parse(gpa, source_code) catch |err| {
612 std.debug.warn("error parsing stdin: {}\n", .{err});
640 std.debug.print("error parsing stdin: {}\n", .{err});
613641 process.exit(1);
614642 };
615643 defer tree.deinit();
......@@ -632,7 +660,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
632660 }
633661
634662 if (input_files.items.len == 0) {
635 std.debug.warn("expected at least one source file argument\n", .{});
663 std.debug.print("expected at least one source file argument\n", .{});
636664 process.exit(1);
637665 }
638666
......@@ -641,10 +669,20 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
641669 .seen = Fmt.SeenMap.init(gpa),
642670 .any_error = false,
643671 .color = color,
672 .out_buffer = std.ArrayList(u8).init(gpa),
644673 };
674 defer fmt.seen.deinit();
675 defer fmt.out_buffer.deinit();
645676
646677 for (input_files.span()) |file_path| {
647 try fmtPath(&fmt, file_path, check_flag);
678 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
679 const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
680 std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
681 process.exit(1);
682 };
683 defer gpa.free(real_path);
684
685 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
648686 }
649687 if (fmt.any_error) {
650688 process.exit(1);
......@@ -670,48 +708,82 @@ const FmtError = error{
670708 ReadOnlyFileSystem,
671709 LinkQuotaExceeded,
672710 FileBusy,
711 EndOfStream,
673712} || fs.File.OpenError;
674713
675fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
676 // get the real path here to avoid Windows failing on relative file paths with . or .. in them
677 var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
678 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
679 fmt.any_error = true;
680 return;
681 };
682 defer fmt.gpa.free(real_path);
683
684 if (fmt.seen.exists(real_path)) return;
685 try fmt.seen.put(real_path);
686
687 const source_code = fs.cwd().readFileAlloc(fmt.gpa, real_path, max_src_size) catch |err| switch (err) {
688 error.IsDir, error.AccessDenied => {
689 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
690 defer dir.close();
691
692 var dir_it = dir.iterate();
693
694 while (try dir_it.next()) |entry| {
695 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
696 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
697 try fmtPath(fmt, full_path, check_mode);
698 }
699 }
700 return;
701 },
714fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
715 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
716 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
702717 else => {
703 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
718 std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
704719 fmt.any_error = true;
705720 return;
706721 },
707722 };
708 defer fmt.gpa.free(source_code);
723}
709724
710 const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
711 std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });
712 fmt.any_error = true;
713 return;
725fn fmtPathDir(
726 fmt: *Fmt,
727 file_path: []const u8,
728 check_mode: bool,
729 parent_dir: fs.Dir,
730 parent_sub_path: []const u8,
731) FmtError!void {
732 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
733 defer dir.close();
734
735 const stat = try dir.stat();
736 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
737
738 var dir_it = dir.iterate();
739 while (try dir_it.next()) |entry| {
740 const is_dir = entry.kind == .Directory;
741 if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
742 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
743 defer fmt.gpa.free(full_path);
744
745 if (is_dir) {
746 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
747 } else {
748 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
749 std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
750 fmt.any_error = true;
751 return;
752 };
753 }
754 }
755 }
756}
757
758fn fmtPathFile(
759 fmt: *Fmt,
760 file_path: []const u8,
761 check_mode: bool,
762 dir: fs.Dir,
763 sub_path: []const u8,
764) FmtError!void {
765 const source_file = try dir.openFile(sub_path, .{});
766 var file_closed = false;
767 errdefer if (!file_closed) source_file.close();
768
769 const stat = try source_file.stat();
770
771 if (stat.kind == .Directory)
772 return error.IsDir;
773
774 const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
775 error.ConnectionResetByPeer => unreachable,
776 error.ConnectionTimedOut => unreachable,
777 else => |e| return e,
714778 };
779 source_file.close();
780 file_closed = true;
781 defer fmt.gpa.free(source_code);
782
783 // Add to set after no longer possible to get error.IsDir.
784 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
785
786 const tree = try std.zig.parse(fmt.gpa, source_code);
715787 defer tree.deinit();
716788
717789 for (tree.errors) |parse_error| {
......@@ -725,18 +797,23 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
725797 if (check_mode) {
726798 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
727799 if (anything_changed) {
728 std.debug.warn("{}\n", .{file_path});
800 std.debug.print("{}\n", .{file_path});
729801 fmt.any_error = true;
730802 }
731803 } else {
732 const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{});
733 defer baf.destroy();
734
735 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
736 if (anything_changed) {
737 std.debug.warn("{}\n", .{file_path});
738 try baf.finish();
739 }
804 // As a heuristic, we make enough capacity for the same as the input source.
805 try fmt.out_buffer.ensureCapacity(source_code.len);
806 fmt.out_buffer.items.len = 0;
807 const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree);
808 if (!anything_changed)
809 return; // Good thing we didn't waste any file system access on this.
810
811 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
812 defer af.deinit();
813
814 try af.file.writeAll(fmt.out_buffer.items);
815 try af.finish();
816 std.debug.print("{}\n", .{file_path});
740817 }
741818}
742819
src-self-hosted/print_targets.zig+1-1
......@@ -62,7 +62,7 @@ pub fn cmdTargets(
6262 allocator: *Allocator,
6363 args: []const []const u8,
6464 /// Output stream
65 stdout: var,
65 stdout: anytype,
6666 native_target: Target,
6767) !void {
6868 const available_glibcs = blk: {
src-self-hosted/stage2.zig+4-21
......@@ -653,23 +653,6 @@ export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file:
653653 return .None;
654654}
655655
656fn enumToString(value: var, type_name: []const u8) ![]const u8 {
657 switch (@typeInfo(@TypeOf(value))) {
658 .Enum => |e| {
659 if (e.is_exhaustive) {
660 return std.fmt.allocPrint(std.heap.c_allocator, ".{}", .{@tagName(value)});
661 } else {
662 return std.fmt.allocPrint(
663 std.heap.c_allocator,
664 "@intToEnum({}, {})",
665 .{ type_name, @enumToInt(value) },
666 );
667 }
668 },
669 else => unreachable,
670 }
671}
672
673656// ABI warning
674657const Stage2Target = extern struct {
675658 arch: c_int,
......@@ -887,13 +870,13 @@ const Stage2Target = extern struct {
887870
888871 .windows => try os_builtin_str_buffer.outStream().print(
889872 \\ .windows = .{{
890 \\ .min = {},
891 \\ .max = {},
873 \\ .min = {s},
874 \\ .max = {s},
892875 \\ }}}},
893876 \\
894877 , .{
895 try enumToString(target.os.version_range.windows.min, "Target.Os.WindowsVersion"),
896 try enumToString(target.os.version_range.windows.max, "Target.Os.WindowsVersion"),
878 target.os.version_range.windows.min,
879 target.os.version_range.windows.max,
897880 }),
898881 }
899882 try os_builtin_str_buffer.appendSlice("};\n");
src-self-hosted/test.zig+507-225
......@@ -5,9 +5,10 @@ const Allocator = std.mem.Allocator;
55const zir = @import("zir.zig");
66const Package = @import("Package.zig");
77
8const cheader = @embedFile("cbe.h");
9
810test "self-hosted" {
9 var ctx: TestContext = undefined;
10 try ctx.init();
11 var ctx = TestContext.init();
1112 defer ctx.deinit();
1213
1314 try @import("stage2_tests").addCases(&ctx);
......@@ -15,311 +16,592 @@ test "self-hosted" {
1516 try ctx.run();
1617}
1718
19const ErrorMsg = struct {
20 msg: []const u8,
21 line: u32,
22 column: u32,
23};
24
1825pub const TestContext = struct {
19 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
20 zir_transform_cases: std.ArrayList(ZIRTransformCase),
26 /// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
27 cases: std.ArrayList(Case),
28
29 pub const Update = struct {
30 /// The input to the current update. We simulate an incremental update
31 /// with the file's contents changed to this value each update.
32 ///
33 /// This value can change entirely between updates, which would be akin
34 /// to deleting the source file and creating a new one from scratch; or
35 /// you can keep it mostly consistent, with small changes, testing the
36 /// effects of the incremental compilation.
37 src: [:0]const u8,
38 case: union(enum) {
39 /// A transformation update transforms the input and tests against
40 /// the expected output ZIR.
41 Transformation: [:0]const u8,
42 /// An error update attempts to compile bad code, and ensures that it
43 /// fails to compile, and for the expected reasons.
44 /// A slice containing the expected errors *in sequential order*.
45 Error: []const ErrorMsg,
46 /// An execution update compiles and runs the input, testing the
47 /// stdout against the expected results
48 /// This is a slice containing the expected message.
49 Execution: []const u8,
50 },
51 };
2152
22 pub const ZIRCompareOutputCase = struct {
23 name: []const u8,
24 src_list: []const []const u8,
25 expected_stdout_list: []const []const u8,
53 pub const TestType = enum {
54 Zig,
55 ZIR,
2656 };
2757
28 pub const ZIRTransformCase = struct {
58 /// A Case consists of a set of *updates*. The same Module is used for each
59 /// update, so each update's source is treated as a single file being
60 /// updated by the test harness and incrementally compiled.
61 pub const Case = struct {
62 /// The name of the test case. This is shown if a test fails, and
63 /// otherwise ignored.
2964 name: []const u8,
30 cross_target: std.zig.CrossTarget,
65 /// The platform the test targets. For non-native platforms, an emulator
66 /// such as QEMU is required for tests to complete.
67 target: std.zig.CrossTarget,
68 /// In order to be able to run e.g. Execution updates, this must be set
69 /// to Executable.
70 output_mode: std.builtin.OutputMode,
3171 updates: std.ArrayList(Update),
72 extension: TestType,
73 cbe: bool = false,
3274
33 pub const Update = struct {
34 expected: Expected,
35 src: [:0]const u8,
36 };
37
38 pub const Expected = union(enum) {
39 zir: []const u8,
40 errors: []const []const u8,
41 };
42
43 pub fn addZIR(case: *ZIRTransformCase, src: [:0]const u8, zir_text: []const u8) void {
44 case.updates.append(.{
75 /// Adds a subcase in which the module is updated with `src`, and the
76 /// resulting ZIR is validated against `result`.
77 pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
78 self.updates.append(.{
4579 .src = src,
46 .expected = .{ .zir = zir_text },
80 .case = .{ .Transformation = result },
4781 }) catch unreachable;
4882 }
4983
50 pub fn addError(case: *ZIRTransformCase, src: [:0]const u8, errors: []const []const u8) void {
51 case.updates.append(.{
84 /// Adds a subcase in which the module is updated with `src`, compiled,
85 /// run, and the output is tested against `result`.
86 pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
87 self.updates.append(.{
5288 .src = src,
53 .expected = .{ .errors = errors },
89 .case = .{ .Execution = result },
5490 }) catch unreachable;
5591 }
92
93 /// Adds a subcase in which the module is updated with `src`, which
94 /// should contain invalid input, and ensures that compilation fails
95 /// for the expected reasons, given in sequential order in `errors` in
96 /// the form `:line:column: error: message`.
97 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
98 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
99 for (errors) |e, i| {
100 if (e[0] != ':') {
101 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
102 }
103 var cur = e[1..];
104 var line_index = std.mem.indexOf(u8, cur, ":");
105 if (line_index == null) {
106 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
107 }
108 const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number");
109 cur = cur[line_index.? + 1 ..];
110 const column_index = std.mem.indexOf(u8, cur, ":");
111 if (column_index == null) {
112 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
113 }
114 const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number");
115 cur = cur[column_index.? + 2 ..];
116 if (!std.mem.eql(u8, cur[0..7], "error: ")) {
117 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
118 }
119 const msg = cur[7..];
120
121 if (line == 0 or column == 0) {
122 @panic("Invalid test: error line and column must be specified starting at one!");
123 }
124
125 array[i] = .{
126 .msg = msg,
127 .line = line - 1,
128 .column = column - 1,
129 };
130 }
131 self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
132 }
133
134 /// Adds a subcase in which the module is updated with `src`, and
135 /// asserts that it compiles without issue
136 pub fn compiles(self: *Case, src: [:0]const u8) void {
137 self.addError(src, &[_][]const u8{});
138 }
56139 };
57140
58 pub fn addZIRCompareOutput(
141 pub fn addExe(
59142 ctx: *TestContext,
60143 name: []const u8,
61 src_list: []const []const u8,
62 expected_stdout_list: []const []const u8,
63 ) void {
64 ctx.zir_cmp_output_cases.append(.{
144 target: std.zig.CrossTarget,
145 T: TestType,
146 ) *Case {
147 ctx.cases.append(Case{
65148 .name = name,
66 .src_list = src_list,
67 .expected_stdout_list = expected_stdout_list,
149 .target = target,
150 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
151 .output_mode = .Exe,
152 .extension = T,
68153 }) catch unreachable;
154 return &ctx.cases.items[ctx.cases.items.len - 1];
155 }
156
157 /// Adds a test case for Zig input, producing an executable
158 pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
159 return ctx.addExe(name, target, .Zig);
69160 }
70161
71 pub fn addZIRTransform(
162 /// Adds a test case for ZIR input, producing an executable
163 pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
164 return ctx.addExe(name, target, .ZIR);
165 }
166
167 pub fn addObj(
72168 ctx: *TestContext,
73169 name: []const u8,
74 cross_target: std.zig.CrossTarget,
75 src: [:0]const u8,
76 expected_zir: []const u8,
77 ) void {
78 const case = ctx.zir_transform_cases.addOne() catch unreachable;
79 case.* = .{
170 target: std.zig.CrossTarget,
171 T: TestType,
172 ) *Case {
173 ctx.cases.append(Case{
80174 .name = name,
81 .cross_target = cross_target,
82 .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),
83 };
84 case.updates.append(.{
85 .src = src,
86 .expected = .{ .zir = expected_zir },
175 .target = target,
176 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
177 .output_mode = .Obj,
178 .extension = T,
87179 }) catch unreachable;
180 return &ctx.cases.items[ctx.cases.items.len - 1];
88181 }
89182
90 pub fn addZIRMulti(
183 /// Adds a test case for Zig input, producing an object file
184 pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
185 return ctx.addObj(name, target, .Zig);
186 }
187
188 /// Adds a test case for ZIR input, producing an object file
189 pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
190 return ctx.addObj(name, target, .ZIR);
191 }
192
193 pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {
194 ctx.cases.append(Case{
195 .name = name,
196 .target = target,
197 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
198 .output_mode = .Obj,
199 .extension = T,
200 .cbe = true,
201 }) catch unreachable;
202 return &ctx.cases.items[ctx.cases.items.len - 1];
203 }
204
205 pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
206 ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
207 }
208
209 pub fn addCompareOutput(
91210 ctx: *TestContext,
92211 name: []const u8,
93 cross_target: std.zig.CrossTarget,
94 ) *ZIRTransformCase {
95 const case = ctx.zir_transform_cases.addOne() catch unreachable;
96 case.* = .{
97 .name = name,
98 .cross_target = cross_target,
99 .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),
100 };
101 return case;
212 T: TestType,
213 src: [:0]const u8,
214 expected_stdout: []const u8,
215 ) void {
216 ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);
217 }
218
219 /// Adds a test case that compiles the Zig source given in `src`, executes
220 /// it, runs it, and tests the output against `expected_stdout`
221 pub fn compareOutput(
222 ctx: *TestContext,
223 name: []const u8,
224 src: [:0]const u8,
225 expected_stdout: []const u8,
226 ) void {
227 return ctx.addCompareOutput(name, .Zig, src, expected_stdout);
228 }
229
230 /// Adds a test case that compiles the ZIR source given in `src`, executes
231 /// it, runs it, and tests the output against `expected_stdout`
232 pub fn compareOutputZIR(
233 ctx: *TestContext,
234 name: []const u8,
235 src: [:0]const u8,
236 expected_stdout: []const u8,
237 ) void {
238 ctx.addCompareOutput(name, .ZIR, src, expected_stdout);
239 }
240
241 pub fn addTransform(
242 ctx: *TestContext,
243 name: []const u8,
244 target: std.zig.CrossTarget,
245 T: TestType,
246 src: [:0]const u8,
247 result: [:0]const u8,
248 ) void {
249 ctx.addObj(name, target, T).addTransform(src, result);
250 }
251
252 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
253 /// the ZIR against `result`
254 pub fn transform(
255 ctx: *TestContext,
256 name: []const u8,
257 target: std.zig.CrossTarget,
258 src: [:0]const u8,
259 result: [:0]const u8,
260 ) void {
261 ctx.addTransform(name, target, .Zig, src, result);
262 }
263
264 /// Adds a test case that cleans up the ZIR source given in `src`, and
265 /// tests the resulting ZIR against `result`
266 pub fn transformZIR(
267 ctx: *TestContext,
268 name: []const u8,
269 target: std.zig.CrossTarget,
270 src: [:0]const u8,
271 result: [:0]const u8,
272 ) void {
273 ctx.addTransform(name, target, .ZIR, src, result);
274 }
275
276 pub fn addError(
277 ctx: *TestContext,
278 name: []const u8,
279 target: std.zig.CrossTarget,
280 T: TestType,
281 src: [:0]const u8,
282 expected_errors: []const []const u8,
283 ) void {
284 ctx.addObj(name, target, T).addError(src, expected_errors);
285 }
286
287 /// Adds a test case that ensures that the Zig given in `src` fails to
288 /// compile for the expected reasons, given in sequential order in
289 /// `expected_errors` in the form `:line:column: error: message`.
290 pub fn compileError(
291 ctx: *TestContext,
292 name: []const u8,
293 target: std.zig.CrossTarget,
294 src: [:0]const u8,
295 expected_errors: []const []const u8,
296 ) void {
297 ctx.addError(name, target, .Zig, src, expected_errors);
298 }
299
300 /// Adds a test case that ensures that the ZIR given in `src` fails to
301 /// compile for the expected reasons, given in sequential order in
302 /// `expected_errors` in the form `:line:column: error: message`.
303 pub fn compileErrorZIR(
304 ctx: *TestContext,
305 name: []const u8,
306 target: std.zig.CrossTarget,
307 src: [:0]const u8,
308 expected_errors: []const []const u8,
309 ) void {
310 ctx.addError(name, target, .ZIR, src, expected_errors);
311 }
312
313 pub fn addCompiles(
314 ctx: *TestContext,
315 name: []const u8,
316 target: std.zig.CrossTarget,
317 T: TestType,
318 src: [:0]const u8,
319 ) void {
320 ctx.addObj(name, target, T).compiles(src);
102321 }
103322
104 fn init(self: *TestContext) !void {
105 self.* = .{
106 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
107 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator),
108 };
323 /// Adds a test case that asserts that the Zig given in `src` compiles
324 /// without any errors.
325 pub fn compiles(
326 ctx: *TestContext,
327 name: []const u8,
328 target: std.zig.CrossTarget,
329 src: [:0]const u8,
330 ) void {
331 ctx.addCompiles(name, target, .Zig, src);
332 }
333
334 /// Adds a test case that asserts that the ZIR given in `src` compiles
335 /// without any errors.
336 pub fn compilesZIR(
337 ctx: *TestContext,
338 name: []const u8,
339 target: std.zig.CrossTarget,
340 src: [:0]const u8,
341 ) void {
342 ctx.addCompiles(name, target, .ZIR, src);
343 }
344
345 /// Adds a test case that first ensures that the Zig given in `src` fails
346 /// to compile for the reasons given in sequential order in
347 /// `expected_errors` in the form `:line:column: error: message`, then
348 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
349 /// by incremental compilation.
350 pub fn incrementalFailure(
351 ctx: *TestContext,
352 name: []const u8,
353 target: std.zig.CrossTarget,
354 src: [:0]const u8,
355 expected_errors: []const []const u8,
356 fixed_src: [:0]const u8,
357 ) void {
358 var case = ctx.addObj(name, target, .Zig);
359 case.addError(src, expected_errors);
360 case.compiles(fixed_src);
361 }
362
363 /// Adds a test case that first ensures that the ZIR given in `src` fails
364 /// to compile for the reasons given in sequential order in
365 /// `expected_errors` in the form `:line:column: error: message`, then
366 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
367 /// by incremental compilation.
368 pub fn incrementalFailureZIR(
369 ctx: *TestContext,
370 name: []const u8,
371 target: std.zig.CrossTarget,
372 src: [:0]const u8,
373 expected_errors: []const []const u8,
374 fixed_src: [:0]const u8,
375 ) void {
376 var case = ctx.addObj(name, target, .ZIR);
377 case.addError(src, expected_errors);
378 case.compiles(fixed_src);
379 }
380
381 fn init() TestContext {
382 const allocator = std.heap.page_allocator;
383 return .{ .cases = std.ArrayList(Case).init(allocator) };
109384 }
110385
111386 fn deinit(self: *TestContext) void {
112 self.zir_cmp_output_cases.deinit();
113 self.zir_transform_cases.deinit();
387 for (self.cases.items) |case| {
388 for (case.updates.items) |u| {
389 if (u.case == .Error) {
390 case.updates.allocator.free(u.case.Error);
391 }
392 }
393 case.updates.deinit();
394 }
395 self.cases.deinit();
114396 self.* = undefined;
115397 }
116398
117399 fn run(self: *TestContext) !void {
118400 var progress = std.Progress{};
119 const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len +
120 self.zir_transform_cases.items.len);
401 const root_node = try progress.start("tests", self.cases.items.len);
121402 defer root_node.end();
122403
123404 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
124405
125 for (self.zir_cmp_output_cases.items) |case| {
126 std.testing.base_allocator_instance.reset();
127 try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
128 try std.testing.allocator_instance.validate();
129 }
130 for (self.zir_transform_cases.items) |case| {
406 for (self.cases.items) |case| {
131407 std.testing.base_allocator_instance.reset();
132 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.cross_target);
133 try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, info.target);
134 try std.testing.allocator_instance.validate();
135 }
136 }
137
138 fn runOneZIRCmpOutputCase(
139 self: *TestContext,
140 allocator: *Allocator,
141 root_node: *std.Progress.Node,
142 case: ZIRCompareOutputCase,
143 target: std.Target,
144 ) !void {
145 var tmp = std.testing.tmpDir(.{});
146 defer tmp.cleanup();
147408
148 const tmp_src_path = "test-case.zir";
149 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
150 defer root_pkg.destroy();
151
152 var prg_node = root_node.start(case.name, case.src_list.len);
153 prg_node.activate();
154 defer prg_node.end();
155
156 var module = try Module.init(allocator, .{
157 .target = target,
158 .output_mode = .Exe,
159 .optimize_mode = .Debug,
160 .bin_file_dir = tmp.dir,
161 .bin_file_path = "a.out",
162 .root_pkg = root_pkg,
163 });
164 defer module.deinit();
165
166 for (case.src_list) |source, i| {
167 var src_node = prg_node.start("update", 2);
168 src_node.activate();
169 defer src_node.end();
409 var prg_node = root_node.start(case.name, case.updates.items.len);
410 prg_node.activate();
411 defer prg_node.end();
170412
171 try tmp.dir.writeFile(tmp_src_path, source);
413 // So that we can see which test case failed when the leak checker goes off,
414 // or there's an internal error
415 progress.initial_delay_ns = 0;
416 progress.refresh_rate_ns = 0;
172417
173 var update_node = src_node.start("parse,analysis,codegen", null);
174 update_node.activate();
175 try module.makeBinFileWritable();
176 try module.update();
177 update_node.end();
178
179 var exec_result = x: {
180 var exec_node = src_node.start("execute", null);
181 exec_node.activate();
182 defer exec_node.end();
183
184 try module.makeBinFileExecutable();
185 break :x try std.ChildProcess.exec(.{
186 .allocator = allocator,
187 .argv = &[_][]const u8{"./a.out"},
188 .cwd_dir = tmp.dir,
189 });
190 };
191 defer allocator.free(exec_result.stdout);
192 defer allocator.free(exec_result.stderr);
193 switch (exec_result.term) {
194 .Exited => |code| {
195 if (code != 0) {
196 std.debug.warn("elf file exited with code {}\n", .{code});
197 return error.BinaryBadExitCode;
198 }
199 },
200 else => return error.BinaryCrashed,
201 }
202 const expected_stdout = case.expected_stdout_list[i];
203 if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
204 std.debug.panic(
205 "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
206 .{ i, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
207 );
208 }
418 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
419 try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
420 try std.testing.allocator_instance.validate();
209421 }
210422 }
211423
212 fn runOneZIRTransformCase(
213 self: *TestContext,
214 allocator: *Allocator,
215 root_node: *std.Progress.Node,
216 case: ZIRTransformCase,
217 target: std.Target,
218 ) !void {
424 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {
219425 var tmp = std.testing.tmpDir(.{});
220426 defer tmp.cleanup();
221427
222 var update_node = root_node.start(case.name, case.updates.items.len);
223 update_node.activate();
224 defer update_node.end();
225
226 const tmp_src_path = "test-case.zir";
428 const tmp_src_path = if (case.extension == .Zig) "test_case.zig" else if (case.extension == .ZIR) "test_case.zir" else unreachable;
227429 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
228430 defer root_pkg.destroy();
229431
432 const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);
433 defer allocator.free(bin_name);
434
230435 var module = try Module.init(allocator, .{
231436 .target = target,
232 .output_mode = .Obj,
437 // TODO: support tests for object file building, and library builds
438 // and linking. This will require a rework to support multi-file
439 // tests.
440 .output_mode = case.output_mode,
441 // TODO: support testing optimizations
233442 .optimize_mode = .Debug,
234443 .bin_file_dir = tmp.dir,
235 .bin_file_path = "test-case.o",
444 .bin_file_path = bin_name,
236445 .root_pkg = root_pkg,
446 .keep_source_files_loaded = true,
447 .object_format = if (case.cbe) .c else null,
237448 });
238449 defer module.deinit();
239450
240 for (case.updates.items) |update| {
241 var prg_node = update_node.start("", 3);
242 prg_node.activate();
243 defer prg_node.end();
451 for (case.updates.items) |update, update_index| {
452 var update_node = root_node.start("update", 3);
453 update_node.activate();
454 defer update_node.end();
244455
456 var sync_node = update_node.start("write", null);
457 sync_node.activate();
245458 try tmp.dir.writeFile(tmp_src_path, update.src);
459 sync_node.end();
246460
247 var module_node = prg_node.start("parse/analysis/codegen", null);
461 var module_node = update_node.start("parse/analysis/codegen", null);
248462 module_node.activate();
463 try module.makeBinFileWritable();
249464 try module.update();
250465 module_node.end();
251466
252 switch (update.expected) {
253 .zir => |expected_zir| {
254 var emit_node = prg_node.start("emit", null);
255 emit_node.activate();
256 var new_zir_module = try zir.emit(allocator, module);
257 defer new_zir_module.deinit(allocator);
258 emit_node.end();
259
260 var write_node = prg_node.start("write", null);
261 write_node.activate();
262 var out_zir = std.ArrayList(u8).init(allocator);
263 defer out_zir.deinit();
264 try new_zir_module.writeToStream(allocator, out_zir.outStream());
265 write_node.end();
266
267 std.testing.expectEqualSlices(u8, expected_zir, out_zir.items);
467 if (update.case != .Error) {
468 var all_errors = try module.getAllErrorsAlloc();
469 defer all_errors.deinit(allocator);
470 if (all_errors.list.len != 0) {
471 std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});
472 for (all_errors.list) |err| {
473 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
474 }
475 std.debug.warn("Test failed.\n", .{});
476 std.process.exit(1);
477 }
478 }
479
480 switch (update.case) {
481 .Transformation => |expected_output| {
482 if (case.cbe) {
483 // The C file is always closed after an update, because we don't support
484 // incremental updates
485 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
486 defer file.close();
487 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
488 defer allocator.free(out);
489
490 if (expected_output.len != out.len) {
491 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
492 std.process.exit(1);
493 }
494 for (expected_output) |e, i| {
495 if (out[i] != e) {
496 std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
497 std.process.exit(1);
498 }
499 }
500 } else {
501 update_node.estimated_total_items = 5;
502 var emit_node = update_node.start("emit", null);
503 emit_node.activate();
504 var new_zir_module = try zir.emit(allocator, module);
505 defer new_zir_module.deinit(allocator);
506 emit_node.end();
507
508 var write_node = update_node.start("write", null);
509 write_node.activate();
510 var out_zir = std.ArrayList(u8).init(allocator);
511 defer out_zir.deinit();
512 try new_zir_module.writeToStream(allocator, out_zir.outStream());
513 write_node.end();
514
515 var test_node = update_node.start("assert", null);
516 test_node.activate();
517 defer test_node.end();
518
519 if (expected_output.len != out_zir.items.len) {
520 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
521 std.process.exit(1);
522 }
523 for (expected_output) |e, i| {
524 if (out_zir.items[i] != e) {
525 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
526 std.process.exit(1);
527 }
528 }
529 }
268530 },
269 .errors => |expected_errors| {
531 .Error => |e| {
532 var test_node = update_node.start("assert", null);
533 test_node.activate();
534 defer test_node.end();
535 var handled_errors = try allocator.alloc(bool, e.len);
536 defer allocator.free(handled_errors);
537 for (handled_errors) |*h| {
538 h.* = false;
539 }
270540 var all_errors = try module.getAllErrorsAlloc();
271 defer all_errors.deinit(module.allocator);
272 for (expected_errors) |expected_error| {
273 for (all_errors.list) |full_err_msg| {
274 const text = try std.fmt.allocPrint(allocator, ":{}:{}: error: {}", .{
275 full_err_msg.line + 1,
276 full_err_msg.column + 1,
277 full_err_msg.msg,
278 });
279 defer allocator.free(text);
280 if (std.mem.eql(u8, text, expected_error)) {
541 defer all_errors.deinit(allocator);
542 for (all_errors.list) |a| {
543 for (e) |ex, i| {
544 if (a.line == ex.line and a.column == ex.column and std.mem.eql(u8, ex.msg, a.msg)) {
545 handled_errors[i] = true;
281546 break;
282547 }
283548 } else {
284 std.debug.warn(
285 "{}\nExpected this error:\n================\n{}\n================\nBut found these errors:\n================\n",
286 .{ case.name, expected_error },
287 );
288 for (all_errors.list) |full_err_msg| {
289 std.debug.warn(":{}:{}: error: {}\n", .{
290 full_err_msg.line + 1,
291 full_err_msg.column + 1,
292 full_err_msg.msg,
293 });
294 }
295 std.debug.warn("================\nTest failed\n", .{});
549 std.debug.warn("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
296550 std.process.exit(1);
297551 }
298552 }
553
554 for (handled_errors) |h, i| {
555 if (!h) {
556 const er = e[i];
557 std.debug.warn("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
558 std.process.exit(1);
559 }
560 }
561 },
562 .Execution => |expected_stdout| {
563 std.debug.assert(!case.cbe);
564
565 update_node.estimated_total_items = 4;
566 var exec_result = x: {
567 var exec_node = update_node.start("execute", null);
568 exec_node.activate();
569 defer exec_node.end();
570
571 try module.makeBinFileExecutable();
572
573 const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
574 defer allocator.free(exe_path);
575
576 break :x try std.ChildProcess.exec(.{
577 .allocator = allocator,
578 .argv = &[_][]const u8{exe_path},
579 .cwd_dir = tmp.dir,
580 });
581 };
582 var test_node = update_node.start("test", null);
583 test_node.activate();
584 defer test_node.end();
585
586 defer allocator.free(exec_result.stdout);
587 defer allocator.free(exec_result.stderr);
588 switch (exec_result.term) {
589 .Exited => |code| {
590 if (code != 0) {
591 std.debug.warn("elf file exited with code {}\n", .{code});
592 return error.BinaryBadExitCode;
593 }
594 },
595 else => return error.BinaryCrashed,
596 }
597 if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
598 std.debug.panic(
599 "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
600 .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
601 );
602 }
299603 },
300604 }
301605 }
302606 }
303607};
304
305fn debugPrintErrors(src: []const u8, errors: var) void {
306 std.debug.warn("\n", .{});
307 var nl = true;
308 var line: usize = 1;
309 for (src) |byte| {
310 if (nl) {
311 std.debug.warn("{: >3}| ", .{line});
312 nl = false;
313 }
314 if (byte == '\n') {
315 nl = true;
316 line += 1;
317 }
318 std.debug.warn("{c}", .{byte});
319 }
320 std.debug.warn("\n", .{});
321 for (errors) |err_msg| {
322 const loc = std.zig.findLineColumn(src, err_msg.byte_offset);
323 std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg });
324 }
325}
src-self-hosted/tracy.zig created+45
......@@ -0,0 +1,45 @@
1pub const std = @import("std");
2
3pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy;
4
5extern fn ___tracy_emit_zone_begin_callstack(
6 srcloc: *const ___tracy_source_location_data,
7 depth: c_int,
8 active: c_int,
9) ___tracy_c_zone_context;
10
11extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
12
13pub const ___tracy_source_location_data = extern struct {
14 name: ?[*:0]const u8,
15 function: [*:0]const u8,
16 file: [*:0]const u8,
17 line: u32,
18 color: u32,
19};
20
21pub const ___tracy_c_zone_context = extern struct {
22 id: u32,
23 active: c_int,
24
25 pub fn end(self: ___tracy_c_zone_context) void {
26 ___tracy_emit_zone_end(self);
27 }
28};
29
30pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
31 pub fn end(self: Ctx) void {}
32};
33
34pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx {
35 if (!enable) return .{};
36
37 const loc: ___tracy_source_location_data = .{
38 .name = null,
39 .function = src.fn_name.ptr,
40 .file = src.file.ptr,
41 .line = src.line,
42 .color = 0,
43 };
44 return ___tracy_emit_zone_begin_callstack(&loc, 1, 1);
45}
src-self-hosted/translate_c.zig+577-514
......@@ -20,7 +20,7 @@ pub const Error = error{OutOfMemory};
2020const TypeError = Error || error{UnsupportedType};
2121const TransError = TypeError || error{UnsupportedTranslation};
2222
23const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql);
23const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql, false);
2424
2525fn addrHash(x: usize) u32 {
2626 switch (@typeInfo(usize).Int.bits) {
......@@ -586,11 +586,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
586586 for (proto_node.params()) |*param, i| {
587587 const param_name = if (param.name_token) |name_tok|
588588 tokenSlice(c, name_tok)
589 else if (param.param_type == .var_args) {
590 assert(i + 1 == proto_node.params_len);
591 proto_node.params_len -= 1;
592 break;
593 } else
589 else
594590 return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name});
595591
596592 const c_param = ZigClangFunctionDecl_getParamDecl(fn_decl, param_id);
......@@ -602,10 +598,20 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
602598 if (!is_const) {
603599 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name});
604600 const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
605 const node = try transCreateNodeVarDecl(c, false, false, mangled_param_name);
606 node.eq_token = try appendToken(c, .Equal, "=");
607 node.init_node = try transCreateNodeIdentifier(c, arg_name);
608 node.semicolon_token = try appendToken(c, .Semicolon, ";");
601
602 const mut_tok = try appendToken(c, .Keyword_var, "var");
603 const name_tok = try appendIdentifier(c, mangled_param_name);
604 const eq_token = try appendToken(c, .Equal, "=");
605 const init_node = try transCreateNodeIdentifier(c, arg_name);
606 const semicolon_token = try appendToken(c, .Semicolon, ";");
607 const node = try ast.Node.VarDecl.create(c.arena, .{
608 .mut_token = mut_tok,
609 .name_token = name_tok,
610 .semicolon_token = semicolon_token,
611 }, .{
612 .eq_token = eq_token,
613 .init_node = init_node,
614 });
609615 try block_scope.statements.append(&node.base);
610616 param.name_token = try appendIdentifier(c, arg_name);
611617 _ = try appendToken(c, .Colon, ":");
......@@ -622,7 +628,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
622628 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
623629 };
624630 const body_node = try block_scope.complete(rp.c);
625 proto_node.body_node = &body_node.base;
631 proto_node.setTrailer("body_node", &body_node.base);
626632 return addTopLevelDecl(c, fn_name, &proto_node.base);
627633}
628634
......@@ -725,23 +731,20 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
725731 break :blk null;
726732 };
727733
728 const node = try c.arena.create(ast.Node.VarDecl);
729 node.* = .{
730 .doc_comments = null,
734 const node = try ast.Node.VarDecl.create(c.arena, .{
735 .name_token = name_tok,
736 .mut_token = mut_tok,
737 .semicolon_token = try appendToken(c, .Semicolon, ";"),
738 }, .{
731739 .visib_token = visib_tok,
732740 .thread_local_token = thread_local_token,
733 .name_token = name_tok,
734741 .eq_token = eq_tok,
735 .mut_token = mut_tok,
736 .comptime_token = null,
737742 .extern_export_token = extern_tok,
738 .lib_name = null,
739743 .type_node = type_node,
740744 .align_node = align_expr,
741745 .section_node = linksection_expr,
742746 .init_node = init_node,
743 .semicolon_token = try appendToken(c, .Semicolon, ";"),
744 };
747 });
745748 return addTopLevelDecl(c, checked_name, &node.base);
746749}
747750
......@@ -776,8 +779,8 @@ fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {
776779}
777780
778781fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {
779 if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv|
780 return transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice
782 if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |name|
783 return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
781784 const rp = makeRestorePoint(c);
782785
783786 const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));
......@@ -795,31 +798,46 @@ fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_l
795798
796799 _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), checked_name);
797800 const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
798 try addTopLevelDecl(c, checked_name, &node.base);
801 try addTopLevelDecl(c, checked_name, node);
799802 return transCreateNodeIdentifier(c, checked_name);
800803}
801804
802fn transCreateNodeTypedef(rp: RestorePoint, typedef_decl: *const ZigClangTypedefNameDecl, toplevel: bool, checked_name: []const u8) Error!?*ast.Node.VarDecl {
803 const node = try transCreateNodeVarDecl(rp.c, toplevel, true, checked_name);
804 node.eq_token = try appendToken(rp.c, .Equal, "=");
805
805fn transCreateNodeTypedef(
806 rp: RestorePoint,
807 typedef_decl: *const ZigClangTypedefNameDecl,
808 toplevel: bool,
809 checked_name: []const u8,
810) Error!?*ast.Node {
811 const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null;
812 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
813 const name_tok = try appendIdentifier(rp.c, checked_name);
814 const eq_token = try appendToken(rp.c, .Equal, "=");
806815 const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
807816 const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);
808 node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
817 const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
809818 error.UnsupportedType => {
810819 try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
811820 return null;
812821 },
813822 error.OutOfMemory => |e| return e,
814823 };
824 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
815825
816 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
817 return node;
826 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
827 .name_token = name_tok,
828 .mut_token = mut_tok,
829 .semicolon_token = semicolon_token,
830 }, .{
831 .visib_token = visib_tok,
832 .eq_token = eq_token,
833 .init_node = init_node,
834 });
835 return &node.base;
818836}
819837
820838fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {
821 if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv|
822 return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice
839 if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |name|
840 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
823841 const record_loc = ZigClangRecordDecl_getLocation(record_decl);
824842
825843 var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl)));
......@@ -847,12 +865,14 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
847865 const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });
848866 _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);
849867
850 const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
868 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
869 const mut_tok = try appendToken(c, .Keyword_const, "const");
870 const name_tok = try appendIdentifier(c, name);
851871
852 node.eq_token = try appendToken(c, .Equal, "=");
872 const eq_token = try appendToken(c, .Equal, "=");
853873
854874 var semicolon: ast.TokenIndex = undefined;
855 node.init_node = blk: {
875 const init_node = blk: {
856876 const rp = makeRestorePoint(c);
857877 const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
858878 const opaque = try transCreateNodeOpaqueType(c);
......@@ -959,7 +979,16 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
959979 semicolon = try appendToken(c, .Semicolon, ";");
960980 break :blk &container_node.base;
961981 };
962 node.semicolon_token = semicolon;
982
983 const node = try ast.Node.VarDecl.create(c.arena, .{
984 .name_token = name_tok,
985 .mut_token = mut_tok,
986 .semicolon_token = semicolon,
987 }, .{
988 .visib_token = visib_tok,
989 .eq_token = eq_token,
990 .init_node = init_node,
991 });
963992
964993 try addTopLevelDecl(c, name, &node.base);
965994 if (!is_unnamed)
......@@ -969,7 +998,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
969998
970999fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
9711000 if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
972 return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice
1001 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
9731002 const rp = makeRestorePoint(c);
9741003 const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
9751004
......@@ -982,10 +1011,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
9821011
9831012 const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});
9841013 _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
985 const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
986 node.eq_token = try appendToken(c, .Equal, "=");
9871014
988 node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
1015 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
1016 const mut_tok = try appendToken(c, .Keyword_const, "const");
1017 const name_tok = try appendIdentifier(c, name);
1018 const eq_token = try appendToken(c, .Equal, "=");
1019
1020 const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
9891021 var pure_enum = true;
9901022 var it = ZigClangEnumDecl_enumerator_begin(enum_def);
9911023 var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
......@@ -1063,23 +1095,34 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
10631095
10641096 // In C each enum value is in the global namespace. So we put them there too.
10651097 // At this point we can rely on the enum emitting successfully.
1066 const tld_node = try transCreateNodeVarDecl(c, true, true, enum_val_name);
1067 tld_node.eq_token = try appendToken(c, .Equal, "=");
1098 const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");
1099 const tld_mut_tok = try appendToken(c, .Keyword_const, "const");
1100 const tld_name_tok = try appendIdentifier(c, enum_val_name);
1101 const tld_eq_token = try appendToken(c, .Equal, "=");
10681102 const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);
10691103 const enum_ident = try transCreateNodeIdentifier(c, name);
10701104 const period_tok = try appendToken(c, .Period, ".");
10711105 const field_ident = try transCreateNodeIdentifier(c, field_name);
1072 const field_access_node = try c.arena.create(ast.Node.InfixOp);
1106 const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
10731107 field_access_node.* = .{
1108 .base = .{ .tag = .Period },
10741109 .op_token = period_tok,
10751110 .lhs = enum_ident,
1076 .op = .Period,
10771111 .rhs = field_ident,
10781112 };
10791113 cast_node.params()[0] = &field_access_node.base;
10801114 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1081 tld_node.init_node = &cast_node.base;
1082 tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");
1115 const tld_init_node = &cast_node.base;
1116 const tld_semicolon_token = try appendToken(c, .Semicolon, ";");
1117 const tld_node = try ast.Node.VarDecl.create(c.arena, .{
1118 .name_token = tld_name_tok,
1119 .mut_token = tld_mut_tok,
1120 .semicolon_token = tld_semicolon_token,
1121 }, .{
1122 .visib_token = tld_visib_tok,
1123 .eq_token = tld_eq_token,
1124 .init_node = tld_init_node,
1125 });
10831126 try addTopLevelDecl(c, field_name, &tld_node.base);
10841127 }
10851128 // make non exhaustive
......@@ -1109,7 +1152,16 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
11091152 } else
11101153 try transCreateNodeOpaqueType(c);
11111154
1112 node.semicolon_token = try appendToken(c, .Semicolon, ";");
1155 const semicolon_token = try appendToken(c, .Semicolon, ";");
1156 const node = try ast.Node.VarDecl.create(c.arena, .{
1157 .name_token = name_tok,
1158 .mut_token = mut_tok,
1159 .semicolon_token = semicolon_token,
1160 }, .{
1161 .visib_token = visib_tok,
1162 .eq_token = eq_token,
1163 .init_node = init_node,
1164 });
11131165
11141166 try addTopLevelDecl(c, name, &node.base);
11151167 if (!is_unnamed)
......@@ -1117,11 +1169,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
11171169 return transCreateNodeIdentifier(c, name);
11181170}
11191171
1120fn createAlias(c: *Context, alias: var) !void {
1121 const node = try transCreateNodeVarDecl(c, true, true, alias.alias);
1122 node.eq_token = try appendToken(c, .Equal, "=");
1123 node.init_node = try transCreateNodeIdentifier(c, alias.name);
1124 node.semicolon_token = try appendToken(c, .Semicolon, ";");
1172fn createAlias(c: *Context, alias: anytype) !void {
1173 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
1174 const mut_tok = try appendToken(c, .Keyword_const, "const");
1175 const name_tok = try appendIdentifier(c, alias.alias);
1176 const eq_token = try appendToken(c, .Equal, "=");
1177 const init_node = try transCreateNodeIdentifier(c, alias.name);
1178 const semicolon_token = try appendToken(c, .Semicolon, ";");
1179
1180 const node = try ast.Node.VarDecl.create(c.arena, .{
1181 .name_token = name_tok,
1182 .mut_token = mut_tok,
1183 .semicolon_token = semicolon_token,
1184 }, .{
1185 .visib_token = visib_tok,
1186 .eq_token = eq_token,
1187 .init_node = init_node,
1188 });
11251189 return addTopLevelDecl(c, alias.alias, &node.base);
11261190}
11271191
......@@ -1155,7 +1219,7 @@ fn transStmt(
11551219 .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
11561220 .ParenExprClass => {
11571221 const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue);
1158 if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
1222 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
11591223 const node = try rp.c.arena.create(ast.Node.GroupedExpression);
11601224 node.* = .{
11611225 .lparen = try appendToken(rp.c, .LParen, "("),
......@@ -1200,7 +1264,7 @@ fn transStmt(
12001264 .OpaqueValueExprClass => {
12011265 const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?;
12021266 const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
1203 if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
1267 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
12041268 const node = try rp.c.arena.create(ast.Node.GroupedExpression);
12051269 node.* = .{
12061270 .lparen = try appendToken(rp.c, .LParen, "("),
......@@ -1230,7 +1294,7 @@ fn transBinaryOperator(
12301294 const op = ZigClangBinaryOperator_getOpcode(stmt);
12311295 const qt = ZigClangBinaryOperator_getType(stmt);
12321296 var op_token: ast.TokenIndex = undefined;
1233 var op_id: ast.Node.InfixOp.Op = undefined;
1297 var op_id: ast.Node.Tag = undefined;
12341298 switch (op) {
12351299 .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
12361300 .Comma => {
......@@ -1461,13 +1525,17 @@ fn transDeclStmtOne(
14611525 @ptrCast(*const ZigClangNamedDecl, var_decl),
14621526 ));
14631527 const mangled_name = try block_scope.makeMangledName(c, name);
1464 const node = try transCreateNodeVarDecl(c, false, ZigClangQualType_isConstQualified(qual_type), mangled_name);
1528 const mut_tok = if (ZigClangQualType_isConstQualified(qual_type))
1529 try appendToken(c, .Keyword_const, "const")
1530 else
1531 try appendToken(c, .Keyword_var, "var");
1532 const name_tok = try appendIdentifier(c, mangled_name);
14651533
14661534 _ = try appendToken(c, .Colon, ":");
14671535 const loc = ZigClangDecl_getLocation(decl);
1468 node.type_node = try transQualType(rp, qual_type, loc);
1536 const type_node = try transQualType(rp, qual_type, loc);
14691537
1470 node.eq_token = try appendToken(c, .Equal, "=");
1538 const eq_token = try appendToken(c, .Equal, "=");
14711539 var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
14721540 try transExprCoercing(rp, scope, expr, .used, .r_value)
14731541 else
......@@ -1478,8 +1546,17 @@ fn transDeclStmtOne(
14781546 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
14791547 init_node = &builtin_node.base;
14801548 }
1481 node.init_node = init_node;
1482 node.semicolon_token = try appendToken(c, .Semicolon, ";");
1549 const semicolon_token = try appendToken(c, .Semicolon, ";");
1550 const node = try ast.Node.VarDecl.create(c.arena, .{
1551 .name_token = name_tok,
1552 .mut_token = mut_tok,
1553 .semicolon_token = semicolon_token,
1554 }, .{
1555 .thread_local_token = thread_local_token,
1556 .eq_token = eq_token,
1557 .type_node = type_node,
1558 .init_node = init_node,
1559 });
14831560 return &node.base;
14841561 },
14851562 .Typedef => {
......@@ -1494,7 +1571,7 @@ fn transDeclStmtOne(
14941571 const mangled_name = try block_scope.makeMangledName(c, name);
14951572 const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse
14961573 return error.UnsupportedTranslation;
1497 return &node.base;
1574 return node;
14981575 },
14991576 else => |kind| return revertAndWarn(
15001577 rp,
......@@ -1561,7 +1638,7 @@ fn transImplicitCastExpr(
15611638 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
15621639 }
15631640
1564 const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
1641 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
15651642 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
15661643
15671644 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
......@@ -1616,7 +1693,7 @@ fn transBoolExpr(
16161693 var res = try transExpr(rp, scope, expr, used, lrvalue);
16171694
16181695 if (isBoolRes(res)) {
1619 if (!grouped and res.id == .GroupedExpression) {
1696 if (!grouped and res.tag == .GroupedExpression) {
16201697 const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
16211698 res = group.expr;
16221699 // get zig fmt to work properly
......@@ -1659,30 +1736,23 @@ fn exprIsStringLiteral(expr: *const ZigClangExpr) bool {
16591736}
16601737
16611738fn isBoolRes(res: *ast.Node) bool {
1662 switch (res.id) {
1663 .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) {
1664 .BoolOr,
1665 .BoolAnd,
1666 .EqualEqual,
1667 .BangEqual,
1668 .LessThan,
1669 .GreaterThan,
1670 .LessOrEqual,
1671 .GreaterOrEqual,
1672 => return true,
1739 switch (res.tag) {
1740 .BoolOr,
1741 .BoolAnd,
1742 .EqualEqual,
1743 .BangEqual,
1744 .LessThan,
1745 .GreaterThan,
1746 .LessOrEqual,
1747 .GreaterOrEqual,
1748 .BoolNot,
1749 .BoolLiteral,
1750 => return true,
16731751
1674 else => {},
1675 },
1676 .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) {
1677 .BoolNot => return true,
1678
1679 else => {},
1680 },
1681 .BoolLiteral => return true,
16821752 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
1683 else => {},
1753
1754 else => return false,
16841755 }
1685 return false;
16861756}
16871757
16881758fn finishBoolExpr(
......@@ -2130,7 +2200,7 @@ fn transInitListExprRecord(
21302200 var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
21312201 if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
21322202 const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
2133 raw_name = try mem.dupe(rp.c.arena, u8, name.value);
2203 raw_name = try mem.dupe(rp.c.arena, u8, name);
21342204 }
21352205 const field_name_tok = try appendIdentifier(rp.c, raw_name);
21362206
......@@ -2161,22 +2231,17 @@ fn transCreateNodeArrayType(
21612231 rp: RestorePoint,
21622232 source_loc: ZigClangSourceLocation,
21632233 ty: *const ZigClangType,
2164 len: var,
2165) TransError!*ast.Node {
2166 var node = try transCreateNodePrefixOp(
2167 rp.c,
2168 .{
2169 .ArrayType = .{
2170 .len_expr = undefined,
2171 .sentinel = null,
2172 },
2173 },
2174 .LBracket,
2175 "[",
2176 );
2177 node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, len);
2234 len: anytype,
2235) !*ast.Node {
2236 const node = try rp.c.arena.create(ast.Node.ArrayType);
2237 const op_token = try appendToken(rp.c, .LBracket, "[");
2238 const len_expr = try transCreateNodeInt(rp.c, len);
21782239 _ = try appendToken(rp.c, .RBracket, "]");
2179 node.rhs = try transType(rp, ty, source_loc);
2240 node.* = .{
2241 .op_token = op_token,
2242 .rhs = try transType(rp, ty, source_loc),
2243 .len_expr = len_expr,
2244 };
21802245 return &node.base;
21812246}
21822247
......@@ -2244,11 +2309,11 @@ fn transInitListExprArray(
22442309 &filler_init_node.base
22452310 else blk: {
22462311 const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");
2247 const mul_node = try rp.c.arena.create(ast.Node.InfixOp);
2312 const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
22482313 mul_node.* = .{
2314 .base = .{ .tag = .ArrayMult },
22492315 .op_token = mul_tok,
22502316 .lhs = &filler_init_node.base,
2251 .op = .ArrayMult,
22522317 .rhs = try transCreateNodeInt(rp.c, leftover_count),
22532318 };
22542319 break :blk &mul_node.base;
......@@ -2258,11 +2323,11 @@ fn transInitListExprArray(
22582323 return rhs_node;
22592324 }
22602325
2261 const cat_node = try rp.c.arena.create(ast.Node.InfixOp);
2326 const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
22622327 cat_node.* = .{
2328 .base = .{ .tag = .ArrayCat },
22632329 .op_token = cat_tok,
22642330 .lhs = &init_node.base,
2265 .op = .ArrayCat,
22662331 .rhs = rhs_node,
22672332 };
22682333 return &cat_node.base;
......@@ -2449,7 +2514,7 @@ fn transDoWhileLoop(
24492514 },
24502515 };
24512516 defer cond_scope.deinit();
2452 const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
2517 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
24532518 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
24542519 _ = try appendToken(rp.c, .RParen, ")");
24552520 if_node.condition = &prefix_op.base;
......@@ -2655,11 +2720,11 @@ fn transCase(
26552720 const ellips = try appendToken(rp.c, .Ellipsis3, "...");
26562721 const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
26572722
2658 const node = try rp.c.arena.create(ast.Node.InfixOp);
2723 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
26592724 node.* = .{
2725 .base = .{ .tag = .Range },
26602726 .op_token = ellips,
26612727 .lhs = lhs_node,
2662 .op = .Range,
26632728 .rhs = rhs_node,
26642729 };
26652730 break :blk &node.base;
......@@ -2855,7 +2920,7 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberE
28552920 const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl);
28562921 if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
28572922 const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
2858 break :blk try mem.dupe(rp.c.arena, u8, name.value);
2923 break :blk try mem.dupe(rp.c.arena, u8, name);
28592924 }
28602925 }
28612926 const decl = @ptrCast(*const ZigClangNamedDecl, member_decl);
......@@ -3036,7 +3101,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
30363101 else
30373102 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
30383103 .AddrOf => {
3039 const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3104 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
30403105 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
30413106 return &op_node.base;
30423107 },
......@@ -3052,7 +3117,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
30523117 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
30533118 .Minus => {
30543119 if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {
3055 const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
3120 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");
30563121 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
30573122 return &op_node.base;
30583123 } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
......@@ -3065,12 +3130,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
30653130 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
30663131 },
30673132 .Not => {
3068 const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
3133 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
30693134 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
30703135 return &op_node.base;
30713136 },
30723137 .LNot => {
3073 const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
3138 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
30743139 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
30753140 return &op_node.base;
30763141 },
......@@ -3085,7 +3150,7 @@ fn transCreatePreCrement(
30853150 rp: RestorePoint,
30863151 scope: *Scope,
30873152 stmt: *const ZigClangUnaryOperator,
3088 op: ast.Node.InfixOp.Op,
3153 op: ast.Node.Tag,
30893154 op_tok_id: std.zig.Token.Id,
30903155 bytes: []const u8,
30913156 used: ResultUsed,
......@@ -3114,12 +3179,21 @@ fn transCreatePreCrement(
31143179 defer block_scope.deinit();
31153180 const ref = try block_scope.makeMangledName(rp.c, "ref");
31163181
3117 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
3118 node.eq_token = try appendToken(rp.c, .Equal, "=");
3119 const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3182 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3183 const name_tok = try appendIdentifier(rp.c, ref);
3184 const eq_token = try appendToken(rp.c, .Equal, "=");
3185 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
31203186 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3121 node.init_node = &rhs_node.base;
3122 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3187 const init_node = &rhs_node.base;
3188 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3189 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3190 .name_token = name_tok,
3191 .mut_token = mut_tok,
3192 .semicolon_token = semicolon_token,
3193 }, .{
3194 .eq_token = eq_token,
3195 .init_node = init_node,
3196 });
31233197 try block_scope.statements.append(&node.base);
31243198
31253199 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
......@@ -3150,7 +3224,7 @@ fn transCreatePostCrement(
31503224 rp: RestorePoint,
31513225 scope: *Scope,
31523226 stmt: *const ZigClangUnaryOperator,
3153 op: ast.Node.InfixOp.Op,
3227 op: ast.Node.Tag,
31543228 op_tok_id: std.zig.Token.Id,
31553229 bytes: []const u8,
31563230 used: ResultUsed,
......@@ -3180,12 +3254,21 @@ fn transCreatePostCrement(
31803254 defer block_scope.deinit();
31813255 const ref = try block_scope.makeMangledName(rp.c, "ref");
31823256
3183 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
3184 node.eq_token = try appendToken(rp.c, .Equal, "=");
3185 const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3257 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3258 const name_tok = try appendIdentifier(rp.c, ref);
3259 const eq_token = try appendToken(rp.c, .Equal, "=");
3260 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
31863261 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3187 node.init_node = &rhs_node.base;
3188 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3262 const init_node = &rhs_node.base;
3263 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3264 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3265 .name_token = name_tok,
3266 .mut_token = mut_tok,
3267 .semicolon_token = semicolon_token,
3268 }, .{
3269 .eq_token = eq_token,
3270 .init_node = init_node,
3271 });
31893272 try block_scope.statements.append(&node.base);
31903273
31913274 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
......@@ -3193,10 +3276,19 @@ fn transCreatePostCrement(
31933276 _ = try appendToken(rp.c, .Semicolon, ";");
31943277
31953278 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
3196 const tmp_node = try transCreateNodeVarDecl(rp.c, false, true, tmp);
3197 tmp_node.eq_token = try appendToken(rp.c, .Equal, "=");
3198 tmp_node.init_node = ref_node;
3199 tmp_node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3279 const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3280 const tmp_name_tok = try appendIdentifier(rp.c, tmp);
3281 const tmp_eq_token = try appendToken(rp.c, .Equal, "=");
3282 const tmp_init_node = ref_node;
3283 const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3284 const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{
3285 .name_token = tmp_name_tok,
3286 .mut_token = tmp_mut_tok,
3287 .semicolon_token = semicolon_token,
3288 }, .{
3289 .eq_token = tmp_eq_token,
3290 .init_node = tmp_init_node,
3291 });
32003292 try block_scope.statements.append(&tmp_node.base);
32013293
32023294 const token = try appendToken(rp.c, op_tok_id, bytes);
......@@ -3254,10 +3346,10 @@ fn transCreateCompoundAssign(
32543346 rp: RestorePoint,
32553347 scope: *Scope,
32563348 stmt: *const ZigClangCompoundAssignOperator,
3257 assign_op: ast.Node.InfixOp.Op,
3349 assign_op: ast.Node.Tag,
32583350 assign_tok_id: std.zig.Token.Id,
32593351 assign_bytes: []const u8,
3260 bin_op: ast.Node.InfixOp.Op,
3352 bin_op: ast.Node.Tag,
32613353 bin_tok_id: std.zig.Token.Id,
32623354 bin_bytes: []const u8,
32633355 used: ResultUsed,
......@@ -3268,14 +3360,21 @@ fn transCreateCompoundAssign(
32683360 const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
32693361 const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
32703362 const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
3271 const is_signed = cIsSignedInteger(getExprQualType(rp.c, lhs));
3363 const lhs_qt = getExprQualType(rp.c, lhs);
3364 const rhs_qt = getExprQualType(rp.c, rhs);
3365 const is_signed = cIsSignedInteger(lhs_qt);
3366 const requires_int_cast = blk: {
3367 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
3368 const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
3369 break :blk are_integers and !are_same_sign;
3370 };
32723371 if (used == .unused) {
32733372 // common case
32743373 // c: lhs += rhs
32753374 // zig: lhs += rhs
32763375 if ((is_mod or is_div) and is_signed) {
32773376 const op_token = try appendToken(rp.c, .Equal, "=");
3278 const op_node = try rp.c.arena.create(ast.Node.InfixOp);
3377 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
32793378 const builtin = if (is_mod) "@rem" else "@divTrunc";
32803379 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
32813380 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
......@@ -3284,9 +3383,9 @@ fn transCreateCompoundAssign(
32843383 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
32853384 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
32863385 op_node.* = .{
3386 .base = .{ .tag = .Assign },
32873387 .op_token = op_token,
32883388 .lhs = lhs_node,
3289 .op = .Assign,
32903389 .rhs = &builtin_node.base,
32913390 };
32923391 _ = try appendToken(rp.c, .Semicolon, ";");
......@@ -3295,15 +3394,18 @@ fn transCreateCompoundAssign(
32953394
32963395 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
32973396 const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
3298 var rhs_node = if (is_shift)
3397 var rhs_node = if (is_shift or requires_int_cast)
32993398 try transExprCoercing(rp, scope, rhs, .used, .r_value)
33003399 else
33013400 try transExpr(rp, scope, rhs, .used, .r_value);
33023401
3303 if (is_shift) {
3402 if (is_shift or requires_int_cast) {
33043403 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
3305 const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
3306 cast_node.params()[0] = rhs_type;
3404 const cast_to_type = if (is_shift)
3405 try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
3406 else
3407 try transQualType(rp, getExprQualType(rp.c, lhs), loc);
3408 cast_node.params()[0] = cast_to_type;
33073409 _ = try appendToken(rp.c, .Comma, ",");
33083410 cast_node.params()[1] = rhs_node;
33093411 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
......@@ -3324,12 +3426,21 @@ fn transCreateCompoundAssign(
33243426 defer block_scope.deinit();
33253427 const ref = try block_scope.makeMangledName(rp.c, "ref");
33263428
3327 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);
3328 node.eq_token = try appendToken(rp.c, .Equal, "=");
3329 const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3429 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3430 const name_tok = try appendIdentifier(rp.c, ref);
3431 const eq_token = try appendToken(rp.c, .Equal, "=");
3432 const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
33303433 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
3331 node.init_node = &addr_node.base;
3332 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3434 const init_node = &addr_node.base;
3435 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3436 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3437 .name_token = name_tok,
3438 .mut_token = mut_tok,
3439 .semicolon_token = semicolon_token,
3440 }, .{
3441 .eq_token = eq_token,
3442 .init_node = init_node,
3443 });
33333444 try block_scope.statements.append(&node.base);
33343445
33353446 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
......@@ -3338,7 +3449,7 @@ fn transCreateCompoundAssign(
33383449
33393450 if ((is_mod or is_div) and is_signed) {
33403451 const op_token = try appendToken(rp.c, .Equal, "=");
3341 const op_node = try rp.c.arena.create(ast.Node.InfixOp);
3452 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
33423453 const builtin = if (is_mod) "@rem" else "@divTrunc";
33433454 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
33443455 builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);
......@@ -3347,9 +3458,9 @@ fn transCreateCompoundAssign(
33473458 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
33483459 _ = try appendToken(rp.c, .Semicolon, ";");
33493460 op_node.* = .{
3461 .base = .{ .tag = .Assign },
33503462 .op_token = op_token,
33513463 .lhs = ref_node,
3352 .op = .Assign,
33533464 .rhs = &builtin_node.base,
33543465 };
33553466 _ = try appendToken(rp.c, .Semicolon, ";");
......@@ -3358,10 +3469,13 @@ fn transCreateCompoundAssign(
33583469 const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
33593470 var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
33603471
3361 if (is_shift) {
3472 if (is_shift or requires_int_cast) {
33623473 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
3363 const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);
3364 cast_node.params()[0] = rhs_type;
3474 const cast_to_type = if (is_shift)
3475 try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
3476 else
3477 try transQualType(rp, getExprQualType(rp.c, lhs), loc);
3478 cast_node.params()[0] = cast_to_type;
33653479 _ = try appendToken(rp.c, .Comma, ",");
33663480 cast_node.params()[1] = rhs_node;
33673481 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
......@@ -3371,8 +3485,8 @@ fn transCreateCompoundAssign(
33713485 const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
33723486 _ = try appendToken(rp.c, .Semicolon, ";");
33733487
3374 const eq_token = try appendToken(rp.c, .Equal, "=");
3375 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false);
3488 const ass_eq_token = try appendToken(rp.c, .Equal, "=");
3489 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false);
33763490 try block_scope.statements.append(assign);
33773491 }
33783492
......@@ -3490,10 +3604,19 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
34903604 defer block_scope.deinit();
34913605
34923606 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
3493 const tmp_var = try transCreateNodeVarDecl(rp.c, false, true, mangled_name);
3494 tmp_var.eq_token = try appendToken(rp.c, .Equal, "=");
3495 tmp_var.init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);
3496 tmp_var.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3607 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3608 const name_tok = try appendIdentifier(rp.c, mangled_name);
3609 const eq_token = try appendToken(rp.c, .Equal, "=");
3610 const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);
3611 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3612 const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{
3613 .name_token = name_tok,
3614 .mut_token = mut_tok,
3615 .semicolon_token = semicolon_token,
3616 }, .{
3617 .eq_token = eq_token,
3618 .init_node = init_node,
3619 });
34973620 try block_scope.statements.append(&tmp_var.base);
34983621
34993622 const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
......@@ -3590,11 +3713,11 @@ fn maybeSuppressResult(
35903713 }
35913714 const lhs = try transCreateNodeIdentifier(rp.c, "_");
35923715 const op_token = try appendToken(rp.c, .Equal, "=");
3593 const op_node = try rp.c.arena.create(ast.Node.InfixOp);
3716 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
35943717 op_node.* = .{
3718 .base = .{ .tag = .Assign },
35953719 .op_token = op_token,
35963720 .lhs = lhs,
3597 .op = .Assign,
35983721 .rhs = result,
35993722 };
36003723 return &op_node.base;
......@@ -3928,9 +4051,9 @@ fn transCreateNodeAssign(
39284051 defer block_scope.deinit();
39294052
39304053 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
3931
3932 const node = try transCreateNodeVarDecl(rp.c, false, true, tmp);
3933 node.eq_token = try appendToken(rp.c, .Equal, "=");
4054 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
4055 const name_tok = try appendIdentifier(rp.c, tmp);
4056 const eq_token = try appendToken(rp.c, .Equal, "=");
39344057 var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value);
39354058 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
39364059 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
......@@ -3938,16 +4061,24 @@ fn transCreateNodeAssign(
39384061 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
39394062 rhs_node = &builtin_node.base;
39404063 }
3941 node.init_node = rhs_node;
3942 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");
4064 const init_node = rhs_node;
4065 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
4066 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
4067 .name_token = name_tok,
4068 .mut_token = mut_tok,
4069 .semicolon_token = semicolon_token,
4070 }, .{
4071 .eq_token = eq_token,
4072 .init_node = init_node,
4073 });
39434074 try block_scope.statements.append(&node.base);
39444075
39454076 const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value);
3946 const eq_token = try appendToken(rp.c, .Equal, "=");
4077 const lhs_eq_token = try appendToken(rp.c, .Equal, "=");
39474078 const ident = try transCreateNodeIdentifier(rp.c, tmp);
39484079 _ = try appendToken(rp.c, .Semicolon, ";");
39494080
3950 const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, eq_token, ident, .used, false);
4081 const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false);
39514082 try block_scope.statements.append(assign);
39524083
39534084 const break_node = try transCreateNodeBreak(rp.c, label_name);
......@@ -3961,26 +4092,26 @@ fn transCreateNodeAssign(
39614092}
39624093
39634094fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
3964 const field_access_node = try c.arena.create(ast.Node.InfixOp);
4095 const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
39654096 field_access_node.* = .{
4097 .base = .{ .tag = .Period },
39664098 .op_token = try appendToken(c, .Period, "."),
39674099 .lhs = container,
3968 .op = .Period,
39694100 .rhs = try transCreateNodeIdentifier(c, field_name),
39704101 };
39714102 return &field_access_node.base;
39724103}
39734104
3974fn transCreateNodePrefixOp(
4105fn transCreateNodeSimplePrefixOp(
39754106 c: *Context,
3976 op: ast.Node.PrefixOp.Op,
4107 comptime tag: ast.Node.Tag,
39774108 op_tok_id: std.zig.Token.Id,
39784109 bytes: []const u8,
3979) !*ast.Node.PrefixOp {
3980 const node = try c.arena.create(ast.Node.PrefixOp);
4110) !*ast.Node.SimplePrefixOp {
4111 const node = try c.arena.create(ast.Node.SimplePrefixOp);
39814112 node.* = .{
4113 .base = .{ .tag = tag },
39824114 .op_token = try appendToken(c, op_tok_id, bytes),
3983 .op = op,
39844115 .rhs = undefined, // translate and set afterward
39854116 };
39864117 return node;
......@@ -3990,7 +4121,7 @@ fn transCreateNodeInfixOp(
39904121 rp: RestorePoint,
39914122 scope: *Scope,
39924123 lhs_node: *ast.Node,
3993 op: ast.Node.InfixOp.Op,
4124 op: ast.Node.Tag,
39944125 op_token: ast.TokenIndex,
39954126 rhs_node: *ast.Node,
39964127 used: ResultUsed,
......@@ -4000,11 +4131,11 @@ fn transCreateNodeInfixOp(
40004131 try appendToken(rp.c, .LParen, "(")
40014132 else
40024133 null;
4003 const node = try rp.c.arena.create(ast.Node.InfixOp);
4134 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
40044135 node.* = .{
4136 .base = .{ .tag = op },
40054137 .op_token = op_token,
40064138 .lhs = lhs_node,
4007 .op = op,
40084139 .rhs = rhs_node,
40094140 };
40104141 if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
......@@ -4022,7 +4153,7 @@ fn transCreateNodeBoolInfixOp(
40224153 rp: RestorePoint,
40234154 scope: *Scope,
40244155 stmt: *const ZigClangBinaryOperator,
4025 op: ast.Node.InfixOp.Op,
4156 op: ast.Node.Tag,
40264157 used: ResultUsed,
40274158 grouped: bool,
40284159) !*ast.Node {
......@@ -4052,8 +4183,8 @@ fn transCreateNodePtrType(
40524183 is_const: bool,
40534184 is_volatile: bool,
40544185 op_tok_id: std.zig.Token.Id,
4055) !*ast.Node.PrefixOp {
4056 const node = try c.arena.create(ast.Node.PrefixOp);
4186) !*ast.Node.PtrType {
4187 const node = try c.arena.create(ast.Node.PtrType);
40574188 const op_token = switch (op_tok_id) {
40584189 .LBracket => blk: {
40594190 const lbracket = try appendToken(c, .LBracket, "[");
......@@ -4073,11 +4204,9 @@ fn transCreateNodePtrType(
40734204 };
40744205 node.* = .{
40754206 .op_token = op_token,
4076 .op = .{
4077 .PtrType = .{
4078 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4079 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
4080 },
4207 .ptr_info = .{
4208 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4209 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
40814210 },
40824211 .rhs = undefined, // translate and set afterward
40834212 };
......@@ -4174,7 +4303,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
41744303 return &node.base;
41754304}
41764305
4177fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
4306fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
41784307 const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
41794308 const node = try c.arena.create(ast.Node.IntegerLiteral);
41804309 node.* = .{
......@@ -4183,7 +4312,7 @@ fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
41834312 return &node.base;
41844313}
41854314
4186fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node {
4315fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node {
41874316 const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});
41884317 const node = try c.arena.create(ast.Node.FloatLiteral);
41894318 node.* = .{
......@@ -4231,28 +4360,10 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
42314360
42324361 _ = try appendToken(c, .RParen, ")");
42334362
4234 const fn_proto = try ast.Node.FnProto.alloc(c.arena, fn_params.items.len);
4235 fn_proto.* = .{
4236 .doc_comments = null,
4237 .visib_token = pub_tok,
4238 .fn_token = fn_tok,
4239 .name_token = name_tok,
4240 .params_len = fn_params.items.len,
4241 .return_type = proto_alias.return_type,
4242 .var_args_token = null,
4243 .extern_export_inline_token = inline_tok,
4244 .body_node = null,
4245 .lib_name = null,
4246 .align_expr = null,
4247 .section_expr = null,
4248 .callconv_expr = null,
4249 };
4250 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
4251
42524363 const block_lbrace = try appendToken(c, .LBrace, "{");
42534364
42544365 const return_expr = try transCreateNodeReturnExpr(c);
4255 const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.init_node.?);
4366 const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getTrailer("init_node").?);
42564367
42574368 const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
42584369 const call_params = call_expr.params();
......@@ -4276,7 +4387,18 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
42764387 .rbrace = try appendToken(c, .RBrace, "}"),
42774388 };
42784389 block.statements()[0] = &return_expr.base;
4279 fn_proto.body_node = &block.base;
4390
4391 const fn_proto = try ast.Node.FnProto.create(c.arena, .{
4392 .params_len = fn_params.items.len,
4393 .fn_token = fn_tok,
4394 .return_type = proto_alias.return_type,
4395 }, .{
4396 .visib_token = pub_tok,
4397 .name_token = name_tok,
4398 .extern_export_inline_token = inline_tok,
4399 .body_node = &block.base,
4400 });
4401 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
42804402 return &fn_proto.base;
42814403}
42824404
......@@ -4355,31 +4477,6 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE
43554477 return node;
43564478}
43574479
4358fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []const u8) !*ast.Node.VarDecl {
4359 const visib_tok = if (is_pub) try appendToken(c, .Keyword_pub, "pub") else null;
4360 const mut_tok = if (is_const) try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var");
4361 const name_tok = try appendIdentifier(c, name);
4362
4363 const node = try c.arena.create(ast.Node.VarDecl);
4364 node.* = .{
4365 .doc_comments = null,
4366 .visib_token = visib_tok,
4367 .thread_local_token = null,
4368 .name_token = name_tok,
4369 .eq_token = undefined,
4370 .mut_token = mut_tok,
4371 .comptime_token = null,
4372 .extern_export_token = null,
4373 .lib_name = null,
4374 .type_node = null,
4375 .align_node = null,
4376 .section_node = null,
4377 .init_node = null,
4378 .semicolon_token = undefined,
4379 };
4380 return node;
4381}
4382
43834480fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
43844481 const while_tok = try appendToken(c, .Keyword_while, "while");
43854482 _ = try appendToken(c, .LParen, "(");
......@@ -4436,7 +4533,7 @@ fn transCreateNodeShiftOp(
44364533 rp: RestorePoint,
44374534 scope: *Scope,
44384535 stmt: *const ZigClangBinaryOperator,
4439 op: ast.Node.InfixOp.Op,
4536 op: ast.Node.Tag,
44404537 op_tok_id: std.zig.Token.Id,
44414538 bytes: []const u8,
44424539) !*ast.Node {
......@@ -4458,11 +4555,11 @@ fn transCreateNodeShiftOp(
44584555 cast_node.params()[1] = rhs;
44594556 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
44604557
4461 const node = try rp.c.arena.create(ast.Node.InfixOp);
4558 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
44624559 node.* = .{
4560 .base = .{ .tag = op },
44634561 .op_token = op_token,
44644562 .lhs = lhs,
4465 .op = op,
44664563 .rhs = &cast_node.base,
44674564 };
44684565
......@@ -4556,12 +4653,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
45564653 .Pointer => {
45574654 const child_qt = ZigClangType_getPointeeType(ty);
45584655 if (qualTypeChildIsFnProto(child_qt)) {
4559 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4656 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
45604657 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
45614658 return &optional_node.base;
45624659 }
45634660 if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {
4564 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4661 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
45654662 const pointer_node = try transCreateNodePtrType(
45664663 rp.c,
45674664 ZigClangQualType_isConstQualified(child_qt),
......@@ -4586,21 +4683,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
45864683
45874684 const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
45884685 const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
4589 var node = try transCreateNodePrefixOp(
4590 rp.c,
4591 .{
4592 .ArrayType = .{
4593 .len_expr = undefined,
4594 .sentinel = null,
4595 },
4596 },
4597 .LBracket,
4598 "[",
4599 );
4600 node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, size);
4601 _ = try appendToken(rp.c, .RBracket, "]");
4602 node.rhs = try transQualType(rp, ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc);
4603 return &node.base;
4686 const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty));
4687 return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
46044688 },
46054689 .IncompleteArray => {
46064690 const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);
......@@ -4794,19 +4878,12 @@ fn finishTransFnProto(
47944878 }
47954879 }
47964880
4797 if (is_var_args) {
4881 const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
47984882 if (param_count > 0) {
47994883 _ = try appendToken(rp.c, .Comma, ",");
48004884 }
4801
4802 fn_params.addOneAssumeCapacity().* = .{
4803 .doc_comments = null,
4804 .comptime_token = null,
4805 .noalias_token = null,
4806 .name_token = null,
4807 .param_type = .{ .var_args = try appendToken(rp.c, .Ellipsis3, "...") },
4808 };
4809 }
4885 break :blk try appendToken(rp.c, .Ellipsis3, "...");
4886 } else null;
48104887
48114888 const rparen_tok = try appendToken(rp.c, .RParen, ")");
48124889
......@@ -4872,44 +4949,53 @@ fn finishTransFnProto(
48724949 }
48734950 };
48744951
4875 const fn_proto = try ast.Node.FnProto.alloc(rp.c.arena, fn_params.items.len);
4876 fn_proto.* = .{
4877 .doc_comments = null,
4878 .visib_token = pub_tok,
4879 .fn_token = fn_tok,
4880 .name_token = name_tok,
4952 // We need to reserve an undefined (but non-null) body node to set later.
4953 var body_node: ?*ast.Node = null;
4954 if (fn_decl_context) |ctx| {
4955 if (ctx.has_body) {
4956 // TODO: we should be able to use undefined here but
4957 // it causes a bug. This is undefined without zig language
4958 // being aware of it.
4959 body_node = @intToPtr(*ast.Node, 0x08);
4960 }
4961 }
4962
4963 const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
48814964 .params_len = fn_params.items.len,
48824965 .return_type = .{ .Explicit = return_type_node },
4883 .var_args_token = null, // TODO this field is broken in the AST data model
4966 .fn_token = fn_tok,
4967 }, .{
4968 .visib_token = pub_tok,
4969 .name_token = name_tok,
48844970 .extern_export_inline_token = extern_export_inline_tok,
4885 .body_node = null,
4886 .lib_name = null,
48874971 .align_expr = align_expr,
48884972 .section_expr = linksection_expr,
48894973 .callconv_expr = callconv_expr,
4890 };
4974 .body_node = body_node,
4975 .var_args_token = var_args_token,
4976 });
48914977 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
48924978 return fn_proto;
48934979}
48944980
48954981fn revertAndWarn(
48964982 rp: RestorePoint,
4897 err: var,
4983 err: anytype,
48984984 source_loc: ZigClangSourceLocation,
48994985 comptime format: []const u8,
4900 args: var,
4986 args: anytype,
49014987) (@TypeOf(err) || error{OutOfMemory}) {
49024988 rp.activate();
49034989 try emitWarning(rp.c, source_loc, format, args);
49044990 return err;
49054991}
49064992
4907fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: var) !void {
4993fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: anytype) !void {
49084994 const args_prefix = .{c.locStr(loc)};
49094995 _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args);
49104996}
49114997
4912pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void {
4998pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
49134999 // pub const name = @compileError(msg);
49145000 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
49155001 const const_tok = try appendToken(c, .Keyword_const, "const");
......@@ -4935,23 +5021,15 @@ pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comp
49355021 };
49365022 call_node.params()[0] = &msg_node.base;
49375023
4938 const var_decl_node = try c.arena.create(ast.Node.VarDecl);
4939 var_decl_node.* = .{
4940 .doc_comments = null,
4941 .visib_token = pub_tok,
4942 .thread_local_token = null,
5024 const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
49435025 .name_token = name_tok,
4944 .eq_token = eq_tok,
49455026 .mut_token = const_tok,
4946 .comptime_token = null,
4947 .extern_export_token = null,
4948 .lib_name = null,
4949 .type_node = null,
4950 .align_node = null,
4951 .section_node = null,
4952 .init_node = &call_node.base,
49535027 .semicolon_token = semi_tok,
4954 };
5028 }, .{
5029 .visib_token = pub_tok,
5030 .eq_token = eq_tok,
5031 .init_node = &call_node.base,
5032 });
49555033 try addTopLevelDecl(c, name, &var_decl_node.base);
49565034}
49575035
......@@ -4960,7 +5038,7 @@ fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenInd
49605038 return appendTokenFmt(c, token_id, "{}", .{bytes});
49615039}
49625040
4963fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: var) !ast.TokenIndex {
5041fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
49645042 assert(token_id != .Invalid);
49655043
49665044 try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
......@@ -5144,10 +5222,12 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
51445222fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
51455223 const scope = &c.global_scope.base;
51465224
5147 const node = try transCreateNodeVarDecl(c, true, true, name);
5148 node.eq_token = try appendToken(c, .Equal, "=");
5225 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
5226 const mut_tok = try appendToken(c, .Keyword_const, "const");
5227 const name_tok = try appendIdentifier(c, name);
5228 const eq_token = try appendToken(c, .Equal, "=");
51495229
5150 node.init_node = try parseCExpr(c, it, source, source_loc, scope);
5230 const init_node = try parseCExpr(c, it, source, source_loc, scope);
51515231 const last = it.next().?;
51525232 if (last.id != .Eof and last.id != .Nl)
51535233 return failDecl(
......@@ -5158,7 +5238,16 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
51585238 .{@tagName(last.id)},
51595239 );
51605240
5161 node.semicolon_token = try appendToken(c, .Semicolon, ";");
5241 const semicolon_token = try appendToken(c, .Semicolon, ";");
5242 const node = try ast.Node.VarDecl.create(c.arena, .{
5243 .name_token = name_tok,
5244 .mut_token = mut_tok,
5245 .semicolon_token = semicolon_token,
5246 }, .{
5247 .visib_token = visib_tok,
5248 .eq_token = eq_token,
5249 .init_node = init_node,
5250 });
51625251 _ = try c.global_scope.macro_table.put(name, &node.base);
51635252}
51645253
......@@ -5202,10 +5291,9 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52025291 const param_name_tok = try appendIdentifier(c, mangled_name);
52035292 _ = try appendToken(c, .Colon, ":");
52045293
5205 const token_index = try appendToken(c, .Keyword_var, "var");
5206 const identifier = try c.arena.create(ast.Node.Identifier);
5207 identifier.* = .{
5208 .token = token_index,
5294 const any_type = try c.arena.create(ast.Node.AnyType);
5295 any_type.* = .{
5296 .token = try appendToken(c, .Keyword_anytype, "anytype"),
52095297 };
52105298
52115299 (try fn_params.addOne()).* = .{
......@@ -5213,7 +5301,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52135301 .comptime_token = null,
52145302 .noalias_token = null,
52155303 .name_token = param_name_tok,
5216 .param_type = .{ .type_expr = &identifier.base },
5304 .param_type = .{ .any_type = &any_type.base },
52175305 };
52185306
52195307 if (it.peek().?.id != .Comma)
......@@ -5236,24 +5324,6 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52365324
52375325 const type_of = try c.createBuiltinCall("@TypeOf", 1);
52385326
5239 const fn_proto = try ast.Node.FnProto.alloc(c.arena, fn_params.items.len);
5240 fn_proto.* = .{
5241 .visib_token = pub_tok,
5242 .extern_export_inline_token = inline_tok,
5243 .fn_token = fn_tok,
5244 .name_token = name_tok,
5245 .params_len = fn_params.items.len,
5246 .return_type = .{ .Explicit = &type_of.base },
5247 .doc_comments = null,
5248 .var_args_token = null,
5249 .body_node = null,
5250 .lib_name = null,
5251 .align_expr = null,
5252 .section_expr = null,
5253 .callconv_expr = null,
5254 };
5255 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
5256
52575327 const return_expr = try transCreateNodeReturnExpr(c);
52585328 const expr = try parseCExpr(c, it, source, source_loc, scope);
52595329 const last = it.next().?;
......@@ -5266,10 +5336,10 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52665336 .{@tagName(last.id)},
52675337 );
52685338 _ = try appendToken(c, .Semicolon, ";");
5269 const type_of_arg = if (expr.id != .Block) expr else blk: {
5339 const type_of_arg = if (expr.tag != .Block) expr else blk: {
52705340 const blk = @fieldParentPtr(ast.Node.Block, "base", expr);
52715341 const blk_last = blk.statements()[blk.statements_len - 1];
5272 std.debug.assert(blk_last.id == .ControlFlowExpression);
5342 std.debug.assert(blk_last.tag == .ControlFlowExpression);
52735343 const br = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", blk_last);
52745344 break :blk br.rhs.?;
52755345 };
......@@ -5279,7 +5349,18 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52795349
52805350 try block_scope.statements.append(&return_expr.base);
52815351 const block_node = try block_scope.complete(c);
5282 fn_proto.body_node = &block_node.base;
5352 const fn_proto = try ast.Node.FnProto.create(c.arena, .{
5353 .fn_token = fn_tok,
5354 .params_len = fn_params.items.len,
5355 .return_type = .{ .Explicit = &type_of.base },
5356 }, .{
5357 .visib_token = pub_tok,
5358 .extern_export_inline_token = inline_tok,
5359 .name_token = name_tok,
5360 .body_node = &block_node.base,
5361 });
5362 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
5363
52835364 _ = try c.global_scope.macro_table.put(name, &fn_proto.base);
52845365}
52855366
......@@ -5320,11 +5401,11 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
53205401 // suppress result
53215402 const lhs = try transCreateNodeIdentifier(c, "_");
53225403 const op_token = try appendToken(c, .Equal, "=");
5323 const op_node = try c.arena.create(ast.Node.InfixOp);
5404 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
53245405 op_node.* = .{
5406 .base = .{ .tag = .Assign },
53255407 .op_token = op_token,
53265408 .lhs = lhs,
5327 .op = .Assign,
53285409 .rhs = last,
53295410 };
53305411 try block_scope.statements.append(&op_node.base);
......@@ -5668,161 +5749,23 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
56685749
56695750 const lparen = try appendToken(c, .LParen, "(");
56705751
5671 if (saw_integer_literal) {
5672 //( if (@typeInfo(dest) == .Pointer))
5673 // @intToPtr(dest, x)
5674 //else
5675 // @as(dest, x) )
5676 const if_node = try transCreateNodeIf(c);
5677 const type_info_node = try c.createBuiltinCall("@typeInfo", 1);
5678 type_info_node.params()[0] = inner_node;
5679 type_info_node.rparen_token = try appendToken(c, .LParen, ")");
5680 const cmp_node = try c.arena.create(ast.Node.InfixOp);
5681 cmp_node.* = .{
5682 .op_token = try appendToken(c, .EqualEqual, "=="),
5683 .lhs = &type_info_node.base,
5684 .op = .EqualEqual,
5685 .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
5686 };
5687 if_node.condition = &cmp_node.base;
5688 _ = try appendToken(c, .RParen, ")");
5689
5690 const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
5691 int_to_ptr.params()[0] = inner_node;
5692 int_to_ptr.params()[1] = node_to_cast;
5693 int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
5694 if_node.body = &int_to_ptr.base;
5695
5696 const else_node = try transCreateNodeElse(c);
5697 if_node.@"else" = else_node;
5698
5699 const as_node = try c.createBuiltinCall("@as", 2);
5700 as_node.params()[0] = inner_node;
5701 as_node.params()[1] = node_to_cast;
5702 as_node.rparen_token = try appendToken(c, .RParen, ")");
5703 else_node.body = &as_node.base;
5704
5705 const group_node = try c.arena.create(ast.Node.GroupedExpression);
5706 group_node.* = .{
5707 .lparen = lparen,
5708 .expr = &if_node.base,
5709 .rparen = try appendToken(c, .RParen, ")"),
5710 };
5711 return &group_node.base;
5712 }
5713
5714 //( if (@typeInfo(@TypeOf(x)) == .Pointer)
5715 // @ptrCast(dest, @alignCast(@alignOf(dest.Child), x))
5716 //else if (@typeInfo(@TypeOf(x)) == .Int and @typeInfo(dest) == .Pointer))
5717 // @intToPtr(dest, x)
5718 //else
5719 // @as(dest, x) )
5720
5721 const if_1 = try transCreateNodeIf(c);
5722 const type_info_1 = try c.createBuiltinCall("@typeInfo", 1);
5723 const type_of_1 = try c.createBuiltinCall("@TypeOf", 1);
5724 type_info_1.params()[0] = &type_of_1.base;
5725 type_of_1.params()[0] = node_to_cast;
5726 type_of_1.rparen_token = try appendToken(c, .RParen, ")");
5727 type_info_1.rparen_token = try appendToken(c, .RParen, ")");
5728
5729 const cmp_1 = try c.arena.create(ast.Node.InfixOp);
5730 cmp_1.* = .{
5731 .op_token = try appendToken(c, .EqualEqual, "=="),
5732 .lhs = &type_info_1.base,
5733 .op = .EqualEqual,
5734 .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
5735 };
5736 if_1.condition = &cmp_1.base;
5737 _ = try appendToken(c, .RParen, ")");
5752 //(@import("std").meta.cast(dest, x))
5753 const import_fn_call = try c.createBuiltinCall("@import", 1);
5754 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
5755 import_fn_call.params()[0] = std_node;
5756 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
5757 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
5758 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
57385759
5739 const period_tok = try appendToken(c, .Period, ".");
5740 const child_ident = try transCreateNodeIdentifier(c, "Child");
5741 const inner_node_child = try c.arena.create(ast.Node.InfixOp);
5742 inner_node_child.* = .{
5743 .op_token = period_tok,
5744 .lhs = inner_node,
5745 .op = .Period,
5746 .rhs = child_ident,
5747 };
5748
5749 const align_of = try c.createBuiltinCall("@alignOf", 1);
5750 align_of.params()[0] = &inner_node_child.base;
5751 align_of.rparen_token = try appendToken(c, .RParen, ")");
5752 // hack to get zig fmt to render a comma in builtin calls
5753 _ = try appendToken(c, .Comma, ",");
5754
5755 const align_cast = try c.createBuiltinCall("@alignCast", 2);
5756 align_cast.params()[0] = &align_of.base;
5757 align_cast.params()[1] = node_to_cast;
5758 align_cast.rparen_token = try appendToken(c, .RParen, ")");
5759
5760 const ptr_cast = try c.createBuiltinCall("@ptrCast", 2);
5761 ptr_cast.params()[0] = inner_node;
5762 ptr_cast.params()[1] = &align_cast.base;
5763 ptr_cast.rparen_token = try appendToken(c, .RParen, ")");
5764 if_1.body = &ptr_cast.base;
5765
5766 const else_1 = try transCreateNodeElse(c);
5767 if_1.@"else" = else_1;
5768
5769 const if_2 = try transCreateNodeIf(c);
5770 const type_info_2 = try c.createBuiltinCall("@typeInfo", 1);
5771 const type_of_2 = try c.createBuiltinCall("@TypeOf", 1);
5772 type_info_2.params()[0] = &type_of_2.base;
5773 type_of_2.params()[0] = node_to_cast;
5774 type_of_2.rparen_token = try appendToken(c, .RParen, ")");
5775 type_info_2.rparen_token = try appendToken(c, .RParen, ")");
5776
5777 const cmp_2 = try c.arena.create(ast.Node.InfixOp);
5778 cmp_2.* = .{
5779 .op_token = try appendToken(c, .EqualEqual, "=="),
5780 .lhs = &type_info_2.base,
5781 .op = .EqualEqual,
5782 .rhs = try transCreateNodeEnumLiteral(c, "Int"),
5783 };
5784 if_2.condition = &cmp_2.base;
5785 const cmp_4 = try c.arena.create(ast.Node.InfixOp);
5786 cmp_4.* = .{
5787 .op_token = try appendToken(c, .Keyword_and, "and"),
5788 .lhs = &cmp_2.base,
5789 .op = .BoolAnd,
5790 .rhs = undefined,
5791 };
5792 const type_info_3 = try c.createBuiltinCall("@typeInfo", 1);
5793 type_info_3.params()[0] = inner_node;
5794 type_info_3.rparen_token = try appendToken(c, .LParen, ")");
5795 const cmp_3 = try c.arena.create(ast.Node.InfixOp);
5796 cmp_3.* = .{
5797 .op_token = try appendToken(c, .EqualEqual, "=="),
5798 .lhs = &type_info_3.base,
5799 .op = .EqualEqual,
5800 .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
5801 };
5802 cmp_4.rhs = &cmp_3.base;
5803 if_2.condition = &cmp_4.base;
5804 else_1.body = &if_2.base;
5805 _ = try appendToken(c, .RParen, ")");
5806
5807 const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
5808 int_to_ptr.params()[0] = inner_node;
5809 int_to_ptr.params()[1] = node_to_cast;
5810 int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
5811 if_2.body = &int_to_ptr.base;
5812
5813 const else_2 = try transCreateNodeElse(c);
5814 if_2.@"else" = else_2;
5815
5816 const as = try c.createBuiltinCall("@as", 2);
5817 as.params()[0] = inner_node;
5818 as.params()[1] = node_to_cast;
5819 as.rparen_token = try appendToken(c, .RParen, ")");
5820 else_2.body = &as.base;
5760 const cast_fn_call = try c.createCall(outer_field_access, 2);
5761 cast_fn_call.params()[0] = inner_node;
5762 cast_fn_call.params()[1] = node_to_cast;
5763 cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
58215764
58225765 const group_node = try c.arena.create(ast.Node.GroupedExpression);
58235766 group_node.* = .{
58245767 .lparen = lparen,
5825 .expr = &if_1.base,
5768 .expr = &cast_fn_call.base,
58265769 .rparen = try appendToken(c, .RParen, ")"),
58275770 };
58285771 return &group_node.base;
......@@ -5841,9 +5784,60 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
58415784 }
58425785}
58435786
5787fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
5788 return switch (tag) {
5789 .Add,
5790 .AddWrap,
5791 .ArrayCat,
5792 .ArrayMult,
5793 .Assign,
5794 .AssignBitAnd,
5795 .AssignBitOr,
5796 .AssignBitShiftLeft,
5797 .AssignBitShiftRight,
5798 .AssignBitXor,
5799 .AssignDiv,
5800 .AssignSub,
5801 .AssignSubWrap,
5802 .AssignMod,
5803 .AssignAdd,
5804 .AssignAddWrap,
5805 .AssignMul,
5806 .AssignMulWrap,
5807 .BangEqual,
5808 .BitAnd,
5809 .BitOr,
5810 .BitShiftLeft,
5811 .BitShiftRight,
5812 .BitXor,
5813 .BoolAnd,
5814 .BoolOr,
5815 .Div,
5816 .EqualEqual,
5817 .ErrorUnion,
5818 .GreaterOrEqual,
5819 .GreaterThan,
5820 .LessOrEqual,
5821 .LessThan,
5822 .MergeErrorSets,
5823 .Mod,
5824 .Mul,
5825 .MulWrap,
5826 .Period,
5827 .Range,
5828 .Sub,
5829 .SubWrap,
5830 .UnwrapOptional,
5831 .Catch,
5832 => true,
5833
5834 else => false,
5835 };
5836}
5837
58445838fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
58455839 if (!isBoolRes(node)) {
5846 if (node.id != .InfixOp) return node;
5840 if (!nodeIsInfixOp(node.tag)) return node;
58475841
58485842 const group_node = try c.arena.create(ast.Node.GroupedExpression);
58495843 group_node.* = .{
......@@ -5862,7 +5856,7 @@ fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
58625856
58635857fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
58645858 if (isBoolRes(node)) {
5865 if (node.id != .InfixOp) return node;
5859 if (!nodeIsInfixOp(node.tag)) return node;
58665860
58675861 const group_node = try c.arena.create(ast.Node.GroupedExpression);
58685862 group_node.* = .{
......@@ -5875,11 +5869,11 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
58755869
58765870 const op_token = try appendToken(c, .BangEqual, "!=");
58775871 const zero = try transCreateNodeInt(c, 0);
5878 const res = try c.arena.create(ast.Node.InfixOp);
5872 const res = try c.arena.create(ast.Node.SimpleInfixOp);
58795873 res.* = .{
5874 .base = .{ .tag = .BangEqual },
58805875 .op_token = op_token,
58815876 .lhs = node,
5882 .op = .BangEqual,
58835877 .rhs = zero,
58845878 };
58855879 const group_node = try c.arena.create(ast.Node.GroupedExpression);
......@@ -5896,7 +5890,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
58965890 while (true) {
58975891 const tok = it.next().?;
58985892 var op_token: ast.TokenIndex = undefined;
5899 var op_id: ast.Node.InfixOp.Op = undefined;
5893 var op_id: ast.Node.Tag = undefined;
59005894 var bool_op = false;
59015895 switch (tok.id) {
59025896 .Period => {
......@@ -5950,7 +5944,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
59505944 if (prev_id == .Keyword_void) {
59515945 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
59525946 ptr.rhs = node;
5953 const optional_node = try transCreateNodePrefixOp(c, .OptionalType, .QuestionMark, "?");
5947 const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
59545948 optional_node.rhs = &ptr.base;
59555949 return &optional_node.base;
59565950 } else {
......@@ -6067,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
60676061 node = &call_node.base;
60686062 continue;
60696063 },
6064 .LBrace => {
6065 // must come immediately after `node`
6066 _ = try appendToken(c, .Comma, ",");
6067
6068 const dot = try appendToken(c, .Period, ".");
6069 _ = try appendToken(c, .LBrace, "{");
6070
6071 var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
6072 defer init_vals.deinit();
6073
6074 while (true) {
6075 const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6076 try init_vals.append(val);
6077 const next = it.next().?;
6078 if (next.id == .Comma)
6079 _ = try appendToken(c, .Comma, ",")
6080 else if (next.id == .RBrace)
6081 break
6082 else {
6083 const first_tok = it.list.at(0);
6084 try failDecl(
6085 c,
6086 source_loc,
6087 source[first_tok.start..first_tok.end],
6088 "unable to translate C expr: expected ',' or '}}'",
6089 .{},
6090 );
6091 return error.ParseError;
6092 }
6093 }
6094 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
6095 tuple_node.* = .{
6096 .dot = dot,
6097 .list_len = init_vals.items.len,
6098 .rtoken = try appendToken(c, .RBrace, "}"),
6099 };
6100 mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
6101
6102
6103 //(@import("std").mem.zeroInit(T, .{x}))
6104 const import_fn_call = try c.createBuiltinCall("@import", 1);
6105 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6106 import_fn_call.params()[0] = std_node;
6107 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6108 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
6109 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
6110
6111 const zero_init_call = try c.createCall(outer_field_access, 2);
6112 zero_init_call.params()[0] = node;
6113 zero_init_call.params()[1] = &tuple_node.base;
6114 zero_init_call.rtoken = try appendToken(c, .RParen, ")");
6115
6116 node = &zero_init_call.base;
6117 continue;
6118 },
60706119 .BangEqual => {
60716120 op_token = try appendToken(c, .BangEqual, "!=");
60726121 op_id = .BangEqual;
......@@ -6103,11 +6152,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
61036152 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
61046153 const lhs_node = try cast_fn(c, node);
61056154 const rhs_node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6106 const op_node = try c.arena.create(ast.Node.InfixOp);
6155 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
61076156 op_node.* = .{
6157 .base = .{ .tag = op_id },
61086158 .op_token = op_token,
61096159 .lhs = lhs_node,
6110 .op = op_id,
61116160 .rhs = try cast_fn(c, rhs_node),
61126161 };
61136162 node = &op_node.base;
......@@ -6119,18 +6168,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
61196168
61206169 switch (op_tok.id) {
61216170 .Bang => {
6122 const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!");
6171 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
61236172 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
61246173 return &node.base;
61256174 },
61266175 .Minus => {
6127 const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-");
6176 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
61286177 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
61296178 return &node.base;
61306179 },
61316180 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),
61326181 .Tilde => {
6133 const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~");
6182 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
61346183 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
61356184 return &node.base;
61366185 },
......@@ -6139,7 +6188,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
61396188 return try transCreateNodePtrDeref(c, node);
61406189 },
61416190 .Ampersand => {
6142 const node = try transCreateNodePrefixOp(c, .AddressOf, .Ampersand, "&");
6191 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
61436192 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
61446193 return &node.base;
61456194 },
......@@ -6160,44 +6209,61 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
61606209}
61616210
61626211fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
6163 if (node.id == .ContainerDecl) {
6164 return node;
6165 } else if (node.id == .PrefixOp) {
6166 return node;
6167 } else if (node.cast(ast.Node.Identifier)) |ident| {
6168 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {
6169 if (kv.value.cast(ast.Node.VarDecl)) |var_decl|
6170 return getContainer(c, var_decl.init_node.?);
6171 }
6172 } else if (node.cast(ast.Node.InfixOp)) |infix| {
6173 if (infix.op != .Period)
6174 return null;
6175 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6176 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6177 for (container.fieldsAndDecls()) |field_ref| {
6178 const field = field_ref.cast(ast.Node.ContainerField).?;
6179 const ident = infix.rhs.cast(ast.Node.Identifier).?;
6180 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6181 return getContainer(c, field.type_expr.?);
6212 switch (node.tag) {
6213 .ContainerDecl,
6214 .AddressOf,
6215 .Await,
6216 .BitNot,
6217 .BoolNot,
6218 .OptionalType,
6219 .Negation,
6220 .NegationWrap,
6221 .Resume,
6222 .Try,
6223 .ArrayType,
6224 .ArrayTypeSentinel,
6225 .PtrType,
6226 .SliceType,
6227 => return node,
6228
6229 .Identifier => {
6230 const ident = node.cast(ast.Node.Identifier).?;
6231 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6232 if (value.cast(ast.Node.VarDecl)) |var_decl|
6233 return getContainer(c, var_decl.getTrailer("init_node").?);
6234 }
6235 },
6236
6237 .Period => {
6238 const infix = node.castTag(.Period).?;
6239
6240 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6241 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6242 for (container.fieldsAndDecls()) |field_ref| {
6243 const field = field_ref.cast(ast.Node.ContainerField).?;
6244 const ident = infix.rhs.cast(ast.Node.Identifier).?;
6245 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6246 return getContainer(c, field.type_expr.?);
6247 }
61826248 }
61836249 }
61846250 }
6185 }
6251 },
6252
6253 else => {},
61866254 }
61876255 return null;
61886256}
61896257
61906258fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
61916259 if (ref.cast(ast.Node.Identifier)) |ident| {
6192 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {
6193 if (kv.value.cast(ast.Node.VarDecl)) |var_decl| {
6194 if (var_decl.type_node) |ty|
6260 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6261 if (value.cast(ast.Node.VarDecl)) |var_decl| {
6262 if (var_decl.getTrailer("type_node")) |ty|
61956263 return getContainer(c, ty);
61966264 }
61976265 }
6198 } else if (ref.cast(ast.Node.InfixOp)) |infix| {
6199 if (infix.op != .Period)
6200 return null;
6266 } else if (ref.castTag(.Period)) |infix| {
62016267 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
62026268 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
62036269 for (container.fieldsAndDecls()) |field_ref| {
......@@ -6215,13 +6281,11 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
62156281}
62166282
62176283fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
6218 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;
6284 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getTrailer("init_node").? else return null;
62196285 if (getContainerTypeOf(c, init)) |ty_node| {
6220 if (ty_node.cast(ast.Node.PrefixOp)) |prefix| {
6221 if (prefix.op == .OptionalType) {
6222 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
6223 return fn_proto;
6224 }
6286 if (ty_node.castTag(.OptionalType)) |prefix| {
6287 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
6288 return fn_proto;
62256289 }
62266290 }
62276291 }
......@@ -6229,8 +6293,7 @@ fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
62296293}
62306294
62316295fn addMacros(c: *Context) !void {
6232 var macro_it = c.global_scope.macro_table.iterator();
6233 while (macro_it.next()) |kv| {
6296 for (c.global_scope.macro_table.items()) |kv| {
62346297 if (getFnProto(c, kv.value)) |proto_node| {
62356298 // If a macro aliases a global variable which is a function pointer, we conclude that
62366299 // the macro is intended to represent a function that assumes the function pointer
src-self-hosted/type.zig+574-20
......@@ -21,8 +21,14 @@ pub const Type = extern union {
2121 switch (self.tag()) {
2222 .u8,
2323 .i8,
24 .isize,
24 .u16,
25 .i16,
26 .u32,
27 .i32,
28 .u64,
29 .i64,
2530 .usize,
31 .isize,
2632 .c_short,
2733 .c_ushort,
2834 .c_int,
......@@ -54,8 +60,10 @@ pub const Type = extern union {
5460 .@"undefined" => return .Undefined,
5561
5662 .fn_noreturn_no_args => return .Fn,
63 .fn_void_no_args => return .Fn,
5764 .fn_naked_noreturn_no_args => return .Fn,
5865 .fn_ccc_void_no_args => return .Fn,
66 .function => return .Fn,
5967
6068 .array, .array_u8_sentinel_0 => return .Array,
6169 .single_const_pointer => return .Pointer,
......@@ -112,6 +120,12 @@ pub const Type = extern union {
112120 .Undefined => return true,
113121 .Null => return true,
114122 .Pointer => {
123 // Hot path for common case:
124 if (a.cast(Payload.SingleConstPointer)) |a_payload| {
125 if (b.cast(Payload.SingleConstPointer)) |b_payload| {
126 return eql(a_payload.pointee_type, b_payload.pointee_type);
127 }
128 }
115129 const is_slice_a = isSlice(a);
116130 const is_slice_b = isSlice(b);
117131 if (is_slice_a != is_slice_b)
......@@ -119,10 +133,14 @@ pub const Type = extern union {
119133 @panic("TODO implement more pointer Type equality comparison");
120134 },
121135 .Int => {
122 if (a.tag() != b.tag()) {
123 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
136 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
137 const a_is_named_int = a.isNamedInt();
138 const b_is_named_int = b.isNamedInt();
139 if (a_is_named_int != b_is_named_int)
124140 return false;
125 }
141 if (a_is_named_int)
142 return a.tag() == b.tag();
143 // Remaining cases are arbitrary sized integers.
126144 // The target will not be branched upon, because we handled target-dependent cases above.
127145 const info_a = a.intInfo(@as(Target, undefined));
128146 const info_b = b.intInfo(@as(Target, undefined));
......@@ -145,6 +163,22 @@ pub const Type = extern union {
145163 return sentinel_b == null;
146164 }
147165 },
166 .Fn => {
167 if (!a.fnReturnType().eql(b.fnReturnType()))
168 return false;
169 if (a.fnCallingConvention() != b.fnCallingConvention())
170 return false;
171 const a_param_len = a.fnParamLen();
172 const b_param_len = b.fnParamLen();
173 if (a_param_len != b_param_len)
174 return false;
175 var i: usize = 0;
176 while (i < a_param_len) : (i += 1) {
177 if (!a.fnParamType(i).eql(b.fnParamType(i)))
178 return false;
179 }
180 return true;
181 },
148182 .Float,
149183 .Struct,
150184 .Optional,
......@@ -152,23 +186,114 @@ pub const Type = extern union {
152186 .ErrorSet,
153187 .Enum,
154188 .Union,
155 .Fn,
156189 .BoundFn,
157190 .Opaque,
158191 .Frame,
159192 .AnyFrame,
160193 .Vector,
161194 .EnumLiteral,
162 => @panic("TODO implement more Type equality comparison"),
195 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
196 }
197 }
198
199 pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
200 if (self.tag_if_small_enough < Tag.no_payload_count) {
201 return Type{ .tag_if_small_enough = self.tag_if_small_enough };
202 } else switch (self.ptr_otherwise.tag) {
203 .u8,
204 .i8,
205 .u16,
206 .i16,
207 .u32,
208 .i32,
209 .u64,
210 .i64,
211 .usize,
212 .isize,
213 .c_short,
214 .c_ushort,
215 .c_int,
216 .c_uint,
217 .c_long,
218 .c_ulong,
219 .c_longlong,
220 .c_ulonglong,
221 .c_longdouble,
222 .c_void,
223 .f16,
224 .f32,
225 .f64,
226 .f128,
227 .bool,
228 .void,
229 .type,
230 .anyerror,
231 .comptime_int,
232 .comptime_float,
233 .noreturn,
234 .@"null",
235 .@"undefined",
236 .fn_noreturn_no_args,
237 .fn_void_no_args,
238 .fn_naked_noreturn_no_args,
239 .fn_ccc_void_no_args,
240 .single_const_pointer_to_comptime_int,
241 .const_slice_u8,
242 => unreachable,
243
244 .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
245 .array => {
246 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
247 const new_payload = try allocator.create(Payload.Array);
248 new_payload.* = .{
249 .base = payload.base,
250 .len = payload.len,
251 .elem_type = try payload.elem_type.copy(allocator),
252 };
253 return Type{ .ptr_otherwise = &new_payload.base };
254 },
255 .single_const_pointer => {
256 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
257 const new_payload = try allocator.create(Payload.SingleConstPointer);
258 new_payload.* = .{
259 .base = payload.base,
260 .pointee_type = try payload.pointee_type.copy(allocator),
261 };
262 return Type{ .ptr_otherwise = &new_payload.base };
263 },
264 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
265 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
266 .function => {
267 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
268 const new_payload = try allocator.create(Payload.Function);
269 const param_types = try allocator.alloc(Type, payload.param_types.len);
270 for (payload.param_types) |param_type, i| {
271 param_types[i] = try param_type.copy(allocator);
272 }
273 new_payload.* = .{
274 .base = payload.base,
275 .return_type = try payload.return_type.copy(allocator),
276 .param_types = param_types,
277 .cc = payload.cc,
278 };
279 return Type{ .ptr_otherwise = &new_payload.base };
280 },
163281 }
164282 }
165283
284 fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
285 const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
286 const new_payload = try allocator.create(T);
287 new_payload.* = payload.*;
288 return Type{ .ptr_otherwise = &new_payload.base };
289 }
290
166291 pub fn format(
167292 self: Type,
168293 comptime fmt: []const u8,
169294 options: std.fmt.FormatOptions,
170 out_stream: var,
171 ) !void {
295 out_stream: anytype,
296 ) @TypeOf(out_stream).Error!void {
172297 comptime assert(fmt.len == 0);
173298 var ty = self;
174299 while (true) {
......@@ -176,8 +301,14 @@ pub const Type = extern union {
176301 switch (t) {
177302 .u8,
178303 .i8,
179 .isize,
304 .u16,
305 .i16,
306 .u32,
307 .i32,
308 .u64,
309 .i64,
180310 .usize,
311 .isize,
181312 .c_short,
182313 .c_ushort,
183314 .c_int,
......@@ -206,9 +337,20 @@ pub const Type = extern union {
206337
207338 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
208339 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
340 .fn_void_no_args => return out_stream.writeAll("fn() void"),
209341 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
210342 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
211343 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
344 .function => {
345 const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise);
346 try out_stream.writeAll("fn(");
347 for (payload.param_types) |param_type, i| {
348 if (i != 0) try out_stream.writeAll(", ");
349 try param_type.format("", .{}, out_stream);
350 }
351 try out_stream.writeAll(") ");
352 try payload.return_type.format("", .{}, out_stream);
353 },
212354
213355 .array_u8_sentinel_0 => {
214356 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
......@@ -243,8 +385,14 @@ pub const Type = extern union {
243385 switch (self.tag()) {
244386 .u8 => return Value.initTag(.u8_type),
245387 .i8 => return Value.initTag(.i8_type),
246 .isize => return Value.initTag(.isize_type),
388 .u16 => return Value.initTag(.u16_type),
389 .i16 => return Value.initTag(.i16_type),
390 .u32 => return Value.initTag(.u32_type),
391 .i32 => return Value.initTag(.i32_type),
392 .u64 => return Value.initTag(.u64_type),
393 .i64 => return Value.initTag(.i64_type),
247394 .usize => return Value.initTag(.usize_type),
395 .isize => return Value.initTag(.isize_type),
248396 .c_short => return Value.initTag(.c_short_type),
249397 .c_ushort => return Value.initTag(.c_ushort_type),
250398 .c_int => return Value.initTag(.c_int_type),
......@@ -269,6 +417,7 @@ pub const Type = extern union {
269417 .@"null" => return Value.initTag(.null_type),
270418 .@"undefined" => return Value.initTag(.undefined_type),
271419 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
420 .fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
272421 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
273422 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
274423 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
......@@ -285,8 +434,14 @@ pub const Type = extern union {
285434 return switch (self.tag()) {
286435 .u8,
287436 .i8,
288 .isize,
437 .u16,
438 .i16,
439 .u32,
440 .i32,
441 .u64,
442 .i64,
289443 .usize,
444 .isize,
290445 .c_short,
291446 .c_ushort,
292447 .c_int,
......@@ -303,8 +458,10 @@ pub const Type = extern union {
303458 .bool,
304459 .anyerror,
305460 .fn_noreturn_no_args,
461 .fn_void_no_args,
306462 .fn_naked_noreturn_no_args,
307463 .fn_ccc_void_no_args,
464 .function,
308465 .single_const_pointer_to_comptime_int,
309466 .const_slice_u8,
310467 .array_u8_sentinel_0,
......@@ -326,6 +483,10 @@ pub const Type = extern union {
326483 };
327484 }
328485
486 pub fn isNoReturn(self: Type) bool {
487 return self.zigTypeTag() == .NoReturn;
488 }
489
329490 /// Asserts that hasCodeGenBits() is true.
330491 pub fn abiAlignment(self: Type, target: Target) u32 {
331492 return switch (self.tag()) {
......@@ -333,11 +494,17 @@ pub const Type = extern union {
333494 .i8,
334495 .bool,
335496 .fn_noreturn_no_args, // represents machine code; not a pointer
497 .fn_void_no_args, // represents machine code; not a pointer
336498 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
337499 .fn_ccc_void_no_args, // represents machine code; not a pointer
500 .function, // represents machine code; not a pointer
338501 .array_u8_sentinel_0,
339502 => return 1,
340503
504 .i16, .u16 => return 2,
505 .i32, .u32 => return 4,
506 .i64, .u64 => return 8,
507
341508 .isize,
342509 .usize,
343510 .single_const_pointer_to_comptime_int,
......@@ -387,12 +554,87 @@ pub const Type = extern union {
387554 };
388555 }
389556
390 pub fn isSinglePointer(self: Type) bool {
557 /// Asserts the type has the ABI size already resolved.
558 pub fn abiSize(self: Type, target: Target) u64 {
391559 return switch (self.tag()) {
560 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
561 .fn_void_no_args => unreachable, // represents machine code; not a pointer
562 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
563 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
564 .function => unreachable, // represents machine code; not a pointer
565 .c_void => unreachable,
566 .void => unreachable,
567 .type => unreachable,
568 .comptime_int => unreachable,
569 .comptime_float => unreachable,
570 .noreturn => unreachable,
571 .@"null" => unreachable,
572 .@"undefined" => unreachable,
573
392574 .u8,
393575 .i8,
576 .bool,
577 => return 1,
578
579 .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
580 .array => {
581 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
582 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
583 return payload.len * elem_size;
584 },
585 .i16, .u16 => return 2,
586 .i32, .u32 => return 4,
587 .i64, .u64 => return 8,
588
394589 .isize,
395590 .usize,
591 .single_const_pointer_to_comptime_int,
592 .const_slice_u8,
593 .single_const_pointer,
594 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
595
596 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
597 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
598 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
599 .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
600 .c_long => return @divExact(CType.long.sizeInBits(target), 8),
601 .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
602 .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
603 .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
604
605 .f16 => return 2,
606 .f32 => return 4,
607 .f64 => return 8,
608 .f128 => return 16,
609 .c_longdouble => return 16,
610
611 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
612
613 .int_signed, .int_unsigned => {
614 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
615 pl.bits
616 else if (self.cast(Payload.IntUnsigned)) |pl|
617 pl.bits
618 else
619 unreachable;
620
621 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
622 },
623 };
624 }
625
626 pub fn isSinglePointer(self: Type) bool {
627 return switch (self.tag()) {
628 .u8,
629 .i8,
630 .u16,
631 .i16,
632 .u32,
633 .i32,
634 .u64,
635 .i64,
636 .usize,
637 .isize,
396638 .c_short,
397639 .c_ushort,
398640 .c_int,
......@@ -420,8 +662,10 @@ pub const Type = extern union {
420662 .array_u8_sentinel_0,
421663 .const_slice_u8,
422664 .fn_noreturn_no_args,
665 .fn_void_no_args,
423666 .fn_naked_noreturn_no_args,
424667 .fn_ccc_void_no_args,
668 .function,
425669 .int_unsigned,
426670 .int_signed,
427671 => false,
......@@ -436,8 +680,14 @@ pub const Type = extern union {
436680 return switch (self.tag()) {
437681 .u8,
438682 .i8,
439 .isize,
683 .u16,
684 .i16,
685 .u32,
686 .i32,
687 .u64,
688 .i64,
440689 .usize,
690 .isize,
441691 .c_short,
442692 .c_ushort,
443693 .c_int,
......@@ -466,8 +716,10 @@ pub const Type = extern union {
466716 .single_const_pointer,
467717 .single_const_pointer_to_comptime_int,
468718 .fn_noreturn_no_args,
719 .fn_void_no_args,
469720 .fn_naked_noreturn_no_args,
470721 .fn_ccc_void_no_args,
722 .function,
471723 .int_unsigned,
472724 .int_signed,
473725 => false,
......@@ -481,8 +733,14 @@ pub const Type = extern union {
481733 return switch (self.tag()) {
482734 .u8,
483735 .i8,
484 .isize,
736 .u16,
737 .i16,
738 .u32,
739 .i32,
740 .u64,
741 .i64,
485742 .usize,
743 .isize,
486744 .c_short,
487745 .c_ushort,
488746 .c_int,
......@@ -509,8 +767,10 @@ pub const Type = extern union {
509767 .array,
510768 .array_u8_sentinel_0,
511769 .fn_noreturn_no_args,
770 .fn_void_no_args,
512771 .fn_naked_noreturn_no_args,
513772 .fn_ccc_void_no_args,
773 .function,
514774 .int_unsigned,
515775 .int_signed,
516776 => unreachable,
......@@ -527,8 +787,14 @@ pub const Type = extern union {
527787 return switch (self.tag()) {
528788 .u8,
529789 .i8,
530 .isize,
790 .u16,
791 .i16,
792 .u32,
793 .i32,
794 .u64,
795 .i64,
531796 .usize,
797 .isize,
532798 .c_short,
533799 .c_ushort,
534800 .c_int,
......@@ -553,8 +819,10 @@ pub const Type = extern union {
553819 .@"null",
554820 .@"undefined",
555821 .fn_noreturn_no_args,
822 .fn_void_no_args,
556823 .fn_naked_noreturn_no_args,
557824 .fn_ccc_void_no_args,
825 .function,
558826 .int_unsigned,
559827 .int_signed,
560828 => unreachable,
......@@ -571,8 +839,14 @@ pub const Type = extern union {
571839 return switch (self.tag()) {
572840 .u8,
573841 .i8,
574 .isize,
842 .u16,
843 .i16,
844 .u32,
845 .i32,
846 .u64,
847 .i64,
575848 .usize,
849 .isize,
576850 .c_short,
577851 .c_ushort,
578852 .c_int,
......@@ -597,8 +871,10 @@ pub const Type = extern union {
597871 .@"null",
598872 .@"undefined",
599873 .fn_noreturn_no_args,
874 .fn_void_no_args,
600875 .fn_naked_noreturn_no_args,
601876 .fn_ccc_void_no_args,
877 .function,
602878 .single_const_pointer,
603879 .single_const_pointer_to_comptime_int,
604880 .const_slice_u8,
......@@ -616,8 +892,14 @@ pub const Type = extern union {
616892 return switch (self.tag()) {
617893 .u8,
618894 .i8,
619 .isize,
895 .u16,
896 .i16,
897 .u32,
898 .i32,
899 .u64,
900 .i64,
620901 .usize,
902 .isize,
621903 .c_short,
622904 .c_ushort,
623905 .c_int,
......@@ -642,8 +924,10 @@ pub const Type = extern union {
642924 .@"null",
643925 .@"undefined",
644926 .fn_noreturn_no_args,
927 .fn_void_no_args,
645928 .fn_naked_noreturn_no_args,
646929 .fn_ccc_void_no_args,
930 .function,
647931 .single_const_pointer,
648932 .single_const_pointer_to_comptime_int,
649933 .const_slice_u8,
......@@ -656,6 +940,11 @@ pub const Type = extern union {
656940 };
657941 }
658942
943 /// Returns true if and only if the type is a fixed-width integer.
944 pub fn isInt(self: Type) bool {
945 return self.isSignedInt() or self.isUnsignedInt();
946 }
947
659948 /// Returns true if and only if the type is a fixed-width, signed integer.
660949 pub fn isSignedInt(self: Type) bool {
661950 return switch (self.tag()) {
......@@ -675,8 +964,10 @@ pub const Type = extern union {
675964 .@"null",
676965 .@"undefined",
677966 .fn_noreturn_no_args,
967 .fn_void_no_args,
678968 .fn_naked_noreturn_no_args,
679969 .fn_ccc_void_no_args,
970 .function,
680971 .array,
681972 .single_const_pointer,
682973 .single_const_pointer_to_comptime_int,
......@@ -689,6 +980,9 @@ pub const Type = extern union {
689980 .c_uint,
690981 .c_ulong,
691982 .c_ulonglong,
983 .u16,
984 .u32,
985 .u64,
692986 => false,
693987
694988 .int_signed,
......@@ -698,11 +992,68 @@ pub const Type = extern union {
698992 .c_int,
699993 .c_long,
700994 .c_longlong,
995 .i16,
996 .i32,
997 .i64,
701998 => true,
702999 };
7031000 }
7041001
705 /// Asserts the type is a fixed-width integer.
1002 /// Returns true if and only if the type is a fixed-width, unsigned integer.
1003 pub fn isUnsignedInt(self: Type) bool {
1004 return switch (self.tag()) {
1005 .f16,
1006 .f32,
1007 .f64,
1008 .f128,
1009 .c_longdouble,
1010 .c_void,
1011 .bool,
1012 .void,
1013 .type,
1014 .anyerror,
1015 .comptime_int,
1016 .comptime_float,
1017 .noreturn,
1018 .@"null",
1019 .@"undefined",
1020 .fn_noreturn_no_args,
1021 .fn_void_no_args,
1022 .fn_naked_noreturn_no_args,
1023 .fn_ccc_void_no_args,
1024 .function,
1025 .array,
1026 .single_const_pointer,
1027 .single_const_pointer_to_comptime_int,
1028 .array_u8_sentinel_0,
1029 .const_slice_u8,
1030 .int_signed,
1031 .i8,
1032 .isize,
1033 .c_short,
1034 .c_int,
1035 .c_long,
1036 .c_longlong,
1037 .i16,
1038 .i32,
1039 .i64,
1040 => false,
1041
1042 .int_unsigned,
1043 .u8,
1044 .usize,
1045 .c_ushort,
1046 .c_uint,
1047 .c_ulong,
1048 .c_ulonglong,
1049 .u16,
1050 .u32,
1051 .u64,
1052 => true,
1053 };
1054 }
1055
1056 /// Asserts the type is an integer.
7061057 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
7071058 return switch (self.tag()) {
7081059 .f16,
......@@ -721,8 +1072,10 @@ pub const Type = extern union {
7211072 .@"null",
7221073 .@"undefined",
7231074 .fn_noreturn_no_args,
1075 .fn_void_no_args,
7241076 .fn_naked_noreturn_no_args,
7251077 .fn_ccc_void_no_args,
1078 .function,
7261079 .array,
7271080 .single_const_pointer,
7281081 .single_const_pointer_to_comptime_int,
......@@ -734,6 +1087,12 @@ pub const Type = extern union {
7341087 .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
7351088 .u8 => .{ .signed = false, .bits = 8 },
7361089 .i8 => .{ .signed = true, .bits = 8 },
1090 .u16 => .{ .signed = false, .bits = 16 },
1091 .i16 => .{ .signed = true, .bits = 16 },
1092 .u32 => .{ .signed = false, .bits = 32 },
1093 .i32 => .{ .signed = true, .bits = 32 },
1094 .u64 => .{ .signed = false, .bits = 64 },
1095 .i64 => .{ .signed = true, .bits = 64 },
7371096 .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
7381097 .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
7391098 .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
......@@ -747,6 +1106,59 @@ pub const Type = extern union {
7471106 };
7481107 }
7491108
1109 pub fn isNamedInt(self: Type) bool {
1110 return switch (self.tag()) {
1111 .f16,
1112 .f32,
1113 .f64,
1114 .f128,
1115 .c_longdouble,
1116 .c_void,
1117 .bool,
1118 .void,
1119 .type,
1120 .anyerror,
1121 .comptime_int,
1122 .comptime_float,
1123 .noreturn,
1124 .@"null",
1125 .@"undefined",
1126 .fn_noreturn_no_args,
1127 .fn_void_no_args,
1128 .fn_naked_noreturn_no_args,
1129 .fn_ccc_void_no_args,
1130 .function,
1131 .array,
1132 .single_const_pointer,
1133 .single_const_pointer_to_comptime_int,
1134 .array_u8_sentinel_0,
1135 .const_slice_u8,
1136 .int_unsigned,
1137 .int_signed,
1138 .u8,
1139 .i8,
1140 .u16,
1141 .i16,
1142 .u32,
1143 .i32,
1144 .u64,
1145 .i64,
1146 => false,
1147
1148 .usize,
1149 .isize,
1150 .c_short,
1151 .c_ushort,
1152 .c_int,
1153 .c_uint,
1154 .c_long,
1155 .c_ulong,
1156 .c_longlong,
1157 .c_ulonglong,
1158 => true,
1159 };
1160 }
1161
7501162 pub fn isFloat(self: Type) bool {
7511163 return switch (self.tag()) {
7521164 .f16,
......@@ -777,8 +1189,10 @@ pub const Type = extern union {
7771189 pub fn fnParamLen(self: Type) usize {
7781190 return switch (self.tag()) {
7791191 .fn_noreturn_no_args => 0,
1192 .fn_void_no_args => 0,
7801193 .fn_naked_noreturn_no_args => 0,
7811194 .fn_ccc_void_no_args => 0,
1195 .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len,
7821196
7831197 .f16,
7841198 .f32,
......@@ -802,6 +1216,12 @@ pub const Type = extern union {
8021216 .const_slice_u8,
8031217 .u8,
8041218 .i8,
1219 .u16,
1220 .i16,
1221 .u32,
1222 .i32,
1223 .u64,
1224 .i64,
8051225 .usize,
8061226 .isize,
8071227 .c_short,
......@@ -823,8 +1243,13 @@ pub const Type = extern union {
8231243 pub fn fnParamTypes(self: Type, types: []Type) void {
8241244 switch (self.tag()) {
8251245 .fn_noreturn_no_args => return,
1246 .fn_void_no_args => return,
8261247 .fn_naked_noreturn_no_args => return,
8271248 .fn_ccc_void_no_args => return,
1249 .function => {
1250 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1251 std.mem.copy(Type, types, payload.param_types);
1252 },
8281253
8291254 .f16,
8301255 .f32,
......@@ -848,6 +1273,68 @@ pub const Type = extern union {
8481273 .const_slice_u8,
8491274 .u8,
8501275 .i8,
1276 .u16,
1277 .i16,
1278 .u32,
1279 .i32,
1280 .u64,
1281 .i64,
1282 .usize,
1283 .isize,
1284 .c_short,
1285 .c_ushort,
1286 .c_int,
1287 .c_uint,
1288 .c_long,
1289 .c_ulong,
1290 .c_longlong,
1291 .c_ulonglong,
1292 .int_unsigned,
1293 .int_signed,
1294 => unreachable,
1295 }
1296 }
1297
1298 /// Asserts the type is a function.
1299 pub fn fnParamType(self: Type, index: usize) Type {
1300 switch (self.tag()) {
1301 .function => {
1302 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1303 return payload.param_types[index];
1304 },
1305
1306 .fn_noreturn_no_args,
1307 .fn_void_no_args,
1308 .fn_naked_noreturn_no_args,
1309 .fn_ccc_void_no_args,
1310 .f16,
1311 .f32,
1312 .f64,
1313 .f128,
1314 .c_longdouble,
1315 .c_void,
1316 .bool,
1317 .void,
1318 .type,
1319 .anyerror,
1320 .comptime_int,
1321 .comptime_float,
1322 .noreturn,
1323 .@"null",
1324 .@"undefined",
1325 .array,
1326 .single_const_pointer,
1327 .single_const_pointer_to_comptime_int,
1328 .array_u8_sentinel_0,
1329 .const_slice_u8,
1330 .u8,
1331 .i8,
1332 .u16,
1333 .i16,
1334 .u32,
1335 .i32,
1336 .u64,
1337 .i64,
8511338 .usize,
8521339 .isize,
8531340 .c_short,
......@@ -869,7 +1356,12 @@ pub const Type = extern union {
8691356 return switch (self.tag()) {
8701357 .fn_noreturn_no_args => Type.initTag(.noreturn),
8711358 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
872 .fn_ccc_void_no_args => Type.initTag(.void),
1359
1360 .fn_void_no_args,
1361 .fn_ccc_void_no_args,
1362 => Type.initTag(.void),
1363
1364 .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type,
8731365
8741366 .f16,
8751367 .f32,
......@@ -893,6 +1385,12 @@ pub const Type = extern union {
8931385 .const_slice_u8,
8941386 .u8,
8951387 .i8,
1388 .u16,
1389 .i16,
1390 .u32,
1391 .i32,
1392 .u64,
1393 .i64,
8961394 .usize,
8971395 .isize,
8981396 .c_short,
......@@ -913,8 +1411,10 @@ pub const Type = extern union {
9131411 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
9141412 return switch (self.tag()) {
9151413 .fn_noreturn_no_args => .Unspecified,
1414 .fn_void_no_args => .Unspecified,
9161415 .fn_naked_noreturn_no_args => .Naked,
9171416 .fn_ccc_void_no_args => .C,
1417 .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc,
9181418
9191419 .f16,
9201420 .f32,
......@@ -938,6 +1438,12 @@ pub const Type = extern union {
9381438 .const_slice_u8,
9391439 .u8,
9401440 .i8,
1441 .u16,
1442 .i16,
1443 .u32,
1444 .i32,
1445 .u64,
1446 .i64,
9411447 .usize,
9421448 .isize,
9431449 .c_short,
......@@ -958,8 +1464,10 @@ pub const Type = extern union {
9581464 pub fn fnIsVarArgs(self: Type) bool {
9591465 return switch (self.tag()) {
9601466 .fn_noreturn_no_args => false,
1467 .fn_void_no_args => false,
9611468 .fn_naked_noreturn_no_args => false,
9621469 .fn_ccc_void_no_args => false,
1470 .function => false,
9631471
9641472 .f16,
9651473 .f32,
......@@ -983,6 +1491,12 @@ pub const Type = extern union {
9831491 .const_slice_u8,
9841492 .u8,
9851493 .i8,
1494 .u16,
1495 .i16,
1496 .u32,
1497 .i32,
1498 .u64,
1499 .i64,
9861500 .usize,
9871501 .isize,
9881502 .c_short,
......@@ -1010,6 +1524,12 @@ pub const Type = extern union {
10101524 .comptime_float,
10111525 .u8,
10121526 .i8,
1527 .u16,
1528 .i16,
1529 .u32,
1530 .i32,
1531 .u64,
1532 .i64,
10131533 .usize,
10141534 .isize,
10151535 .c_short,
......@@ -1033,8 +1553,10 @@ pub const Type = extern union {
10331553 .@"null",
10341554 .@"undefined",
10351555 .fn_noreturn_no_args,
1556 .fn_void_no_args,
10361557 .fn_naked_noreturn_no_args,
10371558 .fn_ccc_void_no_args,
1559 .function,
10381560 .array,
10391561 .single_const_pointer,
10401562 .single_const_pointer_to_comptime_int,
......@@ -1056,6 +1578,12 @@ pub const Type = extern union {
10561578 .comptime_float,
10571579 .u8,
10581580 .i8,
1581 .u16,
1582 .i16,
1583 .u32,
1584 .i32,
1585 .u64,
1586 .i64,
10591587 .usize,
10601588 .isize,
10611589 .c_short,
......@@ -1070,8 +1598,10 @@ pub const Type = extern union {
10701598 .type,
10711599 .anyerror,
10721600 .fn_noreturn_no_args,
1601 .fn_void_no_args,
10731602 .fn_naked_noreturn_no_args,
10741603 .fn_ccc_void_no_args,
1604 .function,
10751605 .single_const_pointer_to_comptime_int,
10761606 .array_u8_sentinel_0,
10771607 .const_slice_u8,
......@@ -1112,6 +1642,12 @@ pub const Type = extern union {
11121642 .comptime_float,
11131643 .u8,
11141644 .i8,
1645 .u16,
1646 .i16,
1647 .u32,
1648 .i32,
1649 .u64,
1650 .i64,
11151651 .usize,
11161652 .isize,
11171653 .c_short,
......@@ -1126,8 +1662,10 @@ pub const Type = extern union {
11261662 .type,
11271663 .anyerror,
11281664 .fn_noreturn_no_args,
1665 .fn_void_no_args,
11291666 .fn_naked_noreturn_no_args,
11301667 .fn_ccc_void_no_args,
1668 .function,
11311669 .single_const_pointer_to_comptime_int,
11321670 .array_u8_sentinel_0,
11331671 .const_slice_u8,
......@@ -1154,8 +1692,14 @@ pub const Type = extern union {
11541692 // The first section of this enum are tags that require no payload.
11551693 u8,
11561694 i8,
1157 isize,
1695 u16,
1696 i16,
1697 u32,
1698 i32,
1699 u64,
1700 i64,
11581701 usize,
1702 isize,
11591703 c_short,
11601704 c_ushort,
11611705 c_int,
......@@ -1180,6 +1724,7 @@ pub const Type = extern union {
11801724 @"null",
11811725 @"undefined",
11821726 fn_noreturn_no_args,
1727 fn_void_no_args,
11831728 fn_naked_noreturn_no_args,
11841729 fn_ccc_void_no_args,
11851730 single_const_pointer_to_comptime_int,
......@@ -1191,6 +1736,7 @@ pub const Type = extern union {
11911736 single_const_pointer,
11921737 int_signed,
11931738 int_unsigned,
1739 function,
11941740
11951741 pub const last_no_payload_tag = Tag.const_slice_u8;
11961742 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -1229,6 +1775,14 @@ pub const Type = extern union {
12291775
12301776 bits: u16,
12311777 };
1778
1779 pub const Function = struct {
1780 base: Payload = Payload{ .tag = .function },
1781
1782 param_types: []Type,
1783 return_type: Type,
1784 cc: std.builtin.CallingConvention,
1785 };
12321786 };
12331787};
12341788
src-self-hosted/value.zig+230-30
......@@ -23,8 +23,14 @@ pub const Value = extern union {
2323 // The first section of this enum are tags that require no payload.
2424 u8_type,
2525 i8_type,
26 isize_type,
26 u16_type,
27 i16_type,
28 u32_type,
29 i32_type,
30 u64_type,
31 i64_type,
2732 usize_type,
33 isize_type,
2834 c_short_type,
2935 c_ushort_type,
3036 c_int_type,
......@@ -49,6 +55,7 @@ pub const Value = extern union {
4955 null_type,
5056 undefined_type,
5157 fn_noreturn_no_args_type,
58 fn_void_no_args_type,
5259 fn_naked_noreturn_no_args_type,
5360 fn_ccc_void_no_args_type,
5461 single_const_pointer_to_comptime_int_type,
......@@ -78,8 +85,8 @@ pub const Value = extern union {
7885 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
7986 };
8087
81 pub fn initTag(comptime small_tag: Tag) Value {
82 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
88 pub fn initTag(small_tag: Tag) Value {
89 assert(@enumToInt(small_tag) < Tag.no_payload_count);
8390 return .{ .tag_if_small_enough = @enumToInt(small_tag) };
8491 }
8592
......@@ -107,17 +114,132 @@ pub const Value = extern union {
107114 return @fieldParentPtr(T, "base", self.ptr_otherwise);
108115 }
109116
117 pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
118 if (self.tag_if_small_enough < Tag.no_payload_count) {
119 return Value{ .tag_if_small_enough = self.tag_if_small_enough };
120 } else switch (self.ptr_otherwise.tag) {
121 .u8_type,
122 .i8_type,
123 .u16_type,
124 .i16_type,
125 .u32_type,
126 .i32_type,
127 .u64_type,
128 .i64_type,
129 .usize_type,
130 .isize_type,
131 .c_short_type,
132 .c_ushort_type,
133 .c_int_type,
134 .c_uint_type,
135 .c_long_type,
136 .c_ulong_type,
137 .c_longlong_type,
138 .c_ulonglong_type,
139 .c_longdouble_type,
140 .f16_type,
141 .f32_type,
142 .f64_type,
143 .f128_type,
144 .c_void_type,
145 .bool_type,
146 .void_type,
147 .type_type,
148 .anyerror_type,
149 .comptime_int_type,
150 .comptime_float_type,
151 .noreturn_type,
152 .null_type,
153 .undefined_type,
154 .fn_noreturn_no_args_type,
155 .fn_void_no_args_type,
156 .fn_naked_noreturn_no_args_type,
157 .fn_ccc_void_no_args_type,
158 .single_const_pointer_to_comptime_int_type,
159 .const_slice_u8_type,
160 .undef,
161 .zero,
162 .the_one_possible_value,
163 .null_value,
164 .bool_true,
165 .bool_false,
166 => unreachable,
167
168 .ty => {
169 const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
170 const new_payload = try allocator.create(Payload.Ty);
171 new_payload.* = .{
172 .base = payload.base,
173 .ty = try payload.ty.copy(allocator),
174 };
175 return Value{ .ptr_otherwise = &new_payload.base };
176 },
177 .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
178 .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
179 .int_big_positive => {
180 @panic("TODO implement copying of big ints");
181 },
182 .int_big_negative => {
183 @panic("TODO implement copying of big ints");
184 },
185 .function => return self.copyPayloadShallow(allocator, Payload.Function),
186 .ref_val => {
187 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
188 const new_payload = try allocator.create(Payload.RefVal);
189 new_payload.* = .{
190 .base = payload.base,
191 .val = try payload.val.copy(allocator),
192 };
193 return Value{ .ptr_otherwise = &new_payload.base };
194 },
195 .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
196 .elem_ptr => {
197 const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
198 const new_payload = try allocator.create(Payload.ElemPtr);
199 new_payload.* = .{
200 .base = payload.base,
201 .array_ptr = try payload.array_ptr.copy(allocator),
202 .index = payload.index,
203 };
204 return Value{ .ptr_otherwise = &new_payload.base };
205 },
206 .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
207 .repeated => {
208 const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
209 const new_payload = try allocator.create(Payload.Repeated);
210 new_payload.* = .{
211 .base = payload.base,
212 .val = try payload.val.copy(allocator),
213 };
214 return Value{ .ptr_otherwise = &new_payload.base };
215 },
216 }
217 }
218
219 fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
220 const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
221 const new_payload = try allocator.create(T);
222 new_payload.* = payload.*;
223 return Value{ .ptr_otherwise = &new_payload.base };
224 }
225
110226 pub fn format(
111227 self: Value,
112228 comptime fmt: []const u8,
113229 options: std.fmt.FormatOptions,
114 out_stream: var,
230 out_stream: anytype,
115231 ) !void {
116232 comptime assert(fmt.len == 0);
117233 var val = self;
118234 while (true) switch (val.tag()) {
119235 .u8_type => return out_stream.writeAll("u8"),
120236 .i8_type => return out_stream.writeAll("i8"),
237 .u16_type => return out_stream.writeAll("u16"),
238 .i16_type => return out_stream.writeAll("i16"),
239 .u32_type => return out_stream.writeAll("u32"),
240 .i32_type => return out_stream.writeAll("i32"),
241 .u64_type => return out_stream.writeAll("u64"),
242 .i64_type => return out_stream.writeAll("i64"),
121243 .isize_type => return out_stream.writeAll("isize"),
122244 .usize_type => return out_stream.writeAll("usize"),
123245 .c_short_type => return out_stream.writeAll("c_short"),
......@@ -144,6 +266,7 @@ pub const Value = extern union {
144266 .null_type => return out_stream.writeAll("@TypeOf(null)"),
145267 .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
146268 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
269 .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
147270 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
148271 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
149272 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
......@@ -203,8 +326,14 @@ pub const Value = extern union {
203326
204327 .u8_type => Type.initTag(.u8),
205328 .i8_type => Type.initTag(.i8),
206 .isize_type => Type.initTag(.isize),
329 .u16_type => Type.initTag(.u16),
330 .i16_type => Type.initTag(.i16),
331 .u32_type => Type.initTag(.u32),
332 .i32_type => Type.initTag(.i32),
333 .u64_type => Type.initTag(.u64),
334 .i64_type => Type.initTag(.i64),
207335 .usize_type => Type.initTag(.usize),
336 .isize_type => Type.initTag(.isize),
208337 .c_short_type => Type.initTag(.c_short),
209338 .c_ushort_type => Type.initTag(.c_ushort),
210339 .c_int_type => Type.initTag(.c_int),
......@@ -229,6 +358,7 @@ pub const Value = extern union {
229358 .null_type => Type.initTag(.@"null"),
230359 .undefined_type => Type.initTag(.@"undefined"),
231360 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
361 .fn_void_no_args_type => Type.initTag(.fn_void_no_args),
232362 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
233363 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
234364 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
......@@ -260,8 +390,14 @@ pub const Value = extern union {
260390 .ty,
261391 .u8_type,
262392 .i8_type,
263 .isize_type,
393 .u16_type,
394 .i16_type,
395 .u32_type,
396 .i32_type,
397 .u64_type,
398 .i64_type,
264399 .usize_type,
400 .isize_type,
265401 .c_short_type,
266402 .c_ushort_type,
267403 .c_int_type,
......@@ -286,12 +422,11 @@ pub const Value = extern union {
286422 .null_type,
287423 .undefined_type,
288424 .fn_noreturn_no_args_type,
425 .fn_void_no_args_type,
289426 .fn_naked_noreturn_no_args_type,
290427 .fn_ccc_void_no_args_type,
291428 .single_const_pointer_to_comptime_int_type,
292429 .const_slice_u8_type,
293 .bool_true,
294 .bool_false,
295430 .null_value,
296431 .function,
297432 .ref_val,
......@@ -304,8 +439,11 @@ pub const Value = extern union {
304439
305440 .the_one_possible_value, // An integer with one possible value is always zero.
306441 .zero,
442 .bool_false,
307443 => return BigIntMutable.init(&space.limbs, 0).toConst(),
308444
445 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
446
309447 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
310448 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
311449 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
......@@ -319,8 +457,14 @@ pub const Value = extern union {
319457 .ty,
320458 .u8_type,
321459 .i8_type,
322 .isize_type,
460 .u16_type,
461 .i16_type,
462 .u32_type,
463 .i32_type,
464 .u64_type,
465 .i64_type,
323466 .usize_type,
467 .isize_type,
324468 .c_short_type,
325469 .c_ushort_type,
326470 .c_int_type,
......@@ -345,12 +489,11 @@ pub const Value = extern union {
345489 .null_type,
346490 .undefined_type,
347491 .fn_noreturn_no_args_type,
492 .fn_void_no_args_type,
348493 .fn_naked_noreturn_no_args_type,
349494 .fn_ccc_void_no_args_type,
350495 .single_const_pointer_to_comptime_int_type,
351496 .const_slice_u8_type,
352 .bool_true,
353 .bool_false,
354497 .null_value,
355498 .function,
356499 .ref_val,
......@@ -363,8 +506,11 @@ pub const Value = extern union {
363506
364507 .zero,
365508 .the_one_possible_value, // an integer with one possible value is always zero
509 .bool_false,
366510 => return 0,
367511
512 .bool_true => return 1,
513
368514 .int_u64 => return self.cast(Payload.Int_u64).?.int,
369515 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
370516 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
......@@ -379,8 +525,14 @@ pub const Value = extern union {
379525 .ty,
380526 .u8_type,
381527 .i8_type,
382 .isize_type,
528 .u16_type,
529 .i16_type,
530 .u32_type,
531 .i32_type,
532 .u64_type,
533 .i64_type,
383534 .usize_type,
535 .isize_type,
384536 .c_short_type,
385537 .c_ushort_type,
386538 .c_int_type,
......@@ -405,12 +557,11 @@ pub const Value = extern union {
405557 .null_type,
406558 .undefined_type,
407559 .fn_noreturn_no_args_type,
560 .fn_void_no_args_type,
408561 .fn_naked_noreturn_no_args_type,
409562 .fn_ccc_void_no_args_type,
410563 .single_const_pointer_to_comptime_int_type,
411564 .const_slice_u8_type,
412 .bool_true,
413 .bool_false,
414565 .null_value,
415566 .function,
416567 .ref_val,
......@@ -423,8 +574,11 @@ pub const Value = extern union {
423574
424575 .the_one_possible_value, // an integer with one possible value is always zero
425576 .zero,
577 .bool_false,
426578 => return 0,
427579
580 .bool_true => return 1,
581
428582 .int_u64 => {
429583 const x = self.cast(Payload.Int_u64).?.int;
430584 if (x == 0) return 0;
......@@ -444,8 +598,14 @@ pub const Value = extern union {
444598 .ty,
445599 .u8_type,
446600 .i8_type,
447 .isize_type,
601 .u16_type,
602 .i16_type,
603 .u32_type,
604 .i32_type,
605 .u64_type,
606 .i64_type,
448607 .usize_type,
608 .isize_type,
449609 .c_short_type,
450610 .c_ushort_type,
451611 .c_int_type,
......@@ -470,12 +630,11 @@ pub const Value = extern union {
470630 .null_type,
471631 .undefined_type,
472632 .fn_noreturn_no_args_type,
633 .fn_void_no_args_type,
473634 .fn_naked_noreturn_no_args_type,
474635 .fn_ccc_void_no_args_type,
475636 .single_const_pointer_to_comptime_int_type,
476637 .const_slice_u8_type,
477 .bool_true,
478 .bool_false,
479638 .null_value,
480639 .function,
481640 .ref_val,
......@@ -488,8 +647,18 @@ pub const Value = extern union {
488647 .zero,
489648 .undef,
490649 .the_one_possible_value, // an integer with one possible value is always zero
650 .bool_false,
491651 => return true,
492652
653 .bool_true => {
654 const info = ty.intInfo(target);
655 if (info.signed) {
656 return info.bits >= 2;
657 } else {
658 return info.bits >= 1;
659 }
660 },
661
493662 .int_u64 => switch (ty.zigTypeTag()) {
494663 .Int => {
495664 const x = self.cast(Payload.Int_u64).?.int;
......@@ -538,8 +707,14 @@ pub const Value = extern union {
538707 .ty,
539708 .u8_type,
540709 .i8_type,
541 .isize_type,
710 .u16_type,
711 .i16_type,
712 .u32_type,
713 .i32_type,
714 .u64_type,
715 .i64_type,
542716 .usize_type,
717 .isize_type,
543718 .c_short_type,
544719 .c_ushort_type,
545720 .c_int_type,
......@@ -564,6 +739,7 @@ pub const Value = extern union {
564739 .null_type,
565740 .undefined_type,
566741 .fn_noreturn_no_args_type,
742 .fn_void_no_args_type,
567743 .fn_naked_noreturn_no_args_type,
568744 .fn_ccc_void_no_args_type,
569745 .single_const_pointer_to_comptime_int_type,
......@@ -594,8 +770,14 @@ pub const Value = extern union {
594770 .ty,
595771 .u8_type,
596772 .i8_type,
597 .isize_type,
773 .u16_type,
774 .i16_type,
775 .u32_type,
776 .i32_type,
777 .u64_type,
778 .i64_type,
598779 .usize_type,
780 .isize_type,
599781 .c_short_type,
600782 .c_ushort_type,
601783 .c_int_type,
......@@ -620,12 +802,11 @@ pub const Value = extern union {
620802 .null_type,
621803 .undefined_type,
622804 .fn_noreturn_no_args_type,
805 .fn_void_no_args_type,
623806 .fn_naked_noreturn_no_args_type,
624807 .fn_ccc_void_no_args_type,
625808 .single_const_pointer_to_comptime_int_type,
626809 .const_slice_u8_type,
627 .bool_true,
628 .bool_false,
629810 .null_value,
630811 .function,
631812 .ref_val,
......@@ -638,8 +819,11 @@ pub const Value = extern union {
638819
639820 .zero,
640821 .the_one_possible_value, // an integer with one possible value is always zero
822 .bool_false,
641823 => return .eq,
642824
825 .bool_true => return .gt,
826
643827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
644828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
645829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
......@@ -683,7 +867,7 @@ pub const Value = extern union {
683867 pub fn toBool(self: Value) bool {
684868 return switch (self.tag()) {
685869 .bool_true => true,
686 .bool_false => false,
870 .bool_false, .zero => false,
687871 else => unreachable,
688872 };
689873 }
......@@ -695,8 +879,14 @@ pub const Value = extern union {
695879 .ty,
696880 .u8_type,
697881 .i8_type,
698 .isize_type,
882 .u16_type,
883 .i16_type,
884 .u32_type,
885 .i32_type,
886 .u64_type,
887 .i64_type,
699888 .usize_type,
889 .isize_type,
700890 .c_short_type,
701891 .c_ushort_type,
702892 .c_int_type,
......@@ -721,6 +911,7 @@ pub const Value = extern union {
721911 .null_type,
722912 .undefined_type,
723913 .fn_noreturn_no_args_type,
914 .fn_void_no_args_type,
724915 .fn_naked_noreturn_no_args_type,
725916 .fn_ccc_void_no_args_type,
726917 .single_const_pointer_to_comptime_int_type,
......@@ -757,8 +948,14 @@ pub const Value = extern union {
757948 .ty,
758949 .u8_type,
759950 .i8_type,
760 .isize_type,
951 .u16_type,
952 .i16_type,
953 .u32_type,
954 .i32_type,
955 .u64_type,
956 .i64_type,
761957 .usize_type,
958 .isize_type,
762959 .c_short_type,
763960 .c_ushort_type,
764961 .c_int_type,
......@@ -783,6 +980,7 @@ pub const Value = extern union {
783980 .null_type,
784981 .undefined_type,
785982 .fn_noreturn_no_args_type,
983 .fn_void_no_args_type,
786984 .fn_naked_noreturn_no_args_type,
787985 .fn_ccc_void_no_args_type,
788986 .single_const_pointer_to_comptime_int_type,
......@@ -836,8 +1034,14 @@ pub const Value = extern union {
8361034 .ty,
8371035 .u8_type,
8381036 .i8_type,
839 .isize_type,
1037 .u16_type,
1038 .i16_type,
1039 .u32_type,
1040 .i32_type,
1041 .u64_type,
1042 .i64_type,
8401043 .usize_type,
1044 .isize_type,
8411045 .c_short_type,
8421046 .c_ushort_type,
8431047 .c_int_type,
......@@ -862,6 +1066,7 @@ pub const Value = extern union {
8621066 .null_type,
8631067 .undefined_type,
8641068 .fn_noreturn_no_args_type,
1069 .fn_void_no_args_type,
8651070 .fn_naked_noreturn_no_args_type,
8661071 .fn_ccc_void_no_args_type,
8671072 .single_const_pointer_to_comptime_int_type,
......@@ -929,11 +1134,6 @@ pub const Value = extern union {
9291134 len: u64,
9301135 };
9311136
932 pub const SingleConstPtrType = struct {
933 base: Payload = Payload{ .tag = .single_const_ptr_type },
934 elem_type: *Type,
935 };
936
9371137 /// Represents a pointer to another immutable value.
9381138 pub const RefVal = struct {
9391139 base: Payload = Payload{ .tag = .ref_val },
src-self-hosted/zir.zig+661-235
......@@ -12,29 +12,56 @@ const TypedValue = @import("TypedValue.zig");
1212const ir = @import("ir.zig");
1313const IrModule = @import("Module.zig");
1414
15/// This struct is relevent only for the ZIR Module text format. It is not used for
16/// semantic analysis of Zig source code.
17pub const Decl = struct {
18 name: []const u8,
19
20 /// Hash of slice into the source of the part after the = and before the next instruction.
21 contents_hash: std.zig.SrcHash,
22
23 inst: *Inst,
24};
25
1526/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
1627/// in-memory, analyzed instructions with types and values.
1728pub const Inst = struct {
1829 tag: Tag,
1930 /// Byte offset into the source.
2031 src: usize,
21 name: []const u8,
22
23 /// Slice into the source of the part after the = and before the next instruction.
24 contents: []const u8 = &[0]u8{},
32 /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
33 analyzed_inst: ?*ir.Inst = null,
2534
2635 /// These names are used directly as the instruction names in the text format.
2736 pub const Tag = enum {
37 /// Function parameter value. These must be first in a function's main block,
38 /// in respective order with the parameters.
39 arg,
40 /// A labeled block of code, which can return a value.
41 block,
42 /// Return a value from a `Block`.
43 @"break",
2844 breakpoint,
45 /// Same as `break` but without an operand; the operand is assumed to be the void value.
46 breakvoid,
2947 call,
3048 compileerror,
49 /// Special case, has no textual representation.
50 @"const",
3151 /// Represents a pointer to a global decl by name.
3252 declref,
53 /// Represents a pointer to a global decl by string name.
54 declref_str,
3355 /// The syntax `@foo` is equivalent to `declval("foo")`.
3456 /// declval is equivalent to declref followed by deref.
3557 declval,
58 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
59 declval_in_module,
60 boolnot,
61 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
3662 str,
3763 int,
64 inttype,
3865 ptrtoint,
3966 fieldptr,
4067 deref,
......@@ -42,30 +69,87 @@ pub const Inst = struct {
4269 @"asm",
4370 @"unreachable",
4471 @"return",
72 returnvoid,
4573 @"fn",
74 fntype,
4675 @"export",
4776 primitive,
48 ref,
49 fntype,
5077 intcast,
5178 bitcast,
5279 elemptr,
5380 add,
81 sub,
5482 cmp,
5583 condbr,
5684 isnull,
5785 isnonnull,
86
87 /// Returns whether the instruction is one of the control flow "noreturn" types.
88 /// Function calls do not count.
89 pub fn isNoReturn(tag: Tag) bool {
90 return switch (tag) {
91 .arg,
92 .block,
93 .breakpoint,
94 .call,
95 .@"const",
96 .declref,
97 .declref_str,
98 .declval,
99 .declval_in_module,
100 .str,
101 .int,
102 .inttype,
103 .ptrtoint,
104 .fieldptr,
105 .deref,
106 .as,
107 .@"asm",
108 .@"fn",
109 .fntype,
110 .@"export",
111 .primitive,
112 .intcast,
113 .bitcast,
114 .elemptr,
115 .add,
116 .sub,
117 .cmp,
118 .isnull,
119 .isnonnull,
120 .boolnot,
121 => false,
122
123 .condbr,
124 .@"unreachable",
125 .@"return",
126 .returnvoid,
127 .@"break",
128 .breakvoid,
129 .compileerror,
130 => true,
131 };
132 }
58133 };
59134
60135 pub fn TagToType(tag: Tag) type {
61136 return switch (tag) {
137 .arg => Arg,
138 .block => Block,
139 .@"break" => Break,
62140 .breakpoint => Breakpoint,
141 .breakvoid => BreakVoid,
63142 .call => Call,
64143 .declref => DeclRef,
144 .declref_str => DeclRefStr,
65145 .declval => DeclVal,
146 .declval_in_module => DeclValInModule,
66147 .compileerror => CompileError,
148 .@"const" => Const,
149 .boolnot => BoolNot,
67150 .str => Str,
68151 .int => Int,
152 .inttype => IntType,
69153 .ptrtoint => PtrToInt,
70154 .fieldptr => FieldPtr,
71155 .deref => Deref,
......@@ -73,15 +157,16 @@ pub const Inst = struct {
73157 .@"asm" => Asm,
74158 .@"unreachable" => Unreachable,
75159 .@"return" => Return,
160 .returnvoid => ReturnVoid,
76161 .@"fn" => Fn,
77162 .@"export" => Export,
78163 .primitive => Primitive,
79 .ref => Ref,
80164 .fntype => FnType,
81165 .intcast => IntCast,
82166 .bitcast => BitCast,
83167 .elemptr => ElemPtr,
84168 .add => Add,
169 .sub => Sub,
85170 .cmp => Cmp,
86171 .condbr => CondBr,
87172 .isnull => IsNull,
......@@ -96,6 +181,35 @@ pub const Inst = struct {
96181 return @fieldParentPtr(T, "base", base);
97182 }
98183
184 pub const Arg = struct {
185 pub const base_tag = Tag.arg;
186 base: Inst,
187
188 positionals: struct {},
189 kw_args: struct {},
190 };
191
192 pub const Block = struct {
193 pub const base_tag = Tag.block;
194 base: Inst,
195
196 positionals: struct {
197 body: Module.Body,
198 },
199 kw_args: struct {},
200 };
201
202 pub const Break = struct {
203 pub const base_tag = Tag.@"break";
204 base: Inst,
205
206 positionals: struct {
207 block: *Block,
208 operand: *Inst,
209 },
210 kw_args: struct {},
211 };
212
99213 pub const Breakpoint = struct {
100214 pub const base_tag = Tag.breakpoint;
101215 base: Inst,
......@@ -104,6 +218,16 @@ pub const Inst = struct {
104218 kw_args: struct {},
105219 };
106220
221 pub const BreakVoid = struct {
222 pub const base_tag = Tag.breakvoid;
223 base: Inst,
224
225 positionals: struct {
226 block: *Block,
227 },
228 kw_args: struct {},
229 };
230
107231 pub const Call = struct {
108232 pub const base_tag = Tag.call;
109233 base: Inst,
......@@ -121,6 +245,16 @@ pub const Inst = struct {
121245 pub const base_tag = Tag.declref;
122246 base: Inst,
123247
248 positionals: struct {
249 name: []const u8,
250 },
251 kw_args: struct {},
252 };
253
254 pub const DeclRefStr = struct {
255 pub const base_tag = Tag.declref_str;
256 base: Inst,
257
124258 positionals: struct {
125259 name: *Inst,
126260 },
......@@ -137,6 +271,16 @@ pub const Inst = struct {
137271 kw_args: struct {},
138272 };
139273
274 pub const DeclValInModule = struct {
275 pub const base_tag = Tag.declval_in_module;
276 base: Inst,
277
278 positionals: struct {
279 decl: *IrModule.Decl,
280 },
281 kw_args: struct {},
282 };
283
140284 pub const CompileError = struct {
141285 pub const base_tag = Tag.compileerror;
142286 base: Inst,
......@@ -147,6 +291,26 @@ pub const Inst = struct {
147291 kw_args: struct {},
148292 };
149293
294 pub const Const = struct {
295 pub const base_tag = Tag.@"const";
296 base: Inst,
297
298 positionals: struct {
299 typed_value: TypedValue,
300 },
301 kw_args: struct {},
302 };
303
304 pub const BoolNot = struct {
305 pub const base_tag = Tag.boolnot;
306 base: Inst,
307
308 positionals: struct {
309 operand: *Inst,
310 },
311 kw_args: struct {},
312 };
313
150314 pub const Str = struct {
151315 pub const base_tag = Tag.str;
152316 base: Inst,
......@@ -168,6 +332,7 @@ pub const Inst = struct {
168332 };
169333
170334 pub const PtrToInt = struct {
335 pub const builtin_name = "@ptrToInt";
171336 pub const base_tag = Tag.ptrtoint;
172337 base: Inst,
173338
......@@ -200,6 +365,7 @@ pub const Inst = struct {
200365
201366 pub const As = struct {
202367 pub const base_tag = Tag.as;
368 pub const builtin_name = "@as";
203369 base: Inst,
204370
205371 positionals: struct {
......@@ -238,6 +404,16 @@ pub const Inst = struct {
238404 pub const base_tag = Tag.@"return";
239405 base: Inst,
240406
407 positionals: struct {
408 operand: *Inst,
409 },
410 kw_args: struct {},
411 };
412
413 pub const ReturnVoid = struct {
414 pub const base_tag = Tag.returnvoid;
415 base: Inst,
416
241417 positionals: struct {},
242418 kw_args: struct {},
243419 };
......@@ -253,23 +429,37 @@ pub const Inst = struct {
253429 kw_args: struct {},
254430 };
255431
256 pub const Export = struct {
257 pub const base_tag = Tag.@"export";
432 pub const FnType = struct {
433 pub const base_tag = Tag.fntype;
258434 base: Inst,
259435
260436 positionals: struct {
261 symbol_name: *Inst,
262 value: *Inst,
437 param_types: []*Inst,
438 return_type: *Inst,
439 },
440 kw_args: struct {
441 cc: std.builtin.CallingConvention = .Unspecified,
442 },
443 };
444
445 pub const IntType = struct {
446 pub const base_tag = Tag.inttype;
447 base: Inst,
448
449 positionals: struct {
450 signed: *Inst,
451 bits: *Inst,
263452 },
264453 kw_args: struct {},
265454 };
266455
267 pub const Ref = struct {
268 pub const base_tag = Tag.ref;
456 pub const Export = struct {
457 pub const base_tag = Tag.@"export";
269458 base: Inst,
270459
271460 positionals: struct {
272 operand: *Inst,
461 symbol_name: *Inst,
462 decl_name: []const u8,
273463 },
274464 kw_args: struct {},
275465 };
......@@ -284,6 +474,14 @@ pub const Inst = struct {
284474 kw_args: struct {},
285475
286476 pub const Builtin = enum {
477 i8,
478 u8,
479 i16,
480 u16,
481 i32,
482 u32,
483 i64,
484 u64,
287485 isize,
288486 usize,
289487 c_short,
......@@ -315,6 +513,14 @@ pub const Inst = struct {
315513
316514 pub fn toTypedValue(self: Builtin) TypedValue {
317515 return switch (self) {
516 .i8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i8_type) },
517 .u8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u8_type) },
518 .i16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i16_type) },
519 .u16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u16_type) },
520 .i32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i32_type) },
521 .u32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type) },
522 .i64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i64_type) },
523 .u64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u64_type) },
318524 .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
319525 .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
320526 .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
......@@ -348,19 +554,6 @@ pub const Inst = struct {
348554 };
349555 };
350556
351 pub const FnType = struct {
352 pub const base_tag = Tag.fntype;
353 base: Inst,
354
355 positionals: struct {
356 param_types: []*Inst,
357 return_type: *Inst,
358 },
359 kw_args: struct {
360 cc: std.builtin.CallingConvention = .Unspecified,
361 },
362 };
363
364557 pub const IntCast = struct {
365558 pub const base_tag = Tag.intcast;
366559 base: Inst,
......@@ -405,6 +598,19 @@ pub const Inst = struct {
405598 kw_args: struct {},
406599 };
407600
601 pub const Sub = struct {
602 pub const base_tag = Tag.sub;
603 base: Inst,
604
605 positionals: struct {
606 lhs: *Inst,
607 rhs: *Inst,
608 },
609 kw_args: struct {},
610 };
611
612 /// TODO get rid of the op positional arg and make that data part of
613 /// the base Inst tag.
408614 pub const Cmp = struct {
409615 pub const base_tag = Tag.cmp;
410616 base: Inst,
......@@ -456,7 +662,7 @@ pub const ErrorMsg = struct {
456662};
457663
458664pub const Module = struct {
459 decls: []*Inst,
665 decls: []*Decl,
460666 arena: std.heap.ArenaAllocator,
461667 error_msg: ?ErrorMsg = null,
462668
......@@ -475,13 +681,31 @@ pub const Module = struct {
475681 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
476682 }
477683
478 const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize });
684 const DeclAndIndex = struct {
685 decl: *Decl,
686 index: usize,
687 };
479688
480689 /// TODO Look into making a table to speed this up.
481 pub fn findDecl(self: Module, name: []const u8) ?*Inst {
482 for (self.decls) |decl| {
690 pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
691 for (self.decls) |decl, i| {
483692 if (mem.eql(u8, decl.name, name)) {
484 return decl;
693 return DeclAndIndex{
694 .decl = decl,
695 .index = i,
696 };
697 }
698 }
699 return null;
700 }
701
702 pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex {
703 for (self.decls) |decl, i| {
704 if (decl.inst == inst) {
705 return DeclAndIndex{
706 .decl = decl,
707 .index = i,
708 };
485709 }
486710 }
487711 return null;
......@@ -489,75 +713,68 @@ pub const Module = struct {
489713
490714 /// The allocator is used for temporary storage, but this function always returns
491715 /// with no resources allocated.
492 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {
493 // First, build a map of *Inst to @ or % indexes
494 var inst_table = InstPtrTable.init(allocator);
495 defer inst_table.deinit();
716 pub fn writeToStream(self: Module, allocator: *Allocator, stream: anytype) !void {
717 var write = Writer{
718 .module = &self,
719 .inst_table = InstPtrTable.init(allocator),
720 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
721 .arena = std.heap.ArenaAllocator.init(allocator),
722 .indent = 2,
723 };
724 defer write.arena.deinit();
725 defer write.inst_table.deinit();
726 defer write.block_table.deinit();
496727
497 try inst_table.ensureCapacity(self.decls.len);
728 // First, build a map of *Inst to @ or % indexes
729 try write.inst_table.ensureCapacity(self.decls.len);
498730
499731 for (self.decls) |decl, decl_i| {
500 try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null });
732 try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
501733
502 if (decl.cast(Inst.Fn)) |fn_inst| {
734 if (decl.inst.cast(Inst.Fn)) |fn_inst| {
503735 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
504 try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i });
736 try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i, .name = undefined });
505737 }
506738 }
507739 }
508740
509741 for (self.decls) |decl, i| {
510742 try stream.print("@{} ", .{decl.name});
511 try self.writeInstToStream(stream, decl, &inst_table);
743 try write.writeInstToStream(stream, decl.inst);
512744 try stream.writeByte('\n');
513745 }
514746 }
747};
748
749const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 });
750
751const Writer = struct {
752 module: *const Module,
753 inst_table: InstPtrTable,
754 block_table: std.AutoHashMap(*Inst.Block, []const u8),
755 arena: std.heap.ArenaAllocator,
756 indent: usize,
515757
516758 fn writeInstToStream(
517 self: Module,
518 stream: var,
519 decl: *Inst,
520 inst_table: *const InstPtrTable,
521 ) @TypeOf(stream).Error!void {
522 // TODO I tried implementing this with an inline for loop and hit a compiler bug
523 switch (decl.tag) {
524 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),
525 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
526 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
527 .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table),
528 .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table),
529 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
530 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
531 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
532 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
533 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
534 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
535 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
536 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
537 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
538 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
539 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
540 .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
541 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
542 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
543 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
544 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
545 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
546 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
547 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
548 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
549 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
550 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
759 self: *Writer,
760 stream: anytype,
761 inst: *Inst,
762 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
763 inline for (@typeInfo(Inst.Tag).Enum.fields) |enum_field| {
764 const expected_tag = @field(Inst.Tag, enum_field.name);
765 if (inst.tag == expected_tag) {
766 return self.writeInstToStreamGeneric(stream, expected_tag, inst);
767 }
551768 }
769 unreachable; // all tags handled
552770 }
553771
554772 fn writeInstToStreamGeneric(
555 self: Module,
556 stream: var,
773 self: *Writer,
774 stream: anytype,
557775 comptime inst_tag: Inst.Tag,
558776 base: *Inst,
559 inst_table: *const InstPtrTable,
560 ) !void {
777 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
561778 const SpecificInst = Inst.TagToType(inst_tag);
562779 const inst = @fieldParentPtr(SpecificInst, "base", base);
563780 const Positionals = @TypeOf(inst.positionals);
......@@ -567,7 +784,7 @@ pub const Module = struct {
567784 if (i != 0) {
568785 try stream.writeAll(", ");
569786 }
570 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);
787 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name));
571788 }
572789
573790 comptime var need_comma = pos_fields.len != 0;
......@@ -577,13 +794,13 @@ pub const Module = struct {
577794 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
578795 if (need_comma) try stream.writeAll(", ");
579796 try stream.print("{}=", .{arg_field.name});
580 try self.writeParamToStream(stream, non_optional, inst_table);
797 try self.writeParamToStream(stream, non_optional);
581798 need_comma = true;
582799 }
583800 } else {
584801 if (need_comma) try stream.writeAll(", ");
585802 try stream.print("{}=", .{arg_field.name});
586 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);
803 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name));
587804 need_comma = true;
588805 }
589806 }
......@@ -591,56 +808,73 @@ pub const Module = struct {
591808 try stream.writeByte(')');
592809 }
593810
594 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {
811 fn writeParamToStream(self: *Writer, stream: anytype, param: anytype) !void {
595812 if (@typeInfo(@TypeOf(param)) == .Enum) {
596813 return stream.writeAll(@tagName(param));
597814 }
598815 switch (@TypeOf(param)) {
599 *Inst => return self.writeInstParamToStream(stream, param, inst_table),
816 *Inst => return self.writeInstParamToStream(stream, param),
600817 []*Inst => {
601818 try stream.writeByte('[');
602819 for (param) |inst, i| {
603820 if (i != 0) {
604821 try stream.writeAll(", ");
605822 }
606 try self.writeInstParamToStream(stream, inst, inst_table);
823 try self.writeInstParamToStream(stream, inst);
607824 }
608825 try stream.writeByte(']');
609826 },
610827 Module.Body => {
611828 try stream.writeAll("{\n");
612829 for (param.instructions) |inst, i| {
613 try stream.print(" %{} ", .{i});
614 try self.writeInstToStream(stream, inst, inst_table);
830 try stream.writeByteNTimes(' ', self.indent);
831 try stream.print("%{} ", .{i});
832 if (inst.cast(Inst.Block)) |block| {
833 const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{i});
834 try self.block_table.put(block, name);
835 }
836 self.indent += 2;
837 try self.writeInstToStream(stream, inst);
838 self.indent -= 2;
615839 try stream.writeByte('\n');
616840 }
841 try stream.writeByteNTimes(' ', self.indent - 2);
617842 try stream.writeByte('}');
618843 },
619844 bool => return stream.writeByte("01"[@boolToInt(param)]),
620845 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
621 BigIntConst => return stream.print("{}", .{param}),
846 BigIntConst, usize => return stream.print("{}", .{param}),
847 TypedValue => unreachable, // this is a special case
848 *IrModule.Decl => unreachable, // this is a special case
849 *Inst.Block => {
850 const name = self.block_table.get(param).?;
851 return std.zig.renderStringLiteral(name, stream);
852 },
622853 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
623854 }
624855 }
625856
626 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {
627 if (inst_table.getValue(inst)) |info| {
857 fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void {
858 if (self.inst_table.get(inst)) |info| {
628859 if (info.index) |i| {
629860 try stream.print("%{}", .{info.index});
630861 } else {
631 try stream.print("@{}", .{info.inst.name});
862 try stream.print("@{}", .{info.name});
632863 }
633864 } else if (inst.cast(Inst.DeclVal)) |decl_val| {
634865 try stream.print("@{}", .{decl_val.positionals.name});
866 } else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
867 try stream.print("@{}", .{decl_val.positionals.decl.name});
635868 } else {
636 //try stream.print("?", .{});
637 unreachable;
869 // This should be unreachable in theory, but since ZIR is used for debugging the compiler
870 // we output some debug text instead.
871 try stream.print("?{}?", .{@tagName(inst.tag)});
638872 }
639873 }
640874};
641875
642876pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
643 var global_name_map = std.StringHashMap(usize).init(allocator);
877 var global_name_map = std.StringHashMap(*Inst).init(allocator);
644878 defer global_name_map.deinit();
645879
646880 var parser: Parser = .{
......@@ -651,7 +885,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
651885 .global_name_map = &global_name_map,
652886 .decls = .{},
653887 .unnamed_index = 0,
888 .block_table = std.StringHashMap(*Inst.Block).init(allocator),
654889 };
890 defer parser.block_table.deinit();
655891 errdefer parser.arena.deinit();
656892
657893 parser.parseRoot() catch |err| switch (err) {
......@@ -673,23 +909,26 @@ const Parser = struct {
673909 arena: std.heap.ArenaAllocator,
674910 i: usize,
675911 source: [:0]const u8,
676 decls: std.ArrayListUnmanaged(*Inst),
677 global_name_map: *std.StringHashMap(usize),
912 decls: std.ArrayListUnmanaged(*Decl),
913 global_name_map: *std.StringHashMap(*Inst),
678914 error_msg: ?ErrorMsg = null,
679915 unnamed_index: usize,
916 block_table: std.StringHashMap(*Inst.Block),
680917
681918 const Body = struct {
682919 instructions: std.ArrayList(*Inst),
683 name_map: std.StringHashMap(usize),
920 name_map: *std.StringHashMap(*Inst),
684921 };
685922
686 fn parseBody(self: *Parser) !Module.Body {
923 fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body {
924 var name_map = std.StringHashMap(*Inst).init(self.allocator);
925 defer name_map.deinit();
926
687927 var body_context = Body{
688928 .instructions = std.ArrayList(*Inst).init(self.allocator),
689 .name_map = std.StringHashMap(usize).init(self.allocator),
929 .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map,
690930 };
691931 defer body_context.instructions.deinit();
692 defer body_context.name_map.deinit();
693932
694933 try requireEatBytes(self, "{");
695934 skipSpace(self);
......@@ -702,12 +941,12 @@ const Parser = struct {
702941 skipSpace(self);
703942 try requireEatBytes(self, "=");
704943 skipSpace(self);
705 const inst = try parseInstruction(self, &body_context, ident);
944 const decl = try parseInstruction(self, &body_context, ident);
706945 const ident_index = body_context.instructions.items.len;
707 if (try body_context.name_map.put(ident, ident_index)) |_| {
946 if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| {
708947 return self.fail("redefinition of identifier '{}'", .{ident});
709948 }
710 try body_context.instructions.append(inst);
949 try body_context.instructions.append(decl.inst);
711950 continue;
712951 },
713952 ' ', '\n' => continue,
......@@ -788,12 +1027,12 @@ const Parser = struct {
7881027 skipSpace(self);
7891028 try requireEatBytes(self, "=");
7901029 skipSpace(self);
791 const inst = try parseInstruction(self, null, ident);
1030 const decl = try parseInstruction(self, null, ident);
7921031 const ident_index = self.decls.items.len;
793 if (try self.global_name_map.put(ident, ident_index)) |_| {
1032 if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| {
7941033 return self.fail("redefinition of identifier '{}'", .{ident});
7951034 }
796 try self.decls.append(self.allocator, inst);
1035 try self.decls.append(self.allocator, decl);
7971036 },
7981037 ' ', '\n' => self.i += 1,
7991038 0 => break,
......@@ -848,7 +1087,7 @@ const Parser = struct {
8481087 }
8491088 }
8501089
851 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {
1090 fn fail(self: *Parser, comptime format: []const u8, args: anytype) InnerError {
8521091 @setCold(true);
8531092 self.error_msg = ErrorMsg{
8541093 .byte_offset = self.i,
......@@ -857,7 +1096,7 @@ const Parser = struct {
8571096 return error.ParseFailure;
8581097 }
8591098
860 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst {
1099 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl {
8611100 const contents_start = self.i;
8621101 const fn_name = try skipToAndOver(self, '(');
8631102 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
......@@ -876,14 +1115,17 @@ const Parser = struct {
8761115 body_ctx: ?*Body,
8771116 inst_name: []const u8,
8781117 contents_start: usize,
879 ) InnerError!*Inst {
1118 ) InnerError!*Decl {
8801119 const inst_specific = try self.arena.allocator.create(InstType);
8811120 inst_specific.base = .{
882 .name = inst_name,
8831121 .src = self.i,
8841122 .tag = InstType.base_tag,
8851123 };
8861124
1125 if (InstType == Inst.Block) {
1126 try self.block_table.put(inst_name, inst_specific);
1127 }
1128
8871129 if (@hasField(InstType, "ty")) {
8881130 inst_specific.ty = opt_type orelse {
8891131 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
......@@ -929,10 +1171,15 @@ const Parser = struct {
9291171 }
9301172 try requireEatBytes(self, ")");
9311173
932 inst_specific.base.contents = self.source[contents_start..self.i];
1174 const decl = try self.arena.allocator.create(Decl);
1175 decl.* = .{
1176 .name = inst_name,
1177 .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]),
1178 .inst = &inst_specific.base,
1179 };
9331180 //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });
9341181
935 return &inst_specific.base;
1182 return decl;
9361183 }
9371184
9381185 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
......@@ -950,7 +1197,7 @@ const Parser = struct {
9501197 };
9511198 }
9521199 switch (T) {
953 Module.Body => return parseBody(self),
1200 Module.Body => return parseBody(self, body_ctx),
9541201 bool => {
9551202 const bool_value = switch (self.source[self.i]) {
9561203 '0' => false,
......@@ -978,6 +1225,16 @@ const Parser = struct {
9781225 *Inst => return parseParameterInst(self, body_ctx),
9791226 []u8, []const u8 => return self.parseStringLiteral(),
9801227 BigIntConst => return self.parseIntegerLiteral(),
1228 usize => {
1229 const big_int = try self.parseIntegerLiteral();
1230 return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)});
1231 },
1232 TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}),
1233 *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}),
1234 *Inst.Block => {
1235 const name = try self.parseStringLiteral();
1236 return self.block_table.get(name).?;
1237 },
9811238 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
9821239 }
9831240 return self.fail("TODO parse parameter {}", .{@typeName(T)});
......@@ -991,7 +1248,7 @@ const Parser = struct {
9911248 };
9921249 const map = if (local_ref)
9931250 if (body_ctx) |bc|
994 &bc.name_map
1251 bc.name_map
9951252 else
9961253 return self.fail("referencing a % instruction in global scope", .{})
9971254 else
......@@ -1004,7 +1261,7 @@ const Parser = struct {
10041261 else => continue,
10051262 };
10061263 const ident = self.source[name_start..self.i];
1007 const kv = map.get(ident) orelse {
1264 return map.get(ident) orelse {
10081265 const bad_name = self.source[name_start - 1 .. self.i];
10091266 const src = name_start - 1;
10101267 if (local_ref) {
......@@ -1014,7 +1271,6 @@ const Parser = struct {
10141271 const declval = try self.arena.allocator.create(Inst.DeclVal);
10151272 declval.* = .{
10161273 .base = .{
1017 .name = try self.generateName(),
10181274 .src = src,
10191275 .tag = Inst.DeclVal.base_tag,
10201276 },
......@@ -1024,11 +1280,6 @@ const Parser = struct {
10241280 return &declval.base;
10251281 }
10261282 };
1027 if (local_ref) {
1028 return body_ctx.?.instructions.items[kv.value];
1029 } else {
1030 return self.decls.items[kv.value];
1031 }
10321283 }
10331284
10341285 fn generateName(self: *Parser) ![]u8 {
......@@ -1046,8 +1297,11 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
10461297 .old_module = &old_module,
10471298 .next_auto_name = 0,
10481299 .names = std.StringHashMap(void).init(allocator),
1049 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator),
1300 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1301 .indent = 0,
1302 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
10501303 };
1304 defer ctx.block_table.deinit();
10511305 defer ctx.decls.deinit(allocator);
10521306 defer ctx.names.deinit();
10531307 defer ctx.primitive_table.deinit();
......@@ -1065,74 +1319,115 @@ const EmitZIR = struct {
10651319 allocator: *Allocator,
10661320 arena: std.heap.ArenaAllocator,
10671321 old_module: *const IrModule,
1068 decls: std.ArrayListUnmanaged(*Inst),
1322 decls: std.ArrayListUnmanaged(*Decl),
10691323 names: std.StringHashMap(void),
10701324 next_auto_name: usize,
1071 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst),
1325 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),
1326 indent: usize,
1327 block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block),
10721328
10731329 fn emit(self: *EmitZIR) !void {
10741330 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
10751331 // by the hash table.
10761332 var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);
10771333 defer src_decls.deinit();
1078 try src_decls.ensureCapacity(self.old_module.decl_table.size);
1079 try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size);
1080 try self.names.ensureCapacity(self.old_module.decl_table.size);
1334 try src_decls.ensureCapacity(self.old_module.decl_table.items().len);
1335 try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len);
1336 try self.names.ensureCapacity(self.old_module.decl_table.items().len);
10811337
1082 var decl_it = self.old_module.decl_table.iterator();
1083 while (decl_it.next()) |kv| {
1084 const decl = kv.value;
1338 for (self.old_module.decl_table.items()) |entry| {
1339 const decl = entry.value;
10851340 src_decls.appendAssumeCapacity(decl);
10861341 self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});
10871342 }
10881343 std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
10891344 fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
1090 return a.src < b.src;
1345 return a.src_index < b.src_index;
10911346 }
10921347 }).lessThan);
10931348
10941349 // Emit all the decls.
10951350 for (src_decls.items) |ir_decl| {
1096 if (self.old_module.export_owners.getValue(ir_decl)) |exports| {
1351 switch (ir_decl.analysis) {
1352 .unreferenced => continue,
1353
1354 .complete => {},
1355 .codegen_failure => {}, // We still can emit the ZIR.
1356 .codegen_failure_retryable => {}, // We still can emit the ZIR.
1357
1358 .in_progress => unreachable,
1359 .outdated => unreachable,
1360
1361 .sema_failure,
1362 .sema_failure_retryable,
1363 .dependency_failure,
1364 => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| {
1365 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1366 fail_inst.* = .{
1367 .base = .{
1368 .src = ir_decl.src(),
1369 .tag = Inst.CompileError.base_tag,
1370 },
1371 .positionals = .{
1372 .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
1373 },
1374 .kw_args = .{},
1375 };
1376 const decl = try self.arena.allocator.create(Decl);
1377 decl.* = .{
1378 .name = mem.spanZ(ir_decl.name),
1379 .contents_hash = undefined,
1380 .inst = &fail_inst.base,
1381 };
1382 try self.decls.append(self.allocator, decl);
1383 continue;
1384 },
1385 }
1386 if (self.old_module.export_owners.get(ir_decl)) |exports| {
10971387 for (exports) |module_export| {
1098 const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name));
10991388 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
11001389 const export_inst = try self.arena.allocator.create(Inst.Export);
11011390 export_inst.* = .{
11021391 .base = .{
1103 .name = try self.autoName(),
11041392 .src = module_export.src,
11051393 .tag = Inst.Export.base_tag,
11061394 },
11071395 .positionals = .{
1108 .symbol_name = symbol_name,
1109 .value = declval,
1396 .symbol_name = symbol_name.inst,
1397 .decl_name = mem.spanZ(module_export.exported_decl.name),
11101398 },
11111399 .kw_args = .{},
11121400 };
1113 try self.decls.append(self.allocator, &export_inst.base);
1401 _ = try self.emitUnnamedDecl(&export_inst.base);
11141402 }
11151403 } else {
1116 const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value);
1404 const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value);
11171405 new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
11181406 }
11191407 }
11201408 }
11211409
1122 fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
1410 const ZirBody = struct {
1411 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1412 instructions: *std.ArrayList(*Inst),
1413 };
1414
1415 fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst {
11231416 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1124 const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
1417 const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
11251418 const owner_decl = func_pl.func.owner_decl;
11261419 break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
11271420 } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
1128 break :blk try self.emitDeclRef(inst.src, declref.decl);
1421 const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
1422 try new_body.instructions.append(decl_ref);
1423 break :blk decl_ref;
11291424 } else blk: {
1130 break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1425 break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
11311426 };
1132 try inst_table.putNoClobber(inst, new_decl);
1133 return new_decl;
1427 try new_body.inst_table.putNoClobber(inst, new_inst);
1428 return new_inst;
11341429 } else {
1135 return inst_table.getValue(inst).?;
1430 return new_body.inst_table.get(inst).?;
11361431 }
11371432 }
11381433
......@@ -1140,7 +1435,6 @@ const EmitZIR = struct {
11401435 const declval = try self.arena.allocator.create(Inst.DeclVal);
11411436 declval.* = .{
11421437 .base = .{
1143 .name = try self.autoName(),
11441438 .src = src,
11451439 .tag = Inst.DeclVal.base_tag,
11461440 },
......@@ -1150,12 +1444,11 @@ const EmitZIR = struct {
11501444 return &declval.base;
11511445 }
11521446
1153 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {
1447 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl {
11541448 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
11551449 const int_inst = try self.arena.allocator.create(Inst.Int);
11561450 int_inst.* = .{
11571451 .base = .{
1158 .name = try self.autoName(),
11591452 .src = src,
11601453 .tag = Inst.Int.base_tag,
11611454 },
......@@ -1164,34 +1457,29 @@ const EmitZIR = struct {
11641457 },
11651458 .kw_args = .{},
11661459 };
1167 try self.decls.append(self.allocator, &int_inst.base);
1168 return &int_inst.base;
1460 return self.emitUnnamedDecl(&int_inst.base);
11691461 }
11701462
1171 fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst {
1172 const declval = try self.emitDeclVal(src, mem.spanZ(decl.name));
1173 const ref_inst = try self.arena.allocator.create(Inst.Ref);
1174 ref_inst.* = .{
1463 fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst {
1464 const declref_inst = try self.arena.allocator.create(Inst.DeclRef);
1465 declref_inst.* = .{
11751466 .base = .{
1176 .name = try self.autoName(),
11771467 .src = src,
1178 .tag = Inst.Ref.base_tag,
1468 .tag = Inst.DeclRef.base_tag,
11791469 },
11801470 .positionals = .{
1181 .operand = declval,
1471 .name = mem.spanZ(module_decl.name),
11821472 },
11831473 .kw_args = .{},
11841474 };
1185 try self.decls.append(self.allocator, &ref_inst.base);
1186
1187 return &ref_inst.base;
1475 return &declref_inst.base;
11881476 }
11891477
1190 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {
1478 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {
11911479 const allocator = &self.arena.allocator;
11921480 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
11931481 const decl = decl_ref.decl;
1194 return self.emitDeclRef(src, decl);
1482 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
11951483 }
11961484 switch (typed_value.ty.zigTypeTag()) {
11971485 .Pointer => {
......@@ -1218,18 +1506,16 @@ const EmitZIR = struct {
12181506 const as_inst = try self.arena.allocator.create(Inst.As);
12191507 as_inst.* = .{
12201508 .base = .{
1221 .name = try self.autoName(),
12221509 .src = src,
12231510 .tag = Inst.As.base_tag,
12241511 },
12251512 .positionals = .{
1226 .dest_type = try self.emitType(src, typed_value.ty),
1227 .value = try self.emitComptimeIntVal(src, typed_value.val),
1513 .dest_type = (try self.emitType(src, typed_value.ty)).inst,
1514 .value = (try self.emitComptimeIntVal(src, typed_value.val)).inst,
12281515 },
12291516 .kw_args = .{},
12301517 };
1231
1232 return &as_inst.base;
1518 return self.emitUnnamedDecl(&as_inst.base);
12331519 },
12341520 .Type => {
12351521 const ty = typed_value.val.toType();
......@@ -1251,11 +1537,10 @@ const EmitZIR = struct {
12511537 try self.emitBody(body, &inst_table, &instructions);
12521538 },
12531539 .sema_failure => {
1254 const err_msg = self.old_module.failed_decls.getValue(module_fn.owner_decl).?;
1540 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
12551541 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
12561542 fail_inst.* = .{
12571543 .base = .{
1258 .name = try self.autoName(),
12591544 .src = src,
12601545 .tag = Inst.CompileError.base_tag,
12611546 },
......@@ -1270,7 +1555,6 @@ const EmitZIR = struct {
12701555 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
12711556 fail_inst.* = .{
12721557 .base = .{
1273 .name = try self.autoName(),
12741558 .src = src,
12751559 .tag = Inst.CompileError.base_tag,
12761560 },
......@@ -1283,7 +1567,7 @@ const EmitZIR = struct {
12831567 },
12841568 }
12851569
1286 const fn_type = try self.emitType(src, module_fn.fn_type);
1570 const fn_type = try self.emitType(src, typed_value.ty);
12871571
12881572 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
12891573 mem.copy(*Inst, arena_instrs, instructions.items);
......@@ -1291,18 +1575,16 @@ const EmitZIR = struct {
12911575 const fn_inst = try self.arena.allocator.create(Inst.Fn);
12921576 fn_inst.* = .{
12931577 .base = .{
1294 .name = try self.autoName(),
12951578 .src = src,
12961579 .tag = Inst.Fn.base_tag,
12971580 },
12981581 .positionals = .{
1299 .fn_type = fn_type,
1582 .fn_type = fn_type.inst,
13001583 .body = .{ .instructions = arena_instrs },
13011584 },
13021585 .kw_args = .{},
13031586 };
1304 try self.decls.append(self.allocator, &fn_inst.base);
1305 return &fn_inst.base;
1587 return self.emitUnnamedDecl(&fn_inst.base);
13061588 },
13071589 .Array => {
13081590 // TODO more checks to make sure this can be emitted as a string literal
......@@ -1318,7 +1600,6 @@ const EmitZIR = struct {
13181600 const str_inst = try self.arena.allocator.create(Inst.Str);
13191601 str_inst.* = .{
13201602 .base = .{
1321 .name = try self.autoName(),
13221603 .src = src,
13231604 .tag = Inst.Str.base_tag,
13241605 },
......@@ -1327,8 +1608,7 @@ const EmitZIR = struct {
13271608 },
13281609 .kw_args = .{},
13291610 };
1330 try self.decls.append(self.allocator, &str_inst.base);
1331 return &str_inst.base;
1611 return self.emitUnnamedDecl(&str_inst.base);
13321612 },
13331613 .Void => return self.emitPrimitive(src, .void_value),
13341614 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
......@@ -1339,7 +1619,6 @@ const EmitZIR = struct {
13391619 const new_inst = try self.arena.allocator.create(T);
13401620 new_inst.* = .{
13411621 .base = .{
1342 .name = try self.autoName(),
13431622 .src = src,
13441623 .tag = T.base_tag,
13451624 },
......@@ -1351,29 +1630,150 @@ const EmitZIR = struct {
13511630
13521631 fn emitBody(
13531632 self: *EmitZIR,
1354 body: IrModule.Body,
1633 body: ir.Body,
13551634 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
13561635 instructions: *std.ArrayList(*Inst),
13571636 ) Allocator.Error!void {
1637 const new_body = ZirBody{
1638 .inst_table = inst_table,
1639 .instructions = instructions,
1640 };
13581641 for (body.instructions) |inst| {
13591642 const new_inst = switch (inst.tag) {
1643 .not => blk: {
1644 const old_inst = inst.cast(ir.Inst.Not).?;
1645 assert(inst.ty.zigTypeTag() == .Bool);
1646 const new_inst = try self.arena.allocator.create(Inst.BoolNot);
1647 new_inst.* = .{
1648 .base = .{
1649 .src = inst.src,
1650 .tag = Inst.BoolNot.base_tag,
1651 },
1652 .positionals = .{
1653 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1654 },
1655 .kw_args = .{},
1656 };
1657 break :blk &new_inst.base;
1658 },
1659 .add => blk: {
1660 const old_inst = inst.cast(ir.Inst.Add).?;
1661 const new_inst = try self.arena.allocator.create(Inst.Add);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = inst.src,
1665 .tag = Inst.Add.base_tag,
1666 },
1667 .positionals = .{
1668 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1669 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1670 },
1671 .kw_args = .{},
1672 };
1673 break :blk &new_inst.base;
1674 },
1675 .sub => blk: {
1676 const old_inst = inst.cast(ir.Inst.Sub).?;
1677 const new_inst = try self.arena.allocator.create(Inst.Sub);
1678 new_inst.* = .{
1679 .base = .{
1680 .src = inst.src,
1681 .tag = Inst.Sub.base_tag,
1682 },
1683 .positionals = .{
1684 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1685 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1686 },
1687 .kw_args = .{},
1688 };
1689 break :blk &new_inst.base;
1690 },
1691 .arg => blk: {
1692 const old_inst = inst.cast(ir.Inst.Arg).?;
1693 const new_inst = try self.arena.allocator.create(Inst.Arg);
1694 new_inst.* = .{
1695 .base = .{
1696 .src = inst.src,
1697 .tag = Inst.Arg.base_tag,
1698 },
1699 .positionals = .{},
1700 .kw_args = .{},
1701 };
1702 break :blk &new_inst.base;
1703 },
1704 .block => blk: {
1705 const old_inst = inst.cast(ir.Inst.Block).?;
1706 const new_inst = try self.arena.allocator.create(Inst.Block);
1707
1708 try self.block_table.put(old_inst, new_inst);
1709
1710 var block_body = std.ArrayList(*Inst).init(self.allocator);
1711 defer block_body.deinit();
1712
1713 try self.emitBody(old_inst.args.body, inst_table, &block_body);
1714
1715 new_inst.* = .{
1716 .base = .{
1717 .src = inst.src,
1718 .tag = Inst.Block.base_tag,
1719 },
1720 .positionals = .{
1721 .body = .{ .instructions = block_body.toOwnedSlice() },
1722 },
1723 .kw_args = .{},
1724 };
1725
1726 break :blk &new_inst.base;
1727 },
1728 .br => blk: {
1729 const old_inst = inst.cast(ir.Inst.Br).?;
1730 const new_block = self.block_table.get(old_inst.args.block).?;
1731 const new_inst = try self.arena.allocator.create(Inst.Break);
1732 new_inst.* = .{
1733 .base = .{
1734 .src = inst.src,
1735 .tag = Inst.Break.base_tag,
1736 },
1737 .positionals = .{
1738 .block = new_block,
1739 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1740 },
1741 .kw_args = .{},
1742 };
1743 break :blk &new_inst.base;
1744 },
13601745 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1746 .brvoid => blk: {
1747 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1748 const new_block = self.block_table.get(old_inst.args.block).?;
1749 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
1750 new_inst.* = .{
1751 .base = .{
1752 .src = inst.src,
1753 .tag = Inst.BreakVoid.base_tag,
1754 },
1755 .positionals = .{
1756 .block = new_block,
1757 },
1758 .kw_args = .{},
1759 };
1760 break :blk &new_inst.base;
1761 },
13611762 .call => blk: {
13621763 const old_inst = inst.cast(ir.Inst.Call).?;
13631764 const new_inst = try self.arena.allocator.create(Inst.Call);
13641765
13651766 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
13661767 for (args) |*elem, i| {
1367 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1768 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
13681769 }
13691770 new_inst.* = .{
13701771 .base = .{
1371 .name = try self.autoName(),
13721772 .src = inst.src,
13731773 .tag = Inst.Call.base_tag,
13741774 },
13751775 .positionals = .{
1376 .func = try self.resolveInst(inst_table, old_inst.args.func),
1776 .func = try self.resolveInst(new_body, old_inst.args.func),
13771777 .args = args,
13781778 },
13791779 .kw_args = .{},
......@@ -1381,7 +1781,22 @@ const EmitZIR = struct {
13811781 break :blk &new_inst.base;
13821782 },
13831783 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1384 .ret => try self.emitTrivial(inst.src, Inst.Return),
1784 .ret => blk: {
1785 const old_inst = inst.cast(ir.Inst.Ret).?;
1786 const new_inst = try self.arena.allocator.create(Inst.Return);
1787 new_inst.* = .{
1788 .base = .{
1789 .src = inst.src,
1790 .tag = Inst.Return.base_tag,
1791 },
1792 .positionals = .{
1793 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1794 },
1795 .kw_args = .{},
1796 };
1797 break :blk &new_inst.base;
1798 },
1799 .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
13851800 .constant => unreachable, // excluded from function bodies
13861801 .assembly => blk: {
13871802 const old_inst = inst.cast(ir.Inst.Assembly).?;
......@@ -1389,33 +1804,32 @@ const EmitZIR = struct {
13891804
13901805 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
13911806 for (inputs) |*elem, i| {
1392 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);
1807 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.inputs[i])).inst;
13931808 }
13941809
13951810 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
13961811 for (clobbers) |*elem, i| {
1397 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);
1812 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i])).inst;
13981813 }
13991814
14001815 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
14011816 for (args) |*elem, i| {
1402 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
1817 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
14031818 }
14041819
14051820 new_inst.* = .{
14061821 .base = .{
1407 .name = try self.autoName(),
14081822 .src = inst.src,
14091823 .tag = Inst.Asm.base_tag,
14101824 },
14111825 .positionals = .{
1412 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),
1413 .return_type = try self.emitType(inst.src, inst.ty),
1826 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst,
1827 .return_type = (try self.emitType(inst.src, inst.ty)).inst,
14141828 },
14151829 .kw_args = .{
14161830 .@"volatile" = old_inst.args.is_volatile,
14171831 .output = if (old_inst.args.output) |o|
1418 try self.emitStringLiteral(inst.src, o)
1832 (try self.emitStringLiteral(inst.src, o)).inst
14191833 else
14201834 null,
14211835 .inputs = inputs,
......@@ -1430,12 +1844,11 @@ const EmitZIR = struct {
14301844 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
14311845 new_inst.* = .{
14321846 .base = .{
1433 .name = try self.autoName(),
14341847 .src = inst.src,
14351848 .tag = Inst.PtrToInt.base_tag,
14361849 },
14371850 .positionals = .{
1438 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
1851 .ptr = try self.resolveInst(new_body, old_inst.args.ptr),
14391852 },
14401853 .kw_args = .{},
14411854 };
......@@ -1446,13 +1859,12 @@ const EmitZIR = struct {
14461859 const new_inst = try self.arena.allocator.create(Inst.BitCast);
14471860 new_inst.* = .{
14481861 .base = .{
1449 .name = try self.autoName(),
14501862 .src = inst.src,
14511863 .tag = Inst.BitCast.base_tag,
14521864 },
14531865 .positionals = .{
1454 .dest_type = try self.emitType(inst.src, inst.ty),
1455 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1866 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1867 .operand = try self.resolveInst(new_body, old_inst.args.operand),
14561868 },
14571869 .kw_args = .{},
14581870 };
......@@ -1463,13 +1875,12 @@ const EmitZIR = struct {
14631875 const new_inst = try self.arena.allocator.create(Inst.Cmp);
14641876 new_inst.* = .{
14651877 .base = .{
1466 .name = try self.autoName(),
14671878 .src = inst.src,
14681879 .tag = Inst.Cmp.base_tag,
14691880 },
14701881 .positionals = .{
1471 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
1472 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
1882 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1883 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
14731884 .op = old_inst.args.op,
14741885 },
14751886 .kw_args = .{},
......@@ -1491,12 +1902,11 @@ const EmitZIR = struct {
14911902 const new_inst = try self.arena.allocator.create(Inst.CondBr);
14921903 new_inst.* = .{
14931904 .base = .{
1494 .name = try self.autoName(),
14951905 .src = inst.src,
14961906 .tag = Inst.CondBr.base_tag,
14971907 },
14981908 .positionals = .{
1499 .condition = try self.resolveInst(inst_table, old_inst.args.condition),
1909 .condition = try self.resolveInst(new_body, old_inst.args.condition),
15001910 .true_body = .{ .instructions = true_body.toOwnedSlice() },
15011911 .false_body = .{ .instructions = false_body.toOwnedSlice() },
15021912 },
......@@ -1509,12 +1919,11 @@ const EmitZIR = struct {
15091919 const new_inst = try self.arena.allocator.create(Inst.IsNull);
15101920 new_inst.* = .{
15111921 .base = .{
1512 .name = try self.autoName(),
15131922 .src = inst.src,
15141923 .tag = Inst.IsNull.base_tag,
15151924 },
15161925 .positionals = .{
1517 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1926 .operand = try self.resolveInst(new_body, old_inst.args.operand),
15181927 },
15191928 .kw_args = .{},
15201929 };
......@@ -1525,12 +1934,11 @@ const EmitZIR = struct {
15251934 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
15261935 new_inst.* = .{
15271936 .base = .{
1528 .name = try self.autoName(),
15291937 .src = inst.src,
15301938 .tag = Inst.IsNonNull.base_tag,
15311939 },
15321940 .positionals = .{
1533 .operand = try self.resolveInst(inst_table, old_inst.args.operand),
1941 .operand = try self.resolveInst(new_body, old_inst.args.operand),
15341942 },
15351943 .kw_args = .{},
15361944 };
......@@ -1538,12 +1946,20 @@ const EmitZIR = struct {
15381946 },
15391947 };
15401948 try instructions.append(new_inst);
1541 try inst_table.putNoClobber(inst, new_inst);
1949 try inst_table.put(inst, new_inst);
15421950 }
15431951 }
15441952
1545 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {
1953 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl {
15461954 switch (ty.tag()) {
1955 .i8 => return self.emitPrimitive(src, .i8),
1956 .u8 => return self.emitPrimitive(src, .u8),
1957 .i16 => return self.emitPrimitive(src, .i16),
1958 .u16 => return self.emitPrimitive(src, .u16),
1959 .i32 => return self.emitPrimitive(src, .i32),
1960 .u32 => return self.emitPrimitive(src, .u32),
1961 .i64 => return self.emitPrimitive(src, .i64),
1962 .u64 => return self.emitPrimitive(src, .u64),
15471963 .isize => return self.emitPrimitive(src, .isize),
15481964 .usize => return self.emitPrimitive(src, .usize),
15491965 .c_short => return self.emitPrimitive(src, .c_short),
......@@ -1575,26 +1991,44 @@ const EmitZIR = struct {
15751991 ty.fnParamTypes(param_types);
15761992 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
15771993 for (param_types) |param_type, i| {
1578 emitted_params[i] = try self.emitType(src, param_type);
1994 emitted_params[i] = (try self.emitType(src, param_type)).inst;
15791995 }
15801996
15811997 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
15821998 fntype_inst.* = .{
15831999 .base = .{
1584 .name = try self.autoName(),
15852000 .src = src,
15862001 .tag = Inst.FnType.base_tag,
15872002 },
15882003 .positionals = .{
15892004 .param_types = emitted_params,
1590 .return_type = try self.emitType(src, ty.fnReturnType()),
2005 .return_type = (try self.emitType(src, ty.fnReturnType())).inst,
15912006 },
15922007 .kw_args = .{
15932008 .cc = ty.fnCallingConvention(),
15942009 },
15952010 };
1596 try self.decls.append(self.allocator, &fntype_inst.base);
1597 return &fntype_inst.base;
2011 return self.emitUnnamedDecl(&fntype_inst.base);
2012 },
2013 .Int => {
2014 const info = ty.intInfo(self.old_module.target());
2015 const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false");
2016 const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
2017 bits_payload.* = .{ .int = info.bits };
2018 const bits = try self.emitComptimeIntVal(src, Value.initPayload(&bits_payload.base));
2019 const inttype_inst = try self.arena.allocator.create(Inst.IntType);
2020 inttype_inst.* = .{
2021 .base = .{
2022 .src = src,
2023 .tag = Inst.IntType.base_tag,
2024 },
2025 .positionals = .{
2026 .signed = signed.inst,
2027 .bits = bits.inst,
2028 },
2029 .kw_args = .{},
2030 };
2031 return self.emitUnnamedDecl(&inttype_inst.base);
15982032 },
15992033 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
16002034 },
......@@ -1607,19 +2041,18 @@ const EmitZIR = struct {
16072041 self.next_auto_name += 1;
16082042 const gop = try self.names.getOrPut(proposed_name);
16092043 if (!gop.found_existing) {
1610 gop.kv.value = {};
2044 gop.entry.value = {};
16112045 return proposed_name;
16122046 }
16132047 }
16142048 }
16152049
1616 fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst {
2050 fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl {
16172051 const gop = try self.primitive_table.getOrPut(tag);
16182052 if (!gop.found_existing) {
16192053 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
16202054 primitive_inst.* = .{
16212055 .base = .{
1622 .name = try self.autoName(),
16232056 .src = src,
16242057 .tag = Inst.Primitive.base_tag,
16252058 },
......@@ -1628,17 +2061,15 @@ const EmitZIR = struct {
16282061 },
16292062 .kw_args = .{},
16302063 };
1631 try self.decls.append(self.allocator, &primitive_inst.base);
1632 gop.kv.value = &primitive_inst.base;
2064 gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base);
16332065 }
1634 return gop.kv.value;
2066 return gop.entry.value;
16352067 }
16362068
1637 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {
2069 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl {
16382070 const str_inst = try self.arena.allocator.create(Inst.Str);
16392071 str_inst.* = .{
16402072 .base = .{
1641 .name = try self.autoName(),
16422073 .src = src,
16432074 .tag = Inst.Str.base_tag,
16442075 },
......@@ -1647,22 +2078,17 @@ const EmitZIR = struct {
16472078 },
16482079 .kw_args = .{},
16492080 };
1650 try self.decls.append(self.allocator, &str_inst.base);
2081 return self.emitUnnamedDecl(&str_inst.base);
2082 }
16512083
1652 const ref_inst = try self.arena.allocator.create(Inst.Ref);
1653 ref_inst.* = .{
1654 .base = .{
1655 .name = try self.autoName(),
1656 .src = src,
1657 .tag = Inst.Ref.base_tag,
1658 },
1659 .positionals = .{
1660 .operand = &str_inst.base,
1661 },
1662 .kw_args = .{},
2084 fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl {
2085 const decl = try self.arena.allocator.create(Decl);
2086 decl.* = .{
2087 .name = try self.autoName(),
2088 .contents_hash = undefined,
2089 .inst = inst,
16632090 };
1664 try self.decls.append(self.allocator, &ref_inst.base);
1665
1666 return &ref_inst.base;
2091 try self.decls.append(self.allocator, decl);
2092 return decl;
16672093 }
16682094};
src/all_types.hpp+25-4
......@@ -692,7 +692,7 @@ enum NodeType {
692692 NodeTypeSuspend,
693693 NodeTypeAnyFrameType,
694694 NodeTypeEnumLiteral,
695 NodeTypeVarFieldType,
695 NodeTypeAnyTypeField,
696696};
697697
698698enum FnInline {
......@@ -705,7 +705,7 @@ struct AstNodeFnProto {
705705 Buf *name;
706706 ZigList<AstNode *> params;
707707 AstNode *return_type;
708 Token *return_var_token;
708 Token *return_anytype_token;
709709 AstNode *fn_def_node;
710710 // populated if this is an extern declaration
711711 Buf *lib_name;
......@@ -734,7 +734,7 @@ struct AstNodeFnDef {
734734struct AstNodeParamDecl {
735735 Buf *name;
736736 AstNode *type;
737 Token *var_token;
737 Token *anytype_token;
738738 Buf doc_comments;
739739 bool is_noalias;
740740 bool is_comptime;
......@@ -1827,6 +1827,7 @@ enum BuiltinFnId {
18271827 BuiltinFnIdBitSizeof,
18281828 BuiltinFnIdWasmMemorySize,
18291829 BuiltinFnIdWasmMemoryGrow,
1830 BuiltinFnIdSrc,
18301831};
18311832
18321833struct BuiltinFnEntry {
......@@ -2144,7 +2145,7 @@ struct CodeGen {
21442145 ZigType *entry_num_lit_float;
21452146 ZigType *entry_undef;
21462147 ZigType *entry_null;
2147 ZigType *entry_var;
2148 ZigType *entry_anytype;
21482149 ZigType *entry_global_error_set;
21492150 ZigType *entry_enum_literal;
21502151 ZigType *entry_any_frame;
......@@ -2640,6 +2641,7 @@ enum IrInstSrcId {
26402641 IrInstSrcIdCall,
26412642 IrInstSrcIdCallArgs,
26422643 IrInstSrcIdCallExtra,
2644 IrInstSrcIdAsyncCallExtra,
26432645 IrInstSrcIdConst,
26442646 IrInstSrcIdReturn,
26452647 IrInstSrcIdContainerInitList,
......@@ -2754,6 +2756,7 @@ enum IrInstSrcId {
27542756 IrInstSrcIdSpillEnd,
27552757 IrInstSrcIdWasmMemorySize,
27562758 IrInstSrcIdWasmMemoryGrow,
2759 IrInstSrcIdSrc,
27572760};
27582761
27592762// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
......@@ -3253,6 +3256,20 @@ struct IrInstSrcCallExtra {
32533256 ResultLoc *result_loc;
32543257};
32553258
3259// This is a pass1 instruction, used by @asyncCall, when the args node
3260// is not a literal.
3261// `args` is expected to be either a struct or a tuple.
3262struct IrInstSrcAsyncCallExtra {
3263 IrInstSrc base;
3264
3265 CallModifier modifier;
3266 IrInstSrc *fn_ref;
3267 IrInstSrc *ret_ptr;
3268 IrInstSrc *new_stack;
3269 IrInstSrc *args;
3270 ResultLoc *result_loc;
3271};
3272
32563273struct IrInstGenCall {
32573274 IrInstGen base;
32583275
......@@ -3761,6 +3778,10 @@ struct IrInstGenWasmMemoryGrow {
37613778 IrInstGen *delta;
37623779};
37633780
3781struct IrInstSrcSrc {
3782 IrInstSrc base;
3783};
3784
37643785struct IrInstSrcSlice {
37653786 IrInstSrc base;
37663787
src/analyze.cpp+40-14
......@@ -1129,7 +1129,7 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *
11291129 ZigValue *result = g->pass1_arena->create<ZigValue>();
11301130 ZigValue *result_ptr = g->pass1_arena->create<ZigValue>();
11311131 result->special = ConstValSpecialUndef;
1132 result->type = (type_entry == nullptr) ? g->builtin_types.entry_var : type_entry;
1132 result->type = (type_entry == nullptr) ? g->builtin_types.entry_anytype : type_entry;
11331133 result_ptr->special = ConstValSpecialStatic;
11341134 result_ptr->type = get_pointer_to_type(g, result->type, false);
11351135 result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;
......@@ -1230,7 +1230,7 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
12301230Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) {
12311231 if (type_val->special != ConstValSpecialLazy) {
12321232 assert(type_val->special == ConstValSpecialStatic);
1233 if (type_val->data.x_type == g->builtin_types.entry_var) {
1233 if (type_val->data.x_type == g->builtin_types.entry_anytype) {
12341234 *is_opaque_type = false;
12351235 return ErrorNone;
12361236 }
......@@ -1511,13 +1511,13 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
15111511 }
15121512 for (; i < fn_type_id->param_count; i += 1) {
15131513 const char *comma_str = (i == 0) ? "" : ",";
1514 buf_appendf(&fn_type->name, "%svar", comma_str);
1514 buf_appendf(&fn_type->name, "%sanytype", comma_str);
15151515 }
15161516 buf_append_str(&fn_type->name, ")");
15171517 if (fn_type_id->cc != CallingConventionUnspecified) {
15181518 buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));
15191519 }
1520 buf_append_str(&fn_type->name, " var");
1520 buf_append_str(&fn_type->name, " anytype");
15211521
15221522 fn_type->data.fn.fn_type_id = *fn_type_id;
15231523 fn_type->data.fn.is_generic = true;
......@@ -1853,10 +1853,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
18531853 buf_sprintf("var args only allowed in functions with C calling convention"));
18541854 return g->builtin_types.entry_invalid;
18551855 }
1856 } else if (param_node->data.param_decl.var_token != nullptr) {
1856 } else if (param_node->data.param_decl.anytype_token != nullptr) {
18571857 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
18581858 add_node_error(g, param_node,
1859 buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'",
1859 buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'",
18601860 calling_convention_name(fn_type_id.cc)));
18611861 return g->builtin_types.entry_invalid;
18621862 }
......@@ -1942,10 +1942,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
19421942 fn_entry->align_bytes = fn_type_id.alignment;
19431943 }
19441944
1945 if (fn_proto->return_var_token != nullptr) {
1945 if (fn_proto->return_anytype_token != nullptr) {
19461946 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
19471947 add_node_error(g, fn_proto->return_type,
1948 buf_sprintf("return type 'var' not allowed in function with calling convention '%s'",
1948 buf_sprintf("return type 'anytype' not allowed in function with calling convention '%s'",
19491949 calling_convention_name(fn_type_id.cc)));
19501950 return g->builtin_types.entry_invalid;
19511951 }
......@@ -3802,7 +3802,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
38023802 case NodeTypeEnumLiteral:
38033803 case NodeTypeAnyFrameType:
38043804 case NodeTypeErrorSetField:
3805 case NodeTypeVarFieldType:
3805 case NodeTypeAnyTypeField:
38063806 zig_unreachable();
38073807 }
38083808}
......@@ -3823,15 +3823,18 @@ static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) {
38233823 }
38243824}
38253825
3826ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry) {
3826ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry) {
38273827 switch (type_entry->id) {
38283828 case ZigTypeIdInvalid:
38293829 return g->builtin_types.entry_invalid;
3830 case ZigTypeIdOpaque:
3831 if (source_node->is_extern)
3832 return type_entry;
3833 ZIG_FALLTHROUGH;
38303834 case ZigTypeIdUnreachable:
38313835 case ZigTypeIdUndefined:
38323836 case ZigTypeIdNull:
3833 case ZigTypeIdOpaque:
3834 add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed",
3837 add_node_error(g, source_node->type, buf_sprintf("variable of type '%s' not allowed",
38353838 buf_ptr(&type_entry->name)));
38363839 return g->builtin_types.entry_invalid;
38373840 case ZigTypeIdComptimeFloat:
......@@ -3973,7 +3976,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
39733976 } else {
39743977 tld_var->analyzing_type = true;
39753978 ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);
3976 explicit_type = validate_var_type(g, var_decl->type, proposed_type);
3979 explicit_type = validate_var_type(g, var_decl, proposed_type);
39773980 }
39783981 }
39793982
......@@ -4012,6 +4015,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
40124015 } else if (!is_extern) {
40134016 add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
40144017 implicit_type = g->builtin_types.entry_invalid;
4018 } else if (explicit_type == nullptr) {
4019 // extern variable without explicit type
4020 add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
4021 implicit_type = g->builtin_types.entry_invalid;
40154022 }
40164023
40174024 ZigType *type = explicit_type ? explicit_type : implicit_type;
......@@ -5864,7 +5871,7 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
58645871
58655872ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
58665873 Error err;
5867 if (ty == g->builtin_types.entry_var) {
5874 if (ty == g->builtin_types.entry_anytype) {
58685875 return ReqCompTimeYes;
58695876 }
58705877 switch (ty->id) {
......@@ -6012,6 +6019,19 @@ ZigValue *create_const_null(CodeGen *g, ZigType *type) {
60126019 return const_val;
60136020}
60146021
6022void init_const_fn(ZigValue *const_val, ZigFn *fn) {
6023 const_val->special = ConstValSpecialStatic;
6024 const_val->type = fn->type_entry;
6025 const_val->data.x_ptr.special = ConstPtrSpecialFunction;
6026 const_val->data.x_ptr.data.fn.fn_entry = fn;
6027}
6028
6029ZigValue *create_const_fn(CodeGen *g, ZigFn *fn) {
6030 ZigValue *const_val = g->pass1_arena->create<ZigValue>();
6031 init_const_fn(const_val, fn);
6032 return const_val;
6033}
6034
60156035void init_const_float(ZigValue *const_val, ZigType *type, double value) {
60166036 const_val->special = ConstValSpecialStatic;
60176037 const_val->type = type;
......@@ -9584,6 +9604,12 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {
95849604 break;
95859605 }
95869606 }
9607 } else if (dest->type->id == ZigTypeIdUnion) {
9608 bigint_init_bigint(&dest->data.x_union.tag, &src->data.x_union.tag);
9609 dest->data.x_union.payload = g->pass1_arena->create<ZigValue>();
9610 copy_const_val(g, dest->data.x_union.payload, src->data.x_union.payload);
9611 dest->data.x_union.payload->parent.id = ConstParentIdUnion;
9612 dest->data.x_union.payload->parent.data.p_union.union_val = dest;
95879613 } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
95889614 dest->data.x_optional = g->pass1_arena->create<ZigValue>();
95899615 copy_const_val(g, dest->data.x_optional, src->data.x_optional);
src/analyze.hpp+4-1
......@@ -77,7 +77,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all
7777ZigType *get_src_ptr_type(ZigType *type);
7878uint32_t get_ptr_align(CodeGen *g, ZigType *type);
7979bool get_ptr_const(CodeGen *g, ZigType *type);
80ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry);
80ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry);
8181ZigType *container_ref_type(ZigType *type_entry);
8282bool type_is_complete(ZigType *type_entry);
8383bool type_is_resolved(ZigType *type_entry, ResolveStatus status);
......@@ -180,6 +180,9 @@ ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size
180180void init_const_null(ZigValue *const_val, ZigType *type);
181181ZigValue *create_const_null(CodeGen *g, ZigType *type);
182182
183void init_const_fn(ZigValue *const_val, ZigFn *fn);
184ZigValue *create_const_fn(CodeGen *g, ZigFn *fn);
185
183186ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);
184187ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);
185188
src/ast_render.cpp+8-8
......@@ -270,8 +270,8 @@ static const char *node_type_str(NodeType node_type) {
270270 return "EnumLiteral";
271271 case NodeTypeErrorSetField:
272272 return "ErrorSetField";
273 case NodeTypeVarFieldType:
274 return "VarFieldType";
273 case NodeTypeAnyTypeField:
274 return "AnyTypeField";
275275 }
276276 zig_unreachable();
277277}
......@@ -466,8 +466,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
466466 }
467467 if (param_decl->data.param_decl.is_var_args) {
468468 fprintf(ar->f, "...");
469 } else if (param_decl->data.param_decl.var_token != nullptr) {
470 fprintf(ar->f, "var");
469 } else if (param_decl->data.param_decl.anytype_token != nullptr) {
470 fprintf(ar->f, "anytype");
471471 } else {
472472 render_node_grouped(ar, param_decl->data.param_decl.type);
473473 }
......@@ -496,8 +496,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
496496 fprintf(ar->f, ")");
497497 }
498498
499 if (node->data.fn_proto.return_var_token != nullptr) {
500 fprintf(ar->f, "var");
499 if (node->data.fn_proto.return_anytype_token != nullptr) {
500 fprintf(ar->f, "anytype");
501501 } else {
502502 AstNode *return_type_node = node->data.fn_proto.return_type;
503503 assert(return_type_node != nullptr);
......@@ -1216,8 +1216,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
12161216 fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
12171217 break;
12181218 }
1219 case NodeTypeVarFieldType: {
1220 fprintf(ar->f, "var");
1219 case NodeTypeAnyTypeField: {
1220 fprintf(ar->f, "anytype");
12211221 break;
12221222 }
12231223 case NodeTypeParamDecl:
src/codegen.cpp+33-14
......@@ -1535,9 +1535,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
15351535 zig_unreachable();
15361536 }
15371537
1538 if (actual_type->id == ZigTypeIdInt &&
1539 !wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed &&
1540 want_runtime_safety)
1538 if (actual_type->id == ZigTypeIdInt && want_runtime_safety && (
1539 // negative to unsigned
1540 (!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed) ||
1541 // unsigned would become negative
1542 (wanted_type->data.integral.is_signed && !actual_type->data.integral.is_signed && actual_bits == wanted_bits)))
15411543 {
15421544 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type));
15431545 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
......@@ -1547,7 +1549,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
15471549 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
15481550
15491551 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1550 gen_safety_crash(g, PanicMsgIdCastNegativeToUnsigned);
1552 gen_safety_crash(g, actual_type->data.integral.is_signed ? PanicMsgIdCastNegativeToUnsigned : PanicMsgIdCastTruncatedData);
15511553
15521554 LLVMPositionBuilderAtEnd(g->builder, ok_block);
15531555 }
......@@ -3540,7 +3542,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl
35403542
35413543 for (size_t field_i = 0; field_i < field_count; field_i += 1) {
35423544 TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i];
3543
3545
35443546 Buf *name = type_enum_field->name;
35453547 auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
35463548 if (entry != nullptr) {
......@@ -3654,7 +3656,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *oper
36543656 } else if (scalar_type->data.integral.is_signed) {
36553657 return LLVMBuildNSWNeg(g->builder, llvm_operand, "");
36563658 } else {
3657 return LLVMBuildNUWNeg(g->builder, llvm_operand, "");
3659 zig_unreachable();
36583660 }
36593661 } else {
36603662 zig_unreachable();
......@@ -3984,7 +3986,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable,
39843986 assert(array_type->data.pointer.child_type->id == ZigTypeIdArray);
39853987 array_type = array_type->data.pointer.child_type;
39863988 }
3987
3989
39883990 assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr);
39893991
39903992 if (safety_check_on) {
......@@ -5258,7 +5260,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
52585260
52595261 for (size_t field_i = 0; field_i < field_count; field_i += 1) {
52605262 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
5261
5263
52625264 Buf *name = type_enum_field->name;
52635265 auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
52645266 if (entry != nullptr) {
......@@ -5471,7 +5473,7 @@ static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) {
54715473 }
54725474 auto bit_count = operand_type->data.integral.bit_count;
54735475 bool is_signed = operand_type->data.integral.is_signed;
5474
5476
54755477 ir_assert(bit_count != 0, instruction);
54765478 if (bit_count == 1 || !is_power_of_2(bit_count)) {
54775479 return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8));
......@@ -5583,8 +5585,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir
55835585
55845586 bool val_is_undef = value_is_all_undef(g, instruction->byte->value);
55855587 LLVMValueRef fill_char;
5586 if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
5587 fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
5588 if (val_is_undef) {
5589 if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
5590 fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
5591 } else {
5592 return nullptr;
5593 }
55885594 } else {
55895595 fill_char = ir_llvm_value(g, instruction->byte);
55905596 }
......@@ -7473,6 +7479,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
74737479 continue;
74747480 }
74757481 ZigValue *field_val = const_val->data.x_struct.fields[i];
7482 if (field_val == nullptr) {
7483 add_node_error(g, type_struct_field->decl_node,
7484 buf_sprintf("compiler bug: generating const value for struct field '%s'",
7485 buf_ptr(type_struct_field->name)));
7486 codegen_report_errors_and_exit(g);
7487 }
74767488 ZigType *field_type = field_val->type;
74777489 assert(field_type != nullptr);
74787490 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {
......@@ -8436,8 +8448,8 @@ static void define_builtin_types(CodeGen *g) {
84368448 }
84378449 {
84388450 ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
8439 buf_init_from_str(&entry->name, "(var)");
8440 g->builtin_types.entry_var = entry;
8451 buf_init_from_str(&entry->name, "(anytype)");
8452 g->builtin_types.entry_anytype = entry;
84418453 }
84428454
84438455 for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) {
......@@ -8714,6 +8726,7 @@ static void define_builtin_fns(CodeGen *g) {
87148726 create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1);
87158727 create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1);
87168728 create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
8729 create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
87178730}
87188731
87198732static const char *bool_to_str(bool b) {
......@@ -9264,7 +9277,7 @@ static void init(CodeGen *g) {
92649277 abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64";
92659278 }
92669279 }
9267
9280
92689281 g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str),
92699282 target_specific_cpu_args, target_specific_features, opt_level, reloc_mode,
92709283 to_llvm_code_model(g), g->function_sections, float_abi, abi_name);
......@@ -9464,9 +9477,15 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
94649477 const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
94659478 buf_ptr(g->zig_lib_dir)));
94669479
9480 const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
9481 buf_ptr(g->zig_lib_dir)));
9482
94679483 args.append("-isystem");
94689484 args.append(libcxx_include_path);
94699485
9486 args.append("-isystem");
9487 args.append(libcxxabi_include_path);
9488
94709489 if (target_abi_is_musl(g->zig_target->abi)) {
94719490 args.append("-D_LIBCPP_HAS_MUSL_LIBC");
94729491 }
src/compiler.cpp+8
......@@ -38,11 +38,19 @@ Error get_compiler_id(Buf **result) {
3838 ZigList<Buf *> lib_paths = {};
3939 if ((err = os_self_exe_shared_libs(lib_paths)))
4040 return err;
41 #if defined(ZIG_OS_DARWIN)
42 // only add the self exe path on mac os
43 Buf *lib_path = lib_paths.at(0);
44 if ((err = cache_add_file(ch, lib_path)))
45 return err;
46 #else
4147 for (size_t i = 0; i < lib_paths.length; i += 1) {
4248 Buf *lib_path = lib_paths.at(i);
4349 if ((err = cache_add_file(ch, lib_path)))
4450 return err;
4551 }
52 #endif
53
4654 if ((err = cache_final(ch, &saved_compiler_id)))
4755 return err;
4856
src/hash_map.hpp+305-127
......@@ -19,45 +19,85 @@ public:
1919 init_capacity(capacity);
2020 }
2121 void deinit(void) {
22 heap::c_allocator.deallocate(_entries, _capacity);
22 _entries.deinit();
23 heap::c_allocator.deallocate(_index_bytes,
24 _indexes_len * capacity_index_size(_indexes_len));
2325 }
2426
2527 struct Entry {
28 uint32_t hash;
29 uint32_t distance_from_start_index;
2630 K key;
2731 V value;
28 bool used;
29 int distance_from_start_index;
3032 };
3133
3234 void clear() {
33 for (int i = 0; i < _capacity; i += 1) {
34 _entries[i].used = false;
35 }
36 _size = 0;
35 _entries.clear();
36 memset(_index_bytes, 0, _indexes_len * capacity_index_size(_indexes_len));
3737 _max_distance_from_start_index = 0;
3838 _modification_count += 1;
3939 }
4040
41 int size() const {
42 return _size;
41 size_t size() const {
42 return _entries.length;
4343 }
4444
4545 void put(const K &key, const V &value) {
4646 _modification_count += 1;
47 internal_put(key, value);
48
49 // if we get too full (60%), double the capacity
50 if (_size * 5 >= _capacity * 3) {
51 Entry *old_entries = _entries;
52 int old_capacity = _capacity;
53 init_capacity(_capacity * 2);
54 // dump all of the old elements into the new table
55 for (int i = 0; i < old_capacity; i += 1) {
56 Entry *old_entry = &old_entries[i];
57 if (old_entry->used)
58 internal_put(old_entry->key, old_entry->value);
47
48 // This allows us to take a pointer to an entry in `internal_put` which
49 // will not become a dead pointer when the array list is appended.
50 _entries.ensure_capacity(_entries.length + 1);
51
52 if (_index_bytes == nullptr) {
53 if (_entries.length < 16) {
54 _entries.append({HashFunction(key), 0, key, value});
55 return;
56 } else {
57 _indexes_len = 32;
58 _index_bytes = heap::c_allocator.allocate<uint8_t>(_indexes_len);
59 _max_distance_from_start_index = 0;
60 for (size_t i = 0; i < _entries.length; i += 1) {
61 Entry *entry = &_entries.items[i];
62 put_index(entry, i, _index_bytes);
63 }
64 return internal_put(key, value, _index_bytes);
65 }
66 }
67
68 // if we would get too full (60%), double the indexes size
69 if ((_entries.length + 1) * 5 >= _indexes_len * 3) {
70 heap::c_allocator.deallocate(_index_bytes,
71 _indexes_len * capacity_index_size(_indexes_len));
72 _indexes_len *= 2;
73 size_t sz = capacity_index_size(_indexes_len);
74 // This zero initializes the bytes, setting them all empty.
75 _index_bytes = heap::c_allocator.allocate<uint8_t>(_indexes_len * sz);
76 _max_distance_from_start_index = 0;
77 for (size_t i = 0; i < _entries.length; i += 1) {
78 Entry *entry = &_entries.items[i];
79 switch (sz) {
80 case 1:
81 put_index(entry, i, (uint8_t*)_index_bytes);
82 continue;
83 case 2:
84 put_index(entry, i, (uint16_t*)_index_bytes);
85 continue;
86 case 4:
87 put_index(entry, i, (uint32_t*)_index_bytes);
88 continue;
89 default:
90 put_index(entry, i, (size_t*)_index_bytes);
91 continue;
92 }
5993 }
60 heap::c_allocator.deallocate(old_entries, old_capacity);
94 }
95
96 switch (capacity_index_size(_indexes_len)) {
97 case 1: return internal_put(key, value, (uint8_t*)_index_bytes);
98 case 2: return internal_put(key, value, (uint16_t*)_index_bytes);
99 case 4: return internal_put(key, value, (uint32_t*)_index_bytes);
100 default: return internal_put(key, value, (size_t*)_index_bytes);
61101 }
62102 }
63103
......@@ -81,40 +121,31 @@ public:
81121 return internal_get(key);
82122 }
83123
84 void maybe_remove(const K &key) {
85 if (maybe_get(key)) {
86 remove(key);
87 }
124 bool remove(const K &key) {
125 bool deleted_something = maybe_remove(key);
126 if (!deleted_something)
127 zig_panic("key not found");
128 return deleted_something;
88129 }
89130
90 void remove(const K &key) {
131 bool maybe_remove(const K &key) {
91132 _modification_count += 1;
92 int start_index = key_to_index(key);
93 for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
94 int index = (start_index + roll_over) % _capacity;
95 Entry *entry = &_entries[index];
96
97 if (!entry->used)
98 zig_panic("key not found");
99
100 if (!EqualFn(entry->key, key))
101 continue;
102
103 for (; roll_over < _capacity; roll_over += 1) {
104 int next_index = (start_index + roll_over + 1) % _capacity;
105 Entry *next_entry = &_entries[next_index];
106 if (!next_entry->used || next_entry->distance_from_start_index == 0) {
107 entry->used = false;
108 _size -= 1;
109 return;
133 if (_index_bytes == nullptr) {
134 uint32_t hash = HashFunction(key);
135 for (size_t i = 0; i < _entries.length; i += 1) {
136 if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) {
137 _entries.swap_remove(i);
138 return true;
110139 }
111 *entry = *next_entry;
112 entry->distance_from_start_index -= 1;
113 entry = next_entry;
114140 }
115 zig_panic("shifting everything in the table");
141 return false;
142 }
143 switch (capacity_index_size(_indexes_len)) {
144 case 1: return internal_remove(key, (uint8_t*)_index_bytes);
145 case 2: return internal_remove(key, (uint16_t*)_index_bytes);
146 case 4: return internal_remove(key, (uint32_t*)_index_bytes);
147 default: return internal_remove(key, (size_t*)_index_bytes);
116148 }
117 zig_panic("key not found");
118149 }
119150
120151 class Iterator {
......@@ -122,24 +153,16 @@ public:
122153 Entry *next() {
123154 if (_inital_modification_count != _table->_modification_count)
124155 zig_panic("concurrent modification");
125 if (_count >= _table->size())
126 return NULL;
127 for (; _index < _table->_capacity; _index += 1) {
128 Entry *entry = &_table->_entries[_index];
129 if (entry->used) {
130 _index += 1;
131 _count += 1;
132 return entry;
133 }
134 }
135 zig_panic("no next item");
156 if (_index >= _table->_entries.length)
157 return nullptr;
158 Entry *entry = &_table->_entries.items[_index];
159 _index += 1;
160 return entry;
136161 }
137162 private:
138163 const HashMap * _table;
139 // how many items have we returned
140 int _count = 0;
141164 // iterator through the entry array
142 int _index = 0;
165 size_t _index = 0;
143166 // used to detect concurrent modification
144167 uint32_t _inital_modification_count;
145168 Iterator(const HashMap * table) :
......@@ -154,89 +177,244 @@ public:
154177 }
155178
156179private:
157
158 Entry *_entries;
159 int _capacity;
160 int _size;
161 int _max_distance_from_start_index;
162 // this is used to detect bugs where a hashtable is edited while an iterator is running.
180 // Maintains insertion order.
181 ZigList<Entry> _entries;
182 // If _indexes_len is less than 2**8, this is an array of uint8_t.
183 // If _indexes_len is less than 2**16, it is an array of uint16_t.
184 // If _indexes_len is less than 2**32, it is an array of uint32_t.
185 // Otherwise it is size_t.
186 // It's off by 1. 0 means empty slot, 1 means index 0, etc.
187 uint8_t *_index_bytes;
188 // This is the number of indexes. When indexes are bytes, it equals number of bytes.
189 // When indexes are uint16_t, _indexes_len is half the number of bytes.
190 size_t _indexes_len;
191
192 size_t _max_distance_from_start_index;
193 // This is used to detect bugs where a hashtable is edited while an iterator is running.
163194 uint32_t _modification_count;
164195
165 void init_capacity(int capacity) {
166 _capacity = capacity;
167 _entries = heap::c_allocator.allocate<Entry>(_capacity);
168 _size = 0;
169 _max_distance_from_start_index = 0;
170 for (int i = 0; i < _capacity; i += 1) {
171 _entries[i].used = false;
196 void init_capacity(size_t capacity) {
197 _entries = {};
198 _entries.ensure_capacity(capacity);
199 _indexes_len = 0;
200 if (capacity >= 16) {
201 // So that at capacity it will only be 60% full.
202 _indexes_len = capacity * 5 / 3;
203 size_t sz = capacity_index_size(_indexes_len);
204 // This zero initializes _index_bytes which sets them all to empty.
205 _index_bytes = heap::c_allocator.allocate<uint8_t>(_indexes_len * sz);
206 } else {
207 _index_bytes = nullptr;
172208 }
209
210 _max_distance_from_start_index = 0;
211 _modification_count = 0;
173212 }
174213
175 void internal_put(K key, V value) {
176 int start_index = key_to_index(key);
177 for (int roll_over = 0, distance_from_start_index = 0;
178 roll_over < _capacity; roll_over += 1, distance_from_start_index += 1)
214 static size_t capacity_index_size(size_t len) {
215 if (len < UINT8_MAX)
216 return 1;
217 if (len < UINT16_MAX)
218 return 2;
219 if (len < UINT32_MAX)
220 return 4;
221 return sizeof(size_t);
222 }
223
224 template <typename I>
225 void internal_put(const K &key, const V &value, I *indexes) {
226 uint32_t hash = HashFunction(key);
227 uint32_t distance_from_start_index = 0;
228 size_t start_index = hash_to_index(hash);
229 for (size_t roll_over = 0; roll_over < _indexes_len;
230 roll_over += 1, distance_from_start_index += 1)
179231 {
180 int index = (start_index + roll_over) % _capacity;
181 Entry *entry = &_entries[index];
182
183 if (entry->used && !EqualFn(entry->key, key)) {
184 if (entry->distance_from_start_index < distance_from_start_index) {
185 // robin hood to the rescue
186 Entry tmp = *entry;
187 if (distance_from_start_index > _max_distance_from_start_index)
188 _max_distance_from_start_index = distance_from_start_index;
189 *entry = {
190 key,
191 value,
192 true,
193 distance_from_start_index,
194 };
195 key = tmp.key;
196 value = tmp.value;
197 distance_from_start_index = tmp.distance_from_start_index;
232 size_t index_index = (start_index + roll_over) % _indexes_len;
233 I index_data = indexes[index_index];
234 if (index_data == 0) {
235 _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value });
236 indexes[index_index] = _entries.length;
237 if (distance_from_start_index > _max_distance_from_start_index)
238 _max_distance_from_start_index = distance_from_start_index;
239 return;
240 }
241 // This pointer survives the following append because we call
242 // _entries.ensure_capacity before internal_put.
243 Entry *entry = &_entries.items[index_data - 1];
244 if (entry->hash == hash && EqualFn(entry->key, key)) {
245 *entry = {hash, distance_from_start_index, key, value};
246 if (distance_from_start_index > _max_distance_from_start_index)
247 _max_distance_from_start_index = distance_from_start_index;
248 return;
249 }
250 if (entry->distance_from_start_index < distance_from_start_index) {
251 // In this case, we did not find the item. We will put a new entry.
252 // However, we will use this index for the new entry, and move
253 // the previous index down the line, to keep the _max_distance_from_start_index
254 // as small as possible.
255 _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value });
256 indexes[index_index] = _entries.length;
257 if (distance_from_start_index > _max_distance_from_start_index)
258 _max_distance_from_start_index = distance_from_start_index;
259
260 distance_from_start_index = entry->distance_from_start_index;
261
262 // Find somewhere to put the index we replaced by shifting
263 // following indexes backwards.
264 roll_over += 1;
265 distance_from_start_index += 1;
266 for (; roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1) {
267 size_t index_index = (start_index + roll_over) % _indexes_len;
268 I next_index_data = indexes[index_index];
269 if (next_index_data == 0) {
270 if (distance_from_start_index > _max_distance_from_start_index)
271 _max_distance_from_start_index = distance_from_start_index;
272 entry->distance_from_start_index = distance_from_start_index;
273 indexes[index_index] = index_data;
274 return;
275 }
276 Entry *next_entry = &_entries.items[next_index_data - 1];
277 if (next_entry->distance_from_start_index < distance_from_start_index) {
278 if (distance_from_start_index > _max_distance_from_start_index)
279 _max_distance_from_start_index = distance_from_start_index;
280 entry->distance_from_start_index = distance_from_start_index;
281 indexes[index_index] = index_data;
282 distance_from_start_index = next_entry->distance_from_start_index;
283 entry = next_entry;
284 index_data = next_index_data;
285 }
198286 }
199 continue;
287 zig_unreachable();
288 }
289 }
290 zig_unreachable();
291 }
292
293 template <typename I>
294 void put_index(Entry *entry, size_t entry_index, I *indexes) {
295 size_t start_index = hash_to_index(entry->hash);
296 size_t index_data = entry_index + 1;
297 for (size_t roll_over = 0, distance_from_start_index = 0;
298 roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1)
299 {
300 size_t index_index = (start_index + roll_over) % _indexes_len;
301 size_t next_index_data = indexes[index_index];
302 if (next_index_data == 0) {
303 if (distance_from_start_index > _max_distance_from_start_index)
304 _max_distance_from_start_index = distance_from_start_index;
305 entry->distance_from_start_index = distance_from_start_index;
306 indexes[index_index] = index_data;
307 return;
308 }
309 Entry *next_entry = &_entries.items[next_index_data - 1];
310 if (next_entry->distance_from_start_index < distance_from_start_index) {
311 if (distance_from_start_index > _max_distance_from_start_index)
312 _max_distance_from_start_index = distance_from_start_index;
313 entry->distance_from_start_index = distance_from_start_index;
314 indexes[index_index] = index_data;
315 distance_from_start_index = next_entry->distance_from_start_index;
316 entry = next_entry;
317 index_data = next_index_data;
200318 }
319 }
320 zig_unreachable();
321 }
201322
202 if (!entry->used) {
203 // adding an entry. otherwise overwriting old value with
204 // same key
205 _size += 1;
323 Entry *internal_get(const K &key) const {
324 if (_index_bytes == nullptr) {
325 uint32_t hash = HashFunction(key);
326 for (size_t i = 0; i < _entries.length; i += 1) {
327 if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) {
328 return &_entries.items[i];
329 }
206330 }
331 return nullptr;
332 }
333 switch (capacity_index_size(_indexes_len)) {
334 case 1: return internal_get2(key, (uint8_t*)_index_bytes);
335 case 2: return internal_get2(key, (uint16_t*)_index_bytes);
336 case 4: return internal_get2(key, (uint32_t*)_index_bytes);
337 default: return internal_get2(key, (size_t*)_index_bytes);
338 }
339 }
207340
208 if (distance_from_start_index > _max_distance_from_start_index)
209 _max_distance_from_start_index = distance_from_start_index;
210 *entry = {
211 key,
212 value,
213 true,
214 distance_from_start_index,
215 };
216 return;
341 template <typename I>
342 Entry *internal_get2(const K &key, I *indexes) const {
343 uint32_t hash = HashFunction(key);
344 size_t start_index = hash_to_index(hash);
345 for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
346 size_t index_index = (start_index + roll_over) % _indexes_len;
347 size_t index_data = indexes[index_index];
348 if (index_data == 0)
349 return nullptr;
350
351 Entry *entry = &_entries.items[index_data - 1];
352 if (entry->hash == hash && EqualFn(entry->key, key))
353 return entry;
217354 }
218 zig_panic("put into a full HashMap");
355 return nullptr;
219356 }
220357
358 size_t hash_to_index(uint32_t hash) const {
359 return ((size_t)hash) % _indexes_len;
360 }
221361
222 Entry *internal_get(const K &key) const {
223 int start_index = key_to_index(key);
224 for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
225 int index = (start_index + roll_over) % _capacity;
226 Entry *entry = &_entries[index];
362 template <typename I>
363 bool internal_remove(const K &key, I *indexes) {
364 uint32_t hash = HashFunction(key);
365 size_t start_index = hash_to_index(hash);
366 for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
367 size_t index_index = (start_index + roll_over) % _indexes_len;
368 size_t index_data = indexes[index_index];
369 if (index_data == 0)
370 return false;
371
372 size_t index = index_data - 1;
373 Entry *entry = &_entries.items[index];
374 if (entry->hash != hash || !EqualFn(entry->key, key))
375 continue;
227376
228 if (!entry->used)
229 return NULL;
377 size_t prev_index = index_index;
378 _entries.swap_remove(index);
379 if (_entries.length > 0 && _entries.length != index) {
380 // Because of the swap remove, now we need to update the index that was
381 // pointing to the last entry and is now pointing to this removed item slot.
382 update_entry_index(_entries.length, index, indexes);
383 }
230384
231 if (EqualFn(entry->key, key))
232 return entry;
385 // Now we have to shift over the following indexes.
386 roll_over += 1;
387 for (; roll_over < _indexes_len; roll_over += 1) {
388 size_t next_index = (start_index + roll_over) % _indexes_len;
389 if (indexes[next_index] == 0) {
390 indexes[prev_index] = 0;
391 return true;
392 }
393 Entry *next_entry = &_entries.items[indexes[next_index] - 1];
394 if (next_entry->distance_from_start_index == 0) {
395 indexes[prev_index] = 0;
396 return true;
397 }
398 indexes[prev_index] = indexes[next_index];
399 prev_index = next_index;
400 next_entry->distance_from_start_index -= 1;
401 }
402 zig_unreachable();
233403 }
234 return NULL;
404 return false;
235405 }
236406
237 int key_to_index(const K &key) const {
238 return (int)(HashFunction(key) % ((uint32_t)_capacity));
407 template <typename I>
408 void update_entry_index(size_t old_entry_index, size_t new_entry_index, I *indexes) {
409 size_t start_index = hash_to_index(_entries.items[new_entry_index].hash);
410 for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
411 size_t index_index = (start_index + roll_over) % _indexes_len;
412 if (indexes[index_index] == old_entry_index + 1) {
413 indexes[index_index] = new_entry_index + 1;
414 return;
415 }
416 }
417 zig_unreachable();
239418 }
240419};
241
242420#endif
src/ir.cpp+584-144
......@@ -13,6 +13,7 @@
1313#include "os.hpp"
1414#include "range_set.hpp"
1515#include "softfloat.hpp"
16#include "softfloat_ext.hpp"
1617#include "util.hpp"
1718#include "mem_list.hpp"
1819#include "all_types.hpp"
......@@ -286,6 +287,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so
286287 IrInstGen *struct_operand, TypeStructField *field);
287288static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
288289static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
290static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field);
289291
290292#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
291293#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
......@@ -308,6 +310,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
308310 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
309311 case IrInstSrcIdCallExtra:
310312 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
313 case IrInstSrcIdAsyncCallExtra:
314 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsyncCallExtra *>(inst));
311315 case IrInstSrcIdUnOp:
312316 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
313317 case IrInstSrcIdCondBr:
......@@ -560,6 +564,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
560564 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst));
561565 case IrInstSrcIdWasmMemoryGrow:
562566 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst));
567 case IrInstSrcIdSrc:
568 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSrc *>(inst));
563569 }
564570 zig_unreachable();
565571}
......@@ -822,12 +828,11 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_
822828 ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
823829 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
824830
825 // TODO handle sentinel terminated arrays
826831 expand_undef_array(g, array_val);
827832 result = g->pass1_arena->create<ZigValue>();
828833 result->special = array_val->special;
829834 result->type = get_array_type(g, array_val->type->data.array.child_type,
830 array_val->type->data.array.len - elem_index, nullptr);
835 array_val->type->data.array.len - elem_index, array_val->type->data.array.sentinel);
831836 result->data.x_array.special = ConstArraySpecialNone;
832837 result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index];
833838 result->parent.id = ConstParentIdArray;
......@@ -1170,6 +1175,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {
11701175 return IrInstSrcIdCallExtra;
11711176}
11721177
1178static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) {
1179 return IrInstSrcIdAsyncCallExtra;
1180}
1181
11731182static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {
11741183 return IrInstSrcIdConst;
11751184}
......@@ -1626,6 +1635,9 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) {
16261635 return IrInstSrcIdWasmMemoryGrow;
16271636}
16281637
1638static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) {
1639 return IrInstSrcIdSrc;
1640}
16291641
16301642static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) {
16311643 return IrInstGenIdDeclVar;
......@@ -2436,6 +2448,25 @@ static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *
24362448 return &call_instruction->base;
24372449}
24382450
2451static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2452 CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc)
2453{
2454 IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction<IrInstSrcAsyncCallExtra>(irb, scope, source_node);
2455 call_instruction->modifier = modifier;
2456 call_instruction->fn_ref = fn_ref;
2457 call_instruction->ret_ptr = ret_ptr;
2458 call_instruction->new_stack = new_stack;
2459 call_instruction->args = args;
2460 call_instruction->result_loc = result_loc;
2461
2462 ir_ref_instruction(fn_ref, irb->current_basic_block);
2463 if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
2464 ir_ref_instruction(new_stack, irb->current_basic_block);
2465 ir_ref_instruction(args, irb->current_basic_block);
2466
2467 return &call_instruction->base;
2468}
2469
24392470static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
24402471 IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,
24412472 ResultLoc *result_loc)
......@@ -5029,6 +5060,11 @@ static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_i
50295060 return &instruction->base;
50305061}
50315062
5063static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
5064 IrInstSrcSrc *instruction = ir_build_instruction<IrInstSrcSrc>(irb, scope, source_node);
5065
5066 return &instruction->base;
5067}
50325068
50335069static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
50345070 results[ReturnKindUnconditional] = 0;
......@@ -6172,11 +6208,10 @@ static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *nod
61726208static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
61736209 LVal lval, ResultLoc *result_loc)
61746210{
6175 size_t arg_offset = 3;
6176 if (call_node->data.fn_call_expr.params.length < arg_offset) {
6211 if (call_node->data.fn_call_expr.params.length != 4) {
61776212 add_node_error(irb->codegen, call_node,
6178 buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
6179 arg_offset, call_node->data.fn_call_expr.params.length));
6213 buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize,
6214 call_node->data.fn_call_expr.params.length));
61806215 return irb->codegen->invalid_inst_src;
61816216 }
61826217
......@@ -6195,20 +6230,37 @@ static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *aw
61956230 if (fn_ref == irb->codegen->invalid_inst_src)
61966231 return fn_ref;
61976232
6198 size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset;
6199 IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
6200 for (size_t i = 0; i < arg_count; i += 1) {
6201 AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset);
6202 IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
6203 if (arg == irb->codegen->invalid_inst_src)
6204 return arg;
6205 args[i] = arg;
6206 }
6207
62086233 CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
62096234 bool is_async_call_builtin = true;
6210 IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
6211 ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
6235 AstNode *args_node = call_node->data.fn_call_expr.params.at(3);
6236 if (args_node->type == NodeTypeContainerInitExpr) {
6237 if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
6238 args_node->data.container_init_expr.entries.length == 0)
6239 {
6240 size_t arg_count = args_node->data.container_init_expr.entries.length;
6241 IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
6242 for (size_t i = 0; i < arg_count; i += 1) {
6243 AstNode *arg_node = args_node->data.container_init_expr.entries.at(i);
6244 IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
6245 if (arg == irb->codegen->invalid_inst_src)
6246 return arg;
6247 args[i] = arg;
6248 }
6249
6250 IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
6251 ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
6252 return ir_lval_wrap(irb, scope, call, lval, result_loc);
6253 } else {
6254 exec_add_error_node(irb->codegen, irb->exec, args_node,
6255 buf_sprintf("TODO: @asyncCall with anon struct literal"));
6256 return irb->codegen->invalid_inst_src;
6257 }
6258 }
6259 IrInstSrc *args = ir_gen_node(irb, args_node, scope);
6260 if (args == irb->codegen->invalid_inst_src)
6261 return args;
6262
6263 IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc);
62126264 return ir_lval_wrap(irb, scope, call, lval, result_loc);
62136265}
62146266
......@@ -7449,6 +7501,11 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
74497501 return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
74507502 lval, result_loc);
74517503 }
7504 case BuiltinFnIdSrc:
7505 {
7506 IrInstSrc *src_inst = ir_build_src(irb, scope, node);
7507 return ir_lval_wrap(irb, scope, src_inst, lval, result_loc);
7508 }
74527509 }
74537510 zig_unreachable();
74547511}
......@@ -9885,7 +9942,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
98859942 is_var_args = true;
98869943 break;
98879944 }
9888 if (param_node->data.param_decl.var_token == nullptr) {
9945 if (param_node->data.param_decl.anytype_token == nullptr) {
98899946 AstNode *type_node = param_node->data.param_decl.type;
98909947 IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);
98919948 if (type_value == irb->codegen->invalid_inst_src)
......@@ -9911,7 +9968,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
99119968 }
99129969
99139970 IrInstSrc *return_type;
9914 if (node->data.fn_proto.return_var_token == nullptr) {
9971 if (node->data.fn_proto.return_anytype_token == nullptr) {
99159972 if (node->data.fn_proto.return_type == nullptr) {
99169973 return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
99179974 } else {
......@@ -10169,9 +10226,9 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope
1016910226 add_node_error(irb->codegen, node,
1017010227 buf_sprintf("inferred array size invalid here"));
1017110228 return irb->codegen->invalid_inst_src;
10172 case NodeTypeVarFieldType:
10229 case NodeTypeAnyTypeField:
1017310230 return ir_lval_wrap(irb, scope,
10174 ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc);
10231 ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc);
1017510232 }
1017610233 zig_unreachable();
1017710234}
......@@ -10239,7 +10296,7 @@ static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *sco
1023910296 case NodeTypeSuspend:
1024010297 case NodeTypeEnumLiteral:
1024110298 case NodeTypeInferredArrayType:
10242 case NodeTypeVarFieldType:
10299 case NodeTypeAnyTypeField:
1024310300 case NodeTypePrefixOpExpr:
1024410301 add_node_error(irb->codegen, node,
1024510302 buf_sprintf("invalid left-hand side to assignment"));
......@@ -10461,7 +10518,7 @@ ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_va
1046110518 if (val == nullptr) return nullptr;
1046210519 assert(const_val->type->id == ZigTypeIdPointer);
1046310520 ZigType *expected_type = const_val->type->data.pointer.child_type;
10464 if (expected_type == codegen->builtin_types.entry_var) {
10521 if (expected_type == codegen->builtin_types.entry_anytype) {
1046510522 return val;
1046610523 }
1046710524 switch (type_has_one_possible_value(codegen, expected_type)) {
......@@ -12585,28 +12642,29 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
1258512642 if (prev_type->id == ZigTypeIdPointer &&
1258612643 prev_type->data.pointer.ptr_len == PtrLenSingle &&
1258712644 prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
12588 ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
12645 ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
1258912646 {
12590 prev_inst = cur_inst;
12647 convert_to_const_slice = false;
12648 prev_inst = cur_inst;
1259112649
1259212650 if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) {
1259312651 // const array pointer and non-const unknown pointer
1259412652 make_the_pointer_const = true;
1259512653 }
12596 continue;
12654 continue;
1259712655 }
1259812656
1259912657 // *[N]T to [*]T
1260012658 if (cur_type->id == ZigTypeIdPointer &&
1260112659 cur_type->data.pointer.ptr_len == PtrLenSingle &&
1260212660 cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
12603 ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
12661 ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
1260412662 {
1260512663 if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) {
1260612664 // const array pointer and non-const unknown pointer
1260712665 make_the_pointer_const = true;
1260812666 }
12609 continue;
12667 continue;
1261012668 }
1261112669
1261212670 // *[N]T to []T
......@@ -12962,7 +13020,11 @@ static IrInstGen *ir_resolve_cast(IrAnalyze *ira, IrInst *source_instr, IrInstGe
1296213020{
1296313021 if (instr_is_comptime(value) || !type_has_bits(ira->codegen, wanted_type)) {
1296413022 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
12965 if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, value->value, value->value->type,
13023 ZigValue *val = ir_resolve_const(ira, value, UndefBad);
13024 if (val == nullptr)
13025 return ira->codegen->invalid_inst_gen;
13026
13027 if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, val, val->type,
1296613028 result->value, wanted_type))
1296713029 {
1296813030 return ira->codegen->invalid_inst_gen;
......@@ -14703,10 +14765,139 @@ static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* sou
1470314765}
1470414766
1470514767static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr,
14706 IrInstGen *value, ZigType *wanted_type)
14768 IrInstGen *struct_operand, ZigType *wanted_type)
1470714769{
14708 ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to struct"));
14709 return ira->codegen->invalid_inst_gen;
14770 Error err;
14771
14772 IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false);
14773 if (type_is_invalid(struct_ptr->value->type))
14774 return ira->codegen->invalid_inst_gen;
14775
14776 if (wanted_type->data.structure.resolve_status == ResolveStatusBeingInferred) {
14777 ir_add_error(ira, source_instr, buf_sprintf("type coercion of anon struct literal to inferred struct"));
14778 return ira->codegen->invalid_inst_gen;
14779 }
14780
14781 if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown)))
14782 return ira->codegen->invalid_inst_gen;
14783
14784 size_t actual_field_count = wanted_type->data.structure.src_field_count;
14785 size_t instr_field_count = struct_operand->value->type->data.structure.src_field_count;
14786
14787 bool need_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope)
14788 || type_requires_comptime(ira->codegen, wanted_type) == ReqCompTimeYes;
14789 bool is_comptime = true;
14790
14791 // Determine if the struct_operand will be comptime.
14792 // Also emit compile errors for missing fields and duplicate fields.
14793 AstNode **field_assign_nodes = heap::c_allocator.allocate<AstNode *>(actual_field_count);
14794 ZigValue **field_values = heap::c_allocator.allocate<ZigValue *>(actual_field_count);
14795 IrInstGen **casted_fields = heap::c_allocator.allocate<IrInstGen *>(actual_field_count);
14796 IrInstGen *const_result = ir_const(ira, source_instr, wanted_type);
14797
14798 for (size_t i = 0; i < instr_field_count; i += 1) {
14799 TypeStructField *src_field = struct_operand->value->type->data.structure.fields[i];
14800 TypeStructField *dst_field = find_struct_type_field(wanted_type, src_field->name);
14801 if (dst_field == nullptr) {
14802 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("no field named '%s' in struct '%s'",
14803 buf_ptr(src_field->name), buf_ptr(&wanted_type->name)));
14804 if (wanted_type->data.structure.decl_node) {
14805 add_error_note(ira->codegen, msg, wanted_type->data.structure.decl_node,
14806 buf_sprintf("struct '%s' declared here", buf_ptr(&wanted_type->name)));
14807 }
14808 add_error_note(ira->codegen, msg, src_field->decl_node,
14809 buf_sprintf("field '%s' declared here", buf_ptr(src_field->name)));
14810 return ira->codegen->invalid_inst_gen;
14811 }
14812
14813 ir_assert(src_field->decl_node != nullptr, source_instr);
14814 AstNode *existing_assign_node = field_assign_nodes[dst_field->src_index];
14815 if (existing_assign_node != nullptr) {
14816 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("duplicate field"));
14817 add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here"));
14818 return ira->codegen->invalid_inst_gen;
14819 }
14820 field_assign_nodes[dst_field->src_index] = src_field->decl_node;
14821
14822 IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, src_field, struct_ptr,
14823 struct_operand->value->type, false);
14824 if (type_is_invalid(field_ptr->value->type))
14825 return ira->codegen->invalid_inst_gen;
14826 IrInstGen *field_value = ir_get_deref(ira, source_instr, field_ptr, nullptr);
14827 if (type_is_invalid(field_value->value->type))
14828 return ira->codegen->invalid_inst_gen;
14829 IrInstGen *casted_value = ir_implicit_cast(ira, field_value, dst_field->type_entry);
14830 if (type_is_invalid(casted_value->value->type))
14831 return ira->codegen->invalid_inst_gen;
14832
14833 casted_fields[dst_field->src_index] = casted_value;
14834 if (need_comptime || instr_is_comptime(casted_value)) {
14835 ZigValue *field_val = ir_resolve_const(ira, casted_value, UndefOk);
14836 if (field_val == nullptr)
14837 return ira->codegen->invalid_inst_gen;
14838 field_val->parent.id = ConstParentIdStruct;
14839 field_val->parent.data.p_struct.struct_val = const_result->value;
14840 field_val->parent.data.p_struct.field_index = dst_field->src_index;
14841 field_values[dst_field->src_index] = field_val;
14842 } else {
14843 is_comptime = false;
14844 }
14845 }
14846
14847 bool any_missing = false;
14848 for (size_t i = 0; i < actual_field_count; i += 1) {
14849 if (field_assign_nodes[i] != nullptr) continue;
14850
14851 // look for a default field value
14852 TypeStructField *field = wanted_type->data.structure.fields[i];
14853 memoize_field_init_val(ira->codegen, wanted_type, field);
14854 if (field->init_val == nullptr) {
14855 ir_add_error(ira, source_instr,
14856 buf_sprintf("missing field: '%s'", buf_ptr(field->name)));
14857 any_missing = true;
14858 continue;
14859 }
14860 if (type_is_invalid(field->init_val->type))
14861 return ira->codegen->invalid_inst_gen;
14862 ZigValue *init_val_copy = ira->codegen->pass1_arena->create<ZigValue>();
14863 copy_const_val(ira->codegen, init_val_copy, field->init_val);
14864 init_val_copy->parent.id = ConstParentIdStruct;
14865 init_val_copy->parent.data.p_struct.struct_val = const_result->value;
14866 init_val_copy->parent.data.p_struct.field_index = i;
14867 field_values[i] = init_val_copy;
14868 casted_fields[i] = ir_const_move(ira, source_instr, init_val_copy);
14869 }
14870 if (any_missing)
14871 return ira->codegen->invalid_inst_gen;
14872
14873 if (is_comptime) {
14874 heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
14875 IrInstGen *const_result = ir_const(ira, source_instr, wanted_type);
14876 const_result->value->data.x_struct.fields = field_values;
14877 return const_result;
14878 }
14879
14880 IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(),
14881 wanted_type, nullptr, true, true);
14882 if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) {
14883 return ira->codegen->invalid_inst_gen;
14884 }
14885
14886 for (size_t i = 0; i < actual_field_count; i += 1) {
14887 TypeStructField *field = wanted_type->data.structure.fields[i];
14888 IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc_inst, wanted_type, true);
14889 if (type_is_invalid(field_ptr->value->type))
14890 return ira->codegen->invalid_inst_gen;
14891 IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, field_ptr, casted_fields[i], true);
14892 if (type_is_invalid(store_ptr_inst->value->type))
14893 return ira->codegen->invalid_inst_gen;
14894 }
14895
14896 heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
14897 heap::c_allocator.deallocate(field_values, actual_field_count);
14898 heap::c_allocator.deallocate(casted_fields, actual_field_count);
14899
14900 return ir_get_deref(ira, source_instr, result_loc_inst, nullptr);
1471014901}
1471114902
1471214903static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr,
......@@ -14727,7 +14918,7 @@ static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* sou
1472714918 TypeUnionField *union_field = find_union_type_field(union_type, only_field->name);
1472814919 if (union_field == nullptr) {
1472914920 ir_add_error_node(ira, only_field->decl_node,
14730 buf_sprintf("no member named '%s' in union '%s'",
14921 buf_sprintf("no field named '%s' in union '%s'",
1473114922 buf_ptr(only_field->name), buf_ptr(&union_type->name)));
1473214923 return ira->codegen->invalid_inst_gen;
1473314924 }
......@@ -14849,7 +15040,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
1484915040 }
1485015041
1485115042 // This means the wanted type is anything.
14852 if (wanted_type == ira->codegen->builtin_types.entry_var) {
15043 if (wanted_type == ira->codegen->builtin_types.entry_anytype) {
1485315044 return value;
1485415045 }
1485515046
......@@ -15127,46 +15318,6 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
1512715318 }
1512815319 }
1512915320
15130 // *[N]T to E![]T
15131 if (wanted_type->id == ZigTypeIdErrorUnion &&
15132 is_slice(wanted_type->data.error_union.payload_type) &&
15133 actual_type->id == ZigTypeIdPointer &&
15134 actual_type->data.pointer.ptr_len == PtrLenSingle &&
15135 actual_type->data.pointer.child_type->id == ZigTypeIdArray)
15136 {
15137 ZigType *slice_type = wanted_type->data.error_union.payload_type;
15138 ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry;
15139 assert(slice_ptr_type->id == ZigTypeIdPointer);
15140 ZigType *array_type = actual_type->data.pointer.child_type;
15141 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
15142 || !actual_type->data.pointer.is_const);
15143 if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
15144 array_type->data.array.child_type, source_node,
15145 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
15146 {
15147 // If the pointers both have ABI align, it works.
15148 bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 &&
15149 actual_type->data.pointer.explicit_alignment == 0;
15150 if (!ok_align) {
15151 // If either one has non ABI align, we have to resolve them both
15152 if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
15153 ResolveStatusAlignmentKnown)))
15154 {
15155 return ira->codegen->invalid_inst_gen;
15156 }
15157 if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
15158 ResolveStatusAlignmentKnown)))
15159 {
15160 return ira->codegen->invalid_inst_gen;
15161 }
15162 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
15163 }
15164 if (ok_align) {
15165 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr);
15166 }
15167 }
15168 }
15169
1517015321 // @Vector(N,T1) to @Vector(N,T2)
1517115322 if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) {
1517215323 if (actual_type->data.vector.len == wanted_type->data.vector.len &&
......@@ -15346,6 +15497,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
1534615497 if (is_pointery_and_elem_is_not_pointery(actual_type)) {
1534715498 ZigType *dest_ptr_type = nullptr;
1534815499 if (wanted_type->id == ZigTypeIdPointer &&
15500 actual_type->id != ZigTypeIdOptional &&
1534915501 wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)
1535015502 {
1535115503 dest_ptr_type = wanted_type;
......@@ -15483,7 +15635,7 @@ static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *ex
1548315635static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) {
1548415636 ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr);
1548515637 ZigType *elem_type = ptr->value->type->data.pointer.child_type;
15486 if (elem_type != g->builtin_types.entry_var)
15638 if (elem_type != g->builtin_types.entry_anytype)
1548715639 return elem_type;
1548815640
1548915641 if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value))
......@@ -15535,7 +15687,7 @@ static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrIns
1553515687 }
1553615688 if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
1553715689 ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value);
15538 if (child_type == ira->codegen->builtin_types.entry_var) {
15690 if (child_type == ira->codegen->builtin_types.entry_anytype) {
1553915691 child_type = pointee->type;
1554015692 }
1554115693 if (pointee->special != ConstValSpecialRuntime) {
......@@ -18353,7 +18505,7 @@ static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclV
1835318505 if (decl_var_instruction->var_type != nullptr) {
1835418506 var_type = decl_var_instruction->var_type->child;
1835518507 ZigType *proposed_type = ir_resolve_type(ira, var_type);
18356 explicit_type = validate_var_type(ira->codegen, var_type->base.source_node, proposed_type);
18508 explicit_type = validate_var_type(ira->codegen, &var->decl_node->data.variable_declaration, proposed_type);
1835718509 if (type_is_invalid(explicit_type)) {
1835818510 var->var_type = ira->codegen->builtin_types.entry_invalid;
1835918511 return ira->codegen->invalid_inst_gen;
......@@ -18935,7 +19087,7 @@ static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out
1893519087 ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child);
1893619088 if (type_is_invalid(dest_type))
1893719089 return ErrorSemanticAnalyzeFail;
18938 *out = (dest_type != ira->codegen->builtin_types.entry_var);
19090 *out = (dest_type != ira->codegen->builtin_types.entry_anytype);
1893919091 return ErrorNone;
1894019092 }
1894119093 case ResultLocIdVar:
......@@ -19141,7 +19293,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
1914119293 if (type_is_invalid(dest_type))
1914219294 return ira->codegen->invalid_inst_gen;
1914319295
19144 if (dest_type == ira->codegen->builtin_types.entry_var) {
19296 if (dest_type == ira->codegen->builtin_types.entry_anytype) {
1914519297 return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);
1914619298 }
1914719299
......@@ -19287,7 +19439,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
1928719439 return ira->codegen->invalid_inst_gen;
1928819440 }
1928919441
19290 if (child_type != ira->codegen->builtin_types.entry_var) {
19442 if (child_type != ira->codegen->builtin_types.entry_anytype) {
1929119443 if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) {
1929219444 // pointer cast won't work; we need a temporary location.
1929319445 result_bit_cast->parent->written = parent_was_written;
......@@ -19448,9 +19600,9 @@ static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSr
1944819600 if (type_is_invalid(implicit_elem_type))
1944919601 return ira->codegen->invalid_inst_gen;
1945019602 } else {
19451 implicit_elem_type = ira->codegen->builtin_types.entry_var;
19603 implicit_elem_type = ira->codegen->builtin_types.entry_anytype;
1945219604 }
19453 if (implicit_elem_type == ira->codegen->builtin_types.entry_var) {
19605 if (implicit_elem_type == ira->codegen->builtin_types.entry_anytype) {
1945419606 Buf *bare_name = buf_alloc();
1945519607 Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
1945619608 instruction->base.base.scope, instruction->base.base.source_node, bare_name);
......@@ -19607,7 +19759,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node
1960719759 assert(param_decl_node->type == NodeTypeParamDecl);
1960819760
1960919761 IrInstGen *casted_arg;
19610 if (param_decl_node->data.param_decl.var_token == nullptr) {
19762 if (param_decl_node->data.param_decl.anytype_token == nullptr) {
1961119763 AstNode *param_type_node = param_decl_node->data.param_decl.type;
1961219764 ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
1961319765 if (type_is_invalid(param_type))
......@@ -19647,7 +19799,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
1964719799 arg_part_of_generic_id = true;
1964819800 casted_arg = arg;
1964919801 } else {
19650 if (param_decl_node->data.param_decl.var_token == nullptr) {
19802 if (param_decl_node->data.param_decl.anytype_token == nullptr) {
1965119803 AstNode *param_type_node = param_decl_node->data.param_decl.type;
1965219804 ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
1965319805 if (type_is_invalid(param_type))
......@@ -19859,7 +20011,7 @@ static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr,
1985920011 }
1986020012
1986120013 if (ptr->value->type->data.pointer.inferred_struct_field != nullptr &&
19862 child_type == ira->codegen->builtin_types.entry_var)
20014 child_type == ira->codegen->builtin_types.entry_anytype)
1986320015 {
1986420016 child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type;
1986520017 }
......@@ -20030,7 +20182,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2003020182 }
2003120183
2003220184 IrInstGen *first_arg;
20033 if (!first_arg_known_bare && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) {
20185 if (!first_arg_known_bare) {
2003420186 first_arg = first_arg_ptr;
2003520187 } else {
2003620188 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);
......@@ -20050,6 +20202,11 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2005020202 }
2005120203
2005220204 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
20205 if (return_type_node == nullptr) {
20206 ir_add_error(ira, &fn_ref->base,
20207 buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
20208 return ira->codegen->invalid_inst_gen;
20209 }
2005320210 ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);
2005420211 if (type_is_invalid(specified_return_type))
2005520212 return ira->codegen->invalid_inst_gen;
......@@ -20160,7 +20317,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2016020317 }
2016120318
2016220319 IrInstGen *first_arg;
20163 if (!first_arg_known_bare && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) {
20320 if (!first_arg_known_bare) {
2016420321 first_arg = first_arg_ptr;
2016520322 } else {
2016620323 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);
......@@ -20212,7 +20369,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2021220369 inst_fn_type_id.alignment = align_bytes;
2021320370 }
2021420371
20215 if (fn_proto_node->data.fn_proto.return_var_token == nullptr) {
20372 if (fn_proto_node->data.fn_proto.return_anytype_token == nullptr) {
2021620373 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
2021720374 ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);
2021820375 if (type_is_invalid(specified_return_type))
......@@ -20311,7 +20468,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2031120468 if (type_is_invalid(dummy_result->value->type))
2031220469 return ira->codegen->invalid_inst_gen;
2031320470 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
20314 if (res_child_type == ira->codegen->builtin_types.entry_var) {
20471 if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
2031520472 res_child_type = impl_fn_type_id->return_type;
2031620473 }
2031720474 if (!handle_is_ptr(ira->codegen, res_child_type)) {
......@@ -20365,9 +20522,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2036520522 return ira->codegen->invalid_inst_gen;
2036620523
2036720524 IrInstGen *first_arg;
20368 if (param_type->id == ZigTypeIdPointer &&
20369 handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type))
20370 {
20525 if (param_type->id == ZigTypeIdPointer) {
2037120526 first_arg = first_arg_ptr;
2037220527 } else {
2037320528 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);
......@@ -20454,7 +20609,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
2045420609 if (type_is_invalid(dummy_result->value->type))
2045520610 return ira->codegen->invalid_inst_gen;
2045620611 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
20457 if (res_child_type == ira->codegen->builtin_types.entry_var) {
20612 if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
2045820613 res_child_type = return_type;
2045920614 }
2046020615 if (!handle_is_ptr(ira->codegen, res_child_type)) {
......@@ -20614,40 +20769,106 @@ static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr,
2061420769 modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc);
2061520770}
2061620771
20617static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) {
20618 IrInstGen *args = instruction->args->child;
20772static IrInstGen *ir_analyze_async_call_extra(IrAnalyze *ira, IrInst* source_instr, CallModifier modifier,
20773 IrInstSrc *pass1_fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstGen **args_ptr, size_t args_len, ResultLoc *result_loc)
20774{
20775 IrInstGen *fn_ref = pass1_fn_ref->child;
20776 if (type_is_invalid(fn_ref->value->type))
20777 return ira->codegen->invalid_inst_gen;
20778
20779 if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) {
20780 ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @asyncCall"));
20781 return ira->codegen->invalid_inst_gen;
20782 }
20783
20784 IrInstGen *first_arg_ptr = nullptr;
20785 IrInst *first_arg_ptr_src = nullptr;
20786 ZigFn *fn = nullptr;
20787 if (instr_is_comptime(fn_ref)) {
20788 if (fn_ref->value->type->id == ZigTypeIdBoundFn) {
20789 assert(fn_ref->value->special == ConstValSpecialStatic);
20790 fn = fn_ref->value->data.x_bound_fn.fn;
20791 first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg;
20792 first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src;
20793 if (type_is_invalid(first_arg_ptr->value->type))
20794 return ira->codegen->invalid_inst_gen;
20795 } else {
20796 fn = ir_resolve_fn(ira, fn_ref);
20797 }
20798 }
20799
20800 IrInstGen *ret_ptr_uncasted = nullptr;
20801 if (ret_ptr != nullptr) {
20802 ret_ptr_uncasted = ret_ptr->child;
20803 if (type_is_invalid(ret_ptr_uncasted->value->type))
20804 return ira->codegen->invalid_inst_gen;
20805 }
20806
20807 ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type;
20808 IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack->child,
20809 &new_stack->base, true, fn);
20810 if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type))
20811 return ira->codegen->invalid_inst_gen;
20812
20813 return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src,
20814 modifier, casted_new_stack, &new_stack->base, true, args_ptr, args_len, ret_ptr_uncasted, result_loc);
20815}
20816
20817static bool ir_extract_tuple_call_args(IrAnalyze *ira, IrInst *source_instr, IrInstGen *args, IrInstGen ***args_ptr, size_t *args_len) {
2061920818 ZigType *args_type = args->value->type;
2062020819 if (type_is_invalid(args_type))
20621 return ira->codegen->invalid_inst_gen;
20820 return false;
2062220821
2062320822 if (args_type->id != ZigTypeIdStruct) {
2062420823 ir_add_error(ira, &args->base,
2062520824 buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name)));
20626 return ira->codegen->invalid_inst_gen;
20825 return false;
2062720826 }
2062820827
20629 IrInstGen **args_ptr = nullptr;
20630 size_t args_len = 0;
20631
2063220828 if (is_tuple(args_type)) {
20633 args_len = args_type->data.structure.src_field_count;
20634 args_ptr = heap::c_allocator.allocate<IrInstGen *>(args_len);
20635 for (size_t i = 0; i < args_len; i += 1) {
20829 *args_len = args_type->data.structure.src_field_count;
20830 *args_ptr = heap::c_allocator.allocate<IrInstGen *>(*args_len);
20831 for (size_t i = 0; i < *args_len; i += 1) {
2063620832 TypeStructField *arg_field = args_type->data.structure.fields[i];
20637 args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base.base, args, arg_field);
20638 if (type_is_invalid(args_ptr[i]->value->type))
20639 return ira->codegen->invalid_inst_gen;
20833 (*args_ptr)[i] = ir_analyze_struct_value_field_value(ira, source_instr, args, arg_field);
20834 if (type_is_invalid((*args_ptr)[i]->value->type))
20835 return false;
2064020836 }
2064120837 } else {
2064220838 ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args"));
20839 return false;
20840 }
20841 return true;
20842}
20843
20844static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) {
20845 IrInstGen *args = instruction->args->child;
20846 IrInstGen **args_ptr = nullptr;
20847 size_t args_len = 0;
20848 if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) {
2064320849 return ira->codegen->invalid_inst_gen;
2064420850 }
20851
2064520852 IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options,
2064620853 instruction->fn_ref, args_ptr, args_len, instruction->result_loc);
2064720854 heap::c_allocator.deallocate(args_ptr, args_len);
2064820855 return result;
2064920856}
2065020857
20858static IrInstGen *ir_analyze_instruction_async_call_extra(IrAnalyze *ira, IrInstSrcAsyncCallExtra *instruction) {
20859 IrInstGen *args = instruction->args->child;
20860 IrInstGen **args_ptr = nullptr;
20861 size_t args_len = 0;
20862 if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) {
20863 return ira->codegen->invalid_inst_gen;
20864 }
20865
20866 IrInstGen *result = ir_analyze_async_call_extra(ira, &instruction->base.base, instruction->modifier,
20867 instruction->fn_ref, instruction->ret_ptr, instruction->new_stack, args_ptr, args_len, instruction->result_loc);
20868 heap::c_allocator.deallocate(args_ptr, args_len);
20869 return result;
20870}
20871
2065120872static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) {
2065220873 IrInstGen **args_ptr = heap::c_allocator.allocate<IrInstGen *>(instruction->args_len);
2065320874 for (size_t i = 0; i < instruction->args_len; i += 1) {
......@@ -20881,17 +21102,24 @@ static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction
2088121102 if (type_is_invalid(expr_type))
2088221103 return ira->codegen->invalid_inst_gen;
2088321104
20884 if (!(expr_type->id == ZigTypeIdInt || expr_type->id == ZigTypeIdComptimeInt ||
20885 expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat ||
20886 expr_type->id == ZigTypeIdVector))
20887 {
20888 ir_add_error(ira, &instruction->base.base,
20889 buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
20890 return ira->codegen->invalid_inst_gen;
20891 }
20892
2089321105 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
2089421106
21107 switch (expr_type->id) {
21108 case ZigTypeIdComptimeInt:
21109 case ZigTypeIdFloat:
21110 case ZigTypeIdComptimeFloat:
21111 case ZigTypeIdVector:
21112 break;
21113 case ZigTypeIdInt:
21114 if (is_wrap_op || expr_type->data.integral.is_signed)
21115 break;
21116 ZIG_FALLTHROUGH;
21117 default:
21118 ir_add_error(ira, &instruction->base.base,
21119 buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
21120 return ira->codegen->invalid_inst_gen;
21121 }
21122
2089521123 ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
2089621124
2089721125 if (instr_is_comptime(value)) {
......@@ -22112,7 +22340,7 @@ static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
2211222340 inferred_struct_field->inferred_struct_type = container_type;
2211322341 inferred_struct_field->field_name = field_name;
2211422342
22115 ZigType *elem_type = ira->codegen->builtin_types.entry_var;
22343 ZigType *elem_type = ira->codegen->builtin_types.entry_anytype;
2211622344 ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
2211722345 container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile,
2211822346 PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr);
......@@ -22407,7 +22635,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
2240722635 usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
2240822636 } else {
2240922637 ir_add_error_node(ira, source_node,
22410 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
22638 buf_sprintf("no field named '%s' in '%s'", buf_ptr(field_name),
2241122639 buf_ptr(&container_type->name)));
2241222640 return ira->codegen->invalid_inst_gen;
2241322641 }
......@@ -23834,7 +24062,7 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi
2383424062 TypeUnionField *type_field = find_union_type_field(union_type, field_name);
2383524063 if (type_field == nullptr) {
2383624064 ir_add_error_node(ira, field_source_node,
23837 buf_sprintf("no member named '%s' in union '%s'",
24065 buf_sprintf("no field named '%s' in union '%s'",
2383824066 buf_ptr(field_name), buf_ptr(&union_type->name)));
2383924067 return ira->codegen->invalid_inst_gen;
2384024068 }
......@@ -23930,7 +24158,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
2393024158 TypeStructField *type_field = find_struct_type_field(container_type, field->name);
2393124159 if (!type_field) {
2393224160 ir_add_error_node(ira, field->source_node,
23933 buf_sprintf("no member named '%s' in struct '%s'",
24161 buf_sprintf("no field named '%s' in struct '%s'",
2393424162 buf_ptr(field->name), buf_ptr(&container_type->name)));
2393524163 return ira->codegen->invalid_inst_gen;
2393624164 }
......@@ -23965,7 +24193,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
2396524193 memoize_field_init_val(ira->codegen, container_type, field);
2396624194 if (field->init_val == nullptr) {
2396724195 ir_add_error(ira, source_instr,
23968 buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name)));
24196 buf_sprintf("missing field: '%s'", buf_ptr(field->name)));
2396924197 any_missing = true;
2397024198 continue;
2397124199 }
......@@ -24890,7 +25118,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
2489025118 fields[5]->special = ConstValSpecialStatic;
2489125119 fields[5]->type = ira->codegen->builtin_types.entry_bool;
2489225120 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
24893 // sentinel: var
25121 // sentinel: anytype
2489425122 ensure_field_index(result->type, "sentinel", 6);
2489525123 fields[6]->special = ConstValSpecialStatic;
2489625124 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
......@@ -25018,7 +25246,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2501825246 fields[1]->special = ConstValSpecialStatic;
2501925247 fields[1]->type = ira->codegen->builtin_types.entry_type;
2502025248 fields[1]->data.x_type = type_entry->data.array.child_type;
25021 // sentinel: var
25249 // sentinel: anytype
2502225250 fields[2]->special = ConstValSpecialStatic;
2502325251 fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type);
2502425252 fields[2]->data.x_optional = type_entry->data.array.sentinel;
......@@ -25318,7 +25546,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2531825546 result->special = ConstValSpecialStatic;
2531925547 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
2532025548
25321 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3);
25549 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4);
2532225550 result->data.x_struct.fields = fields;
2532325551
2532425552 // layout: ContainerLayout
......@@ -25373,7 +25601,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2537325601 inner_fields[2]->type = ira->codegen->builtin_types.entry_type;
2537425602 inner_fields[2]->data.x_type = struct_field->type_entry;
2537525603
25376 // default_value: var
25604 // default_value: anytype
2537725605 inner_fields[3]->special = ConstValSpecialStatic;
2537825606 inner_fields[3]->type = get_optional_type2(ira->codegen, struct_field->type_entry);
2537925607 if (inner_fields[3]->type == nullptr) return ErrorSemanticAnalyzeFail;
......@@ -25399,6 +25627,12 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2539925627 return err;
2540025628 }
2540125629
25630 // is_tuple: bool
25631 ensure_field_index(result->type, "is_tuple", 3);
25632 fields[3]->special = ConstValSpecialStatic;
25633 fields[3]->type = ira->codegen->builtin_types.entry_bool;
25634 fields[3]->data.x_bool = is_tuple(type_entry);
25635
2540225636 break;
2540325637 }
2540425638 case ZigTypeIdFn:
......@@ -25504,9 +25738,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2550425738 break;
2550525739 }
2550625740 case ZigTypeIdFnFrame:
25507 ir_add_error(ira, source_instr,
25508 buf_sprintf("compiler bug: TODO @typeInfo for async function frames. https://github.com/ziglang/zig/issues/3066"));
25509 return ErrorSemanticAnalyzeFail;
25741 {
25742 result = ira->codegen->pass1_arena->create<ZigValue>();
25743 result->special = ConstValSpecialStatic;
25744 result->type = ir_type_info_get_type(ira, "Frame", nullptr);
25745 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
25746 result->data.x_struct.fields = fields;
25747 ZigFn *fn = type_entry->data.frame.fn;
25748 // function: anytype
25749 ensure_field_index(result->type, "function", 0);
25750 fields[0] = create_const_fn(ira->codegen, fn);
25751 break;
25752 }
2551025753 }
2551125754
2551225755 assert(result != nullptr);
......@@ -25676,6 +25919,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2567625919 {
2567725920 return ira->codegen->invalid_inst_gen->value->type;
2567825921 }
25922 if (sentinel != nullptr && (size_enum_index == BuiltinPtrSizeOne || size_enum_index == BuiltinPtrSizeC)) {
25923 ir_add_error(ira, source_instr,
25924 buf_sprintf("sentinels are only allowed on slices and unknown-length pointers"));
25925 return ira->codegen->invalid_inst_gen->value->type;
25926 }
2567925927 BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3);
2568025928 if (bi == nullptr)
2568125929 return ira->codegen->invalid_inst_gen->value->type;
......@@ -25709,7 +25957,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2570925957 0, // host_int_bytes
2571025958 is_allowzero,
2571125959 VECTOR_INDEX_NONE, nullptr, sentinel);
25712 if (size_enum_index != 2)
25960 if (size_enum_index != BuiltinPtrSizeSlice)
2571325961 return ptr_type;
2571425962 return get_slice_type(ira->codegen, ptr_type);
2571525963 }
......@@ -25775,10 +26023,90 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2577526023 ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0);
2577626024 return get_any_frame_type(ira->codegen, child_type);
2577726025 }
25778 case ZigTypeIdErrorSet:
25779 case ZigTypeIdEnum:
25780 case ZigTypeIdFnFrame:
2578126026 case ZigTypeIdEnumLiteral:
26027 return ira->codegen->builtin_types.entry_enum_literal;
26028 case ZigTypeIdFnFrame: {
26029 assert(payload->special == ConstValSpecialStatic);
26030 assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr));
26031 ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0);
26032 assert(function->type->id == ZigTypeIdFn);
26033 ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;
26034 return get_fn_frame_type(ira->codegen, fn);
26035 }
26036 case ZigTypeIdErrorSet: {
26037 assert(payload->special == ConstValSpecialStatic);
26038 assert(payload->type->id == ZigTypeIdOptional);
26039 ZigValue *slice = payload->data.x_optional;
26040 if (slice == nullptr)
26041 return ira->codegen->builtin_types.entry_global_error_set;
26042 assert(slice->special == ConstValSpecialStatic);
26043 assert(is_slice(slice->type));
26044 ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
26045 Buf bare_name = BUF_INIT;
26046 buf_init_from_buf(&err_set_type->name, get_anon_type_name(ira->codegen, ira->old_irb.exec, "error", source_instr->scope, source_instr->source_node, &bare_name));
26047 err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
26048 err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
26049 err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
26050 ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index];
26051 assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);;
26052 assert(ptr->data.x_ptr.data.base_array.elem_index == 0);
26053 ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val;
26054 assert(arr->special == ConstValSpecialStatic);
26055 assert(arr->data.x_array.special == ConstArraySpecialNone);
26056 ZigValue *len = slice->data.x_struct.fields[slice_len_index];
26057 size_t count = bigint_as_usize(&len->data.x_bigint);
26058 err_set_type->data.error_set.err_count = count;
26059 err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(count);
26060 bool *already_set = heap::c_allocator.allocate<bool>(ira->codegen->errors_by_index.length + count);
26061 for (size_t i = 0; i < count; i++) {
26062 ZigValue *error = &arr->data.x_array.data.s_none.elements[i];
26063 assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));
26064 ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();
26065 err_entry->decl_node = source_instr->source_node;
26066 ZigValue *name_slice = get_const_field(ira, source_instr->source_node, error, "name", 0);
26067 ZigValue *name_ptr = name_slice->data.x_struct.fields[slice_ptr_index];
26068 ZigValue *name_len = name_slice->data.x_struct.fields[slice_len_index];
26069 assert(name_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
26070 assert(name_ptr->data.x_ptr.data.base_array.elem_index == 0);
26071 ZigValue *name_arr = name_ptr->data.x_ptr.data.base_array.array_val;
26072 assert(name_arr->special == ConstValSpecialStatic);
26073 switch (name_arr->data.x_array.special) {
26074 case ConstArraySpecialUndef:
26075 return ira->codegen->invalid_inst_gen->value->type;
26076 case ConstArraySpecialNone: {
26077 buf_resize(&err_entry->name, 0);
26078 size_t name_count = bigint_as_usize(&name_len->data.x_bigint);
26079 for (size_t j = 0; j < name_count; j++) {
26080 ZigValue *ch_val = &name_arr->data.x_array.data.s_none.elements[j];
26081 unsigned ch = bigint_as_u32(&ch_val->data.x_bigint);
26082 buf_append_char(&err_entry->name, ch);
26083 }
26084 break;
26085 }
26086 case ConstArraySpecialBuf:
26087 buf_init_from_buf(&err_entry->name, name_arr->data.x_array.data.s_buf);
26088 break;
26089 }
26090 auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry);
26091 if (existing_entry) {
26092 err_entry->value = existing_entry->value->value;
26093 } else {
26094 size_t error_value_count = ira->codegen->errors_by_index.length;
26095 assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
26096 err_entry->value = error_value_count;
26097 ira->codegen->errors_by_index.append(err_entry);
26098 }
26099 if (already_set[err_entry->value]) {
26100 ir_add_error(ira, source_instr, buf_sprintf("duplicate error: %s", buf_ptr(&err_entry->name)));
26101 return ira->codegen->invalid_inst_gen->value->type;
26102 } else {
26103 already_set[err_entry->value] = true;
26104 }
26105 err_set_type->data.error_set.errors[i] = err_entry;
26106 }
26107 return err_set_type;
26108 }
26109 case ZigTypeIdEnum:
2578226110 ir_add_error(ira, source_instr, buf_sprintf(
2578326111 "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
2578426112 return ira->codegen->invalid_inst_gen->value->type;
......@@ -26370,6 +26698,10 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa
2637026698 }
2637126699
2637226700 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) {
26701 ZigValue *val = ir_resolve_const(ira, target, UndefBad);
26702 if (val == nullptr)
26703 return ira->codegen->invalid_inst_gen;
26704
2637326705 return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type);
2637426706 }
2637526707
......@@ -26407,16 +26739,20 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo
2640726739 }
2640826740 }
2640926741
26410 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) {
26411 return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type);
26412 }
26413
2641426742 if (target->value->type->id != ZigTypeIdFloat) {
2641526743 ir_add_error(ira, &instruction->target->base, buf_sprintf("expected float type, found '%s'",
2641626744 buf_ptr(&target->value->type->name)));
2641726745 return ira->codegen->invalid_inst_gen;
2641826746 }
2641926747
26748 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) {
26749 ZigValue *val = ir_resolve_const(ira, target, UndefBad);
26750 if (val == nullptr)
26751 return ira->codegen->invalid_inst_gen;
26752
26753 return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type);
26754 }
26755
2642026756 return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type);
2642126757}
2642226758
......@@ -28660,7 +28996,37 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2866028996 ir_add_error(ira, &instruction->base.base,
2866128997 buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
2866228998 return ira->codegen->invalid_inst_gen;
28663 }
28999 } else if(switch_type->id == ZigTypeIdMetaType) {
29000 HashMap<const ZigType*, IrInstGen*, type_ptr_hash, type_ptr_eql> prevs;
29001 // HashMap doubles capacity when reaching 60% capacity,
29002 // because we know the size at init we can avoid reallocation by doubling it here
29003 prevs.init(instruction->range_count * 2);
29004 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
29005 IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i];
29006
29007 IrInstGen *value = range->start->child;
29008 IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type);
29009 if (type_is_invalid(casted_value->value->type)) {
29010 prevs.deinit();
29011 return ira->codegen->invalid_inst_gen;
29012 }
29013
29014 ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad);
29015 if (!const_expr_val) {
29016 prevs.deinit();
29017 return ira->codegen->invalid_inst_gen;
29018 }
29019
29020 auto entry = prevs.put_unique(const_expr_val->data.x_type, value);
29021 if(entry != nullptr) {
29022 ErrorMsg *msg = ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value"));
29023 add_error_note(ira->codegen, msg, entry->value->base.source_node, buf_sprintf("previous value is here"));
29024 prevs.deinit();
29025 return ira->codegen->invalid_inst_gen;
29026 }
29027 }
29028 prevs.deinit();
29029 }
2866429030 return ir_const_void(ira, &instruction->base.base);
2866529031}
2866629032
......@@ -29650,7 +30016,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
2965030016 if (arg_index >= fn_type_id->param_count) {
2965130017 if (instruction->allow_var) {
2965230018 // TODO remove this with var args
29653 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var);
30019 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
2965430020 }
2965530021 ir_add_error(ira, &arg_index_inst->base,
2965630022 buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
......@@ -29664,7 +30030,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
2966430030 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
2966530031
2966630032 if (instruction->allow_var) {
29667 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var);
30033 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
2966830034 } else {
2966930035 ir_add_error(ira, &arg_index_inst->base,
2967030036 buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
......@@ -30173,6 +30539,21 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
3017330539 case BuiltinFnIdSqrt:
3017430540 f128M_sqrt(in, out);
3017530541 break;
30542 case BuiltinFnIdFabs:
30543 f128M_abs(in, out);
30544 break;
30545 case BuiltinFnIdFloor:
30546 f128M_roundToInt(in, softfloat_round_min, false, out);
30547 break;
30548 case BuiltinFnIdCeil:
30549 f128M_roundToInt(in, softfloat_round_max, false, out);
30550 break;
30551 case BuiltinFnIdTrunc:
30552 f128M_trunc(in, out);
30553 break;
30554 case BuiltinFnIdRound:
30555 f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
30556 break;
3017630557 case BuiltinFnIdNearbyInt:
3017730558 case BuiltinFnIdSin:
3017830559 case BuiltinFnIdCos:
......@@ -30181,11 +30562,6 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
3018130562 case BuiltinFnIdLog:
3018230563 case BuiltinFnIdLog10:
3018330564 case BuiltinFnIdLog2:
30184 case BuiltinFnIdFabs:
30185 case BuiltinFnIdFloor:
30186 case BuiltinFnIdCeil:
30187 case BuiltinFnIdTrunc:
30188 case BuiltinFnIdRound:
3018930565 return ir_add_error(ira, source_instr,
3019030566 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
3019130567 float_op_to_name(fop), buf_ptr(&float_type->name)));
......@@ -30769,6 +31145,64 @@ static IrInstGen *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstSrcSpil
3076931145 return ir_build_spill_end_gen(ira, &instruction->base.base, begin, operand->value->type);
3077031146}
3077131147
31148static IrInstGen *ir_analyze_instruction_src(IrAnalyze *ira, IrInstSrcSrc *instruction) {
31149 ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope);
31150 if (fn_entry == nullptr) {
31151 ir_add_error(ira, &instruction->base.base, buf_sprintf("@src outside function"));
31152 return ira->codegen->invalid_inst_gen;
31153 }
31154
31155 ZigType *u8_ptr = get_pointer_to_type_extra2(
31156 ira->codegen, ira->codegen->builtin_types.entry_u8,
31157 true, false, PtrLenUnknown,
31158 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, ira->codegen->intern.for_zero_byte());
31159 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
31160
31161 ZigType *source_location_type = get_builtin_type(ira->codegen, "SourceLocation");
31162 if (type_resolve(ira->codegen, source_location_type, ResolveStatusSizeKnown)) {
31163 zig_unreachable();
31164 }
31165
31166 ZigValue *result = ira->codegen->pass1_arena->create<ZigValue>();
31167 result->special = ConstValSpecialStatic;
31168 result->type = source_location_type;
31169
31170 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4);
31171 result->data.x_struct.fields = fields;
31172
31173 // file: [:0]const u8
31174 ensure_field_index(source_location_type, "file", 0);
31175 fields[0]->special = ConstValSpecialStatic;
31176
31177 ZigType *import = instruction->base.base.source_node->owner;
31178 Buf *path = import->data.structure.root_struct->path;
31179 ZigValue *file_name = create_const_str_lit(ira->codegen, path)->data.x_ptr.data.ref.pointee;
31180 init_const_slice(ira->codegen, fields[0], file_name, 0, buf_len(path), true);
31181 fields[0]->type = u8_slice;
31182
31183 // fn_name: [:0]const u8
31184 ensure_field_index(source_location_type, "fn_name", 1);
31185 fields[1]->special = ConstValSpecialStatic;
31186
31187 ZigValue *fn_name = create_const_str_lit(ira->codegen, &fn_entry->symbol_name)->data.x_ptr.data.ref.pointee;
31188 init_const_slice(ira->codegen, fields[1], fn_name, 0, buf_len(&fn_entry->symbol_name), true);
31189 fields[1]->type = u8_slice;
31190
31191 // line: u32
31192 ensure_field_index(source_location_type, "line", 2);
31193 fields[2]->special = ConstValSpecialStatic;
31194 fields[2]->type = ira->codegen->builtin_types.entry_u32;
31195 bigint_init_unsigned(&fields[2]->data.x_bigint, instruction->base.base.source_node->line + 1);
31196
31197 // column: u32
31198 ensure_field_index(source_location_type, "column", 3);
31199 fields[3]->special = ConstValSpecialStatic;
31200 fields[3]->type = ira->codegen->builtin_types.entry_u32;
31201 bigint_init_unsigned(&fields[3]->data.x_bigint, instruction->base.base.source_node->column + 1);
31202
31203 return ir_const_move(ira, &instruction->base.base, result);
31204}
31205
3077231206static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) {
3077331207 switch (instruction->id) {
3077431208 case IrInstSrcIdInvalid:
......@@ -30802,6 +31236,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3080231236 return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction);
3080331237 case IrInstSrcIdCallExtra:
3080431238 return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction);
31239 case IrInstSrcIdAsyncCallExtra:
31240 return ir_analyze_instruction_async_call_extra(ira, (IrInstSrcAsyncCallExtra *)instruction);
3080531241 case IrInstSrcIdBr:
3080631242 return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction);
3080731243 case IrInstSrcIdCondBr:
......@@ -31040,6 +31476,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3104031476 return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction);
3104131477 case IrInstSrcIdWasmMemoryGrow:
3104231478 return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction);
31479 case IrInstSrcIdSrc:
31480 return ir_analyze_instruction_src(ira, (IrInstSrcSrc *)instruction);
3104331481 }
3104431482 zig_unreachable();
3104531483}
......@@ -31309,6 +31747,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3130931747 case IrInstSrcIdDeclVar:
3131031748 case IrInstSrcIdStorePtr:
3131131749 case IrInstSrcIdCallExtra:
31750 case IrInstSrcIdAsyncCallExtra:
3131231751 case IrInstSrcIdCall:
3131331752 case IrInstSrcIdCallArgs:
3131431753 case IrInstSrcIdReturn:
......@@ -31435,6 +31874,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3143531874 case IrInstSrcIdAlloca:
3143631875 case IrInstSrcIdSpillEnd:
3143731876 case IrInstSrcIdWasmMemorySize:
31877 case IrInstSrcIdSrc:
3143831878 return false;
3143931879
3144031880 case IrInstSrcIdAsm:
src/ir_print.cpp+64-54
......@@ -5,6 +5,7 @@
55 * See http://opensource.org/licenses/MIT
66 */
77
8#include "all_types.hpp"
89#include "analyze.hpp"
910#include "ir.hpp"
1011#include "ir_print.hpp"
......@@ -55,6 +56,36 @@ struct IrPrintGen {
5556static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst);
5657static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst);
5758
59static void ir_print_call_modifier(FILE *f, CallModifier modifier) {
60 switch (modifier) {
61 case CallModifierNone:
62 break;
63 case CallModifierNoSuspend:
64 fprintf(f, "nosuspend ");
65 break;
66 case CallModifierAsync:
67 fprintf(f, "async ");
68 break;
69 case CallModifierNeverTail:
70 fprintf(f, "notail ");
71 break;
72 case CallModifierNeverInline:
73 fprintf(f, "noinline ");
74 break;
75 case CallModifierAlwaysTail:
76 fprintf(f, "tail ");
77 break;
78 case CallModifierAlwaysInline:
79 fprintf(f, "inline ");
80 break;
81 case CallModifierCompileTime:
82 fprintf(f, "comptime ");
83 break;
84 case CallModifierBuiltin:
85 zig_unreachable();
86 }
87}
88
5889const char* ir_inst_src_type_str(IrInstSrcId id) {
5990 switch (id) {
6091 case IrInstSrcIdInvalid:
......@@ -97,6 +128,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
97128 return "SrcVarPtr";
98129 case IrInstSrcIdCallExtra:
99130 return "SrcCallExtra";
131 case IrInstSrcIdAsyncCallExtra:
132 return "SrcAsyncCallExtra";
100133 case IrInstSrcIdCall:
101134 return "SrcCall";
102135 case IrInstSrcIdCallArgs:
......@@ -325,6 +358,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
325358 return "SrcWasmMemorySize";
326359 case IrInstSrcIdWasmMemoryGrow:
327360 return "SrcWasmMemoryGrow";
361 case IrInstSrcIdSrc:
362 return "SrcSrc";
328363 }
329364 zig_unreachable();
330365}
......@@ -849,6 +884,23 @@ static void ir_print_call_extra(IrPrintSrc *irp, IrInstSrcCallExtra *instruction
849884 ir_print_result_loc(irp, instruction->result_loc);
850885}
851886
887static void ir_print_async_call_extra(IrPrintSrc *irp, IrInstSrcAsyncCallExtra *instruction) {
888 fprintf(irp->f, "modifier=");
889 ir_print_call_modifier(irp->f, instruction->modifier);
890 fprintf(irp->f, ", fn=");
891 ir_print_other_inst_src(irp, instruction->fn_ref);
892 if (instruction->ret_ptr != nullptr) {
893 fprintf(irp->f, ", ret_ptr=");
894 ir_print_other_inst_src(irp, instruction->ret_ptr);
895 }
896 fprintf(irp->f, ", new_stack=");
897 ir_print_other_inst_src(irp, instruction->new_stack);
898 fprintf(irp->f, ", args=");
899 ir_print_other_inst_src(irp, instruction->args);
900 fprintf(irp->f, ", result=");
901 ir_print_result_loc(irp, instruction->result_loc);
902}
903
852904static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction) {
853905 fprintf(irp->f, "opts=");
854906 ir_print_other_inst_src(irp, instruction->options);
......@@ -866,33 +918,7 @@ static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction)
866918}
867919
868920static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction) {
869 switch (call_instruction->modifier) {
870 case CallModifierNone:
871 break;
872 case CallModifierNoSuspend:
873 fprintf(irp->f, "nosuspend ");
874 break;
875 case CallModifierAsync:
876 fprintf(irp->f, "async ");
877 break;
878 case CallModifierNeverTail:
879 fprintf(irp->f, "notail ");
880 break;
881 case CallModifierNeverInline:
882 fprintf(irp->f, "noinline ");
883 break;
884 case CallModifierAlwaysTail:
885 fprintf(irp->f, "tail ");
886 break;
887 case CallModifierAlwaysInline:
888 fprintf(irp->f, "inline ");
889 break;
890 case CallModifierCompileTime:
891 fprintf(irp->f, "comptime ");
892 break;
893 case CallModifierBuiltin:
894 zig_unreachable();
895 }
921 ir_print_call_modifier(irp->f, call_instruction->modifier);
896922 if (call_instruction->fn_entry) {
897923 fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));
898924 } else {
......@@ -911,33 +937,7 @@ static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction)
911937}
912938
913939static void ir_print_call_gen(IrPrintGen *irp, IrInstGenCall *call_instruction) {
914 switch (call_instruction->modifier) {
915 case CallModifierNone:
916 break;
917 case CallModifierNoSuspend:
918 fprintf(irp->f, "nosuspend ");
919 break;
920 case CallModifierAsync:
921 fprintf(irp->f, "async ");
922 break;
923 case CallModifierNeverTail:
924 fprintf(irp->f, "notail ");
925 break;
926 case CallModifierNeverInline:
927 fprintf(irp->f, "noinline ");
928 break;
929 case CallModifierAlwaysTail:
930 fprintf(irp->f, "tail ");
931 break;
932 case CallModifierAlwaysInline:
933 fprintf(irp->f, "inline ");
934 break;
935 case CallModifierCompileTime:
936 fprintf(irp->f, "comptime ");
937 break;
938 case CallModifierBuiltin:
939 zig_unreachable();
940 }
940 ir_print_call_modifier(irp->f, call_instruction->modifier);
941941 if (call_instruction->fn_entry) {
942942 fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));
943943 } else {
......@@ -1744,6 +1744,10 @@ static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *
17441744 fprintf(irp->f, ")");
17451745}
17461746
1747static void ir_print_builtin_src(IrPrintSrc *irp, IrInstSrcSrc *instruction) {
1748 fprintf(irp->f, "@src()");
1749}
1750
17471751static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) {
17481752 fprintf(irp->f, "@memset(");
17491753 ir_print_other_inst_src(irp, instruction->dest_ptr);
......@@ -2613,6 +2617,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
26132617 case IrInstSrcIdCallExtra:
26142618 ir_print_call_extra(irp, (IrInstSrcCallExtra *)instruction);
26152619 break;
2620 case IrInstSrcIdAsyncCallExtra:
2621 ir_print_async_call_extra(irp, (IrInstSrcAsyncCallExtra *)instruction);
2622 break;
26162623 case IrInstSrcIdCall:
26172624 ir_print_call_src(irp, (IrInstSrcCall *)instruction);
26182625 break;
......@@ -2994,6 +3001,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
29943001 case IrInstSrcIdWasmMemoryGrow:
29953002 ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction);
29963003 break;
3004 case IrInstSrcIdSrc:
3005 ir_print_builtin_src(irp, (IrInstSrcSrc *)instruction);
3006 break;
29973007 }
29983008 fprintf(irp->f, "\n");
29993009}
src/list.hpp+3
......@@ -19,6 +19,9 @@ struct ZigList {
1919 ensure_capacity(length + 1);
2020 items[length++] = item;
2121 }
22 void append_assuming_capacity(const T& item) {
23 items[length++] = item;
24 }
2225 // remember that the pointer to this item is invalid after you
2326 // modify the length of the list
2427 const T & at(size_t index) const {
src/os.cpp+213-29
......@@ -6,8 +6,13 @@
66 */
77
88#include "os.hpp"
9#include "buffer.hpp"
10#include "heap.hpp"
911#include "util.hpp"
1012#include "error.hpp"
13#include "util_base.hpp"
14#include <stdint.h>
15#include <stdio.h>
1116
1217#if defined(_WIN32)
1318
......@@ -73,6 +78,8 @@ typedef SSIZE_T ssize_t;
7378#endif
7479
7580#if defined(ZIG_OS_WINDOWS)
81static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le);
82static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice<uint8_t> utf8);
7683static uint64_t windows_perf_freq;
7784#elif defined(__MACH__)
7885static clock_serv_t macos_calendar_clock;
......@@ -148,15 +155,21 @@ static void os_spawn_process_windows(ZigList<const char *> &args, Termination *t
148155 os_windows_create_command_line(&command_line, args);
149156
150157 PROCESS_INFORMATION piProcInfo = {0};
151 STARTUPINFO siStartInfo = {0};
152 siStartInfo.cb = sizeof(STARTUPINFO);
158 STARTUPINFOW siStartInfo = {0};
159 siStartInfo.cb = sizeof(STARTUPINFOW);
153160
154 const char *exe = args.at(0);
155 BOOL success = CreateProcessA(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
161 Slice<uint8_t> exe_slice = str(args.at(0));
162 auto exe_utf16_slice = Slice<WCHAR>::alloc(exe_slice.len + 1);
163 exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0;
164
165 auto command_line_utf16 = Slice<WCHAR>::alloc(buf_len(&command_line) + 1);
166 command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0;
167
168 BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr,
156169 &siStartInfo, &piProcInfo);
157170
158171 if (!success) {
159 zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line));
172 zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line));
160173 }
161174
162175 WaitForSingleObject(piProcInfo.hProcess, INFINITE);
......@@ -269,11 +282,13 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
269282
270283Error os_path_real(Buf *rel_path, Buf *out_abs_path) {
271284#if defined(ZIG_OS_WINDOWS)
272 buf_resize(out_abs_path, 4096);
273 if (_fullpath(buf_ptr(out_abs_path), buf_ptr(rel_path), buf_len(out_abs_path)) == nullptr) {
274 zig_panic("_fullpath failed");
285 PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path));
286 PathSpace out_abs_path_space;
287
288 if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) {
289 zig_panic("_wfullpath failed");
275290 }
276 buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path)));
291 utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]);
277292 return ErrorNone;
278293#elif defined(ZIG_OS_POSIX)
279294 buf_resize(out_abs_path, PATH_MAX + 1);
......@@ -773,7 +788,8 @@ Error os_fetch_file(FILE *f, Buf *out_buf) {
773788
774789Error os_file_exists(Buf *full_path, bool *result) {
775790#if defined(ZIG_OS_WINDOWS)
776 *result = GetFileAttributes(buf_ptr(full_path)) != INVALID_FILE_ATTRIBUTES;
791 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
792 *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES;
777793 return ErrorNone;
778794#else
779795 *result = access(buf_ptr(full_path), F_OK) != -1;
......@@ -1021,7 +1037,12 @@ Error os_exec_process(ZigList<const char *> &args,
10211037}
10221038
10231039Error os_write_file(Buf *full_path, Buf *contents) {
1040#if defined(ZIG_OS_WINDOWS)
1041 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
1042 FILE *f = _wfopen(&path_space.data.items[0], L"wb");
1043#else
10241044 FILE *f = fopen(buf_ptr(full_path), "wb");
1045#endif
10251046 if (!f) {
10261047 zig_panic("os_write_file failed for %s", buf_ptr(full_path));
10271048 }
......@@ -1056,7 +1077,12 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) {
10561077Error os_dump_file(Buf *src_path, FILE *dest_file) {
10571078 Error err;
10581079
1080#if defined(ZIG_OS_WINDOWS)
1081 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
1082 FILE *src_f = _wfopen(&path_space.data.items[0], L"rb");
1083#else
10591084 FILE *src_f = fopen(buf_ptr(src_path), "rb");
1085#endif
10601086 if (!src_f) {
10611087 int err = errno;
10621088 if (err == ENOENT) {
......@@ -1173,7 +1199,12 @@ Error os_update_file(Buf *src_path, Buf *dst_path) {
11731199}
11741200
11751201Error os_copy_file(Buf *src_path, Buf *dest_path) {
1202#if defined(ZIG_OS_WINDOWS)
1203 PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
1204 FILE *src_f = _wfopen(&src_path_space.data.items[0], L"rb");
1205#else
11761206 FILE *src_f = fopen(buf_ptr(src_path), "rb");
1207#endif
11771208 if (!src_f) {
11781209 int err = errno;
11791210 if (err == ENOENT) {
......@@ -1184,7 +1215,12 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {
11841215 return ErrorFileSystem;
11851216 }
11861217 }
1218#if defined(ZIG_OS_WINDOWS)
1219 PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path));
1220 FILE *dest_f = _wfopen(&dest_path_space.data.items[0], L"wb");
1221#else
11871222 FILE *dest_f = fopen(buf_ptr(dest_path), "wb");
1223#endif
11881224 if (!dest_f) {
11891225 int err = errno;
11901226 if (err == ENOENT) {
......@@ -1205,7 +1241,12 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {
12051241}
12061242
12071243Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {
1244#if defined(ZIG_OS_WINDOWS)
1245 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
1246 FILE *f = _wfopen(&path_space.data.items[0], L"rb");
1247#else
12081248 FILE *f = fopen(buf_ptr(full_path), "rb");
1249#endif
12091250 if (!f) {
12101251 switch (errno) {
12111252 case EACCES:
......@@ -1230,11 +1271,11 @@ Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {
12301271
12311272Error os_get_cwd(Buf *out_cwd) {
12321273#if defined(ZIG_OS_WINDOWS)
1233 char buf[4096];
1234 if (GetCurrentDirectory(4096, buf) == 0) {
1274 PathSpace path_space;
1275 if (GetCurrentDirectoryW(PATH_MAX_WIDE, &path_space.data.items[0]) == 0) {
12351276 zig_panic("GetCurrentDirectory failed");
12361277 }
1237 buf_init_from_str(out_cwd, buf);
1278 utf16le_ptr_to_utf8(out_cwd, &path_space.data.items[0]);
12381279 return ErrorNone;
12391280#elif defined(ZIG_OS_POSIX)
12401281 char buf[PATH_MAX];
......@@ -1330,7 +1371,9 @@ Error os_rename(Buf *src_path, Buf *dest_path) {
13301371 return ErrorNone;
13311372 }
13321373#if defined(ZIG_OS_WINDOWS)
1333 if (!MoveFileExA(buf_ptr(src_path), buf_ptr(dest_path), MOVEFILE_REPLACE_EXISTING)) {
1374 PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
1375 PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path));
1376 if (!MoveFileExW(&src_path_space.data.items[0], &dest_path_space.data.items[0], MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
13341377 return ErrorFileSystem;
13351378 }
13361379#else
......@@ -1426,7 +1469,15 @@ Error os_make_path(Buf *path) {
14261469
14271470Error os_make_dir(Buf *path) {
14281471#if defined(ZIG_OS_WINDOWS)
1429 if (!CreateDirectory(buf_ptr(path), NULL)) {
1472 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(path));
1473 if (memEql(buf_to_slice(path), str("C:\\dev\\tést"))) {
1474 for (size_t i = 0; i < path_space.len; i++) {
1475 fprintf(stderr, "%d ", path_space.data.items[i]);
1476 }
1477 fprintf(stderr, "\n");
1478 }
1479
1480 if (!CreateDirectoryW(&path_space.data.items[0], NULL)) {
14301481 if (GetLastError() == ERROR_ALREADY_EXISTS)
14311482 return ErrorPathAlreadyExists;
14321483 if (GetLastError() == ERROR_PATH_NOT_FOUND)
......@@ -1531,18 +1582,13 @@ int os_init(void) {
15311582
15321583Error os_self_exe_path(Buf *out_path) {
15331584#if defined(ZIG_OS_WINDOWS)
1534 buf_resize(out_path, 256);
1535 for (;;) {
1536 DWORD copied_amt = GetModuleFileName(nullptr, buf_ptr(out_path), buf_len(out_path));
1537 if (copied_amt <= 0) {
1538 return ErrorFileNotFound;
1539 }
1540 if (copied_amt < buf_len(out_path)) {
1541 buf_resize(out_path, copied_amt);
1542 return ErrorNone;
1543 }
1544 buf_resize(out_path, buf_len(out_path) * 2);
1585 PathSpace path_space;
1586 DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE);
1587 if (copied_amt <= 0) {
1588 return ErrorFileNotFound;
15451589 }
1590 utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]);
1591 return ErrorNone;
15461592
15471593#elif defined(ZIG_OS_DARWIN)
15481594 // How long is the executable's path?
......@@ -1719,6 +1765,15 @@ static uint8_t utf8CodepointSequenceLength(uint32_t c) {
17191765 zig_unreachable();
17201766}
17211767
1768// Ported from std.unicode.utf8ByteSequenceLength
1769static uint8_t utf8ByteSequenceLength(uint8_t first_byte) {
1770 if (first_byte < 0b10000000) return 1;
1771 if ((first_byte & 0b11100000) == 0b11000000) return 2;
1772 if ((first_byte & 0b11110000) == 0b11100000) return 3;
1773 if ((first_byte & 0b11111000) == 0b11110000) return 4;
1774 zig_unreachable();
1775}
1776
17221777// Ported from std/unicode.zig
17231778static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {
17241779 size_t length = utf8CodepointSequenceLength(c);
......@@ -1753,6 +1808,80 @@ static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {
17531808 return length;
17541809}
17551810
1811// Ported from std.unicode.utf8Decode2
1812static uint32_t utf8Decode2(Slice<uint8_t> bytes) {
1813 assert(bytes.len == 2);
1814 assert((bytes.at(0) & 0b11100000) == 0b11000000);
1815
1816 uint32_t value = bytes.at(0) & 0b00011111;
1817 assert((bytes.at(1) & 0b11000000) == 0b10000000);
1818 value <<= 6;
1819 value |= bytes.at(1) & 0b00111111;
1820
1821 assert(value >= 0x80);
1822 return value;
1823}
1824
1825// Ported from std.unicode.utf8Decode3
1826static uint32_t utf8Decode3(Slice<uint8_t> bytes) {
1827 assert(bytes.len == 3);
1828 assert((bytes.at(0) & 0b11110000) == 0b11100000);
1829
1830 uint32_t value = bytes.at(0) & 0b00001111;
1831 assert((bytes.at(1) & 0b11000000) == 0b10000000);
1832 value <<= 6;
1833 value |= bytes.at(1) & 0b00111111;
1834
1835 assert((bytes.at(2) & 0b11000000) == 0b10000000);
1836 value <<= 6;
1837 value |= bytes.at(2) & 0b00111111;
1838
1839 assert(value >= 0x80);
1840 assert(value < 0xd800 || value > 0xdfff);
1841 return value;
1842}
1843
1844// Ported from std.unicode.utf8Decode4
1845static uint32_t utf8Decode4(Slice<uint8_t> bytes) {
1846 assert(bytes.len == 4);
1847 assert((bytes.at(0) & 0b11111000) == 0b11110000);
1848
1849 uint32_t value = bytes.at(0) & 0b00000111;
1850 assert((bytes.at(1) & 0b11000000) == 0b10000000);
1851 value <<= 6;
1852 value |= bytes.at(1) & 0b00111111;
1853
1854 assert((bytes.at(2) & 0b11000000) == 0b10000000);
1855 value <<= 6;
1856 value |= bytes.at(2) & 0b00111111;
1857
1858 assert((bytes.at(3) & 0b11000000) == 0b10000000);
1859 value <<= 6;
1860 value |= bytes.at(3) & 0b00111111;
1861
1862 assert(value >= 0x10000 && value <= 0x10FFFF);
1863 return value;
1864}
1865
1866// Ported from std.unicode.utf8Decode
1867static uint32_t utf8Decode(Slice<uint8_t> bytes) {
1868 switch (bytes.len) {
1869 case 1:
1870 return bytes.at(0);
1871 break;
1872 case 2:
1873 return utf8Decode2(bytes);
1874 break;
1875 case 3:
1876 return utf8Decode3(bytes);
1877 break;
1878 case 4:
1879 return utf8Decode4(bytes);
1880 break;
1881 default:
1882 zig_unreachable();
1883 }
1884}
17561885// Ported from std.unicode.utf16leToUtf8Alloc
17571886static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {
17581887 // optimistically guess that it will all be ascii.
......@@ -1770,6 +1899,60 @@ static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {
17701899 out_index += utf8_len;
17711900 }
17721901}
1902
1903// Ported from std.unicode.utf8ToUtf16Le
1904static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice<uint8_t> utf8) {
1905 size_t dest_i = 0;
1906 size_t src_i = 0;
1907 while (src_i < utf8.len) {
1908 uint8_t n = utf8ByteSequenceLength(utf8.at(src_i));
1909 size_t next_src_i = src_i + n;
1910 uint32_t codepoint = utf8Decode(utf8.slice(src_i, next_src_i));
1911 if (codepoint < 0x10000) {
1912 utf16_le[dest_i] = codepoint;
1913 dest_i += 1;
1914 } else {
1915 WCHAR high = ((codepoint - 0x10000) >> 10) + 0xD800;
1916 WCHAR low = (codepoint & 0x3FF) + 0xDC00;
1917 utf16_le[dest_i] = high;
1918 utf16_le[dest_i + 1] = low;
1919 dest_i += 2;
1920 }
1921 src_i = next_src_i;
1922 }
1923 return dest_i;
1924}
1925
1926// Ported from std.os.windows.sliceToPrefixedFileW
1927PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) {
1928 PathSpace path_space;
1929 for (size_t idx = 0; idx < path.len; idx++) {
1930 assert(path.ptr[idx] != '*' && path.ptr[idx] != '?' && path.ptr[idx] != '"' &&
1931 path.ptr[idx] != '<' && path.ptr[idx] != '>' && path.ptr[idx] != '|');
1932 }
1933
1934 size_t start_index;
1935 if (memStartsWith(path, str("\\?")) || !isAbsoluteWindows(path)) {
1936 start_index = 0;
1937 } else {
1938 static WCHAR prefix[4] = { u'\\', u'?', u'?', u'\\' };
1939 memCopy(path_space.data.slice(), Slice<WCHAR> { prefix, 4 });
1940 start_index = 4;
1941 }
1942
1943 path_space.len = start_index + utf8_to_utf16le(path_space.data.slice().sliceFrom(start_index).ptr, path);
1944 assert(path_space.len <= path_space.data.len);
1945
1946 Slice<WCHAR> path_slice = path_space.data.slice().slice(0, path_space.len);
1947 for (size_t elem_idx = 0; elem_idx < path_slice.len; elem_idx += 1) {
1948 if (path_slice.at(elem_idx) == '/') {
1949 path_slice.at(elem_idx) = '\\';
1950 }
1951 }
1952
1953 path_space.data.items[path_space.len] = 0;
1954 return path_space;
1955}
17731956#endif
17741957
17751958// Ported from std.os.getAppDataDir
......@@ -1862,8 +2045,8 @@ Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {
18622045
18632046Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) {
18642047#if defined(ZIG_OS_WINDOWS)
1865 // TODO use CreateFileW
1866 HANDLE result = CreateFileA(buf_ptr(full_path),
2048 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
2049 HANDLE result = CreateFileW(&path_space.data.items[0],
18672050 need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ,
18682051 need_write ? 0 : FILE_SHARE_READ,
18692052 nullptr,
......@@ -1967,8 +2150,9 @@ Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_
19672150
19682151Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {
19692152#if defined(ZIG_OS_WINDOWS)
2153 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
19702154 for (;;) {
1971 HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ | GENERIC_WRITE,
2155 HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE,
19722156 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
19732157
19742158 if (result == INVALID_HANDLE_VALUE) {
src/os.hpp+8
......@@ -155,4 +155,12 @@ Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname)
155155
156156Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);
157157
158const size_t PATH_MAX_WIDE = 32767;
159
160struct PathSpace {
161 Array<wchar_t, PATH_MAX_WIDE> data;
162 size_t len;
163};
164
165PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path);
158166#endif
src/parser.cpp+14-14
......@@ -786,7 +786,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
786786 return nullptr;
787787}
788788
789// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
789// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
790790static AstNode *ast_parse_fn_proto(ParseContext *pc) {
791791 Token *first = eat_token_if(pc, TokenIdKeywordFn);
792792 if (first == nullptr) {
......@@ -801,10 +801,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
801801 AstNode *align_expr = ast_parse_byte_align(pc);
802802 AstNode *section_expr = ast_parse_link_section(pc);
803803 AstNode *callconv_expr = ast_parse_callconv(pc);
804 Token *var = eat_token_if(pc, TokenIdKeywordVar);
804 Token *anytype = eat_token_if(pc, TokenIdKeywordAnyType);
805805 Token *exmark = nullptr;
806806 AstNode *return_type = nullptr;
807 if (var == nullptr) {
807 if (anytype == nullptr) {
808808 exmark = eat_token_if(pc, TokenIdBang);
809809 return_type = ast_expect(pc, ast_parse_type_expr);
810810 }
......@@ -816,7 +816,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
816816 res->data.fn_proto.align_expr = align_expr;
817817 res->data.fn_proto.section_expr = section_expr;
818818 res->data.fn_proto.callconv_expr = callconv_expr;
819 res->data.fn_proto.return_var_token = var;
819 res->data.fn_proto.return_anytype_token = anytype;
820820 res->data.fn_proto.auto_err_set = exmark != nullptr;
821821 res->data.fn_proto.return_type = return_type;
822822
......@@ -870,9 +870,9 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
870870
871871 AstNode *type_expr = nullptr;
872872 if (eat_token_if(pc, TokenIdColon) != nullptr) {
873 Token *var_tok = eat_token_if(pc, TokenIdKeywordVar);
874 if (var_tok != nullptr) {
875 type_expr = ast_create_node(pc, NodeTypeVarFieldType, var_tok);
873 Token *anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType);
874 if (anytype_tok != nullptr) {
875 type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok);
876876 } else {
877877 type_expr = ast_expect(pc, ast_parse_type_expr);
878878 }
......@@ -2191,14 +2191,14 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {
21912191}
21922192
21932193// ParamType
2194// <- KEYWORD_var
2194// <- KEYWORD_anytype
21952195// / DOT3
21962196// / TypeExpr
21972197static AstNode *ast_parse_param_type(ParseContext *pc) {
2198 Token *var_token = eat_token_if(pc, TokenIdKeywordVar);
2199 if (var_token != nullptr) {
2200 AstNode *res = ast_create_node(pc, NodeTypeParamDecl, var_token);
2201 res->data.param_decl.var_token = var_token;
2198 Token *anytype_token = eat_token_if(pc, TokenIdKeywordAnyType);
2199 if (anytype_token != nullptr) {
2200 AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token);
2201 res->data.param_decl.anytype_token = anytype_token;
22022202 return res;
22032203 }
22042204
......@@ -2679,7 +2679,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
26792679
26802680 if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) {
26812681 expect_token(pc, TokenIdLParen);
2682 AstNode *align_expr = ast_parse_expr(pc);
2682 AstNode *align_expr = ast_expect(pc, ast_parse_expr);
26832683 child->data.pointer_type.align_expr = align_expr;
26842684 if (eat_token_if(pc, TokenIdColon) != nullptr) {
26852685 Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral);
......@@ -3207,7 +3207,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
32073207 visit_field(&node->data.suspend.block, visit, context);
32083208 break;
32093209 case NodeTypeEnumLiteral:
3210 case NodeTypeVarFieldType:
3210 case NodeTypeAnyTypeField:
32113211 break;
32123212 }
32133213}
src/softfloat_ext.cpp created+25
......@@ -0,0 +1,25 @@
1#include "softfloat_ext.hpp"
2
3extern "C" {
4 #include "softfloat.h"
5}
6
7void f128M_abs(const float128_t *aPtr, float128_t *zPtr) {
8 float128_t zero_float;
9 ui32_to_f128M(0, &zero_float);
10 if (f128M_lt(aPtr, &zero_float)) {
11 f128M_sub(&zero_float, aPtr, zPtr);
12 } else {
13 *zPtr = *aPtr;
14 }
15}
16
17void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {
18 float128_t zero_float;
19 ui32_to_f128M(0, &zero_float);
20 if (f128M_lt(aPtr, &zero_float)) {
21 f128M_roundToInt(aPtr, softfloat_round_max, false, zPtr);
22 } else {
23 f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);
24 }
25}
\ No newline at end of file
src/softfloat_ext.hpp created+9
......@@ -0,0 +1,9 @@
1#ifndef ZIG_SOFTFLOAT_EXT_HPP
2#define ZIG_SOFTFLOAT_EXT_HPP
3
4#include "softfloat_types.h"
5
6void f128M_abs(const float128_t *aPtr, float128_t *zPtr);
7void f128M_trunc(const float128_t *aPtr, float128_t *zPtr);
8
9#endif
\ No newline at end of file
src/tokenizer.cpp+2
......@@ -106,6 +106,7 @@ static const struct ZigKeyword zig_keywords[] = {
106106 {"allowzero", TokenIdKeywordAllowZero},
107107 {"and", TokenIdKeywordAnd},
108108 {"anyframe", TokenIdKeywordAnyFrame},
109 {"anytype", TokenIdKeywordAnyType},
109110 {"asm", TokenIdKeywordAsm},
110111 {"async", TokenIdKeywordAsync},
111112 {"await", TokenIdKeywordAwait},
......@@ -1569,6 +1570,7 @@ const char * token_name(TokenId id) {
15691570 case TokenIdKeywordAlign: return "align";
15701571 case TokenIdKeywordAnd: return "and";
15711572 case TokenIdKeywordAnyFrame: return "anyframe";
1573 case TokenIdKeywordAnyType: return "anytype";
15721574 case TokenIdKeywordAsm: return "asm";
15731575 case TokenIdKeywordBreak: return "break";
15741576 case TokenIdKeywordCatch: return "catch";
src/tokenizer.hpp+1
......@@ -54,6 +54,7 @@ enum TokenId {
5454 TokenIdKeywordAllowZero,
5555 TokenIdKeywordAnd,
5656 TokenIdKeywordAnyFrame,
57 TokenIdKeywordAnyType,
5758 TokenIdKeywordAsm,
5859 TokenIdKeywordAsync,
5960 TokenIdKeywordAwait,
src/util.hpp+1-1
......@@ -159,7 +159,7 @@ struct Slice {
159159
160160 inline T &at(size_t i) {
161161 assert(i < len);
162 return &ptr[i];
162 return ptr[i];
163163 }
164164
165165 inline Slice<T> slice(size_t start, size_t end) {
test/cli.zig+27
......@@ -34,6 +34,7 @@ pub fn main() !void {
3434 testZigInitExe,
3535 testGodboltApi,
3636 testMissingOutputPath,
37 testZigFmt,
3738 };
3839 for (test_fns) |testFn| {
3940 try fs.cwd().deleteTree(dir_path);
......@@ -143,3 +144,29 @@ fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
143144 zig_exe, "build-exe", source_path, "--output-dir", output_path,
144145 });
145146}
147
148fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
149 _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
150
151 const unformatted_code = " // no reason for indent";
152
153 const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
154 try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
155
156 const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
157 // stderr should be file path + \n
158 testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));
159 testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');
160
161 const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
162 try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
163
164 const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
165 // running it on the dir, only the new file should be changed
166 testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));
167 testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');
168
169 const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
170 // both files have been formatted, nothing should change now
171 testing.expect(run_result3.stderr.len == 0);
172}
test/compile_errors.zig+143-28
......@@ -2,6 +2,55 @@ const tests = @import("tests.zig");
22const std = @import("std");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addTest("invalid pointer with @Type",
6 \\export fn entry() void {
7 \\ _ = @Type(.{ .Pointer = .{
8 \\ .size = .One,
9 \\ .is_const = false,
10 \\ .is_volatile = false,
11 \\ .alignment = 1,
12 \\ .child = u8,
13 \\ .is_allowzero = false,
14 \\ .sentinel = 0,
15 \\ }});
16 \\}
17 , &[_][]const u8{
18 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
19 });
20
21 cases.addTest("int/float conversion to comptime_int/float",
22 \\export fn foo() void {
23 \\ var a: f32 = 2;
24 \\ _ = @floatToInt(comptime_int, a);
25 \\}
26 \\export fn bar() void {
27 \\ var a: u32 = 2;
28 \\ _ = @intToFloat(comptime_float, a);
29 \\}
30 , &[_][]const u8{
31 "tmp.zig:3:35: error: unable to evaluate constant expression",
32 "tmp.zig:3:9: note: referenced here",
33 "tmp.zig:7:37: error: unable to evaluate constant expression",
34 "tmp.zig:7:9: note: referenced here",
35 });
36
37 cases.add("extern variable has no type",
38 \\extern var foo;
39 \\pub export fn entry() void {
40 \\ foo;
41 \\}
42 , &[_][]const u8{
43 "tmp.zig:1:1: error: unable to infer variable type",
44 });
45
46 cases.add("@src outside function",
47 \\comptime {
48 \\ @src();
49 \\}
50 , &[_][]const u8{
51 "tmp.zig:2:5: error: @src outside function",
52 });
53
554 cases.add("call assigned to constant",
655 \\const Foo = struct {
756 \\ x: i32,
......@@ -9,7 +58,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
958 \\fn foo() Foo {
1059 \\ return .{ .x = 42 };
1160 \\}
12 \\fn bar(val: var) Foo {
61 \\fn bar(val: anytype) Foo {
1362 \\ return .{ .x = val };
1463 \\}
1564 \\export fn entry() void {
......@@ -74,17 +123,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
74123 \\ _ = @floatToInt(u32, a);
75124 \\}
76125 \\export fn qux() void {
77 \\ var a: u32 = 2;
78 \\ _ = @intCast(comptime_int, a);
126 \\ var a: f32 = 2;
127 \\ _ = @intCast(u32, a);
79128 \\}
80129 , &[_][]const u8{
81 "tmp.zig:3:32: error: expected type 'comptime_int', found 'u32'",
130 "tmp.zig:3:32: error: unable to evaluate constant expression",
82131 "tmp.zig:3:9: note: referenced here",
83132 "tmp.zig:7:21: error: expected float type, found 'u32'",
84133 "tmp.zig:7:9: note: referenced here",
85134 "tmp.zig:11:26: error: expected float type, found 'u32'",
86135 "tmp.zig:11:9: note: referenced here",
87 "tmp.zig:15:32: error: expected type 'comptime_int', found 'u32'",
136 "tmp.zig:15:23: error: expected integer type, found 'f32'",
88137 "tmp.zig:15:9: note: referenced here",
89138 });
90139
......@@ -102,17 +151,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
102151 \\ _ = @intToFloat(f32, a);
103152 \\}
104153 \\export fn qux() void {
105 \\ var a: f32 = 2;
106 \\ _ = @floatCast(comptime_float, a);
154 \\ var a: u32 = 2;
155 \\ _ = @floatCast(f32, a);
107156 \\}
108157 , &[_][]const u8{
109 "tmp.zig:3:36: error: expected type 'comptime_float', found 'f32'",
158 "tmp.zig:3:36: error: unable to evaluate constant expression",
110159 "tmp.zig:3:9: note: referenced here",
111160 "tmp.zig:7:21: error: expected integer type, found 'f32'",
112161 "tmp.zig:7:9: note: referenced here",
113162 "tmp.zig:11:26: error: expected int type, found 'f32'",
114163 "tmp.zig:11:9: note: referenced here",
115 "tmp.zig:15:36: error: expected type 'comptime_float', found 'f32'",
164 "tmp.zig:15:25: error: expected float type, found 'u32'",
116165 "tmp.zig:15:9: note: referenced here",
117166 });
118167
......@@ -1013,7 +1062,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
10131062 \\ storev(&v[i], 42);
10141063 \\}
10151064 \\
1016 \\fn storev(ptr: var, val: i32) void {
1065 \\fn storev(ptr: anytype, val: i32) void {
10171066 \\ ptr.* = val;
10181067 \\}
10191068 , &[_][]const u8{
......@@ -1028,7 +1077,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
10281077 \\ var x = loadv(&v[i]);
10291078 \\}
10301079 \\
1031 \\fn loadv(ptr: var) i32 {
1080 \\fn loadv(ptr: anytype) i32 {
10321081 \\ return ptr.*;
10331082 \\}
10341083 , &[_][]const u8{
......@@ -1136,13 +1185,26 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
11361185 "tmp.zig:2:15: error: @Type not available for 'TypeInfo.Struct'",
11371186 });
11381187
1188 cases.add("wrong type for argument tuple to @asyncCall",
1189 \\export fn entry1() void {
1190 \\ var frame: @Frame(foo) = undefined;
1191 \\ @asyncCall(&frame, {}, foo, {});
1192 \\}
1193 \\
1194 \\fn foo() i32 {
1195 \\ return 0;
1196 \\}
1197 , &[_][]const u8{
1198 "tmp.zig:3:33: error: expected tuple or struct, found 'void'",
1199 });
1200
11391201 cases.add("wrong type for result ptr to @asyncCall",
11401202 \\export fn entry() void {
11411203 \\ _ = async amain();
11421204 \\}
11431205 \\fn amain() i32 {
11441206 \\ var frame: @Frame(foo) = undefined;
1145 \\ return await @asyncCall(&frame, false, foo);
1207 \\ return await @asyncCall(&frame, false, foo, .{});
11461208 \\}
11471209 \\fn foo() i32 {
11481210 \\ return 1234;
......@@ -1283,7 +1345,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
12831345 \\export fn entry() void {
12841346 \\ var ptr: fn () callconv(.Async) void = func;
12851347 \\ var bytes: [64]u8 = undefined;
1286 \\ _ = @asyncCall(&bytes, {}, ptr);
1348 \\ _ = @asyncCall(&bytes, {}, ptr, .{});
12871349 \\}
12881350 \\fn func() callconv(.Async) void {}
12891351 , &[_][]const u8{
......@@ -1459,7 +1521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
14591521 \\export fn entry() void {
14601522 \\ var ptr = afunc;
14611523 \\ var bytes: [100]u8 align(16) = undefined;
1462 \\ _ = @asyncCall(&bytes, {}, ptr);
1524 \\ _ = @asyncCall(&bytes, {}, ptr, .{});
14631525 \\}
14641526 \\fn afunc() void { }
14651527 , &[_][]const u8{
......@@ -1798,7 +1860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
17981860 \\ while (true) {}
17991861 \\}
18001862 , &[_][]const u8{
1801 "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,var) var'",
1863 "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,anytype) anytype'",
18021864 "note: only one of the functions is generic",
18031865 });
18041866
......@@ -1998,11 +2060,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
19982060 });
19992061
20002062 cases.add("export generic function",
2001 \\export fn foo(num: var) i32 {
2063 \\export fn foo(num: anytype) i32 {
20022064 \\ return 0;
20032065 \\}
20042066 , &[_][]const u8{
2005 "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'C'",
2067 "tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C'",
20062068 });
20072069
20082070 cases.add("C pointer to c_void",
......@@ -2802,7 +2864,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
28022864 });
28032865
28042866 cases.add("missing parameter name of generic function",
2805 \\fn dump(var) void {}
2867 \\fn dump(anytype) void {}
28062868 \\export fn entry() void {
28072869 \\ var a: u8 = 9;
28082870 \\ dump(a);
......@@ -2825,13 +2887,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
28252887 });
28262888
28272889 cases.add("generic fn as parameter without comptime keyword",
2828 \\fn f(_: fn (var) void) void {}
2829 \\fn g(_: var) void {}
2890 \\fn f(_: fn (anytype) void) void {}
2891 \\fn g(_: anytype) void {}
28302892 \\export fn entry() void {
28312893 \\ f(g);
28322894 \\}
28332895 , &[_][]const u8{
2834 "tmp.zig:1:9: error: parameter of type 'fn(var) var' must be declared comptime",
2896 "tmp.zig:1:9: error: parameter of type 'fn(anytype) anytype' must be declared comptime",
28352897 });
28362898
28372899 cases.add("optional pointer to void in extern struct",
......@@ -3131,7 +3193,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
31313193
31323194 cases.add("var makes structs required to be comptime known",
31333195 \\export fn entry() void {
3134 \\ const S = struct{v: var};
3196 \\ const S = struct{v: anytype};
31353197 \\ var s = S{.v=@as(i32, 10)};
31363198 \\}
31373199 , &[_][]const u8{
......@@ -4354,6 +4416,40 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
43544416 "tmp.zig:5:14: note: previous value is here",
43554417 });
43564418
4419 cases.add("switch expression - duplicate type",
4420 \\fn foo(comptime T: type, x: T) u8 {
4421 \\ return switch (T) {
4422 \\ u32 => 0,
4423 \\ u64 => 1,
4424 \\ u32 => 2,
4425 \\ else => 3,
4426 \\ };
4427 \\}
4428 \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); }
4429 , &[_][]const u8{
4430 "tmp.zig:5:9: error: duplicate switch value",
4431 "tmp.zig:3:9: note: previous value is here",
4432 });
4433
4434 cases.add("switch expression - duplicate type (struct alias)",
4435 \\const Test = struct {
4436 \\ bar: i32,
4437 \\};
4438 \\const Test2 = Test;
4439 \\fn foo(comptime T: type, x: T) u8 {
4440 \\ return switch (T) {
4441 \\ Test => 0,
4442 \\ u64 => 1,
4443 \\ Test2 => 2,
4444 \\ else => 3,
4445 \\ };
4446 \\}
4447 \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); }
4448 , &[_][]const u8{
4449 "tmp.zig:9:9: error: duplicate switch value",
4450 "tmp.zig:7:9: note: previous value is here",
4451 });
4452
43574453 cases.add("switch expression - switch on pointer type with no else",
43584454 \\fn foo(x: *u8) void {
43594455 \\ switch (x) {
......@@ -6004,10 +6100,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
60046100 });
60056101
60066102 cases.add("calling a generic function only known at runtime",
6007 \\var foos = [_]fn(var) void { foo1, foo2 };
6103 \\var foos = [_]fn(anytype) void { foo1, foo2 };
60086104 \\
6009 \\fn foo1(arg: var) void {}
6010 \\fn foo2(arg: var) void {}
6105 \\fn foo1(arg: anytype) void {}
6106 \\fn foo2(arg: anytype) void {}
60116107 \\
60126108 \\pub fn main() !void {
60136109 \\ foos[0](true);
......@@ -6852,12 +6948,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
68526948 });
68536949
68546950 cases.add("getting return type of generic function",
6855 \\fn generic(a: var) void {}
6951 \\fn generic(a: anytype) void {}
68566952 \\comptime {
68576953 \\ _ = @TypeOf(generic).ReturnType;
68586954 \\}
68596955 , &[_][]const u8{
6860 "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var) var' is generic",
6956 "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(anytype) anytype' is generic",
68616957 });
68626958
68636959 cases.add("unsupported modifier at start of asm output constraint",
......@@ -7425,7 +7521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
74257521 });
74267522
74277523 cases.add("issue #5221: invalid struct init type referenced by @typeInfo and passed into function",
7428 \\fn ignore(comptime param: var) void {}
7524 \\fn ignore(comptime param: anytype) void {}
74297525 \\
74307526 \\export fn foo() void {
74317527 \\ const MyStruct = struct {
......@@ -7522,4 +7618,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
75227618 , &[_][]const u8{
75237619 "tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only",
75247620 });
7621
7622 cases.add("Issue #5586: Make unary minus for unsigned types a compile error",
7623 \\export fn f(x: u32) u32 {
7624 \\ const y = -%x;
7625 \\ return -y;
7626 \\}
7627 , &[_][]const u8{
7628 "tmp.zig:3:12: error: negation of type 'u32'",
7629 });
7630
7631 cases.add("Issue #5618: coercion of ?*c_void to *c_void must fail.",
7632 \\export fn foo() void {
7633 \\ var u: ?*c_void = null;
7634 \\ var v: *c_void = undefined;
7635 \\ v = u;
7636 \\}
7637 , &[_][]const u8{
7638 "tmp.zig:4:9: error: expected type '*c_void', found '?*c_void'",
7639 });
75257640}
test/run_translated_c.zig+73-1
......@@ -268,5 +268,77 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
268268 \\ if (count != 4) abort();
269269 \\ return 0;
270270 \\}
271 ,"");
271 , "");
272
273 cases.add("array value type casts properly",
274 \\#include <stdlib.h>
275 \\unsigned int choose[53][10];
276 \\static int hash_binary(int k)
277 \\{
278 \\ choose[0][k] = 3;
279 \\ int sum = 0;
280 \\ sum += choose[0][k];
281 \\ return sum;
282 \\}
283 \\
284 \\int main() {
285 \\ int s = hash_binary(4);
286 \\ if (s != 3) abort();
287 \\ return 0;
288 \\}
289 , "");
290
291 cases.add("array value type casts properly use +=",
292 \\#include <stdlib.h>
293 \\static int hash_binary(int k)
294 \\{
295 \\ unsigned int choose[1][1] = {{3}};
296 \\ int sum = -1;
297 \\ int prev = 0;
298 \\ prev = sum += choose[0][0];
299 \\ if (sum != 2) abort();
300 \\ return sum + prev;
301 \\}
302 \\
303 \\int main() {
304 \\ int x = hash_binary(4);
305 \\ if (x != 4) abort();
306 \\ return 0;
307 \\}
308 , "");
309
310 cases.add("ensure array casts outisde +=",
311 \\#include <stdlib.h>
312 \\static int hash_binary(int k)
313 \\{
314 \\ unsigned int choose[3] = {1, 2, 3};
315 \\ int sum = -2;
316 \\ int prev = sum + choose[k];
317 \\ if (prev != 0) abort();
318 \\ return sum + prev;
319 \\}
320 \\
321 \\int main() {
322 \\ int x = hash_binary(1);
323 \\ if (x != -2) abort();
324 \\ return 0;
325 \\}
326 , "");
327
328 cases.add("array cast int to uint",
329 \\#include <stdlib.h>
330 \\static unsigned int hash_binary(int k)
331 \\{
332 \\ int choose[3] = {-1, -2, 3};
333 \\ unsigned int sum = 2;
334 \\ sum += choose[k];
335 \\ return sum;
336 \\}
337 \\
338 \\int main() {
339 \\ unsigned int x = hash_binary(1);
340 \\ if (x != 0) abort();
341 \\ return 0;
342 \\}
343 , "");
272344}
test/runtime_safety.zig+11-1
......@@ -280,7 +280,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
280280 \\pub fn main() void {
281281 \\ var bytes: [1]u8 align(16) = undefined;
282282 \\ var ptr = other;
283 \\ var frame = @asyncCall(&bytes, {}, ptr);
283 \\ var frame = @asyncCall(&bytes, {}, ptr, .{});
284284 \\}
285285 \\fn other() callconv(.Async) void {
286286 \\ suspend;
......@@ -757,6 +757,16 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
757757 \\}
758758 );
759759
760 cases.addRuntimeSafety("unsigned integer not fitting in cast to signed integer - same bit count",
761 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
762 \\ @import("std").os.exit(126);
763 \\}
764 \\pub fn main() void {
765 \\ var value: u8 = 245;
766 \\ var casted = @intCast(i8, value);
767 \\}
768 );
769
760770 cases.addRuntimeSafety("unwrap error",
761771 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
762772 \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
test/stage1/behavior.zig+3
......@@ -50,6 +50,7 @@ comptime {
5050 _ = @import("behavior/bugs/4769_b.zig");
5151 _ = @import("behavior/bugs/4769_c.zig");
5252 _ = @import("behavior/bugs/4954.zig");
53 _ = @import("behavior/bugs/5413.zig");
5354 _ = @import("behavior/bugs/5474.zig");
5455 _ = @import("behavior/bugs/5487.zig");
5556 _ = @import("behavior/bugs/394.zig");
......@@ -131,4 +132,6 @@ comptime {
131132 }
132133 _ = @import("behavior/while.zig");
133134 _ = @import("behavior/widening.zig");
135 _ = @import("behavior/src.zig");
136 _ = @import("behavior/translate_c_macros.zig");
134137}
test/stage1/behavior/async_fn.zig+18-18
......@@ -282,7 +282,7 @@ test "async fn pointer in a struct field" {
282282 };
283283 var foo = Foo{ .bar = simpleAsyncFn2 };
284284 var bytes: [64]u8 align(16) = undefined;
285 const f = @asyncCall(&bytes, {}, foo.bar, &data);
285 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
286286 comptime expect(@TypeOf(f) == anyframe->void);
287287 expect(data == 2);
288288 resume f;
......@@ -318,7 +318,7 @@ test "@asyncCall with return type" {
318318 var foo = Foo{ .bar = Foo.middle };
319319 var bytes: [150]u8 align(16) = undefined;
320320 var aresult: i32 = 0;
321 _ = @asyncCall(&bytes, &aresult, foo.bar);
321 _ = @asyncCall(&bytes, &aresult, foo.bar, .{});
322322 expect(aresult == 0);
323323 resume Foo.global_frame;
324324 expect(aresult == 1234);
......@@ -332,7 +332,7 @@ test "async fn with inferred error set" {
332332 var frame: [1]@Frame(middle) = undefined;
333333 var fn_ptr = middle;
334334 var result: @TypeOf(fn_ptr).ReturnType.ErrorSet!void = undefined;
335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr);
335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
336336 resume global_frame;
337337 std.testing.expectError(error.Fail, result);
338338 }
......@@ -827,7 +827,7 @@ test "cast fn to async fn when it is inferred to be async" {
827827 ptr = func;
828828 var buf: [100]u8 align(16) = undefined;
829829 var result: i32 = undefined;
830 const f = @asyncCall(&buf, &result, ptr);
830 const f = @asyncCall(&buf, &result, ptr, .{});
831831 _ = await f;
832832 expect(result == 1234);
833833 ok = true;
......@@ -855,7 +855,7 @@ test "cast fn to async fn when it is inferred to be async, awaited directly" {
855855 ptr = func;
856856 var buf: [100]u8 align(16) = undefined;
857857 var result: i32 = undefined;
858 _ = await @asyncCall(&buf, &result, ptr);
858 _ = await @asyncCall(&buf, &result, ptr, .{});
859859 expect(result == 1234);
860860 ok = true;
861861 }
......@@ -951,7 +951,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {
951951 fn doTheTest() void {
952952 var frame: [1]@Frame(middle) = undefined;
953953 var result: @TypeOf(middle).ReturnType.ErrorSet!void = undefined;
954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle);
954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
955955 resume global_frame;
956956 std.testing.expectError(error.Fail, result);
957957 }
......@@ -982,7 +982,7 @@ test "@asyncCall with actual frame instead of byte buffer" {
982982 };
983983 var frame: @Frame(S.func) = undefined;
984984 var result: i32 = undefined;
985 const ptr = @asyncCall(&frame, &result, S.func);
985 const ptr = @asyncCall(&frame, &result, S.func, .{});
986986 resume ptr;
987987 expect(result == 1234);
988988}
......@@ -1005,7 +1005,7 @@ test "@asyncCall using the result location inside the frame" {
10051005 };
10061006 var foo = Foo{ .bar = S.simple2 };
10071007 var bytes: [64]u8 align(16) = undefined;
1008 const f = @asyncCall(&bytes, {}, foo.bar, &data);
1008 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
10091009 comptime expect(@TypeOf(f) == anyframe->i32);
10101010 expect(data == 2);
10111011 resume f;
......@@ -1016,7 +1016,7 @@ test "@asyncCall using the result location inside the frame" {
10161016
10171017test "@TypeOf an async function call of generic fn with error union type" {
10181018 const S = struct {
1019 fn func(comptime x: var) anyerror!i32 {
1019 fn func(comptime x: anytype) anyerror!i32 {
10201020 const T = @TypeOf(async func(x));
10211021 comptime expect(T == @TypeOf(@frame()).Child);
10221022 return undefined;
......@@ -1032,7 +1032,7 @@ test "using @TypeOf on a generic function call" {
10321032
10331033 var buf: [100]u8 align(16) = undefined;
10341034
1035 fn amain(x: var) void {
1035 fn amain(x: anytype) void {
10361036 if (x == 0) {
10371037 global_ok = true;
10381038 return;
......@@ -1042,7 +1042,7 @@ test "using @TypeOf on a generic function call" {
10421042 }
10431043 const F = @TypeOf(async amain(x - 1));
10441044 const frame = @intToPtr(*F, @ptrToInt(&buf));
1045 return await @asyncCall(frame, {}, amain, x - 1);
1045 return await @asyncCall(frame, {}, amain, .{x - 1});
10461046 }
10471047 };
10481048 _ = async S.amain(@as(u32, 1));
......@@ -1057,7 +1057,7 @@ test "recursive call of await @asyncCall with struct return type" {
10571057
10581058 var buf: [100]u8 align(16) = undefined;
10591059
1060 fn amain(x: var) Foo {
1060 fn amain(x: anytype) Foo {
10611061 if (x == 0) {
10621062 global_ok = true;
10631063 return Foo{ .x = 1, .y = 2, .z = 3 };
......@@ -1067,7 +1067,7 @@ test "recursive call of await @asyncCall with struct return type" {
10671067 }
10681068 const F = @TypeOf(async amain(x - 1));
10691069 const frame = @intToPtr(*F, @ptrToInt(&buf));
1070 return await @asyncCall(frame, {}, amain, x - 1);
1070 return await @asyncCall(frame, {}, amain, .{x - 1});
10711071 }
10721072
10731073 const Foo = struct {
......@@ -1078,7 +1078,7 @@ test "recursive call of await @asyncCall with struct return type" {
10781078 };
10791079 var res: S.Foo = undefined;
10801080 var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined;
1081 _ = @asyncCall(&frame, &res, S.amain, @as(u32, 1));
1081 _ = @asyncCall(&frame, &res, S.amain, .{@as(u32, 1)});
10821082 resume S.global_frame;
10831083 expect(S.global_ok);
10841084 expect(res.x == 1);
......@@ -1336,7 +1336,7 @@ test "async function passed 0-bit arg after non-0-bit arg" {
13361336 bar(1, .{}) catch unreachable;
13371337 }
13381338
1339 fn bar(x: i32, args: var) anyerror!void {
1339 fn bar(x: i32, args: anytype) anyerror!void {
13401340 global_frame = @frame();
13411341 suspend;
13421342 global_int = x;
......@@ -1357,7 +1357,7 @@ test "async function passed align(16) arg after align(8) arg" {
13571357 bar(10, .{a}) catch unreachable;
13581358 }
13591359
1360 fn bar(x: u64, args: var) anyerror!void {
1360 fn bar(x: u64, args: anytype) anyerror!void {
13611361 expect(x == 10);
13621362 global_frame = @frame();
13631363 suspend;
......@@ -1377,7 +1377,7 @@ test "async function call resolves target fn frame, comptime func" {
13771377 fn foo() anyerror!void {
13781378 const stack_size = 1000;
13791379 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
1380 return await @asyncCall(&stack_frame, {}, bar);
1380 return await @asyncCall(&stack_frame, {}, bar, .{});
13811381 }
13821382
13831383 fn bar() anyerror!void {
......@@ -1400,7 +1400,7 @@ test "async function call resolves target fn frame, runtime func" {
14001400 const stack_size = 1000;
14011401 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
14021402 var func: fn () callconv(.Async) anyerror!void = bar;
1403 return await @asyncCall(&stack_frame, {}, func);
1403 return await @asyncCall(&stack_frame, {}, func, .{});
14041404 }
14051405
14061406 fn bar() anyerror!void {
test/stage1/behavior/bitcast.zig+2-2
......@@ -171,7 +171,7 @@ test "nested bitcast" {
171171
172172test "bitcast passed as tuple element" {
173173 const S = struct {
174 fn foo(args: var) void {
174 fn foo(args: anytype) void {
175175 comptime expect(@TypeOf(args[0]) == f32);
176176 expect(args[0] == 12.34);
177177 }
......@@ -181,7 +181,7 @@ test "bitcast passed as tuple element" {
181181
182182test "triple level result location with bitcast sandwich passed as tuple element" {
183183 const S = struct {
184 fn foo(args: var) void {
184 fn foo(args: anytype) void {
185185 comptime expect(@TypeOf(args[0]) == f64);
186186 expect(args[0] > 12.33 and args[0] < 12.35);
187187 }
test/stage1/behavior/bugs/2114.zig+1-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22const expect = std.testing.expect;
33const math = std.math;
44
5fn ctz(x: var) usize {
5fn ctz(x: anytype) usize {
66 return @ctz(@TypeOf(x), x);
77}
88
test/stage1/behavior/bugs/3742.zig+1-1
......@@ -23,7 +23,7 @@ pub fn isCommand(comptime T: type) bool {
2323}
2424
2525pub const ArgSerializer = struct {
26 pub fn serializeCommand(command: var) void {
26 pub fn serializeCommand(command: anytype) void {
2727 const CmdT = @TypeOf(command);
2828
2929 if (comptime isCommand(CmdT)) {
test/stage1/behavior/bugs/4328.zig+4-4
......@@ -17,11 +17,11 @@ const S = extern struct {
1717
1818test "Extern function calls in @TypeOf" {
1919 const Test = struct {
20 fn test_fn_1(a: var, b: var) @TypeOf(printf("%d %s\n", a, b)) {
20 fn test_fn_1(a: anytype, b: anytype) @TypeOf(printf("%d %s\n", a, b)) {
2121 return 0;
2222 }
2323
24 fn test_fn_2(a: var) @TypeOf((S{ .state = 0 }).s_do_thing(a)) {
24 fn test_fn_2(a: anytype) @TypeOf((S{ .state = 0 }).s_do_thing(a)) {
2525 return 1;
2626 }
2727
......@@ -56,7 +56,7 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
5656 return .{ .dummy_field = 0 };
5757 }
5858
59 fn test_fn_2(a: var) @TypeOf(fopen("test", "r").*.dummy_field) {
59 fn test_fn_2(a: anytype) @TypeOf(fopen("test", "r").*.dummy_field) {
6060 return 255;
6161 }
6262
......@@ -68,4 +68,4 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
6868
6969 Test.doTheTest();
7070 comptime Test.doTheTest();
71}
\ No newline at end of file
71}
test/stage1/behavior/bugs/5413.zig created+6
......@@ -0,0 +1,6 @@
1const expect = @import("std").testing.expect;
2
3test "Peer type resolution with string literals and unknown length u8 pointers" {
4 expect(@TypeOf("", "a", @as([*:0]const u8, "")) == [*:0]const u8);
5 expect(@TypeOf(@as([*:0]const u8, "baz"), "foo", "bar") == [*:0]const u8);
6}
test/stage1/behavior/byval_arg_var.zig+2-2
......@@ -13,11 +13,11 @@ fn start() void {
1313 foo("string literal");
1414}
1515
16fn foo(x: var) void {
16fn foo(x: anytype) void {
1717 bar(x);
1818}
1919
20fn bar(x: var) void {
20fn bar(x: anytype) void {
2121 result = x;
2222}
2323
test/stage1/behavior/call.zig+1-1
......@@ -57,7 +57,7 @@ test "tuple parameters" {
5757
5858test "comptime call with bound function as parameter" {
5959 const S = struct {
60 fn ReturnType(func: var) type {
60 fn ReturnType(func: anytype) type {
6161 return switch (@typeInfo(@TypeOf(func))) {
6262 .BoundFn => |info| info,
6363 else => unreachable,
test/stage1/behavior/cast.zig+13
......@@ -384,6 +384,19 @@ test "@intCast i32 to u7" {
384384 expect(z == 0xff);
385385}
386386
387test "@floatCast cast down" {
388 {
389 var double: f64 = 0.001534;
390 var single = @floatCast(f32, double);
391 expect(single == 0.001534);
392 }
393 {
394 const double: f64 = 0.001534;
395 const single = @floatCast(f32, double);
396 expect(single == 0.001534);
397 }
398}
399
387400test "implicit cast undefined to optional" {
388401 expect(MakeType(void).getNull() == null);
389402 expect(MakeType(void).getNonNull() != null);
test/stage1/behavior/enum.zig+25-1
......@@ -208,7 +208,7 @@ test "@tagName non-exhaustive enum" {
208208 comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
209209}
210210
211fn testEnumTagNameBare(n: var) []const u8 {
211fn testEnumTagNameBare(n: anytype) []const u8 {
212212 return @tagName(n);
213213}
214214
......@@ -1140,3 +1140,27 @@ test "tagName on enum literals" {
11401140 expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
11411141 comptime expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
11421142}
1143
1144test "method call on an enum" {
1145 const S = struct {
1146 const E = enum {
1147 one,
1148 two,
1149
1150 fn method(self: *E) bool {
1151 return self.* == .two;
1152 }
1153
1154 fn generic_method(self: *E, foo: anytype) bool {
1155 return self.* == .two and foo == bool;
1156 }
1157 };
1158 fn doTheTest() void {
1159 var e = E.two;
1160 expect(e.method());
1161 expect(e.generic_method(bool));
1162 }
1163 };
1164 S.doTheTest();
1165 comptime S.doTheTest();
1166}
test/stage1/behavior/error.zig+1-1
......@@ -227,7 +227,7 @@ test "error: Infer error set from literals" {
227227 _ = comptime intLiteral("n") catch |err| handleErrors(err);
228228}
229229
230fn handleErrors(err: var) noreturn {
230fn handleErrors(err: anytype) noreturn {
231231 switch (err) {
232232 error.T => {},
233233 }
test/stage1/behavior/eval.zig+5-6
......@@ -670,10 +670,10 @@ fn loopNTimes(comptime n: usize) void {
670670}
671671
672672test "variable inside inline loop that has different types on different iterations" {
673 testVarInsideInlineLoop(.{true, @as(u32, 42)});
673 testVarInsideInlineLoop(.{ true, @as(u32, 42) });
674674}
675675
676fn testVarInsideInlineLoop(args: var) void {
676fn testVarInsideInlineLoop(args: anytype) void {
677677 comptime var i = 0;
678678 inline while (i < args.len) : (i += 1) {
679679 const x = args[i];
......@@ -758,7 +758,7 @@ test "comptime bitwise operators" {
758758test "*align(1) u16 is the same as *align(1:0:2) u16" {
759759 comptime {
760760 expect(*align(1:0:2) u16 == *align(1) u16);
761 expect(*align(:0:2) u16 == *u16);
761 expect(*align(2:0:2) u16 == *u16);
762762 }
763763}
764764
......@@ -814,17 +814,16 @@ test "two comptime calls with array default initialized to undefined" {
814814 dynamic_linker: DynamicLinker = DynamicLinker{},
815815
816816 pub fn parse() void {
817 var result: CrossTarget = .{ };
817 var result: CrossTarget = .{};
818818 result.getCpuArch();
819819 }
820820
821 pub fn getCpuArch(self: CrossTarget) void { }
821 pub fn getCpuArch(self: CrossTarget) void {}
822822 };
823823
824824 const DynamicLinker = struct {
825825 buffer: [255]u8 = undefined,
826826 };
827
828827 };
829828
830829 comptime {
test/stage1/behavior/fn.zig+3-3
......@@ -104,7 +104,7 @@ test "number literal as an argument" {
104104 comptime numberLiteralArg(3);
105105}
106106
107fn numberLiteralArg(a: var) void {
107fn numberLiteralArg(a: anytype) void {
108108 expect(a == 3);
109109}
110110
......@@ -132,7 +132,7 @@ test "pass by non-copying value through var arg" {
132132 expect(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3);
133133}
134134
135fn addPointCoordsVar(pt: var) i32 {
135fn addPointCoordsVar(pt: anytype) i32 {
136136 comptime expect(@TypeOf(pt) == Point);
137137 return pt.x + pt.y;
138138}
......@@ -267,7 +267,7 @@ test "ability to give comptime types and non comptime types to same parameter" {
267267 expect(foo(i32) == 20);
268268 }
269269
270 fn foo(arg: var) i32 {
270 fn foo(arg: anytype) i32 {
271271 if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
272272 return 9 + arg;
273273 }
test/stage1/behavior/generics.zig+4-4
......@@ -47,7 +47,7 @@ comptime {
4747 expect(max_f64(1.2, 3.4) == 3.4);
4848}
4949
50fn max_var(a: var, b: var) @TypeOf(a + b) {
50fn max_var(a: anytype, b: anytype) @TypeOf(a + b) {
5151 return if (a > b) a else b;
5252}
5353
......@@ -133,15 +133,15 @@ fn getFirstByte(comptime T: type, mem: []const T) u8 {
133133 return getByte(@ptrCast(*const u8, &mem[0]));
134134}
135135
136const foos = [_]fn (var) bool{
136const foos = [_]fn (anytype) bool{
137137 foo1,
138138 foo2,
139139};
140140
141fn foo1(arg: var) bool {
141fn foo1(arg: anytype) bool {
142142 return arg;
143143}
144fn foo2(arg: var) bool {
144fn foo2(arg: anytype) bool {
145145 return !arg;
146146}
147147
test/stage1/behavior/math.zig+122
......@@ -634,6 +634,128 @@ fn testSqrt(comptime T: type, x: T) void {
634634 expect(@sqrt(x * x) == x);
635635}
636636
637test "@fabs" {
638 testFabs(f128, 12.0);
639 comptime testFabs(f128, 12.0);
640 testFabs(f64, 12.0);
641 comptime testFabs(f64, 12.0);
642 testFabs(f32, 12.0);
643 comptime testFabs(f32, 12.0);
644 testFabs(f16, 12.0);
645 comptime testFabs(f16, 12.0);
646
647 const x = 14.0;
648 const y = -x;
649 const z = @fabs(y);
650 comptime expectEqual(x, z);
651}
652
653fn testFabs(comptime T: type, x: T) void {
654 const y = -x;
655 const z = @fabs(y);
656 expectEqual(x, z);
657}
658
659test "@floor" {
660 // FIXME: Generates a floorl function call
661 // testFloor(f128, 12.0);
662 comptime testFloor(f128, 12.0);
663 testFloor(f64, 12.0);
664 comptime testFloor(f64, 12.0);
665 testFloor(f32, 12.0);
666 comptime testFloor(f32, 12.0);
667 testFloor(f16, 12.0);
668 comptime testFloor(f16, 12.0);
669
670 const x = 14.0;
671 const y = x + 0.7;
672 const z = @floor(y);
673 comptime expectEqual(x, z);
674}
675
676fn testFloor(comptime T: type, x: T) void {
677 const y = x + 0.6;
678 const z = @floor(y);
679 expectEqual(x, z);
680}
681
682test "@ceil" {
683 // FIXME: Generates a ceill function call
684 //testCeil(f128, 12.0);
685 comptime testCeil(f128, 12.0);
686 testCeil(f64, 12.0);
687 comptime testCeil(f64, 12.0);
688 testCeil(f32, 12.0);
689 comptime testCeil(f32, 12.0);
690 testCeil(f16, 12.0);
691 comptime testCeil(f16, 12.0);
692
693 const x = 14.0;
694 const y = x - 0.7;
695 const z = @ceil(y);
696 comptime expectEqual(x, z);
697}
698
699fn testCeil(comptime T: type, x: T) void {
700 const y = x - 0.8;
701 const z = @ceil(y);
702 expectEqual(x, z);
703}
704
705test "@trunc" {
706 // FIXME: Generates a truncl function call
707 //testTrunc(f128, 12.0);
708 comptime testTrunc(f128, 12.0);
709 testTrunc(f64, 12.0);
710 comptime testTrunc(f64, 12.0);
711 testTrunc(f32, 12.0);
712 comptime testTrunc(f32, 12.0);
713 testTrunc(f16, 12.0);
714 comptime testTrunc(f16, 12.0);
715
716 const x = 14.0;
717 const y = x + 0.7;
718 const z = @trunc(y);
719 comptime expectEqual(x, z);
720}
721
722fn testTrunc(comptime T: type, x: T) void {
723 {
724 const y = x + 0.8;
725 const z = @trunc(y);
726 expectEqual(x, z);
727 }
728
729 {
730 const y = -x - 0.8;
731 const z = @trunc(y);
732 expectEqual(-x, z);
733 }
734}
735
736test "@round" {
737 // FIXME: Generates a roundl function call
738 //testRound(f128, 12.0);
739 comptime testRound(f128, 12.0);
740 testRound(f64, 12.0);
741 comptime testRound(f64, 12.0);
742 testRound(f32, 12.0);
743 comptime testRound(f32, 12.0);
744 testRound(f16, 12.0);
745 comptime testRound(f16, 12.0);
746
747 const x = 14.0;
748 const y = x + 0.4;
749 const z = @round(y);
750 comptime expectEqual(x, z);
751}
752
753fn testRound(comptime T: type, x: T) void {
754 const y = x - 0.5;
755 const z = @round(y);
756 expectEqual(x, z);
757}
758
637759test "comptime_int param and return" {
638760 const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
639761 expect(a == 137114567242441932203689521744947848950);
test/stage1/behavior/misc.zig+7
......@@ -713,3 +713,10 @@ test "auto created variables have correct alignment" {
713713 expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);
714714 comptime expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);
715715}
716
717extern var opaque_extern_var: @Type(.Opaque);
718var var_to_export: u32 = 42;
719test "extern variable with non-pointer opaque type" {
720 @export(var_to_export, .{ .name = "opaque_extern_var" });
721 expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);
722}
test/stage1/behavior/optional.zig+14-2
......@@ -67,8 +67,20 @@ fn test_cmp_optional_non_optional() void {
6767 // test evaluation is always lexical
6868 // ensure that the optional isn't always computed before the non-optional
6969 var mutable_state: i32 = 0;
70 _ = blk1: { mutable_state += 1; break :blk1 @as(?f64, 10.0); } != blk2: { expect(mutable_state == 1); break :blk2 @as(f64, 5.0); };
71 _ = blk1: { mutable_state += 1; break :blk1 @as(f64, 10.0); } != blk2: { expect(mutable_state == 2); break :blk2 @as(?f64, 5.0); };
70 _ = blk1: {
71 mutable_state += 1;
72 break :blk1 @as(?f64, 10.0);
73 } != blk2: {
74 expect(mutable_state == 1);
75 break :blk2 @as(f64, 5.0);
76 };
77 _ = blk1: {
78 mutable_state += 1;
79 break :blk1 @as(f64, 10.0);
80 } != blk2: {
81 expect(mutable_state == 2);
82 break :blk2 @as(?f64, 5.0);
83 };
7284}
7385
7486test "passing an optional integer as a parameter" {
test/stage1/behavior/slice.zig+5
......@@ -280,6 +280,11 @@ test "slice syntax resulting in pointer-to-array" {
280280 expect(slice[0] == 5);
281281 comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);
282282 }
283
284 fn testConcatStrLiterals() void {
285 expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
286 expectEqualSlices("a"[0..:0] ++ "b"[0..:0], "ab");
287 }
283288 };
284289
285290 S.doTheTest();
test/stage1/behavior/src.zig created+17
......@@ -0,0 +1,17 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "@src" {
5 doTheTest();
6}
7
8fn doTheTest() void {
9 const src = @src();
10
11 expect(src.line == 9);
12 expect(src.column == 17);
13 expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
14 expect(std.mem.endsWith(u8, src.file, "src.zig"));
15 expect(src.fn_name[src.fn_name.len] == 0);
16 expect(src.file[src.file.len] == 0);
17}
test/stage1/behavior/struct.zig+38-5
......@@ -713,7 +713,7 @@ test "packed struct field passed to generic function" {
713713 a: u1,
714714 };
715715
716 fn genericReadPackedField(ptr: var) u5 {
716 fn genericReadPackedField(ptr: anytype) u5 {
717717 return ptr.*;
718718 }
719719 };
......@@ -754,7 +754,7 @@ test "fully anonymous struct" {
754754 .s = "hi",
755755 });
756756 }
757 fn dump(args: var) void {
757 fn dump(args: anytype) void {
758758 expect(args.int == 1234);
759759 expect(args.float == 12.34);
760760 expect(args.b);
......@@ -771,7 +771,7 @@ test "fully anonymous list literal" {
771771 fn doTheTest() void {
772772 dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" });
773773 }
774 fn dump(args: var) void {
774 fn dump(args: anytype) void {
775775 expect(args.@"0" == 1234);
776776 expect(args.@"1" == 12.34);
777777 expect(args.@"2");
......@@ -792,8 +792,8 @@ test "anonymous struct literal assigned to variable" {
792792
793793test "struct with var field" {
794794 const Point = struct {
795 x: var,
796 y: var,
795 x: anytype,
796 y: anytype,
797797 };
798798 const pt = Point{
799799 .x = 1,
......@@ -851,3 +851,36 @@ test "struct with union field" {
851851 expectEqual(@as(u32, 2), True.ref);
852852 expectEqual(true, True.kind.Bool);
853853}
854
855test "type coercion of anon struct literal to struct" {
856 const S = struct {
857 const S2 = struct {
858 A: u32,
859 B: []const u8,
860 C: void,
861 D: Foo = .{},
862 };
863
864 const Foo = struct {
865 field: i32 = 1234,
866 };
867
868 fn doTheTest() void {
869 var y: u32 = 42;
870 const t0 = .{ .A = 123, .B = "foo", .C = {} };
871 const t1 = .{ .A = y, .B = "foo", .C = {} };
872 const y0: S2 = t0;
873 var y1: S2 = t1;
874 expect(y0.A == 123);
875 expect(std.mem.eql(u8, y0.B, "foo"));
876 expect(y0.C == {});
877 expect(y0.D.field == 1234);
878 expect(y1.A == y);
879 expect(std.mem.eql(u8, y1.B, "foo"));
880 expect(y1.C == {});
881 expect(y1.D.field == 1234);
882 }
883 };
884 S.doTheTest();
885 comptime S.doTheTest();
886}
test/stage1/behavior/translate_c_macros.h created+9
......@@ -0,0 +1,9 @@
1// initializer list expression
2typedef struct Color {
3 unsigned char r;
4 unsigned char g;
5 unsigned char b;
6 unsigned char a;
7} Color;
8#define CLITERAL(type) (type)
9#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
\ No newline at end of file
test/stage1/behavior/translate_c_macros.zig created+12
......@@ -0,0 +1,12 @@
1const expect = @import("std").testing.expect;
2
3const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h"));
4
5test "initializer list expression" {
6 @import("std").testing.expectEqual(h.Color{
7 .r = 200,
8 .g = 200,
9 .b = 200,
10 .a = 255,
11 }, h.LIGHTGRAY);
12}
test/stage1/behavior/tuple.zig+2-2
......@@ -42,7 +42,7 @@ test "tuple multiplication" {
4242 comptime S.doTheTest();
4343
4444 const T = struct {
45 fn consume_tuple(tuple: var, len: usize) void {
45 fn consume_tuple(tuple: anytype, len: usize) void {
4646 expect(tuple.len == len);
4747 }
4848
......@@ -82,7 +82,7 @@ test "tuple multiplication" {
8282
8383test "pass tuple to comptime var parameter" {
8484 const S = struct {
85 fn Foo(comptime args: var) void {
85 fn Foo(comptime args: anytype) void {
8686 expect(args[0] == 1);
8787 }
8888
test/stage1/behavior/type.zig+23
......@@ -213,3 +213,26 @@ test "Type.AnyFrame" {
213213 anyframe->anyframe->u8,
214214 });
215215}
216
217test "Type.EnumLiteral" {
218 testTypes(&[_]type{
219 @TypeOf(.Dummy),
220 });
221}
222
223fn add(a: i32, b: i32) i32 {
224 return a + b;
225}
226
227test "Type.Frame" {
228 testTypes(&[_]type{
229 @Frame(add),
230 });
231}
232
233test "Type.ErrorSet" {
234 // error sets don't compare equal so just check if they compile
235 _ = @Type(@typeInfo(error{}));
236 _ = @Type(@typeInfo(error{A}));
237 _ = @Type(@typeInfo(error{ A, B, C }));
238}
test/stage1/behavior/type_info.zig+46-5
......@@ -202,7 +202,7 @@ fn testUnion() void {
202202 expect(typeinfo_info.Union.fields[4].enum_field != null);
203203 expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
204204 expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
205 expect(typeinfo_info.Union.decls.len == 20);
205 expect(typeinfo_info.Union.decls.len == 21);
206206
207207 const TestNoTagUnion = union {
208208 Foo: void,
......@@ -251,14 +251,13 @@ fn testStruct() void {
251251}
252252
253253const TestStruct = packed struct {
254 const Self = @This();
255
256254 fieldA: usize,
257255 fieldB: void,
258256 fieldC: *Self,
259257 fieldD: u32 = 4,
260258
261259 pub fn foo(self: *const Self) void {}
260 const Self = @This();
262261};
263262
264263test "type info: function type info" {
......@@ -281,7 +280,7 @@ fn testFunction() void {
281280 expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
282281}
283282
284extern fn foo(a: usize, b: bool, args: ...) usize;
283extern fn foo(a: usize, b: bool, ...) usize;
285284
286285test "typeInfo with comptime parameter in struct fn def" {
287286 const S = struct {
......@@ -386,6 +385,48 @@ test "@typeInfo does not force declarations into existence" {
386385}
387386
388387test "defaut value for a var-typed field" {
389 const S = struct { x: var };
388 const S = struct { x: anytype };
390389 expect(@typeInfo(S).Struct.fields[0].default_value == null);
391390}
391
392fn add(a: i32, b: i32) i32 {
393 return a + b;
394}
395
396test "type info for async frames" {
397 switch (@typeInfo(@Frame(add))) {
398 .Frame => |frame| {
399 expect(frame.function == add);
400 },
401 else => unreachable,
402 }
403}
404
405test "type info: value is correctly copied" {
406 comptime {
407 var ptrInfo = @typeInfo([]u32);
408 ptrInfo.Pointer.size = .One;
409 expect(@typeInfo([]u32).Pointer.size == .Slice);
410 }
411}
412
413test "Declarations are returned in declaration order" {
414 const S = struct {
415 const a = 1;
416 const b = 2;
417 const c = 3;
418 const d = 4;
419 const e = 5;
420 };
421 const d = @typeInfo(S).Struct.decls;
422 expect(std.mem.eql(u8, d[0].name, "a"));
423 expect(std.mem.eql(u8, d[1].name, "b"));
424 expect(std.mem.eql(u8, d[2].name, "c"));
425 expect(std.mem.eql(u8, d[3].name, "d"));
426 expect(std.mem.eql(u8, d[4].name, "e"));
427}
428
429test "Struct.is_tuple" {
430 expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple);
431 expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple);
432}
test/stage1/behavior/union.zig+22-1
......@@ -296,7 +296,7 @@ const TaggedUnionWithAVoid = union(enum) {
296296 B: i32,
297297};
298298
299fn testTaggedUnionInit(x: var) bool {
299fn testTaggedUnionInit(x: anytype) bool {
300300 const y = TaggedUnionWithAVoid{ .A = x };
301301 return @as(@TagType(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
302302}
......@@ -669,3 +669,24 @@ test "cast from anonymous struct to union" {
669669 S.doTheTest();
670670 comptime S.doTheTest();
671671}
672
673test "method call on an empty union" {
674 const S = struct {
675 const MyUnion = union(Tag) {
676 pub const Tag = enum { X1, X2 };
677 X1: [0]u8,
678 X2: [0]u8,
679
680 pub fn useIt(self: *@This()) bool {
681 return true;
682 }
683 };
684
685 fn doTheTest() void {
686 var u = MyUnion{ .X1 = [0]u8{} };
687 expect(u.useIt());
688 }
689 };
690 S.doTheTest();
691 comptime S.doTheTest();
692}
test/stage1/behavior/var_args.zig+8-8
......@@ -1,6 +1,6 @@
11const expect = @import("std").testing.expect;
22
3fn add(args: var) i32 {
3fn add(args: anytype) i32 {
44 var sum = @as(i32, 0);
55 {
66 comptime var i: usize = 0;
......@@ -17,7 +17,7 @@ test "add arbitrary args" {
1717 expect(add(.{}) == 0);
1818}
1919
20fn readFirstVarArg(args: var) void {
20fn readFirstVarArg(args: anytype) void {
2121 const value = args[0];
2222}
2323
......@@ -31,7 +31,7 @@ test "pass args directly" {
3131 expect(addSomeStuff(.{}) == 0);
3232}
3333
34fn addSomeStuff(args: var) i32 {
34fn addSomeStuff(args: anytype) i32 {
3535 return add(args);
3636}
3737
......@@ -47,7 +47,7 @@ test "runtime parameter before var args" {
4747 }
4848}
4949
50fn extraFn(extra: u32, args: var) usize {
50fn extraFn(extra: u32, args: anytype) usize {
5151 if (args.len >= 1) {
5252 expect(args[0] == false);
5353 }
......@@ -57,15 +57,15 @@ fn extraFn(extra: u32, args: var) usize {
5757 return args.len;
5858}
5959
60const foos = [_]fn (var) bool{
60const foos = [_]fn (anytype) bool{
6161 foo1,
6262 foo2,
6363};
6464
65fn foo1(args: var) bool {
65fn foo1(args: anytype) bool {
6666 return true;
6767}
68fn foo2(args: var) bool {
68fn foo2(args: anytype) bool {
6969 return false;
7070}
7171
......@@ -78,6 +78,6 @@ test "pass zero length array to var args param" {
7878 doNothingWithFirstArg(.{""});
7979}
8080
81fn doNothingWithFirstArg(args: var) void {
81fn doNothingWithFirstArg(args: anytype) void {
8282 const a = args[0];
8383}
test/stage1/behavior/vector.zig+4-4
......@@ -171,7 +171,7 @@ test "load vector elements via comptime index" {
171171 expect(v[1] == 2);
172172 expect(loadv(&v[2]) == 3);
173173 }
174 fn loadv(ptr: var) i32 {
174 fn loadv(ptr: anytype) i32 {
175175 return ptr.*;
176176 }
177177 };
......@@ -194,7 +194,7 @@ test "store vector elements via comptime index" {
194194 storev(&v[0], 100);
195195 expect(v[0] == 100);
196196 }
197 fn storev(ptr: var, x: i32) void {
197 fn storev(ptr: anytype, x: i32) void {
198198 ptr.* = x;
199199 }
200200 };
......@@ -392,7 +392,7 @@ test "vector shift operators" {
392392 if (builtin.os.tag == .wasi) return error.SkipZigTest;
393393
394394 const S = struct {
395 fn doTheTestShift(x: var, y: var) void {
395 fn doTheTestShift(x: anytype, y: anytype) void {
396396 const N = @typeInfo(@TypeOf(x)).Array.len;
397397 const TX = @typeInfo(@TypeOf(x)).Array.child;
398398 const TY = @typeInfo(@TypeOf(y)).Array.child;
......@@ -409,7 +409,7 @@ test "vector shift operators" {
409409 expectEqual(x[i] << y[i], v);
410410 }
411411 }
412 fn doTheTestShiftExact(x: var, y: var, dir: enum { Left, Right }) void {
412 fn doTheTestShiftExact(x: anytype, y: anytype, dir: enum { Left, Right }) void {
413413 const N = @typeInfo(@TypeOf(x)).Array.len;
414414 const TX = @typeInfo(@TypeOf(x)).Array.child;
415415 const TY = @typeInfo(@TypeOf(y)).Array.child;
test/stage2/cbe.zig created+91
......@@ -0,0 +1,91 @@
1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3
4// These tests should work with all platforms, but we're using linux_x64 for
5// now for consistency. Will be expanded eventually.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
10
11pub fn addCases(ctx: *TestContext) !void {
12 ctx.c("empty start function", linux_x64,
13 \\export fn _start() noreturn {}
14 ,
15 \\noreturn void _start(void) {}
16 \\
17 );
18 ctx.c("less empty start function", linux_x64,
19 \\fn main() noreturn {}
20 \\
21 \\export fn _start() noreturn {
22 \\ main();
23 \\}
24 ,
25 \\noreturn void main(void);
26 \\
27 \\noreturn void _start(void) {
28 \\ main();
29 \\}
30 \\
31 \\noreturn void main(void) {}
32 \\
33 );
34 // TODO: implement return values
35 // TODO: figure out a way to prevent asm constants from being generated
36 ctx.c("inline asm", linux_x64,
37 \\fn exitGood() void {
38 \\ asm volatile ("syscall"
39 \\ :
40 \\ : [number] "{rax}" (231),
41 \\ [arg1] "{rdi}" (0)
42 \\ );
43 \\}
44 \\
45 \\export fn _start() noreturn {
46 \\ exitGood();
47 \\}
48 ,
49 \\#include <stddef.h>
50 \\
51 \\void exitGood(void);
52 \\
53 \\const char *const exitGood__anon_0 = "{rax}";
54 \\const char *const exitGood__anon_1 = "{rdi}";
55 \\const char *const exitGood__anon_2 = "syscall";
56 \\
57 \\noreturn void _start(void) {
58 \\ exitGood();
59 \\}
60 \\
61 \\void exitGood(void) {
62 \\ register size_t rax_constant __asm__("rax") = 231;
63 \\ register size_t rdi_constant __asm__("rdi") = 0;
64 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
65 \\ return;
66 \\}
67 \\
68 );
69 ctx.c("basic return", linux_x64,
70 \\fn main() u8 {
71 \\ return 103;
72 \\}
73 \\
74 \\export fn _start() noreturn {
75 \\ _ = main();
76 \\}
77 ,
78 \\#include <stdint.h>
79 \\
80 \\uint8_t main(void);
81 \\
82 \\noreturn void _start(void) {
83 \\ (void)main();
84 \\}
85 \\
86 \\uint8_t main(void) {
87 \\ return 103;
88 \\}
89 \\
90 );
91}
test/stage2/compare_output.zig+223-21
......@@ -1,28 +1,230 @@
11const std = @import("std");
22const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3// self-hosted does not yet support PE executable files / COFF object files
4// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
5const linux_x64 = std.zig.CrossTarget{
6 .cpu_arch = .x86_64,
7 .os_tag = .linux,
8};
39
410pub fn addCases(ctx: *TestContext) !void {
5 // TODO: re-enable these tests.
6 // https://github.com/ziglang/zig/issues/1364
11 if (std.Target.current.os.tag != .linux or
12 std.Target.current.cpu.arch != .x86_64)
13 {
14 // TODO implement self-hosted PE (.exe file) linking
15 // TODO implement more ZIR so we don't depend on x86_64-linux
16 return;
17 }
718
8 //// hello world
9 //try ctx.testCompareOutputLibC(
10 // \\extern fn puts([*]const u8) void;
11 // \\pub export fn main() c_int {
12 // \\ puts("Hello, world!");
13 // \\ return 0;
14 // \\}
15 //, "Hello, world!" ++ std.cstr.line_sep);
19 {
20 var case = ctx.exe("hello world with updates", linux_x64);
21 // Regular old hello world
22 case.addCompareOutput(
23 \\export fn _start() noreturn {
24 \\ print();
25 \\
26 \\ exit();
27 \\}
28 \\
29 \\fn print() void {
30 \\ asm volatile ("syscall"
31 \\ :
32 \\ : [number] "{rax}" (1),
33 \\ [arg1] "{rdi}" (1),
34 \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
35 \\ [arg3] "{rdx}" (14)
36 \\ : "rcx", "r11", "memory"
37 \\ );
38 \\ return;
39 \\}
40 \\
41 \\fn exit() noreturn {
42 \\ asm volatile ("syscall"
43 \\ :
44 \\ : [number] "{rax}" (231),
45 \\ [arg1] "{rdi}" (0)
46 \\ : "rcx", "r11", "memory"
47 \\ );
48 \\ unreachable;
49 \\}
50 ,
51 "Hello, World!\n",
52 );
53 // Now change the message only
54 case.addCompareOutput(
55 \\export fn _start() noreturn {
56 \\ print();
57 \\
58 \\ exit();
59 \\}
60 \\
61 \\fn print() void {
62 \\ asm volatile ("syscall"
63 \\ :
64 \\ : [number] "{rax}" (1),
65 \\ [arg1] "{rdi}" (1),
66 \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
67 \\ [arg3] "{rdx}" (104)
68 \\ : "rcx", "r11", "memory"
69 \\ );
70 \\ return;
71 \\}
72 \\
73 \\fn exit() noreturn {
74 \\ asm volatile ("syscall"
75 \\ :
76 \\ : [number] "{rax}" (231),
77 \\ [arg1] "{rdi}" (0)
78 \\ : "rcx", "r11", "memory"
79 \\ );
80 \\ unreachable;
81 \\}
82 ,
83 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
84 );
85 // Now we print it twice.
86 case.addCompareOutput(
87 \\export fn _start() noreturn {
88 \\ print();
89 \\ print();
90 \\
91 \\ exit();
92 \\}
93 \\
94 \\fn print() void {
95 \\ asm volatile ("syscall"
96 \\ :
97 \\ : [number] "{rax}" (1),
98 \\ [arg1] "{rdi}" (1),
99 \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
100 \\ [arg3] "{rdx}" (104)
101 \\ : "rcx", "r11", "memory"
102 \\ );
103 \\ return;
104 \\}
105 \\
106 \\fn exit() noreturn {
107 \\ asm volatile ("syscall"
108 \\ :
109 \\ : [number] "{rax}" (231),
110 \\ [arg1] "{rdi}" (0)
111 \\ : "rcx", "r11", "memory"
112 \\ );
113 \\ unreachable;
114 \\}
115 ,
116 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
117 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
118 \\
119 );
120 }
16121
17 //// function calling another function
18 //try ctx.testCompareOutputLibC(
19 // \\extern fn puts(s: [*]const u8) void;
20 // \\pub export fn main() c_int {
21 // \\ return foo("OK");
22 // \\}
23 // \\fn foo(s: [*]const u8) c_int {
24 // \\ puts(s);
25 // \\ return 0;
26 // \\}
27 //, "OK" ++ std.cstr.line_sep);
122 {
123 var case = ctx.exe("adding numbers at comptime", linux_x64);
124 case.addCompareOutput(
125 \\export fn _start() noreturn {
126 \\ asm volatile ("syscall"
127 \\ :
128 \\ : [number] "{rax}" (1),
129 \\ [arg1] "{rdi}" (1),
130 \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
131 \\ [arg3] "{rdx}" (10 + 4)
132 \\ : "rcx", "r11", "memory"
133 \\ );
134 \\ asm volatile ("syscall"
135 \\ :
136 \\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)),
137 \\ [arg1] "{rdi}" (0)
138 \\ : "rcx", "r11", "memory"
139 \\ );
140 \\ unreachable;
141 \\}
142 ,
143 "Hello, World!\n",
144 );
145 }
146
147 {
148 var case = ctx.exe("adding numbers at runtime", linux_x64);
149 case.addCompareOutput(
150 \\export fn _start() noreturn {
151 \\ add(3, 4);
152 \\
153 \\ exit();
154 \\}
155 \\
156 \\fn add(a: u32, b: u32) void {
157 \\ if (a + b != 7) unreachable;
158 \\}
159 \\
160 \\fn exit() noreturn {
161 \\ asm volatile ("syscall"
162 \\ :
163 \\ : [number] "{rax}" (231),
164 \\ [arg1] "{rdi}" (0)
165 \\ : "rcx", "r11", "memory"
166 \\ );
167 \\ unreachable;
168 \\}
169 ,
170 "",
171 );
172 }
173 {
174 var case = ctx.exe("assert function", linux_x64);
175 case.addCompareOutput(
176 \\export fn _start() noreturn {
177 \\ add(3, 4);
178 \\
179 \\ exit();
180 \\}
181 \\
182 \\fn add(a: u32, b: u32) void {
183 \\ assert(a + b == 7);
184 \\}
185 \\
186 \\pub fn assert(ok: bool) void {
187 \\ if (!ok) unreachable; // assertion failure
188 \\}
189 \\
190 \\fn exit() noreturn {
191 \\ asm volatile ("syscall"
192 \\ :
193 \\ : [number] "{rax}" (231),
194 \\ [arg1] "{rdi}" (0)
195 \\ : "rcx", "r11", "memory"
196 \\ );
197 \\ unreachable;
198 \\}
199 ,
200 "",
201 );
202 case.addCompareOutput(
203 \\export fn _start() noreturn {
204 \\ add(100, 200);
205 \\
206 \\ exit();
207 \\}
208 \\
209 \\fn add(a: u32, b: u32) void {
210 \\ assert(a + b == 300);
211 \\}
212 \\
213 \\pub fn assert(ok: bool) void {
214 \\ if (!ok) unreachable; // assertion failure
215 \\}
216 \\
217 \\fn exit() noreturn {
218 \\ asm volatile ("syscall"
219 \\ :
220 \\ : [number] "{rax}" (231),
221 \\ [arg1] "{rdi}" (0)
222 \\ : "rcx", "r11", "memory"
223 \\ );
224 \\ unreachable;
225 \\}
226 ,
227 "",
228 );
229 }
28230}
test/stage2/compile_errors.zig+92-14
......@@ -1,55 +1,133 @@
11const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
2const std = @import("std");
3
4const ErrorMsg = @import("../../src-self-hosted/Module.zig").ErrorMsg;
5
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
210
311pub fn addCases(ctx: *TestContext) !void {
4 // TODO: re-enable these tests.
5 // https://github.com/ziglang/zig/issues/1364
12 ctx.compileErrorZIR("call undefined local", linux_x64,
13 \\@noreturn = primitive(noreturn)
14 \\
15 \\@start_fnty = fntype([], @noreturn, cc=Naked)
16 \\@start = fn(@start_fnty, {
17 \\ %0 = call(%test, [])
18 \\})
19 // TODO: address inconsistency in this message and the one in the next test
20 , &[_][]const u8{":5:13: error: unrecognized identifier: %test"});
21
22 ctx.compileErrorZIR("call with non-existent target", linux_x64,
23 \\@noreturn = primitive(noreturn)
24 \\
25 \\@start_fnty = fntype([], @noreturn, cc=Naked)
26 \\@start = fn(@start_fnty, {
27 \\ %0 = call(@notafunc, [])
28 \\})
29 \\@0 = str("_start")
30 \\@1 = export(@0, "start")
31 , &[_][]const u8{":5:13: error: decl 'notafunc' not found"});
32
33 // TODO: this error should occur at the call site, not the fntype decl
34 ctx.compileErrorZIR("call naked function", linux_x64,
35 \\@noreturn = primitive(noreturn)
36 \\
37 \\@start_fnty = fntype([], @noreturn, cc=Naked)
38 \\@s = fn(@start_fnty, {})
39 \\@start = fn(@start_fnty, {
40 \\ %0 = call(@s, [])
41 \\})
42 \\@0 = str("_start")
43 \\@1 = export(@0, "start")
44 , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"});
45
46 ctx.incrementalFailureZIR("exported symbol collision", linux_x64,
47 \\@noreturn = primitive(noreturn)
48 \\
49 \\@start_fnty = fntype([], @noreturn)
50 \\@start = fn(@start_fnty, {})
51 \\
52 \\@0 = str("_start")
53 \\@1 = export(@0, "start")
54 \\@2 = export(@0, "start")
55 , &[_][]const u8{":8:13: error: exported symbol collision: _start"},
56 \\@noreturn = primitive(noreturn)
57 \\
58 \\@start_fnty = fntype([], @noreturn)
59 \\@start = fn(@start_fnty, {})
60 \\
61 \\@0 = str("_start")
62 \\@1 = export(@0, "start")
63 );
664
7 //try ctx.testCompileError(
65 ctx.compileError("function redefinition", linux_x64,
66 \\fn entry() void {}
67 \\fn entry() void {}
68 , &[_][]const u8{":2:4: error: redefinition of 'entry'"});
69
70 //ctx.incrementalFailure("function redefinition", linux_x64,
71 // \\fn entry() void {}
72 // \\fn entry() void {}
73 //, &[_][]const u8{":2:4: error: redefinition of 'entry'"},
74 // \\fn entry() void {}
75 //);
76
77 //// TODO: need to make sure this works with other variants of export.
78 //ctx.incrementalFailure("exported symbol collision", linux_x64,
79 // \\export fn entry() void {}
880 // \\export fn entry() void {}
81 //, &[_][]const u8{":2:11: error: redefinition of 'entry'"},
982 // \\export fn entry() void {}
10 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");
83 //);
1184
12 //try ctx.testCompileError(
13 // \\fn() void {}
14 //, "1.zig", 1, 1, "missing function name");
85 // ctx.incrementalFailure("missing function name", linux_x64,
86 // \\fn() void {}
87 // , &[_][]const u8{":1:3: error: missing function name"},
88 // \\fn a() void {}
89 // );
90
91 // TODO: re-enable these tests.
92 // https://github.com/ziglang/zig/issues/1364
1593
16 //try ctx.testCompileError(
94 //ctx.testCompileError(
1795 // \\comptime {
1896 // \\ return;
1997 // \\}
2098 //, "1.zig", 2, 5, "return expression outside function definition");
2199
22 //try ctx.testCompileError(
100 //ctx.testCompileError(
23101 // \\export fn entry() void {
24102 // \\ defer return;
25103 // \\}
26104 //, "1.zig", 2, 11, "cannot return from defer expression");
27105
28 //try ctx.testCompileError(
106 //ctx.testCompileError(
29107 // \\export fn entry() c_int {
30108 // \\ return 36893488147419103232;
31109 // \\}
32110 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
33111
34 //try ctx.testCompileError(
112 //ctx.testCompileError(
35113 // \\comptime {
36114 // \\ var a: *align(4) align(4) i32 = 0;
37115 // \\}
38116 //, "1.zig", 2, 22, "Extra align qualifier");
39117
40 //try ctx.testCompileError(
118 //ctx.testCompileError(
41119 // \\comptime {
42120 // \\ var b: *const const i32 = 0;
43121 // \\}
44122 //, "1.zig", 2, 19, "Extra align qualifier");
45123
46 //try ctx.testCompileError(
124 //ctx.testCompileError(
47125 // \\comptime {
48126 // \\ var c: *volatile volatile i32 = 0;
49127 // \\}
50128 //, "1.zig", 2, 22, "Extra align qualifier");
51129
52 //try ctx.testCompileError(
130 //ctx.testCompileError(
53131 // \\comptime {
54132 // \\ var d: *allowzero allowzero i32 = 0;
55133 // \\}
test/stage2/test.zig+2-1
......@@ -3,5 +3,6 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33pub fn addCases(ctx: *TestContext) !void {
44 try @import("compile_errors.zig").addCases(ctx);
55 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);
6 try @import("zir.zig").addCases(ctx);
7 try @import("cbe.zig").addCases(ctx);
78}
test/stage2/zir.zig+157-338
......@@ -8,33 +8,31 @@ const linux_x64 = std.zig.CrossTarget{
88 .os_tag = .linux,
99};
1010
11pub fn addCases(ctx: *TestContext) void {
12 ctx.addZIRTransform("referencing decls which appear later in the file", linux_x64,
11pub fn addCases(ctx: *TestContext) !void {
12 ctx.transformZIR("referencing decls which appear later in the file", linux_x64,
1313 \\@void = primitive(void)
1414 \\@fnty = fntype([], @void, cc=C)
1515 \\
1616 \\@9 = str("entry")
17 \\@10 = ref(@9)
18 \\@11 = export(@10, @entry)
17 \\@11 = export(@9, "entry")
1918 \\
2019 \\@entry = fn(@fnty, {
21 \\ %11 = return()
20 \\ %11 = returnvoid()
2221 \\})
2322 ,
2423 \\@void = primitive(void)
2524 \\@fnty = fntype([], @void, cc=C)
26 \\@9 = str("entry")
27 \\@10 = ref(@9)
28 \\@unnamed$6 = str("entry")
29 \\@unnamed$7 = ref(@unnamed$6)
30 \\@unnamed$8 = export(@unnamed$7, @entry)
31 \\@unnamed$10 = fntype([], @void, cc=C)
32 \\@entry = fn(@unnamed$10, {
33 \\ %0 = return()
25 \\@9 = declref("9__anon_0")
26 \\@9__anon_0 = str("entry")
27 \\@unnamed$4 = str("entry")
28 \\@unnamed$5 = export(@unnamed$4, "entry")
29 \\@unnamed$6 = fntype([], @void, cc=C)
30 \\@entry = fn(@unnamed$6, {
31 \\ %0 = returnvoid()
3432 \\})
3533 \\
3634 );
37 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
35 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
3836 \\@void = primitive(void)
3937 \\@usize = primitive(usize)
4038 \\@fnty = fntype([], @void, cc=C)
......@@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void {
4543 \\
4644 \\@entry = fn(@fnty, {
4745 \\ %a = str("\x32\x08\x01\x0a")
48 \\ %aref = ref(%a)
49 \\ %eptr0 = elemptr(%aref, @0)
50 \\ %eptr1 = elemptr(%aref, @1)
51 \\ %eptr2 = elemptr(%aref, @2)
52 \\ %eptr3 = elemptr(%aref, @3)
46 \\ %eptr0 = elemptr(%a, @0)
47 \\ %eptr1 = elemptr(%a, @1)
48 \\ %eptr2 = elemptr(%a, @2)
49 \\ %eptr3 = elemptr(%a, @3)
5350 \\ %v0 = deref(%eptr0)
5451 \\ %v1 = deref(%eptr1)
5552 \\ %v2 = deref(%eptr2)
......@@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void {
6158 \\ %expected = int(69)
6259 \\ %ok = cmp(%result, eq, %expected)
6360 \\ %10 = condbr(%ok, {
64 \\ %11 = return()
61 \\ %11 = returnvoid()
6562 \\ }, {
6663 \\ %12 = breakpoint()
6764 \\ })
6865 \\})
6966 \\
7067 \\@9 = str("entry")
71 \\@10 = ref(@9)
72 \\@11 = export(@10, @entry)
68 \\@11 = export(@9, "entry")
7369 ,
7470 \\@void = primitive(void)
7571 \\@fnty = fntype([], @void, cc=C)
......@@ -77,65 +73,62 @@ pub fn addCases(ctx: *TestContext) void {
7773 \\@1 = int(1)
7874 \\@2 = int(2)
7975 \\@3 = int(3)
80 \\@unnamed$7 = fntype([], @void, cc=C)
81 \\@entry = fn(@unnamed$7, {
82 \\ %0 = return()
76 \\@unnamed$6 = fntype([], @void, cc=C)
77 \\@entry = fn(@unnamed$6, {
78 \\ %0 = returnvoid()
8379 \\})
84 \\@a = str("2\x08\x01\n")
85 \\@9 = str("entry")
86 \\@10 = ref(@9)
87 \\@unnamed$14 = str("entry")
88 \\@unnamed$15 = ref(@unnamed$14)
89 \\@unnamed$16 = export(@unnamed$15, @entry)
80 \\@entry__anon_1 = str("2\x08\x01\n")
81 \\@9 = declref("9__anon_0")
82 \\@9__anon_0 = str("entry")
83 \\@unnamed$11 = str("entry")
84 \\@unnamed$12 = export(@unnamed$11, "entry")
9085 \\
9186 );
9287
9388 {
94 var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64);
95 case.addZIR(
89 var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64);
90 case.addTransform(
9691 \\@void = primitive(void)
9792 \\@fnty = fntype([], @void, cc=C)
9893 \\
9994 \\@9 = str("entry")
100 \\@10 = ref(@9)
101 \\@11 = export(@10, @entry)
95 \\@11 = export(@9, "entry")
10296 \\
10397 \\@entry = fn(@fnty, {
10498 \\ %0 = call(@a, [])
105 \\ %1 = return()
99 \\ %1 = returnvoid()
106100 \\})
107101 \\
108102 \\@a = fn(@fnty, {
109103 \\ %0 = call(@b, [])
110 \\ %1 = return()
104 \\ %1 = returnvoid()
111105 \\})
112106 \\
113107 \\@b = fn(@fnty, {
114108 \\ %0 = call(@a, [])
115 \\ %1 = return()
109 \\ %1 = returnvoid()
116110 \\})
117111 ,
118112 \\@void = primitive(void)
119113 \\@fnty = fntype([], @void, cc=C)
120 \\@9 = str("entry")
121 \\@10 = ref(@9)
122 \\@unnamed$6 = str("entry")
123 \\@unnamed$7 = ref(@unnamed$6)
124 \\@unnamed$8 = export(@unnamed$7, @entry)
125 \\@unnamed$12 = fntype([], @void, cc=C)
126 \\@entry = fn(@unnamed$12, {
114 \\@9 = declref("9__anon_0")
115 \\@9__anon_0 = str("entry")
116 \\@unnamed$4 = str("entry")
117 \\@unnamed$5 = export(@unnamed$4, "entry")
118 \\@unnamed$6 = fntype([], @void, cc=C)
119 \\@entry = fn(@unnamed$6, {
127120 \\ %0 = call(@a, [], modifier=auto)
128 \\ %1 = return()
121 \\ %1 = returnvoid()
129122 \\})
130 \\@unnamed$17 = fntype([], @void, cc=C)
131 \\@a = fn(@unnamed$17, {
123 \\@unnamed$8 = fntype([], @void, cc=C)
124 \\@a = fn(@unnamed$8, {
132125 \\ %0 = call(@b, [], modifier=auto)
133 \\ %1 = return()
126 \\ %1 = returnvoid()
134127 \\})
135 \\@unnamed$22 = fntype([], @void, cc=C)
136 \\@b = fn(@unnamed$22, {
128 \\@unnamed$10 = fntype([], @void, cc=C)
129 \\@b = fn(@unnamed$10, {
137130 \\ %0 = call(@a, [], modifier=auto)
138 \\ %1 = return()
131 \\ %1 = returnvoid()
139132 \\})
140133 \\
141134 );
......@@ -145,65 +138,62 @@ pub fn addCases(ctx: *TestContext) void {
145138 \\@fnty = fntype([], @void, cc=C)
146139 \\
147140 \\@9 = str("entry")
148 \\@10 = ref(@9)
149 \\@11 = export(@10, @entry)
141 \\@11 = export(@9, "entry")
150142 \\
151143 \\@entry = fn(@fnty, {
152144 \\ %0 = call(@a, [])
153 \\ %1 = return()
145 \\ %1 = returnvoid()
154146 \\})
155147 \\
156148 \\@a = fn(@fnty, {
157149 \\ %0 = call(@b, [])
158 \\ %1 = return()
150 \\ %1 = returnvoid()
159151 \\})
160152 \\
161153 \\@b = fn(@fnty, {
162154 \\ %9 = compileerror("message")
163155 \\ %0 = call(@a, [])
164 \\ %1 = return()
156 \\ %1 = returnvoid()
165157 \\})
166158 ,
167159 &[_][]const u8{
168 ":19:21: error: message",
160 ":18:21: error: message",
169161 },
170162 );
171163 // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
172164 // referencing either of them. This tests that the cycle is detected, and the error
173165 // goes away.
174 case.addZIR(
166 case.addTransform(
175167 \\@void = primitive(void)
176168 \\@fnty = fntype([], @void, cc=C)
177169 \\
178170 \\@9 = str("entry")
179 \\@10 = ref(@9)
180 \\@11 = export(@10, @entry)
171 \\@11 = export(@9, "entry")
181172 \\
182173 \\@entry = fn(@fnty, {
183 \\ %1 = return()
174 \\ %0 = returnvoid()
184175 \\})
185176 \\
186177 \\@a = fn(@fnty, {
187178 \\ %0 = call(@b, [])
188 \\ %1 = return()
179 \\ %1 = returnvoid()
189180 \\})
190181 \\
191182 \\@b = fn(@fnty, {
192183 \\ %9 = compileerror("message")
193184 \\ %0 = call(@a, [])
194 \\ %1 = return()
185 \\ %1 = returnvoid()
195186 \\})
196187 ,
197188 \\@void = primitive(void)
198189 \\@fnty = fntype([], @void, cc=C)
199 \\@9 = str("entry")
200 \\@10 = ref(@9)
201 \\@unnamed$6 = str("entry")
202 \\@unnamed$7 = ref(@unnamed$6)
203 \\@unnamed$8 = export(@unnamed$7, @entry)
204 \\@unnamed$10 = fntype([], @void, cc=C)
205 \\@entry = fn(@unnamed$10, {
206 \\ %0 = return()
190 \\@9 = declref("9__anon_2")
191 \\@9__anon_2 = str("entry")
192 \\@unnamed$4 = str("entry")
193 \\@unnamed$5 = export(@unnamed$4, "entry")
194 \\@unnamed$6 = fntype([], @void, cc=C)
195 \\@entry = fn(@unnamed$6, {
196 \\ %0 = returnvoid()
207197 \\})
208198 \\
209199 );
......@@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void {
217207 return;
218208 }
219209
220 ctx.addZIRCompareOutput(
221 "hello world ZIR, update msg",
222 &[_][]const u8{
223 \\@noreturn = primitive(noreturn)
224 \\@void = primitive(void)
225 \\@usize = primitive(usize)
226 \\@0 = int(0)
227 \\@1 = int(1)
228 \\@2 = int(2)
229 \\@3 = int(3)
230 \\
231 \\@syscall_array = str("syscall")
232 \\@sysoutreg_array = str("={rax}")
233 \\@rax_array = str("{rax}")
234 \\@rdi_array = str("{rdi}")
235 \\@rcx_array = str("rcx")
236 \\@r11_array = str("r11")
237 \\@rdx_array = str("{rdx}")
238 \\@rsi_array = str("{rsi}")
239 \\@memory_array = str("memory")
240 \\@len_array = str("len")
241 \\
242 \\@msg = str("Hello, world!\n")
243 \\
244 \\@start_fnty = fntype([], @noreturn, cc=Naked)
245 \\@start = fn(@start_fnty, {
246 \\ %SYS_exit_group = int(231)
247 \\ %exit_code = as(@usize, @0)
248 \\
249 \\ %syscall = ref(@syscall_array)
250 \\ %sysoutreg = ref(@sysoutreg_array)
251 \\ %rax = ref(@rax_array)
252 \\ %rdi = ref(@rdi_array)
253 \\ %rcx = ref(@rcx_array)
254 \\ %rdx = ref(@rdx_array)
255 \\ %rsi = ref(@rsi_array)
256 \\ %r11 = ref(@r11_array)
257 \\ %memory = ref(@memory_array)
258 \\
259 \\ %SYS_write = as(@usize, @1)
260 \\ %STDOUT_FILENO = as(@usize, @1)
261 \\
262 \\ %msg_ptr = ref(@msg)
263 \\ %msg_addr = ptrtoint(%msg_ptr)
264 \\
265 \\ %len_name = ref(@len_array)
266 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
267 \\ %msg_len = deref(%msg_len_ptr)
268 \\ %rc_write = asm(%syscall, @usize,
269 \\ volatile=1,
270 \\ output=%sysoutreg,
271 \\ inputs=[%rax, %rdi, %rsi, %rdx],
272 \\ clobbers=[%rcx, %r11, %memory],
273 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
274 \\
275 \\ %rc_exit = asm(%syscall, @usize,
276 \\ volatile=1,
277 \\ output=%sysoutreg,
278 \\ inputs=[%rax, %rdi],
279 \\ clobbers=[%rcx, %r11, %memory],
280 \\ args=[%SYS_exit_group, %exit_code])
281 \\
282 \\ %99 = unreachable()
283 \\});
284 \\
285 \\@9 = str("_start")
286 \\@10 = ref(@9)
287 \\@11 = export(@10, @start)
288 ,
289 \\@noreturn = primitive(noreturn)
290 \\@void = primitive(void)
291 \\@usize = primitive(usize)
292 \\@0 = int(0)
293 \\@1 = int(1)
294 \\@2 = int(2)
295 \\@3 = int(3)
296 \\
297 \\@syscall_array = str("syscall")
298 \\@sysoutreg_array = str("={rax}")
299 \\@rax_array = str("{rax}")
300 \\@rdi_array = str("{rdi}")
301 \\@rcx_array = str("rcx")
302 \\@r11_array = str("r11")
303 \\@rdx_array = str("{rdx}")
304 \\@rsi_array = str("{rsi}")
305 \\@memory_array = str("memory")
306 \\@len_array = str("len")
307 \\
308 \\@msg = str("Hello, world!\n")
309 \\@msg2 = str("HELL WORLD\n")
310 \\
311 \\@start_fnty = fntype([], @noreturn, cc=Naked)
312 \\@start = fn(@start_fnty, {
313 \\ %SYS_exit_group = int(231)
314 \\ %exit_code = as(@usize, @0)
315 \\
316 \\ %syscall = ref(@syscall_array)
317 \\ %sysoutreg = ref(@sysoutreg_array)
318 \\ %rax = ref(@rax_array)
319 \\ %rdi = ref(@rdi_array)
320 \\ %rcx = ref(@rcx_array)
321 \\ %rdx = ref(@rdx_array)
322 \\ %rsi = ref(@rsi_array)
323 \\ %r11 = ref(@r11_array)
324 \\ %memory = ref(@memory_array)
325 \\
326 \\ %SYS_write = as(@usize, @1)
327 \\ %STDOUT_FILENO = as(@usize, @1)
328 \\
329 \\ %msg_ptr = ref(@msg2)
330 \\ %msg_addr = ptrtoint(%msg_ptr)
331 \\
332 \\ %len_name = ref(@len_array)
333 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
334 \\ %msg_len = deref(%msg_len_ptr)
335 \\ %rc_write = asm(%syscall, @usize,
336 \\ volatile=1,
337 \\ output=%sysoutreg,
338 \\ inputs=[%rax, %rdi, %rsi, %rdx],
339 \\ clobbers=[%rcx, %r11, %memory],
340 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
341 \\
342 \\ %rc_exit = asm(%syscall, @usize,
343 \\ volatile=1,
344 \\ output=%sysoutreg,
345 \\ inputs=[%rax, %rdi],
346 \\ clobbers=[%rcx, %r11, %memory],
347 \\ args=[%SYS_exit_group, %exit_code])
348 \\
349 \\ %99 = unreachable()
350 \\});
351 \\
352 \\@9 = str("_start")
353 \\@10 = ref(@9)
354 \\@11 = export(@10, @start)
355 ,
356 \\@noreturn = primitive(noreturn)
357 \\@void = primitive(void)
358 \\@usize = primitive(usize)
359 \\@0 = int(0)
360 \\@1 = int(1)
361 \\@2 = int(2)
362 \\@3 = int(3)
363 \\
364 \\@syscall_array = str("syscall")
365 \\@sysoutreg_array = str("={rax}")
366 \\@rax_array = str("{rax}")
367 \\@rdi_array = str("{rdi}")
368 \\@rcx_array = str("rcx")
369 \\@r11_array = str("r11")
370 \\@rdx_array = str("{rdx}")
371 \\@rsi_array = str("{rsi}")
372 \\@memory_array = str("memory")
373 \\@len_array = str("len")
374 \\
375 \\@msg = str("Hello, world!\n")
376 \\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n")
377 \\
378 \\@start_fnty = fntype([], @noreturn, cc=Naked)
379 \\@start = fn(@start_fnty, {
380 \\ %SYS_exit_group = int(231)
381 \\ %exit_code = as(@usize, @0)
382 \\
383 \\ %syscall = ref(@syscall_array)
384 \\ %sysoutreg = ref(@sysoutreg_array)
385 \\ %rax = ref(@rax_array)
386 \\ %rdi = ref(@rdi_array)
387 \\ %rcx = ref(@rcx_array)
388 \\ %rdx = ref(@rdx_array)
389 \\ %rsi = ref(@rsi_array)
390 \\ %r11 = ref(@r11_array)
391 \\ %memory = ref(@memory_array)
392 \\
393 \\ %SYS_write = as(@usize, @1)
394 \\ %STDOUT_FILENO = as(@usize, @1)
395 \\
396 \\ %msg_ptr = ref(@msg2)
397 \\ %msg_addr = ptrtoint(%msg_ptr)
398 \\
399 \\ %len_name = ref(@len_array)
400 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
401 \\ %msg_len = deref(%msg_len_ptr)
402 \\ %rc_write = asm(%syscall, @usize,
403 \\ volatile=1,
404 \\ output=%sysoutreg,
405 \\ inputs=[%rax, %rdi, %rsi, %rdx],
406 \\ clobbers=[%rcx, %r11, %memory],
407 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
408 \\
409 \\ %rc_exit = asm(%syscall, @usize,
410 \\ volatile=1,
411 \\ output=%sysoutreg,
412 \\ inputs=[%rax, %rdi],
413 \\ clobbers=[%rcx, %r11, %memory],
414 \\ args=[%SYS_exit_group, %exit_code])
415 \\
416 \\ %99 = unreachable()
417 \\});
418 \\
419 \\@9 = str("_start")
420 \\@10 = ref(@9)
421 \\@11 = export(@10, @start)
422 },
423 &[_][]const u8{
424 \\Hello, world!
425 \\
426 ,
427 \\HELL WORLD
428 \\
429 ,
430 \\Editing the same msg2 decl but this time with a much longer message which will
431 \\cause the data to need to be relocated in virtual address space.
432 \\
433 },
210 ctx.compareOutputZIR("hello world ZIR",
211 \\@noreturn = primitive(noreturn)
212 \\@void = primitive(void)
213 \\@usize = primitive(usize)
214 \\@0 = int(0)
215 \\@1 = int(1)
216 \\@2 = int(2)
217 \\@3 = int(3)
218 \\
219 \\@msg = str("Hello, world!\n")
220 \\
221 \\@start_fnty = fntype([], @noreturn, cc=Naked)
222 \\@start = fn(@start_fnty, {
223 \\ %SYS_exit_group = int(231)
224 \\ %exit_code = as(@usize, @0)
225 \\
226 \\ %syscall = str("syscall")
227 \\ %sysoutreg = str("={rax}")
228 \\ %rax = str("{rax}")
229 \\ %rdi = str("{rdi}")
230 \\ %rcx = str("rcx")
231 \\ %rdx = str("{rdx}")
232 \\ %rsi = str("{rsi}")
233 \\ %r11 = str("r11")
234 \\ %memory = str("memory")
235 \\
236 \\ %SYS_write = as(@usize, @1)
237 \\ %STDOUT_FILENO = as(@usize, @1)
238 \\
239 \\ %msg_addr = ptrtoint(@msg)
240 \\
241 \\ %len_name = str("len")
242 \\ %msg_len_ptr = fieldptr(@msg, %len_name)
243 \\ %msg_len = deref(%msg_len_ptr)
244 \\ %rc_write = asm(%syscall, @usize,
245 \\ volatile=1,
246 \\ output=%sysoutreg,
247 \\ inputs=[%rax, %rdi, %rsi, %rdx],
248 \\ clobbers=[%rcx, %r11, %memory],
249 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
250 \\
251 \\ %rc_exit = asm(%syscall, @usize,
252 \\ volatile=1,
253 \\ output=%sysoutreg,
254 \\ inputs=[%rax, %rdi],
255 \\ clobbers=[%rcx, %r11, %memory],
256 \\ args=[%SYS_exit_group, %exit_code])
257 \\
258 \\ %99 = unreachable()
259 \\});
260 \\
261 \\@9 = str("_start")
262 \\@11 = export(@9, "start")
263 ,
264 \\Hello, world!
265 \\
434266 );
435267
436 ctx.addZIRCompareOutput(
437 "function call with no args no return value",
438 &[_][]const u8{
439 \\@noreturn = primitive(noreturn)
440 \\@void = primitive(void)
441 \\@usize = primitive(usize)
442 \\@0 = int(0)
443 \\@1 = int(1)
444 \\@2 = int(2)
445 \\@3 = int(3)
446 \\
447 \\@syscall_array = str("syscall")
448 \\@sysoutreg_array = str("={rax}")
449 \\@rax_array = str("{rax}")
450 \\@rdi_array = str("{rdi}")
451 \\@rcx_array = str("rcx")
452 \\@r11_array = str("r11")
453 \\@memory_array = str("memory")
454 \\
455 \\@exit0_fnty = fntype([], @noreturn)
456 \\@exit0 = fn(@exit0_fnty, {
457 \\ %SYS_exit_group = int(231)
458 \\ %exit_code = as(@usize, @0)
459 \\
460 \\ %syscall = ref(@syscall_array)
461 \\ %sysoutreg = ref(@sysoutreg_array)
462 \\ %rax = ref(@rax_array)
463 \\ %rdi = ref(@rdi_array)
464 \\ %rcx = ref(@rcx_array)
465 \\ %r11 = ref(@r11_array)
466 \\ %memory = ref(@memory_array)
467 \\
468 \\ %rc = asm(%syscall, @usize,
469 \\ volatile=1,
470 \\ output=%sysoutreg,
471 \\ inputs=[%rax, %rdi],
472 \\ clobbers=[%rcx, %r11, %memory],
473 \\ args=[%SYS_exit_group, %exit_code])
474 \\
475 \\ %99 = unreachable()
476 \\});
477 \\
478 \\@start_fnty = fntype([], @noreturn, cc=Naked)
479 \\@start = fn(@start_fnty, {
480 \\ %0 = call(@exit0, [])
481 \\})
482 \\@9 = str("_start")
483 \\@10 = ref(@9)
484 \\@11 = export(@10, @start)
485 },
486 &[_][]const u8{""},
487 );
268 ctx.compareOutputZIR("function call with no args no return value",
269 \\@noreturn = primitive(noreturn)
270 \\@void = primitive(void)
271 \\@usize = primitive(usize)
272 \\@0 = int(0)
273 \\@1 = int(1)
274 \\@2 = int(2)
275 \\@3 = int(3)
276 \\
277 \\@exit0_fnty = fntype([], @noreturn)
278 \\@exit0 = fn(@exit0_fnty, {
279 \\ %SYS_exit_group = int(231)
280 \\ %exit_code = as(@usize, @0)
281 \\
282 \\ %syscall = str("syscall")
283 \\ %sysoutreg = str("={rax}")
284 \\ %rax = str("{rax}")
285 \\ %rdi = str("{rdi}")
286 \\ %rcx = str("rcx")
287 \\ %r11 = str("r11")
288 \\ %memory = str("memory")
289 \\
290 \\ %rc = asm(%syscall, @usize,
291 \\ volatile=1,
292 \\ output=%sysoutreg,
293 \\ inputs=[%rax, %rdi],
294 \\ clobbers=[%rcx, %r11, %memory],
295 \\ args=[%SYS_exit_group, %exit_code])
296 \\
297 \\ %99 = unreachable()
298 \\});
299 \\
300 \\@start_fnty = fntype([], @noreturn, cc=Naked)
301 \\@start = fn(@start_fnty, {
302 \\ %0 = call(@exit0, [])
303 \\})
304 \\@9 = str("_start")
305 \\@11 = export(@9, "start")
306 , "");
488307}
test/standalone/guess_number/main.zig+1-1
......@@ -4,7 +4,7 @@ const io = std.io;
44const fmt = std.fmt;
55
66pub fn main() !void {
7 const stdout = io.getStdOut().outStream();
7 const stdout = io.getStdOut().writer();
88 const stdin = io.getStdIn();
99
1010 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
test/tests.zig+1
......@@ -537,6 +537,7 @@ pub fn addPkgTests(
537537 these_tests.enable_qemu = is_qemu_enabled;
538538 these_tests.enable_wasmtime = is_wasmtime_enabled;
539539 these_tests.glibc_multi_install_dir = glibc_dir;
540 these_tests.addIncludeDir("test");
540541
541542 step.dependOn(&these_tests.step);
542543 }
test/translate_c.zig+42-17
......@@ -3,6 +3,31 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("initializer list macro",
7 \\typedef struct Color {
8 \\ unsigned char r;
9 \\ unsigned char g;
10 \\ unsigned char b;
11 \\ unsigned char a;
12 \\} Color;
13 \\#define CLITERAL(type) (type)
14 \\#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
15 , &[_][]const u8{ // TODO properly translate this
16 \\pub const struct_Color = extern struct {
17 \\ r: u8,
18 \\ g: u8,
19 \\ b: u8,
20 \\ a: u8,
21 \\};
22 \\pub const Color = struct_Color;
23 ,
24 \\pub inline fn CLITERAL(type_1: anytype) @TypeOf(type_1) {
25 \\ return type_1;
26 \\}
27 ,
28 \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 });
29 });
30
631 cases.add("complex switch",
732 \\int main() {
833 \\ int i = 2;
......@@ -21,7 +46,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2146 cases.add("correct semicolon after infixop",
2247 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
2348 , &[_][]const u8{
24 \\pub inline fn __ferror_unlocked_body(_fp: var) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {
49 \\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {
2550 \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0;
2651 \\}
2752 });
......@@ -30,7 +55,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3055 \\#define FOO(x) ((x >= 0) + (x >= 0))
3156 \\#define BAR 1 && 2 > 4
3257 , &[_][]const u8{
33 \\pub inline fn FOO(x: var) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) {
58 \\pub inline fn FOO(x: anytype) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) {
3459 \\ return @boolToInt(x >= 0) + @boolToInt(x >= 0);
3560 \\}
3661 ,
......@@ -81,7 +106,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
81106 \\ break :blk bar;
82107 \\};
83108 ,
84 \\pub inline fn bar(x: var) @TypeOf(baz(1, 2)) {
109 \\pub inline fn bar(x: anytype) @TypeOf(baz(1, 2)) {
85110 \\ return blk: {
86111 \\ _ = &x;
87112 \\ _ = 3;
......@@ -1473,7 +1498,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
14731498 cases.add("macro pointer cast",
14741499 \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
14751500 , &[_][]const u8{
1476 \\pub const NRF_GPIO = (if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, @alignCast(@alignOf([*c]NRF_GPIO_Type.Child), NRF_GPIO_BASE)) else if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Int and @typeInfo([*c]NRF_GPIO_Type) == .Pointer) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
1501 \\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
14771502 });
14781503
14791504 cases.add("basic macro function",
......@@ -1483,11 +1508,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
14831508 , &[_][]const u8{
14841509 \\pub extern var c: c_int;
14851510 ,
1486 \\pub inline fn BASIC(c_1: var) @TypeOf(c_1 * 2) {
1511 \\pub inline fn BASIC(c_1: anytype) @TypeOf(c_1 * 2) {
14871512 \\ return c_1 * 2;
14881513 \\}
14891514 ,
1490 \\pub inline fn FOO(L: var, b: var) @TypeOf(L + b) {
1515 \\pub inline fn FOO(L: anytype, b: anytype) @TypeOf(L + b) {
14911516 \\ return L + b;
14921517 \\}
14931518 });
......@@ -2123,7 +2148,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21232148 cases.add("macro call",
21242149 \\#define CALL(arg) bar(arg)
21252150 , &[_][]const u8{
2126 \\pub inline fn CALL(arg: var) @TypeOf(bar(arg)) {
2151 \\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) {
21272152 \\ return bar(arg);
21282153 \\}
21292154 });
......@@ -2683,11 +2708,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
26832708 \\#define FOO(bar) baz((void *)(baz))
26842709 \\#define BAR (void*) a
26852710 , &[_][]const u8{
2686 \\pub inline fn FOO(bar: var) @TypeOf(baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)))) {
2687 \\ return baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)));
2711 \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
2712 \\ return baz((@import("std").meta.cast(?*c_void, baz)));
26882713 \\}
26892714 ,
2690 \\pub const BAR = (if (@typeInfo(@TypeOf(a)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), a)) else if (@typeInfo(@TypeOf(a)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, a) else @as(?*c_void, a));
2715 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
26912716 });
26922717
26932718 cases.add("macro conditional operator",
......@@ -2713,11 +2738,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27132738 \\#define MIN(a, b) ((b) < (a) ? (b) : (a))
27142739 \\#define MAX(a, b) ((b) > (a) ? (b) : (a))
27152740 , &[_][]const u8{
2716 \\pub inline fn MIN(a: var, b: var) @TypeOf(if (b < a) b else a) {
2741 \\pub inline fn MIN(a: anytype, b: anytype) @TypeOf(if (b < a) b else a) {
27172742 \\ return if (b < a) b else a;
27182743 \\}
27192744 ,
2720 \\pub inline fn MAX(a: var, b: var) @TypeOf(if (b > a) b else a) {
2745 \\pub inline fn MAX(a: anytype, b: anytype) @TypeOf(if (b > a) b else a) {
27212746 \\ return if (b > a) b else a;
27222747 \\}
27232748 });
......@@ -2797,7 +2822,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27972822 \\pub fn a() callconv(.C) void {}
27982823 \\pub fn b() callconv(.C) void {}
27992824 \\pub export fn c() void {}
2800 \\pub fn foo() callconv(.C) void {}
2825 \\pub fn foo(...) callconv(.C) void {}
28012826 });
28022827
28032828 cases.add("casting away const and volatile",
......@@ -2905,8 +2930,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
29052930 \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
29062931 \\
29072932 , &[_][]const u8{
2908 \\pub inline fn DefaultScreen(dpy: var) @TypeOf((if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen) {
2909 \\ return (if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen;
2933 \\pub inline fn DefaultScreen(dpy: anytype) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) {
2934 \\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;
29102935 \\}
29112936 });
29122937
......@@ -2914,9 +2939,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
29142939 \\#define NULL ((void*)0)
29152940 \\#define FOO ((int)0x8000)
29162941 , &[_][]const u8{
2917 \\pub const NULL = (if (@typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, 0) else @as(?*c_void, 0));
2942 \\pub const NULL = (@import("std").meta.cast(?*c_void, 0));
29182943 ,
2919 \\pub const FOO = (if (@typeInfo(c_int) == .Pointer) @intToPtr(c_int, 0x8000) else @as(c_int, 0x8000));
2944 \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));
29202945 });
29212946
29222947 if (std.Target.current.abi == .msvc) {