authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-11 23:49:20-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-11 23:49:20-05:00
logef6260b3a7ed09e5dc5d8383ad20f229411bd9ff
tree23f2b5396a95cae6c730cdfafb7dfcdba7eca8f2
parent5d9e3cb77f864ccdbcae43329e13a9c8e1f8494e
parentf2d601661d286b135293373a83ce1a8628272379

Merge remote-tracking branch 'origin/master' into llvm6


94 files changed, 6078 insertions(+), 4154 deletions(-)

CMakeLists.txt+6-1
...@@ -480,11 +480,12 @@ set(ZIG_STD_FILES...@@ -480,11 +480,12 @@ set(ZIG_STD_FILES
480 "os/child_process.zig"480 "os/child_process.zig"
481 "os/darwin.zig"481 "os/darwin.zig"
482 "os/darwin_errno.zig"482 "os/darwin_errno.zig"
483 "os/file.zig"
483 "os/get_user_id.zig"484 "os/get_user_id.zig"
484 "os/index.zig"485 "os/index.zig"
485 "os/linux/index.zig"
486 "os/linux/errno.zig"486 "os/linux/errno.zig"
487 "os/linux/i386.zig"487 "os/linux/i386.zig"
488 "os/linux/index.zig"
488 "os/linux/x86_64.zig"489 "os/linux/x86_64.zig"
489 "os/path.zig"490 "os/path.zig"
490 "os/windows/error.zig"491 "os/windows/error.zig"
...@@ -520,6 +521,10 @@ set(ZIG_STD_FILES...@@ -520,6 +521,10 @@ set(ZIG_STD_FILES
520 "special/panic.zig"521 "special/panic.zig"
521 "special/test_runner.zig"522 "special/test_runner.zig"
522 "unicode.zig"523 "unicode.zig"
524 "zig/ast.zig"
525 "zig/index.zig"
526 "zig/parser.zig"
527 "zig/tokenizer.zig"
523)528)
524529
525set(ZIG_C_HEADER_FILES530set(ZIG_C_HEADER_FILES
build.zig+2-7
...@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;...@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
10const Buffer = std.Buffer;10const Buffer = std.Buffer;
11const io = std.io;11const io = std.io;
1212
13pub fn build(b: &Builder) %void {13pub fn build(b: &Builder) !void {
14 const mode = b.standardReleaseOptions();14 const mode = b.standardReleaseOptions();
1515
16 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");16 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
...@@ -78,7 +78,6 @@ pub fn build(b: &Builder) %void {...@@ -78,7 +78,6 @@ pub fn build(b: &Builder) %void {
78 exe.linkSystemLibrary("c");78 exe.linkSystemLibrary("c");
7979
80 b.default_step.dependOn(&exe.step);80 b.default_step.dependOn(&exe.step);
81 b.default_step.dependOn(docs_step);
8281
83 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;82 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;
84 if (!skip_self_hosted) {83 if (!skip_self_hosted) {
...@@ -108,10 +107,6 @@ pub fn build(b: &Builder) %void {...@@ -108,10 +107,6 @@ pub fn build(b: &Builder) %void {
108 "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",107 "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",
109 with_lldb));108 with_lldb));
110109
111 test_step.dependOn(tests.addPkgTests(b, test_filter,
112 "src-self-hosted/main.zig", "fmt", "Run the fmt tests",
113 with_lldb));
114
115 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));110 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
116 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));111 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
117 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));112 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
...@@ -149,7 +144,7 @@ const LibraryDep = struct {...@@ -149,7 +144,7 @@ const LibraryDep = struct {
149 includes: ArrayList([]const u8),144 includes: ArrayList([]const u8),
150};145};
151146
152fn findLLVM(b: &Builder, llvm_config_exe: []const u8) %LibraryDep {147fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep {
153 const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});148 const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
154 const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});149 const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
155 const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});150 const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});
ci/appveyor/build_script.bat+2-14
...@@ -24,18 +24,6 @@ cd %ZIGBUILDDIR%...@@ -24,18 +24,6 @@ cd %ZIGBUILDDIR%
24cmake.exe .. -Thost=x64 -G"Visual Studio 14 2015 Win64" "-DCMAKE_INSTALL_PREFIX=%ZIGBUILDDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release "-DZIG_LIBC_INCLUDE_DIR=C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt" "-DZIG_LIBC_LIB_DIR=C:\Program Files (x86)\Windows Kits\10\bin\x64\ucrt" "-DZIG_LIBC_STATIC_LIB_DIR=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64" || exit /b24cmake.exe .. -Thost=x64 -G"Visual Studio 14 2015 Win64" "-DCMAKE_INSTALL_PREFIX=%ZIGBUILDDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release "-DZIG_LIBC_INCLUDE_DIR=C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt" "-DZIG_LIBC_LIB_DIR=C:\Program Files (x86)\Windows Kits\10\bin\x64\ucrt" "-DZIG_LIBC_STATIC_LIB_DIR=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64" || exit /b
25msbuild /p:Configuration=Release INSTALL.vcxproj || exit /b25msbuild /p:Configuration=Release INSTALL.vcxproj || exit /b
2626
27bin\zig.exe build --build-file ..\build.zig test -Dverbose-link || exit /b27bin\zig.exe build --build-file ..\build.zig test || exit /b
2828
29@echo "MSVC build succeeded, proceeding with MinGW build"29@echo "MSVC build succeeded"
30cd %APPVEYOR_BUILD_FOLDER%
31SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%"
32SET "MSYSTEM=MINGW64"
33
34bash -lc "pacman -Syu --needed --noconfirm"
35bash -lc "pacman -Su --needed --noconfirm"
36
37bash -lc "pacman -S --needed --noconfirm make mingw64/mingw-w64-x86_64-make mingw64/mingw-w64-x86_64-cmake mingw64/mingw-w64-x86_64-clang mingw64/mingw-w64-x86_64-llvm mingw64/mingw-w64-x86_64-lld mingw64/mingw-w64-x86_64-gcc"
38
39bash -lc "cd ${APPVEYOR_BUILD_FOLDER} && mkdir build && cd build && cmake .. -G""MSYS Makefiles"" -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | cc -E -x c - -v 2>&1 | grep -B1 ""End of search list."" | head -n1 | cut -c 2- | sed ""s/ .*//"") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbegin.o)) && make && make install"
40
41@echo "MinGW build successful"
doc/docgen.zig+13-22
...@@ -12,7 +12,7 @@ const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt();...@@ -12,7 +12,7 @@ const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt();
12const obj_ext = std.build.Target(std.build.Target.Native).oFileExt();12const obj_ext = std.build.Target(std.build.Target.Native).oFileExt();
13const tmp_dir_name = "docgen_tmp";13const tmp_dir_name = "docgen_tmp";
1414
15pub fn main() %void {15pub fn main() !void {
16 // TODO use a more general purpose allocator here16 // TODO use a more general purpose allocator here
17 var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size);17 var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size);
18 defer inc_allocator.deinit();18 defer inc_allocator.deinit();
...@@ -31,10 +31,10 @@ pub fn main() %void {...@@ -31,10 +31,10 @@ pub fn main() %void {
31 const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));31 const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));
32 defer allocator.free(out_file_name);32 defer allocator.free(out_file_name);
3333
34 var in_file = try io.File.openRead(in_file_name, allocator);34 var in_file = try os.File.openRead(allocator, in_file_name);
35 defer in_file.close();35 defer in_file.close();
3636
37 var out_file = try io.File.openWrite(out_file_name, allocator);37 var out_file = try os.File.openWrite(allocator, out_file_name);
38 defer out_file.close();38 defer out_file.close();
3939
40 var file_in_stream = io.FileInStream.init(&in_file);40 var file_in_stream = io.FileInStream.init(&in_file);
...@@ -42,7 +42,7 @@ pub fn main() %void {...@@ -42,7 +42,7 @@ pub fn main() %void {
42 const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);42 const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);
4343
44 var file_out_stream = io.FileOutStream.init(&out_file);44 var file_out_stream = io.FileOutStream.init(&out_file);
45 var buffered_out_stream = io.BufferedOutStream.init(&file_out_stream.stream);45 var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
4646
47 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);47 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
48 var toc = try genToc(allocator, &tokenizer);48 var toc = try genToc(allocator, &tokenizer);
...@@ -218,8 +218,6 @@ const Tokenizer = struct {...@@ -218,8 +218,6 @@ const Tokenizer = struct {
218 }218 }
219};219};
220220
221error ParseError;
222
223fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error {221fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error {
224 const loc = tokenizer.getTokenLocation(token);222 const loc = tokenizer.getTokenLocation(token);
225 warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args);223 warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args);
...@@ -243,13 +241,13 @@ fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const...@@ -243,13 +241,13 @@ fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const
243 return error.ParseError;241 return error.ParseError;
244}242}
245243
246fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) %void {244fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void {
247 if (token.id != id) {245 if (token.id != id) {
248 return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id));246 return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
249 }247 }
250}248}
251249
252fn eatToken(tokenizer: &Tokenizer, id: Token.Id) %Token {250fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token {
253 const token = tokenizer.next();251 const token = tokenizer.next();
254 try assertToken(tokenizer, token, id);252 try assertToken(tokenizer, token, id);
255 return token;253 return token;
...@@ -316,7 +314,7 @@ const Action = enum {...@@ -316,7 +314,7 @@ const Action = enum {
316 Close,314 Close,
317};315};
318316
319fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc {317fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
320 var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator);318 var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator);
321 errdefer urls.deinit();319 errdefer urls.deinit();
322320
...@@ -540,7 +538,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc {...@@ -540,7 +538,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc {
540 };538 };
541}539}
542540
543fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 {541fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 {
544 var buf = try std.Buffer.initSize(allocator, 0);542 var buf = try std.Buffer.initSize(allocator, 0);
545 defer buf.deinit();543 defer buf.deinit();
546544
...@@ -560,7 +558,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 {...@@ -560,7 +558,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 {
560 return buf.toOwnedSlice();558 return buf.toOwnedSlice();
561}559}
562560
563fn escapeHtml(allocator: &mem.Allocator, input: []const u8) %[]u8 {561fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 {
564 var buf = try std.Buffer.initSize(allocator, 0);562 var buf = try std.Buffer.initSize(allocator, 0);
565 defer buf.deinit();563 defer buf.deinit();
566564
...@@ -596,15 +594,13 @@ const TermState = enum {...@@ -596,15 +594,13 @@ const TermState = enum {
596 ExpectEnd,594 ExpectEnd,
597};595};
598596
599error UnsupportedEscape;
600
601test "term color" {597test "term color" {
602 const input_bytes = "A\x1b[32;1mgreen\x1b[0mB";598 const input_bytes = "A\x1b[32;1mgreen\x1b[0mB";
603 const result = try termColor(std.debug.global_allocator, input_bytes);599 const result = try termColor(std.debug.global_allocator, input_bytes);
604 assert(mem.eql(u8, result, "A<span class=\"t32\">green</span>B"));600 assert(mem.eql(u8, result, "A<span class=\"t32\">green</span>B"));
605}601}
606602
607fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 {603fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 {
608 var buf = try std.Buffer.initSize(allocator, 0);604 var buf = try std.Buffer.initSize(allocator, 0);
609 defer buf.deinit();605 defer buf.deinit();
610606
...@@ -684,9 +680,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 {...@@ -684,9 +680,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 {
684 return buf.toOwnedSlice();680 return buf.toOwnedSlice();
685}681}
686682
687error ExampleFailedToCompile;683fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var, zig_exe: []const u8) !void {
688
689fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io.OutStream, zig_exe: []const u8) %void {
690 var code_progress_index: usize = 0;684 var code_progress_index: usize = 0;
691 for (toc.nodes) |node| {685 for (toc.nodes) |node| {
692 switch (node) {686 switch (node) {
...@@ -729,7 +723,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io...@@ -729,7 +723,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
729 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);723 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
730 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);724 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
731 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);725 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
732 try io.writeFile(tmp_source_file_name, trimmed_raw_source, null);726 try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);
733 727
734 switch (code.id) {728 switch (code.id) {
735 Code.Id.Exe => |expected_outcome| {729 Code.Id.Exe => |expected_outcome| {
...@@ -974,10 +968,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io...@@ -974,10 +968,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
974968
975}969}
976970
977error ChildCrashed;971fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {
978error ChildExitError;
979
980fn exec(allocator: &mem.Allocator, args: []const []const u8) %os.ChildProcess.ExecResult {
981 const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);972 const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
982 switch (result.term) {973 switch (result.term) {
983 os.ChildProcess.Term.Exited => |exit_code| {974 os.ChildProcess.Term.Exited => |exit_code| {
doc/langref.html.in+62-76
...@@ -108,7 +108,7 @@...@@ -108,7 +108,7 @@
108 {#code_begin|exe|hello#}108 {#code_begin|exe|hello#}
109const std = @import("std");109const std = @import("std");
110110
111pub fn main() %void {111pub fn main() !void {
112 // If this program is run without stdout attached, exit with an error.112 // If this program is run without stdout attached, exit with an error.
113 var stdout_file = try std.io.getStdOut();113 var stdout_file = try std.io.getStdOut();
114 // If this program encounters pipe failure when printing to stdout, exit114 // If this program encounters pipe failure when printing to stdout, exit
...@@ -129,8 +129,8 @@ pub fn main() void {...@@ -129,8 +129,8 @@ pub fn main() void {
129}129}
130 {#code_end#}130 {#code_end#}
131 <p>131 <p>
132 Note that we also left off the <code class="zig">%</code> from the return type.132 Note that we also left off the <code class="zig">!</code> from the return type.
133 In Zig, if your main function cannot fail, you may use the <code class="zig">void</code> return type.133 In Zig, if your main function cannot fail, you must use the <code class="zig">void</code> return type.
134 </p>134 </p>
135 {#see_also|Values|@import|Errors|Root Source File#}135 {#see_also|Values|@import|Errors|Root Source File#}
136 {#header_close#}136 {#header_close#}
...@@ -141,10 +141,7 @@ const warn = std.debug.warn;...@@ -141,10 +141,7 @@ const warn = std.debug.warn;
141const os = std.os;141const os = std.os;
142const assert = std.debug.assert;142const assert = std.debug.assert;
143143
144// error declaration, makes `error.ArgNotFound` available144pub fn main() void {
145error ArgNotFound;
146
147pub fn main() %void {
148 // integers145 // integers
149 const one_plus_one: i32 = 1 + 1;146 const one_plus_one: i32 = 1 + 1;
150 warn("1 + 1 = {}\n", one_plus_one);147 warn("1 + 1 = {}\n", one_plus_one);
...@@ -173,7 +170,7 @@ pub fn main() %void {...@@ -173,7 +170,7 @@ pub fn main() %void {
173 @typeName(@typeOf(nullable_value)), nullable_value);170 @typeName(@typeOf(nullable_value)), nullable_value);
174171
175 // error union172 // error union
176 var number_or_error: %i32 = error.ArgNotFound;173 var number_or_error: error!i32 = error.ArgNotFound;
177174
178 warn("\nerror union 1\ntype: {}\nvalue: {}\n",175 warn("\nerror union 1\ntype: {}\nvalue: {}\n",
179 @typeName(@typeOf(number_or_error)), number_or_error);176 @typeName(@typeOf(number_or_error)), number_or_error);
...@@ -681,7 +678,7 @@ const warn = @import("std").debug.warn;...@@ -681,7 +678,7 @@ const warn = @import("std").debug.warn;
681extern fn foo_strict(x: f64) f64;678extern fn foo_strict(x: f64) f64;
682extern fn foo_optimized(x: f64) f64;679extern fn foo_optimized(x: f64) f64;
683680
684pub fn main() %void {681pub fn main() void {
685 const x = 0.001;682 const x = 0.001;
686 warn("optimized = {}\n", foo_optimized(x));683 warn("optimized = {}\n", foo_optimized(x));
687 warn("strict = {}\n", foo_strict(x));684 warn("strict = {}\n", foo_strict(x));
...@@ -1036,7 +1033,7 @@ a catch |err| b</code></pre></td>...@@ -1036,7 +1033,7 @@ a catch |err| b</code></pre></td>
1036 <code>err</code> is the <code>error</code> and is in scope of the expression <code>b</code>.1033 <code>err</code> is the <code>error</code> and is in scope of the expression <code>b</code>.
1037 </td>1034 </td>
1038 <td>1035 <td>
1039 <pre><code class="zig">const value: %u32 = null;1036 <pre><code class="zig">const value: error!u32 = error.Broken;
1040const unwrapped = value catch 1234;1037const unwrapped = value catch 1234;
1041unwrapped == 1234</code></pre>1038unwrapped == 1234</code></pre>
1042 </td>1039 </td>
...@@ -1269,9 +1266,10 @@ const ptr = &amp;x;...@@ -1269,9 +1266,10 @@ const ptr = &amp;x;
1269 {#header_close#}1266 {#header_close#}
1270 {#header_open|Precedence#}1267 {#header_open|Precedence#}
1271 <pre><code>x() x[] x.y1268 <pre><code>x() x[] x.y
1272!x -x -%x ~x *x &amp;x ?x %x ??x1269a!b
1270!x -x -%x ~x *x &amp;x ?x ??x
1273x{}1271x{}
1274* / % ** *%1272! * / % ** *%
1275+ - ++ +% -%1273+ - ++ +% -%
1276&lt;&lt; &gt;&gt;1274&lt;&lt; &gt;&gt;
1277&amp;1275&amp;
...@@ -2268,8 +2266,8 @@ fn eventuallyNullSequence() ?u32 {...@@ -2268,8 +2266,8 @@ fn eventuallyNullSequence() ?u32 {
2268 break :blk numbers_left;2266 break :blk numbers_left;
2269 };2267 };
2270}2268}
2271error ReachedZero;2269
2272fn eventuallyErrorSequence() %u32 {2270fn eventuallyErrorSequence() error!u32 {
2273 return if (numbers_left == 0) error.ReachedZero else blk: {2271 return if (numbers_left == 0) error.ReachedZero else blk: {
2274 numbers_left -= 1;2272 numbers_left -= 1;
2275 break :blk numbers_left;2273 break :blk numbers_left;
...@@ -2398,7 +2396,7 @@ fn typeNameLength(comptime T: type) usize {...@@ -2398,7 +2396,7 @@ fn typeNameLength(comptime T: type) usize {
2398// If expressions have three uses, corresponding to the three types:2396// If expressions have three uses, corresponding to the three types:
2399// * bool2397// * bool
2400// * ?T2398// * ?T
2401// * %T2399// * error!T
24022400
2403const assert = @import("std").debug.assert;2401const assert = @import("std").debug.assert;
24042402
...@@ -2459,20 +2457,18 @@ test "if nullable" {...@@ -2459,20 +2457,18 @@ test "if nullable" {
2459 }2457 }
2460}2458}
24612459
2462error BadValue;
2463error LessBadValue;
2464test "if error union" {2460test "if error union" {
2465 // If expressions test for errors.2461 // If expressions test for errors.
2466 // Note the |err| capture on the else.2462 // Note the |err| capture on the else.
24672463
2468 const a: %u32 = 0;2464 const a: error!u32 = 0;
2469 if (a) |value| {2465 if (a) |value| {
2470 assert(value == 0);2466 assert(value == 0);
2471 } else |err| {2467 } else |err| {
2472 unreachable;2468 unreachable;
2473 }2469 }
24742470
2475 const b: %u32 = error.BadValue;2471 const b: error!u32 = error.BadValue;
2476 if (b) |value| {2472 if (b) |value| {
2477 unreachable;2473 unreachable;
2478 } else |err| {2474 } else |err| {
...@@ -2490,7 +2486,7 @@ test "if error union" {...@@ -2490,7 +2486,7 @@ test "if error union" {
2490 }2486 }
24912487
2492 // Access the value by reference using a pointer capture.2488 // Access the value by reference using a pointer capture.
2493 var c: %u32 = 3;2489 var c: error!u32 = 3;
2494 if (c) |*value| {2490 if (c) |*value| {
2495 *value = 9;2491 *value = 9;
2496 } else |err| {2492 } else |err| {
...@@ -2558,8 +2554,7 @@ test "defer unwinding" {...@@ -2558,8 +2554,7 @@ test "defer unwinding" {
2558//2554//
2559// This is especially useful in allowing a function to clean up properly2555// This is especially useful in allowing a function to clean up properly
2560// on error, and replaces goto error handling tactics as seen in c.2556// on error, and replaces goto error handling tactics as seen in c.
2561error DeferError;2557fn deferErrorExample(is_error: bool) !void {
2562fn deferErrorExample(is_error: bool) %void {
2563 warn("\nstart of function\n");2558 warn("\nstart of function\n");
25642559
2565 // This will always be executed on exit2560 // This will always be executed on exit
...@@ -2668,7 +2663,7 @@ test "foo" {...@@ -2668,7 +2663,7 @@ test "foo" {
2668 assert(value == 1234);2663 assert(value == 1234);
2669}2664}
26702665
2671fn bar() %u32 {2666fn bar() error!u32 {
2672 return 1234;2667 return 1234;
2673}2668}
26742669
...@@ -2791,13 +2786,8 @@ test "fn reflection" {...@@ -2791,13 +2786,8 @@ test "fn reflection" {
2791 One of the distinguishing features of Zig is its exception handling strategy.2786 One of the distinguishing features of Zig is its exception handling strategy.
2792 </p>2787 </p>
2793 <p>2788 <p>
2794 Among the top level declarations available is the error value declaration:2789 TODO rewrite the errors section to take into account error sets
2795 </p>2790 </p>
2796 {#code_begin|syntax#}
2797error FileNotFound;
2798error OutOfMemory;
2799error UnexpectedToken;
2800 {#code_end#}
2801 <p>2791 <p>
2802 These error values are assigned an unsigned integer value greater than 0 at2792 These error values are assigned an unsigned integer value greater than 0 at
2803 compile time. You are allowed to declare the same error value more than once,2793 compile time. You are allowed to declare the same error value more than once,
...@@ -2809,26 +2799,23 @@ error UnexpectedToken;...@@ -2809,26 +2799,23 @@ error UnexpectedToken;
2809 </p>2799 </p>
2810 <p>2800 <p>
2811 Each error value across the entire compilation unit gets a unique integer,2801 Each error value across the entire compilation unit gets a unique integer,
2812 and this determines the size of the pure error type.2802 and this determines the size of the error set type.
2813 </p>2803 </p>
2814 <p>2804 <p>
2815 The pure error type is one of the error values, and in the same way that pointers2805 The error set type is one of the error values, and in the same way that pointers
2816 cannot be null, a pure error is always an error.2806 cannot be null, a error set instance is always an error.
2817 </p>2807 </p>
2818 {#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}2808 {#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}
2819 <p>2809 <p>
2820 Most of the time you will not find yourself using a pure error type. Instead,2810 Most of the time you will not find yourself using an error set type. Instead,
2821 likely you will be using the error union type. This is when you take a normal type,2811 likely you will be using the error union type. This is when you take an error set
2822 and prefix it with the <code>%</code> operator.2812 and a normal type, and create an error union with the <code>!</code> binary operator.
2823 </p>2813 </p>
2824 <p>2814 <p>
2825 Here is a function to parse a string into a 64-bit integer:2815 Here is a function to parse a string into a 64-bit integer:
2826 </p>2816 </p>
2827 {#code_begin|test#}2817 {#code_begin|test#}
2828error InvalidChar;2818pub fn parseU64(buf: []const u8, radix: u8) !u64 {
2829error Overflow;
2830
2831pub fn parseU64(buf: []const u8, radix: u8) %u64 {
2832 var x: u64 = 0;2819 var x: u64 = 0;
28332820
2834 for (buf) |c| {2821 for (buf) |c| {
...@@ -2867,13 +2854,14 @@ test "parse u64" {...@@ -2867,13 +2854,14 @@ test "parse u64" {
2867}2854}
2868 {#code_end#}2855 {#code_end#}
2869 <p>2856 <p>
2870 Notice the return type is <code>%u64</code>. This means that the function2857 Notice the return type is <code>!u64</code>. This means that the function
2871 either returns an unsigned 64 bit integer, or an error.2858 either returns an unsigned 64 bit integer, or an error. We left off the error set
2859 to the left of the <code>!</code>, so the error set is inferred.
2872 </p>2860 </p>
2873 <p>2861 <p>
2874 Within the function definition, you can see some return statements that return2862 Within the function definition, you can see some return statements that return
2875 a pure error, and at the bottom a return statement that returns a <code>u64</code>.2863 an error, and at the bottom a return statement that returns a <code>u64</code>.
2876 Both types implicitly cast to <code>%u64</code>.2864 Both types implicitly cast to <code>error!u64</code>.
2877 </p>2865 </p>
2878 <p>2866 <p>
2879 What it looks like to use this function varies depending on what you're2867 What it looks like to use this function varies depending on what you're
...@@ -2900,7 +2888,7 @@ fn doAThing(str: []u8) void {...@@ -2900,7 +2888,7 @@ fn doAThing(str: []u8) void {
2900 <p>Let's say you wanted to return the error if you got one, otherwise continue with the2888 <p>Let's say you wanted to return the error if you got one, otherwise continue with the
2901 function logic:</p>2889 function logic:</p>
2902 {#code_begin|syntax#}2890 {#code_begin|syntax#}
2903fn doAThing(str: []u8) %void {2891fn doAThing(str: []u8) !void {
2904 const number = parseU64(str, 10) catch |err| return err;2892 const number = parseU64(str, 10) catch |err| return err;
2905 // ...2893 // ...
2906}2894}
...@@ -2909,7 +2897,7 @@ fn doAThing(str: []u8) %void {...@@ -2909,7 +2897,7 @@ fn doAThing(str: []u8) %void {
2909 There is a shortcut for this. The <code>try</code> expression:2897 There is a shortcut for this. The <code>try</code> expression:
2910 </p>2898 </p>
2911 {#code_begin|syntax#}2899 {#code_begin|syntax#}
2912fn doAThing(str: []u8) %void {2900fn doAThing(str: []u8) !void {
2913 const number = try parseU64(str, 10);2901 const number = try parseU64(str, 10);
2914 // ...2902 // ...
2915}2903}
...@@ -2959,7 +2947,7 @@ fn doAThing(str: []u8) void {...@@ -2959,7 +2947,7 @@ fn doAThing(str: []u8) void {
2959 Example:2947 Example:
2960 </p>2948 </p>
2961 {#code_begin|syntax#}2949 {#code_begin|syntax#}
2962fn createFoo(param: i32) %Foo {2950fn createFoo(param: i32) !Foo {
2963 const foo = try tryToAllocateFoo();2951 const foo = try tryToAllocateFoo();
2964 // now we have allocated foo. we need to free it if the function fails.2952 // now we have allocated foo. we need to free it if the function fails.
2965 // but we want to return it if the function succeeds.2953 // but we want to return it if the function succeeds.
...@@ -2999,15 +2987,13 @@ fn createFoo(param: i32) %Foo {...@@ -2999,15 +2987,13 @@ fn createFoo(param: i32) %Foo {
2999 </ul>2987 </ul>
3000 {#see_also|defer|if|switch#}2988 {#see_also|defer|if|switch#}
3001 {#header_open|Error Union Type#}2989 {#header_open|Error Union Type#}
3002 <p>An error union is created by putting a <code>%</code> in front of a type.2990 <p>An error union is created with the <code>!</code> binary operator.
3003 You can use compile-time reflection to access the child type of an error union:</p>2991 You can use compile-time reflection to access the child type of an error union:</p>
3004 {#code_begin|test#}2992 {#code_begin|test#}
3005const assert = @import("std").debug.assert;2993const assert = @import("std").debug.assert;
30062994
3007error SomeError;
3008
3009test "error union" {2995test "error union" {
3010 var foo: %i32 = undefined;2996 var foo: error!i32 = undefined;
30112997
3012 // Implicitly cast from child type of an error union:2998 // Implicitly cast from child type of an error union:
3013 foo = 1234;2999 foo = 1234;
...@@ -3015,8 +3001,11 @@ test "error union" {...@@ -3015,8 +3001,11 @@ test "error union" {
3015 // Implicitly cast from an error set:3001 // Implicitly cast from an error set:
3016 foo = error.SomeError;3002 foo = error.SomeError;
30173003
3018 // Use compile-time reflection to access the child type of an error union:3004 // Use compile-time reflection to access the payload type of an error union:
3019 comptime assert(@typeOf(foo).Child == i32);3005 comptime assert(@typeOf(foo).Payload == i32);
3006
3007 // Use compile-time reflection to access the error set type of an error union:
3008 comptime assert(@typeOf(foo).ErrorSet == error);
3020}3009}
3021 {#code_end#}3010 {#code_end#}
3022 {#header_close#}3011 {#header_close#}
...@@ -3610,7 +3599,7 @@ pub fn main() void {...@@ -3610,7 +3599,7 @@ pub fn main() void {
36103599
3611 {#code_begin|syntax#}3600 {#code_begin|syntax#}
3612/// Calls print and then flushes the buffer.3601/// Calls print and then flushes the buffer.
3613pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void {3602pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) error!void {
3614 const State = enum {3603 const State = enum {
3615 Start,3604 Start,
3616 OpenBrace,3605 OpenBrace,
...@@ -3682,7 +3671,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void {...@@ -3682,7 +3671,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void {
3682 and emits a function that actually looks like this:3671 and emits a function that actually looks like this:
3683 </p>3672 </p>
3684 {#code_begin|syntax#}3673 {#code_begin|syntax#}
3685pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void {3674pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void {
3686 try self.write("here is a string: '");3675 try self.write("here is a string: '");
3687 try self.printValue(arg0);3676 try self.printValue(arg0);
3688 try self.write("' here is a number: ");3677 try self.write("' here is a number: ");
...@@ -3696,7 +3685,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void {...@@ -3696,7 +3685,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void {
3696 on the type:3685 on the type:
3697 </p>3686 </p>
3698 {#code_begin|syntax#}3687 {#code_begin|syntax#}
3699pub fn printValue(self: &OutStream, value: var) %void {3688pub fn printValue(self: &OutStream, value: var) !void {
3700 const T = @typeOf(value);3689 const T = @typeOf(value);
3701 if (@isInteger(T)) {3690 if (@isInteger(T)) {
3702 return self.printInt(T, value);3691 return self.printInt(T, value);
...@@ -4647,7 +4636,7 @@ pub const TypeId = enum {...@@ -4647,7 +4636,7 @@ pub const TypeId = enum {
4647 {#code_begin|syntax#}4636 {#code_begin|syntax#}
4648const Builder = @import("std").build.Builder;4637const Builder = @import("std").build.Builder;
46494638
4650pub fn build(b: &Builder) %void {4639pub fn build(b: &Builder) void {
4651 const exe = b.addExecutable("example", "example.zig");4640 const exe = b.addExecutable("example", "example.zig");
4652 exe.setBuildMode(b.standardReleaseOptions());4641 exe.setBuildMode(b.standardReleaseOptions());
4653 b.default_step.dependOn(&exe.step);4642 b.default_step.dependOn(&exe.step);
...@@ -4789,7 +4778,7 @@ comptime {...@@ -4789,7 +4778,7 @@ comptime {
4789 {#code_begin|exe_err#}4778 {#code_begin|exe_err#}
4790const math = @import("std").math;4779const math = @import("std").math;
4791const warn = @import("std").debug.warn;4780const warn = @import("std").debug.warn;
4792pub fn main() %void {4781pub fn main() !void {
4793 var byte: u8 = 255;4782 var byte: u8 = 255;
47944783
4795 byte = if (math.add(u8, byte, 1)) |result| result else |err| {4784 byte = if (math.add(u8, byte, 1)) |result| result else |err| {
...@@ -4817,7 +4806,7 @@ pub fn main() %void {...@@ -4817,7 +4806,7 @@ pub fn main() %void {
4817 </p>4806 </p>
4818 {#code_begin|exe#}4807 {#code_begin|exe#}
4819const warn = @import("std").debug.warn;4808const warn = @import("std").debug.warn;
4820pub fn main() %void {4809pub fn main() void {
4821 var byte: u8 = 255;4810 var byte: u8 = 255;
48224811
4823 var result: u8 = undefined;4812 var result: u8 = undefined;
...@@ -4926,14 +4915,12 @@ pub fn main() void {...@@ -4926,14 +4915,12 @@ pub fn main() void {
4926 {#header_close#}4915 {#header_close#}
4927 {#header_open|Attempt to Unwrap Error#}4916 {#header_open|Attempt to Unwrap Error#}
4928 <p>At compile-time:</p>4917 <p>At compile-time:</p>
4929 {#code_begin|test_err|unable to unwrap error 'UnableToReturnNumber'#}4918 {#code_begin|test_err|caught unexpected error 'UnableToReturnNumber'#}
4930comptime {4919comptime {
4931 const number = getNumberOrFail() catch unreachable;4920 const number = getNumberOrFail() catch unreachable;
4932}4921}
49334922
4934error UnableToReturnNumber;4923fn getNumberOrFail() !i32 {
4935
4936fn getNumberOrFail() %i32 {
4937 return error.UnableToReturnNumber;4924 return error.UnableToReturnNumber;
4938}4925}
4939 {#code_end#}4926 {#code_end#}
...@@ -4953,9 +4940,7 @@ pub fn main() void {...@@ -4953,9 +4940,7 @@ pub fn main() void {
4953 }4940 }
4954}4941}
49554942
4956error UnableToReturnNumber;4943fn getNumberOrFail() !i32 {
4957
4958fn getNumberOrFail() %i32 {
4959 return error.UnableToReturnNumber;4944 return error.UnableToReturnNumber;
4960}4945}
4961 {#code_end#}4946 {#code_end#}
...@@ -4963,7 +4948,6 @@ fn getNumberOrFail() %i32 {...@@ -4963,7 +4948,6 @@ fn getNumberOrFail() %i32 {
4963 {#header_open|Invalid Error Code#}4948 {#header_open|Invalid Error Code#}
4964 <p>At compile-time:</p>4949 <p>At compile-time:</p>
4965 {#code_begin|test_err|integer value 11 represents no error#}4950 {#code_begin|test_err|integer value 11 represents no error#}
4966error AnError;
4967comptime {4951comptime {
4968 const err = error.AnError;4952 const err = error.AnError;
4969 const number = u32(err) + 10;4953 const number = u32(err) + 10;
...@@ -5363,7 +5347,7 @@ int main(int argc, char **argv) {...@@ -5363,7 +5347,7 @@ int main(int argc, char **argv) {
5363 {#code_begin|syntax#}5347 {#code_begin|syntax#}
5364const Builder = @import("std").build.Builder;5348const Builder = @import("std").build.Builder;
53655349
5366pub fn build(b: &Builder) %void {5350pub fn build(b: &Builder) void {
5367 const obj = b.addObject("base64", "base64.zig");5351 const obj = b.addObject("base64", "base64.zig");
53685352
5369 const exe = b.addCExecutable("test");5353 const exe = b.addCExecutable("test");
...@@ -5641,14 +5625,12 @@ fn readU32Be() u32 {}...@@ -5641,14 +5625,12 @@ fn readU32Be() u32 {}
5641 {#header_open|Grammar#}5625 {#header_open|Grammar#}
5642 <pre><code class="nohighlight">Root = many(TopLevelItem) EOF5626 <pre><code class="nohighlight">Root = many(TopLevelItem) EOF
56435627
5644TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl5628TopLevelItem = CompTimeExpression(Block) | TopLevelDecl | TestDecl
56455629
5646TestDecl = "test" String Block5630TestDecl = "test" String Block
56475631
5648TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)5632TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
56495633
5650ErrorValueDecl = "error" Symbol ";"
5651
5652GlobalVarDecl = option("export") VariableDeclaration ";"5634GlobalVarDecl = option("export") VariableDeclaration ";"
56535635
5654LocalVarDecl = option("comptime") VariableDeclaration5636LocalVarDecl = option("comptime") VariableDeclaration
...@@ -5663,7 +5645,7 @@ UseDecl = "use" Expression ";"...@@ -5663,7 +5645,7 @@ UseDecl = "use" Expression ";"
56635645
5664ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"5646ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
56655647
5666FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") TypeExpr5648FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr
56675649
5668FnDef = option("inline" | "export") FnProto Block5650FnDef = option("inline" | "export") FnProto Block
56695651
...@@ -5675,7 +5657,9 @@ Block = option(Symbol ":") "{" many(Statement) "}"...@@ -5675,7 +5657,9 @@ Block = option(Symbol ":") "{" many(Statement) "}"
56755657
5676Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"5658Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
56775659
5678TypeExpr = PrefixOpExpression | "var"5660TypeExpr = ErrorSetExpr | "var"
5661
5662ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
56795663
5680BlockOrExpression = Block | Expression5664BlockOrExpression = Block | Expression
56815665
...@@ -5757,9 +5741,9 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |...@@ -5757,9 +5741,9 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
57575741
5758CurlySuffixExpression = TypeExpr option(ContainerInitExpression)5742CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
57595743
5760MultiplyOperator = "*" | "/" | "%" | "**" | "*%"5744MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
57615745
5762PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression5746PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
57635747
5764SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)5748SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
57655749
...@@ -5777,9 +5761,9 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")...@@ -5777,9 +5761,9 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
57775761
5778StructLiteralField = "." Symbol "=" Expression5762StructLiteralField = "." Symbol "=" Expression
57795763
5780PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "??" | "-%" | "try"5764PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
57815765
5782PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))5766PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
57835767
5784ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr5768ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
57855769
...@@ -5787,6 +5771,8 @@ GroupedExpression = "(" Expression ")"...@@ -5787,6 +5771,8 @@ GroupedExpression = "(" Expression ")"
57875771
5788KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"5772KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
57895773
5774ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
5775
5790ContainerDecl = option("extern" | "packed")5776ContainerDecl = option("extern" | "packed")
5791 ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))5777 ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
5792 "{" many(ContainerMember) "}"</code></pre>5778 "{" many(ContainerMember) "}"</code></pre>
example/cat/main.zig+5-5
...@@ -5,7 +5,7 @@ const os = std.os;...@@ -5,7 +5,7 @@ const os = std.os;
5const warn = std.debug.warn;5const warn = std.debug.warn;
6const allocator = std.debug.global_allocator;6const allocator = std.debug.global_allocator;
77
8pub fn main() %void {8pub fn main() !void {
9 var args_it = os.args();9 var args_it = os.args();
10 const exe = try unwrapArg(??args_it.next(allocator));10 const exe = try unwrapArg(??args_it.next(allocator));
11 var catted_anything = false;11 var catted_anything = false;
...@@ -20,7 +20,7 @@ pub fn main() %void {...@@ -20,7 +20,7 @@ pub fn main() %void {
20 } else if (arg[0] == '-') {20 } else if (arg[0] == '-') {
21 return usage(exe);21 return usage(exe);
22 } else {22 } else {
23 var file = io.File.openRead(arg, null) catch |err| {23 var file = os.File.openRead(allocator, arg) catch |err| {
24 warn("Unable to open file: {}\n", @errorName(err));24 warn("Unable to open file: {}\n", @errorName(err));
25 return err;25 return err;
26 };26 };
...@@ -36,12 +36,12 @@ pub fn main() %void {...@@ -36,12 +36,12 @@ pub fn main() %void {
36 }36 }
37}37}
3838
39fn usage(exe: []const u8) %void {39fn usage(exe: []const u8) !void {
40 warn("Usage: {} [FILE]...\n", exe);40 warn("Usage: {} [FILE]...\n", exe);
41 return error.Invalid;41 return error.Invalid;
42}42}
4343
44fn cat_file(stdout: &io.File, file: &io.File) %void {44fn cat_file(stdout: &os.File, file: &os.File) !void {
45 var buf: [1024 * 4]u8 = undefined;45 var buf: [1024 * 4]u8 = undefined;
4646
47 while (true) {47 while (true) {
...@@ -61,7 +61,7 @@ fn cat_file(stdout: &io.File, file: &io.File) %void {...@@ -61,7 +61,7 @@ fn cat_file(stdout: &io.File, file: &io.File) %void {
61 }61 }
62}62}
6363
64fn unwrapArg(arg: %[]u8) %[]u8 {64fn unwrapArg(arg: error![]u8) ![]u8 {
65 return arg catch |err| {65 return arg catch |err| {
66 warn("Unable to parse command line: {}\n", err);66 warn("Unable to parse command line: {}\n", err);
67 return err;67 return err;
example/guess_number/main.zig+1-1
...@@ -5,7 +5,7 @@ const fmt = std.fmt;...@@ -5,7 +5,7 @@ const fmt = std.fmt;
5const Rand = std.rand.Rand;5const Rand = std.rand.Rand;
6const os = std.os;6const os = std.os;
77
8pub fn main() %void {8pub fn main() !void {
9 var stdout_file = try io.getStdOut();9 var stdout_file = try io.getStdOut();
10 var stdout_file_stream = io.FileOutStream.init(&stdout_file);10 var stdout_file_stream = io.FileOutStream.init(&stdout_file);
11 const stdout = &stdout_file_stream.stream;11 const stdout = &stdout_file_stream.stream;
example/hello_world/hello.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn main() %void {3pub fn main() !void {
4 // If this program is run without stdout attached, exit with an error.4 // If this program is run without stdout attached, exit with an error.
5 var stdout_file = try std.io.getStdOut();5 var stdout_file = try std.io.getStdOut();
6 // If this program encounters pipe failure when printing to stdout, exit6 // If this program encounters pipe failure when printing to stdout, exit
example/mix_o_files/base64.zig+3
...@@ -8,3 +8,6 @@ export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8,...@@ -8,3 +8,6 @@ export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8,
8 base64_decoder.decode(dest[0..decoded_size], src);8 base64_decoder.decode(dest[0..decoded_size], src);
9 return decoded_size;9 return decoded_size;
10}10}
11
12var x: c_int = 1234;
13export var x_ptr = &x;
example/mix_o_files/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) void {
4 const obj = b.addObject("base64", "base64.zig");4 const obj = b.addObject("base64", "base64.zig");
55
6 const exe = b.addCExecutable("test");6 const exe = b.addCExecutable("test");
example/mix_o_files/test.c+4
...@@ -4,6 +4,8 @@...@@ -4,6 +4,8 @@
4#include <assert.h>4#include <assert.h>
5#include <string.h>5#include <string.h>
66
7extern int *x_ptr;
8
7int main(int argc, char **argv) {9int main(int argc, char **argv) {
8 const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz";10 const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz";
9 char buf[200];11 char buf[200];
...@@ -12,5 +14,7 @@ int main(int argc, char **argv) {...@@ -12,5 +14,7 @@ int main(int argc, char **argv) {
12 buf[len] = 0;14 buf[len] = 0;
13 assert(strcmp(buf, "all your base are belong to us") == 0);15 assert(strcmp(buf, "all your base are belong to us") == 0);
1416
17 assert(*x_ptr == 1234);
18
15 return 0;19 return 0;
16}20}
example/shared_library/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) void {
4 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));4 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
55
6 const exe = b.addCExecutable("test");6 const exe = b.addCExecutable("test");
src-self-hosted/ast.zig deleted-271
...@@ -1,271 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const Token = @import("tokenizer.zig").Token;
5const mem = std.mem;
6
7pub const Node = struct {
8 id: Id,
9
10 pub const Id = enum {
11 Root,
12 VarDecl,
13 Identifier,
14 FnProto,
15 ParamDecl,
16 Block,
17 InfixOp,
18 PrefixOp,
19 IntegerLiteral,
20 FloatLiteral,
21 };
22
23 pub fn iterate(base: &Node, index: usize) ?&Node {
24 return switch (base.id) {
25 Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
26 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
27 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
28 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
29 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
30 Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
31 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
32 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
33 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
34 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
35 };
36 }
37
38 pub fn destroy(base: &Node, allocator: &mem.Allocator) void {
39 return switch (base.id) {
40 Id.Root => allocator.destroy(@fieldParentPtr(NodeRoot, "base", base)),
41 Id.VarDecl => allocator.destroy(@fieldParentPtr(NodeVarDecl, "base", base)),
42 Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
43 Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
44 Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
45 Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
46 Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
47 Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
48 Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
49 Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
50 };
51 }
52};
53
54pub const NodeRoot = struct {
55 base: Node,
56 decls: ArrayList(&Node),
57
58 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
59 if (index < self.decls.len) {
60 return self.decls.items[self.decls.len - index - 1];
61 }
62 return null;
63 }
64};
65
66pub const NodeVarDecl = struct {
67 base: Node,
68 visib_token: ?Token,
69 name_token: Token,
70 eq_token: Token,
71 mut_token: Token,
72 comptime_token: ?Token,
73 extern_token: ?Token,
74 lib_name: ?&Node,
75 type_node: ?&Node,
76 align_node: ?&Node,
77 init_node: ?&Node,
78
79 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
80 var i = index;
81
82 if (self.type_node) |type_node| {
83 if (i < 1) return type_node;
84 i -= 1;
85 }
86
87 if (self.align_node) |align_node| {
88 if (i < 1) return align_node;
89 i -= 1;
90 }
91
92 if (self.init_node) |init_node| {
93 if (i < 1) return init_node;
94 i -= 1;
95 }
96
97 return null;
98 }
99};
100
101pub const NodeIdentifier = struct {
102 base: Node,
103 name_token: Token,
104
105 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
106 return null;
107 }
108};
109
110pub const NodeFnProto = struct {
111 base: Node,
112 visib_token: ?Token,
113 fn_token: Token,
114 name_token: ?Token,
115 params: ArrayList(&Node),
116 return_type: &Node,
117 var_args_token: ?Token,
118 extern_token: ?Token,
119 inline_token: ?Token,
120 cc_token: ?Token,
121 body_node: ?&Node,
122 lib_name: ?&Node, // populated if this is an extern declaration
123 align_expr: ?&Node, // populated if align(A) is present
124
125 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
126 var i = index;
127
128 if (self.body_node) |body_node| {
129 if (i < 1) return body_node;
130 i -= 1;
131 }
132
133 if (i < 1) return self.return_type;
134 i -= 1;
135
136 if (self.align_expr) |align_expr| {
137 if (i < 1) return align_expr;
138 i -= 1;
139 }
140
141 if (i < self.params.len) return self.params.items[self.params.len - i - 1];
142 i -= self.params.len;
143
144 if (self.lib_name) |lib_name| {
145 if (i < 1) return lib_name;
146 i -= 1;
147 }
148
149 return null;
150 }
151};
152
153pub const NodeParamDecl = struct {
154 base: Node,
155 comptime_token: ?Token,
156 noalias_token: ?Token,
157 name_token: ?Token,
158 type_node: &Node,
159 var_args_token: ?Token,
160
161 pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {
162 var i = index;
163
164 if (i < 1) return self.type_node;
165 i -= 1;
166
167 return null;
168 }
169};
170
171pub const NodeBlock = struct {
172 base: Node,
173 begin_token: Token,
174 end_token: Token,
175 statements: ArrayList(&Node),
176
177 pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
178 var i = index;
179
180 if (i < self.statements.len) return self.statements.items[i];
181 i -= self.statements.len;
182
183 return null;
184 }
185};
186
187pub const NodeInfixOp = struct {
188 base: Node,
189 op_token: Token,
190 lhs: &Node,
191 op: InfixOp,
192 rhs: &Node,
193
194 const InfixOp = enum {
195 EqualEqual,
196 BangEqual,
197 };
198
199 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
200 var i = index;
201
202 if (i < 1) return self.lhs;
203 i -= 1;
204
205 switch (self.op) {
206 InfixOp.EqualEqual => {},
207 InfixOp.BangEqual => {},
208 }
209
210 if (i < 1) return self.rhs;
211 i -= 1;
212
213 return null;
214 }
215};
216
217pub const NodePrefixOp = struct {
218 base: Node,
219 op_token: Token,
220 op: PrefixOp,
221 rhs: &Node,
222
223 const PrefixOp = union(enum) {
224 Return,
225 AddrOf: AddrOfInfo,
226 };
227 const AddrOfInfo = struct {
228 align_expr: ?&Node,
229 bit_offset_start_token: ?Token,
230 bit_offset_end_token: ?Token,
231 const_token: ?Token,
232 volatile_token: ?Token,
233 };
234
235 pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {
236 var i = index;
237
238 switch (self.op) {
239 PrefixOp.Return => {},
240 PrefixOp.AddrOf => |addr_of_info| {
241 if (addr_of_info.align_expr) |align_expr| {
242 if (i < 1) return align_expr;
243 i -= 1;
244 }
245 },
246 }
247
248 if (i < 1) return self.rhs;
249 i -= 1;
250
251 return null;
252 }
253};
254
255pub const NodeIntegerLiteral = struct {
256 base: Node,
257 token: Token,
258
259 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
260 return null;
261 }
262};
263
264pub const NodeFloatLiteral = struct {
265 base: Node,
266 token: Token,
267
268 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
269 return null;
270 }
271};
src-self-hosted/main.zig+56-44
...@@ -14,21 +14,8 @@ const builtin = @import("builtin");...@@ -14,21 +14,8 @@ const builtin = @import("builtin");
14const ArrayList = std.ArrayList;14const ArrayList = std.ArrayList;
15const c = @import("c.zig");15const c = @import("c.zig");
1616
17error InvalidCommandLineArguments;
18error ZigLibDirNotFound;
19error ZigInstallationNotFound;
20
21const default_zig_cache_name = "zig-cache";17const default_zig_cache_name = "zig-cache";
2218
23pub fn main() %void {
24 main2() catch |err| {
25 if (err != error.InvalidCommandLineArguments) {
26 warn("{}\n", @errorName(err));
27 }
28 return err;
29 };
30}
31
32const Cmd = enum {19const Cmd = enum {
33 None,20 None,
34 Build,21 Build,
...@@ -39,21 +26,25 @@ const Cmd = enum {...@@ -39,21 +26,25 @@ const Cmd = enum {
39 Targets,26 Targets,
40};27};
4128
42fn badArgs(comptime format: []const u8, args: ...) error {29fn badArgs(comptime format: []const u8, args: ...) noreturn {
43 var stderr = try io.getStdErr();30 var stderr = io.getStdErr() catch std.os.exit(1);
44 var stderr_stream_adapter = io.FileOutStream.init(&stderr);31 var stderr_stream_adapter = io.FileOutStream.init(&stderr);
45 const stderr_stream = &stderr_stream_adapter.stream;32 const stderr_stream = &stderr_stream_adapter.stream;
46 try stderr_stream.print(format ++ "\n\n", args);33 stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
47 try printUsage(&stderr_stream_adapter.stream);34 printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
48 return error.InvalidCommandLineArguments;35 std.os.exit(1);
49}36}
5037
51pub fn main2() %void {38pub fn main() !void {
52 const allocator = std.heap.c_allocator;39 const allocator = std.heap.c_allocator;
5340
54 const args = try os.argsAlloc(allocator);41 const args = try os.argsAlloc(allocator);
55 defer os.argsFree(allocator, args);42 defer os.argsFree(allocator, args);
5643
44 if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
45 return fmtMain(allocator, args[2..]);
46 }
47
57 var cmd = Cmd.None;48 var cmd = Cmd.None;
58 var build_kind: Module.Kind = undefined;49 var build_kind: Module.Kind = undefined;
59 var build_mode: builtin.Mode = builtin.Mode.Debug;50 var build_mode: builtin.Mode = builtin.Mode.Debug;
...@@ -173,7 +164,7 @@ pub fn main2() %void {...@@ -173,7 +164,7 @@ pub fn main2() %void {
173 } else if (mem.eql(u8, arg, "--pkg-end")) {164 } else if (mem.eql(u8, arg, "--pkg-end")) {
174 @panic("TODO --pkg-end");165 @panic("TODO --pkg-end");
175 } else if (arg_i + 1 >= args.len) {166 } else if (arg_i + 1 >= args.len) {
176 return badArgs("expected another argument after {}", arg);167 badArgs("expected another argument after {}", arg);
177 } else {168 } else {
178 arg_i += 1;169 arg_i += 1;
179 if (mem.eql(u8, arg, "--output")) {170 if (mem.eql(u8, arg, "--output")) {
...@@ -188,7 +179,7 @@ pub fn main2() %void {...@@ -188,7 +179,7 @@ pub fn main2() %void {
188 } else if (mem.eql(u8, args[arg_i], "off")) {179 } else if (mem.eql(u8, args[arg_i], "off")) {
189 color = ErrColor.Off;180 color = ErrColor.Off;
190 } else {181 } else {
191 return badArgs("--color options are 'auto', 'on', or 'off'");182 badArgs("--color options are 'auto', 'on', or 'off'");
192 }183 }
193 } else if (mem.eql(u8, arg, "--emit")) {184 } else if (mem.eql(u8, arg, "--emit")) {
194 if (mem.eql(u8, args[arg_i], "asm")) {185 if (mem.eql(u8, args[arg_i], "asm")) {
...@@ -198,7 +189,7 @@ pub fn main2() %void {...@@ -198,7 +189,7 @@ pub fn main2() %void {
198 } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {189 } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
199 emit_file_type = Emit.LlvmIr;190 emit_file_type = Emit.LlvmIr;
200 } else {191 } else {
201 return badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");192 badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
202 }193 }
203 } else if (mem.eql(u8, arg, "--name")) {194 } else if (mem.eql(u8, arg, "--name")) {
204 out_name_arg = args[arg_i];195 out_name_arg = args[arg_i];
...@@ -266,7 +257,7 @@ pub fn main2() %void {...@@ -266,7 +257,7 @@ pub fn main2() %void {
266 } else if (mem.eql(u8, arg, "--test-cmd")) {257 } else if (mem.eql(u8, arg, "--test-cmd")) {
267 @panic("TODO --test-cmd");258 @panic("TODO --test-cmd");
268 } else {259 } else {
269 return badArgs("invalid argument: {}", arg);260 badArgs("invalid argument: {}", arg);
270 }261 }
271 }262 }
272 } else if (cmd == Cmd.None) {263 } else if (cmd == Cmd.None) {
...@@ -289,18 +280,18 @@ pub fn main2() %void {...@@ -289,18 +280,18 @@ pub fn main2() %void {
289 cmd = Cmd.Test;280 cmd = Cmd.Test;
290 build_kind = Module.Kind.Exe;281 build_kind = Module.Kind.Exe;
291 } else {282 } else {
292 return badArgs("unrecognized command: {}", arg);283 badArgs("unrecognized command: {}", arg);
293 }284 }
294 } else switch (cmd) {285 } else switch (cmd) {
295 Cmd.Build, Cmd.TranslateC, Cmd.Test => {286 Cmd.Build, Cmd.TranslateC, Cmd.Test => {
296 if (in_file_arg == null) {287 if (in_file_arg == null) {
297 in_file_arg = arg;288 in_file_arg = arg;
298 } else {289 } else {
299 return badArgs("unexpected extra parameter: {}", arg);290 badArgs("unexpected extra parameter: {}", arg);
300 }291 }
301 },292 },
302 Cmd.Version, Cmd.Zen, Cmd.Targets => {293 Cmd.Version, Cmd.Zen, Cmd.Targets => {
303 return badArgs("unexpected extra parameter: {}", arg);294 badArgs("unexpected extra parameter: {}", arg);
304 },295 },
305 Cmd.None => unreachable,296 Cmd.None => unreachable,
306 }297 }
...@@ -337,15 +328,15 @@ pub fn main2() %void {...@@ -337,15 +328,15 @@ pub fn main2() %void {
337// }328// }
338329
339 switch (cmd) {330 switch (cmd) {
340 Cmd.None => return badArgs("expected command"),331 Cmd.None => badArgs("expected command"),
341 Cmd.Zen => return printZen(),332 Cmd.Zen => return printZen(),
342 Cmd.Build, Cmd.Test, Cmd.TranslateC => {333 Cmd.Build, Cmd.Test, Cmd.TranslateC => {
343 if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {334 if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
344 return badArgs("expected source file argument or at least one --object or --assembly argument");335 badArgs("expected source file argument or at least one --object or --assembly argument");
345 } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {336 } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
346 return badArgs("expected source file argument");337 badArgs("expected source file argument");
347 } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {338 } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
348 return badArgs("When building an object file, --object arguments are invalid");339 badArgs("When building an object file, --object arguments are invalid");
349 }340 }
350341
351 const root_name = switch (cmd) {342 const root_name = switch (cmd) {
...@@ -355,9 +346,9 @@ pub fn main2() %void {...@@ -355,9 +346,9 @@ pub fn main2() %void {
355 } else if (in_file_arg) |in_file_path| {346 } else if (in_file_arg) |in_file_path| {
356 const basename = os.path.basename(in_file_path);347 const basename = os.path.basename(in_file_path);
357 var it = mem.split(basename, ".");348 var it = mem.split(basename, ".");
358 break :x it.next() ?? return badArgs("file name cannot be empty");349 break :x it.next() ?? badArgs("file name cannot be empty");
359 } else {350 } else {
360 return badArgs("--name [name] not provided and unable to infer");351 badArgs("--name [name] not provided and unable to infer");
361 }352 }
362 },353 },
363 Cmd.Test => "test",354 Cmd.Test => "test",
...@@ -432,7 +423,7 @@ pub fn main2() %void {...@@ -432,7 +423,7 @@ pub fn main2() %void {
432 module.linker_rdynamic = rdynamic;423 module.linker_rdynamic = rdynamic;
433424
434 if (mmacosx_version_min != null and mios_version_min != null) {425 if (mmacosx_version_min != null and mios_version_min != null) {
435 return badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");426 badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
436 }427 }
437428
438 if (mmacosx_version_min) |ver| {429 if (mmacosx_version_min) |ver| {
...@@ -472,7 +463,7 @@ pub fn main2() %void {...@@ -472,7 +463,7 @@ pub fn main2() %void {
472 }463 }
473}464}
474465
475fn printUsage(stream: &io.OutStream) %void {466fn printUsage(stream: var) !void {
476 try stream.write(467 try stream.write(
477 \\Usage: zig [command] [options]468 \\Usage: zig [command] [options]
478 \\469 \\
...@@ -481,6 +472,7 @@ fn printUsage(stream: &io.OutStream) %void {...@@ -481,6 +472,7 @@ fn printUsage(stream: &io.OutStream) %void {
481 \\ build-exe [source] create executable from source or object files472 \\ build-exe [source] create executable from source or object files
482 \\ build-lib [source] create library from source or object files473 \\ build-lib [source] create library from source or object files
483 \\ build-obj [source] create object from source or assembly474 \\ build-obj [source] create object from source or assembly
475 \\ fmt [file] parse file and render in canonical zig format
484 \\ translate-c [source] convert c code to zig code476 \\ translate-c [source] convert c code to zig code
485 \\ targets list available compilation targets477 \\ targets list available compilation targets
486 \\ test [source] create and run a test build478 \\ test [source] create and run a test build
...@@ -548,7 +540,7 @@ fn printUsage(stream: &io.OutStream) %void {...@@ -548,7 +540,7 @@ fn printUsage(stream: &io.OutStream) %void {
548 );540 );
549}541}
550542
551fn printZen() %void {543fn printZen() !void {
552 var stdout_file = try io.getStdErr();544 var stdout_file = try io.getStdErr();
553 try stdout_file.write(545 try stdout_file.write(
554 \\546 \\
...@@ -568,8 +560,33 @@ fn printZen() %void {...@@ -568,8 +560,33 @@ fn printZen() %void {
568 );560 );
569}561}
570562
563fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
564 for (file_paths) |file_path| {
565 var file = try os.File.openRead(allocator, file_path);
566 defer file.close();
567
568 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
569 warn("unable to open '{}': {}", file_path, err);
570 continue;
571 };
572 defer allocator.free(source_code);
573
574 var tokenizer = std.zig.Tokenizer.init(source_code);
575 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
576 defer parser.deinit();
577
578 const tree = try parser.parse();
579 defer tree.deinit();
580
581 const baf = try io.BufferedAtomicFile.create(allocator, file_path);
582 defer baf.destroy();
583
584 try parser.renderSource(baf.stream(), tree.root_node);
585 }
586}
587
571/// Caller must free result588/// Caller must free result
572fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) %[]u8 {589fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {
573 if (zig_install_prefix_arg) |zig_install_prefix| {590 if (zig_install_prefix_arg) |zig_install_prefix| {
574 return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {591 return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {
575 warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));592 warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));
...@@ -585,21 +602,21 @@ fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const...@@ -585,21 +602,21 @@ fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const
585}602}
586603
587/// Caller must free result604/// Caller must free result
588fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) %[]u8 {605fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
589 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");606 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
590 errdefer allocator.free(test_zig_dir);607 errdefer allocator.free(test_zig_dir);
591608
592 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");609 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
593 defer allocator.free(test_index_file);610 defer allocator.free(test_index_file);
594611
595 var file = try io.File.openRead(test_index_file, allocator);612 var file = try os.File.openRead(allocator, test_index_file);
596 file.close();613 file.close();
597614
598 return test_zig_dir;615 return test_zig_dir;
599}616}
600617
601/// Caller must free result618/// Caller must free result
602fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {619fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
603 const self_exe_path = try os.selfExeDirPath(allocator);620 const self_exe_path = try os.selfExeDirPath(allocator);
604 defer allocator.free(self_exe_path);621 defer allocator.free(self_exe_path);
605622
...@@ -626,8 +643,3 @@ fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {...@@ -626,8 +643,3 @@ fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {
626643
627 return error.FileNotFound;644 return error.FileNotFound;
628}645}
629
630test "import tests" {
631 _ = @import("tokenizer.zig");
632 _ = @import("parser.zig");
633}
src-self-hosted/module.zig+10-12
...@@ -8,9 +8,9 @@ const c = @import("c.zig");...@@ -8,9 +8,9 @@ const c = @import("c.zig");
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const Target = @import("target.zig").Target;9const Target = @import("target.zig").Target;
10const warn = std.debug.warn;10const warn = std.debug.warn;
11const Tokenizer = @import("tokenizer.zig").Tokenizer;11const Tokenizer = std.zig.Tokenizer;
12const Token = @import("tokenizer.zig").Token;12const Token = std.zig.Token;
13const Parser = @import("parser.zig").Parser;13const Parser = std.zig.Parser;
14const ArrayList = std.ArrayList;14const ArrayList = std.ArrayList;
1515
16pub const Module = struct {16pub const Module = struct {
...@@ -110,7 +110,7 @@ pub const Module = struct {...@@ -110,7 +110,7 @@ pub const Module = struct {
110 };110 };
111111
112 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,112 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
113 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) %&Module113 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
114 {114 {
115 var name_buffer = try Buffer.init(allocator, name);115 var name_buffer = try Buffer.init(allocator, name);
116 errdefer name_buffer.deinit();116 errdefer name_buffer.deinit();
...@@ -198,7 +198,7 @@ pub const Module = struct {...@@ -198,7 +198,7 @@ pub const Module = struct {
198 self.allocator.destroy(self);198 self.allocator.destroy(self);
199 }199 }
200200
201 pub fn build(self: &Module) %void {201 pub fn build(self: &Module) !void {
202 if (self.llvm_argv.len != 0) {202 if (self.llvm_argv.len != 0) {
203 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator,203 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator,
204 [][]const []const u8 { [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, });204 [][]const []const u8 { [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, });
...@@ -213,14 +213,11 @@ pub const Module = struct {...@@ -213,14 +213,11 @@ pub const Module = struct {
213 };213 };
214 errdefer self.allocator.free(root_src_real_path);214 errdefer self.allocator.free(root_src_real_path);
215215
216 const source_code = io.readFileAllocExtra(root_src_real_path, self.allocator, 3) catch |err| {216 const source_code = io.readFileAlloc(self.allocator, root_src_real_path) catch |err| {
217 try printError("unable to open '{}': {}", root_src_real_path, err);217 try printError("unable to open '{}': {}", root_src_real_path, err);
218 return err;218 return err;
219 };219 };
220 errdefer self.allocator.free(source_code);220 errdefer self.allocator.free(source_code);
221 source_code[source_code.len - 3] = '\n';
222 source_code[source_code.len - 2] = '\n';
223 source_code[source_code.len - 1] = '\n';
224221
225 warn("====input:====\n");222 warn("====input:====\n");
226223
...@@ -263,11 +260,12 @@ pub const Module = struct {...@@ -263,11 +260,12 @@ pub const Module = struct {
263 260
264 }261 }
265262
266 pub fn link(self: &Module, out_file: ?[]const u8) %void {263 pub fn link(self: &Module, out_file: ?[]const u8) !void {
267 warn("TODO link");264 warn("TODO link");
265 return error.Todo;
268 }266 }
269267
270 pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) %&LinkLib {268 pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib {
271 const is_libc = mem.eql(u8, name, "c");269 const is_libc = mem.eql(u8, name, "c");
272270
273 if (is_libc) {271 if (is_libc) {
...@@ -297,7 +295,7 @@ pub const Module = struct {...@@ -297,7 +295,7 @@ pub const Module = struct {
297 }295 }
298};296};
299297
300fn printError(comptime format: []const u8, args: ...) %void {298fn printError(comptime format: []const u8, args: ...) !void {
301 var stderr_file = try std.io.getStdErr();299 var stderr_file = try std.io.getStdErr();
302 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);300 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
303 const out_stream = &stderr_file_out_stream.stream;301 const out_stream = &stderr_file_out_stream.stream;
src-self-hosted/parser.zig deleted-1166
...@@ -1,1166 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const mem = std.mem;
5const ast = @import("ast.zig");
6const Tokenizer = @import("tokenizer.zig").Tokenizer;
7const Token = @import("tokenizer.zig").Token;
8const builtin = @import("builtin");
9const io = std.io;
10
11// TODO when we make parse errors into error types instead of printing directly,
12// get rid of this
13const warn = std.debug.warn;
14
15error ParseError;
16
17pub const Parser = struct {
18 allocator: &mem.Allocator,
19 tokenizer: &Tokenizer,
20 put_back_tokens: [2]Token,
21 put_back_count: usize,
22 source_file_name: []const u8,
23
24 pub const Tree = struct {
25 root_node: &ast.NodeRoot,
26
27 pub fn deinit(self: &const Tree) void {
28 // TODO free the whole arena
29 }
30 };
31
32 // This memory contents are used only during a function call. It's used to repurpose memory;
33 // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and
34 // source rendering.
35 const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
36 utility_bytes: []align(utility_bytes_align) u8,
37
38 /// `allocator` should be an arena allocator. Parser never calls free on anything. After you're
39 /// done with a Parser, free the arena. After the arena is freed, no member functions of Parser
40 /// may be called.
41 pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
42 return Parser {
43 .allocator = allocator,
44 .tokenizer = tokenizer,
45 .put_back_tokens = undefined,
46 .put_back_count = 0,
47 .source_file_name = source_file_name,
48 .utility_bytes = []align(utility_bytes_align) u8{},
49 };
50 }
51
52 pub fn deinit(self: &Parser) void {
53 self.allocator.free(self.utility_bytes);
54 }
55
56 const TopLevelDeclCtx = struct {
57 visib_token: ?Token,
58 extern_token: ?Token,
59 };
60
61 const DestPtr = union(enum) {
62 Field: &&ast.Node,
63 NullableField: &?&ast.Node,
64 List: &ArrayList(&ast.Node),
65
66 pub fn store(self: &const DestPtr, value: &ast.Node) %void {
67 switch (*self) {
68 DestPtr.Field => |ptr| *ptr = value,
69 DestPtr.NullableField => |ptr| *ptr = value,
70 DestPtr.List => |list| try list.append(value),
71 }
72 }
73 };
74
75 const State = union(enum) {
76 TopLevel,
77 TopLevelExtern: ?Token,
78 TopLevelDecl: TopLevelDeclCtx,
79 Expression: DestPtr,
80 ExpectOperand,
81 Operand: &ast.Node,
82 AfterOperand,
83 InfixOp: &ast.NodeInfixOp,
84 PrefixOp: &ast.NodePrefixOp,
85 AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
86 TypeExpr: DestPtr,
87 VarDecl: &ast.NodeVarDecl,
88 VarDeclAlign: &ast.NodeVarDecl,
89 VarDeclEq: &ast.NodeVarDecl,
90 ExpectToken: @TagType(Token.Id),
91 FnProto: &ast.NodeFnProto,
92 FnProtoAlign: &ast.NodeFnProto,
93 ParamDecl: &ast.NodeFnProto,
94 ParamDeclComma,
95 FnDef: &ast.NodeFnProto,
96 Block: &ast.NodeBlock,
97 Statement: &ast.NodeBlock,
98 };
99
100 /// Returns an AST tree, allocated with the parser's allocator.
101 /// Result should be freed with `freeAst` when done.
102 pub fn parse(self: &Parser) %Tree {
103 var stack = self.initUtilityArrayList(State);
104 defer self.deinitUtilityArrayList(stack);
105
106 const root_node = try self.createRoot();
107 // TODO errdefer arena free root node
108
109 try stack.append(State.TopLevel);
110
111 while (true) {
112 //{
113 // const token = self.getNextToken();
114 // warn("{} ", @tagName(token.id));
115 // self.putBackToken(token);
116 // var i: usize = stack.len;
117 // while (i != 0) {
118 // i -= 1;
119 // warn("{} ", @tagName(stack.items[i]));
120 // }
121 // warn("\n");
122 //}
123
124 // This gives us 1 free append that can't fail
125 const state = stack.pop();
126
127 switch (state) {
128 State.TopLevel => {
129 const token = self.getNextToken();
130 switch (token.id) {
131 Token.Id.Keyword_pub, Token.Id.Keyword_export => {
132 stack.append(State { .TopLevelExtern = token }) catch unreachable;
133 continue;
134 },
135 Token.Id.Eof => return Tree {.root_node = root_node},
136 else => {
137 self.putBackToken(token);
138 // TODO shouldn't need this cast
139 stack.append(State { .TopLevelExtern = null }) catch unreachable;
140 continue;
141 },
142 }
143 },
144 State.TopLevelExtern => |visib_token| {
145 const token = self.getNextToken();
146 if (token.id == Token.Id.Keyword_extern) {
147 stack.append(State {
148 .TopLevelDecl = TopLevelDeclCtx {
149 .visib_token = visib_token,
150 .extern_token = token,
151 },
152 }) catch unreachable;
153 continue;
154 }
155 self.putBackToken(token);
156 stack.append(State {
157 .TopLevelDecl = TopLevelDeclCtx {
158 .visib_token = visib_token,
159 .extern_token = null,
160 },
161 }) catch unreachable;
162 continue;
163 },
164 State.TopLevelDecl => |ctx| {
165 const token = self.getNextToken();
166 switch (token.id) {
167 Token.Id.Keyword_var, Token.Id.Keyword_const => {
168 stack.append(State.TopLevel) catch unreachable;
169 // TODO shouldn't need these casts
170 const var_decl_node = try self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
171 token, (?Token)(null), ctx.extern_token);
172 try stack.append(State { .VarDecl = var_decl_node });
173 continue;
174 },
175 Token.Id.Keyword_fn => {
176 stack.append(State.TopLevel) catch unreachable;
177 // TODO shouldn't need these casts
178 const fn_proto = try self.createAttachFnProto(&root_node.decls, token,
179 ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
180 try stack.append(State { .FnDef = fn_proto });
181 try stack.append(State { .FnProto = fn_proto });
182 continue;
183 },
184 Token.Id.StringLiteral => {
185 @panic("TODO extern with string literal");
186 },
187 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
188 stack.append(State.TopLevel) catch unreachable;
189 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
190 // TODO shouldn't need this cast
191 const fn_proto = try self.createAttachFnProto(&root_node.decls, fn_token,
192 ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
193 try stack.append(State { .FnDef = fn_proto });
194 try stack.append(State { .FnProto = fn_proto });
195 continue;
196 },
197 else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
198 }
199 },
200 State.VarDecl => |var_decl| {
201 var_decl.name_token = try self.eatToken(Token.Id.Identifier);
202 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
203
204 const next_token = self.getNextToken();
205 if (next_token.id == Token.Id.Colon) {
206 try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
207 continue;
208 }
209
210 self.putBackToken(next_token);
211 continue;
212 },
213 State.VarDeclAlign => |var_decl| {
214 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
215
216 const next_token = self.getNextToken();
217 if (next_token.id == Token.Id.Keyword_align) {
218 _ = try self.eatToken(Token.Id.LParen);
219 try stack.append(State { .ExpectToken = Token.Id.RParen });
220 try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
221 continue;
222 }
223
224 self.putBackToken(next_token);
225 continue;
226 },
227 State.VarDeclEq => |var_decl| {
228 const token = self.getNextToken();
229 if (token.id == Token.Id.Equal) {
230 var_decl.eq_token = token;
231 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
232 try stack.append(State {
233 .Expression = DestPtr {.NullableField = &var_decl.init_node},
234 });
235 continue;
236 }
237 if (token.id == Token.Id.Semicolon) {
238 continue;
239 }
240 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
241 },
242 State.ExpectToken => |token_id| {
243 _ = try self.eatToken(token_id);
244 continue;
245 },
246
247 State.Expression => |dest_ptr| {
248 // save the dest_ptr for later
249 stack.append(state) catch unreachable;
250 try stack.append(State.ExpectOperand);
251 continue;
252 },
253 State.ExpectOperand => {
254 // we'll either get an operand (like 1 or x),
255 // or a prefix operator (like ~ or return).
256 const token = self.getNextToken();
257 switch (token.id) {
258 Token.Id.Keyword_return => {
259 try stack.append(State { .PrefixOp = try self.createPrefixOp(token,
260 ast.NodePrefixOp.PrefixOp.Return) });
261 try stack.append(State.ExpectOperand);
262 continue;
263 },
264 Token.Id.Ampersand => {
265 const prefix_op = try self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
266 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
267 .align_expr = null,
268 .bit_offset_start_token = null,
269 .bit_offset_end_token = null,
270 .const_token = null,
271 .volatile_token = null,
272 }
273 });
274 try stack.append(State { .PrefixOp = prefix_op });
275 try stack.append(State.ExpectOperand);
276 try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
277 continue;
278 },
279 Token.Id.Identifier => {
280 try stack.append(State {
281 .Operand = &(try self.createIdentifier(token)).base
282 });
283 try stack.append(State.AfterOperand);
284 continue;
285 },
286 Token.Id.IntegerLiteral => {
287 try stack.append(State {
288 .Operand = &(try self.createIntegerLiteral(token)).base
289 });
290 try stack.append(State.AfterOperand);
291 continue;
292 },
293 Token.Id.FloatLiteral => {
294 try stack.append(State {
295 .Operand = &(try self.createFloatLiteral(token)).base
296 });
297 try stack.append(State.AfterOperand);
298 continue;
299 },
300 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
301 }
302 },
303
304 State.AfterOperand => {
305 // we'll either get an infix operator (like != or ^),
306 // or a postfix operator (like () or {}),
307 // otherwise this expression is done (like on a ; or else).
308 var token = self.getNextToken();
309 switch (token.id) {
310 Token.Id.EqualEqual => {
311 try stack.append(State {
312 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
313 });
314 try stack.append(State.ExpectOperand);
315 continue;
316 },
317 Token.Id.BangEqual => {
318 try stack.append(State {
319 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
320 });
321 try stack.append(State.ExpectOperand);
322 continue;
323 },
324 else => {
325 // no postfix/infix operator after this operand.
326 self.putBackToken(token);
327 // reduce the stack
328 var expression: &ast.Node = stack.pop().Operand;
329 while (true) {
330 switch (stack.pop()) {
331 State.Expression => |dest_ptr| {
332 // we're done
333 try dest_ptr.store(expression);
334 break;
335 },
336 State.InfixOp => |infix_op| {
337 infix_op.rhs = expression;
338 infix_op.lhs = stack.pop().Operand;
339 expression = &infix_op.base;
340 continue;
341 },
342 State.PrefixOp => |prefix_op| {
343 prefix_op.rhs = expression;
344 expression = &prefix_op.base;
345 continue;
346 },
347 else => unreachable,
348 }
349 }
350 continue;
351 },
352 }
353 },
354
355 State.AddrOfModifiers => |addr_of_info| {
356 var token = self.getNextToken();
357 switch (token.id) {
358 Token.Id.Keyword_align => {
359 stack.append(state) catch unreachable;
360 if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
361 _ = try self.eatToken(Token.Id.LParen);
362 try stack.append(State { .ExpectToken = Token.Id.RParen });
363 try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
364 continue;
365 },
366 Token.Id.Keyword_const => {
367 stack.append(state) catch unreachable;
368 if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
369 addr_of_info.const_token = token;
370 continue;
371 },
372 Token.Id.Keyword_volatile => {
373 stack.append(state) catch unreachable;
374 if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
375 addr_of_info.volatile_token = token;
376 continue;
377 },
378 else => {
379 self.putBackToken(token);
380 continue;
381 },
382 }
383 },
384
385 State.TypeExpr => |dest_ptr| {
386 const token = self.getNextToken();
387 if (token.id == Token.Id.Keyword_var) {
388 @panic("TODO param with type var");
389 }
390 self.putBackToken(token);
391
392 stack.append(State { .Expression = dest_ptr }) catch unreachable;
393 continue;
394 },
395
396 State.FnProto => |fn_proto| {
397 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
398 try stack.append(State { .ParamDecl = fn_proto });
399 try stack.append(State { .ExpectToken = Token.Id.LParen });
400
401 const next_token = self.getNextToken();
402 if (next_token.id == Token.Id.Identifier) {
403 fn_proto.name_token = next_token;
404 continue;
405 }
406 self.putBackToken(next_token);
407 continue;
408 },
409
410 State.FnProtoAlign => |fn_proto| {
411 const token = self.getNextToken();
412 if (token.id == Token.Id.Keyword_align) {
413 @panic("TODO fn proto align");
414 }
415 self.putBackToken(token);
416 stack.append(State {
417 .TypeExpr = DestPtr {.Field = &fn_proto.return_type},
418 }) catch unreachable;
419 continue;
420 },
421
422 State.ParamDecl => |fn_proto| {
423 var token = self.getNextToken();
424 if (token.id == Token.Id.RParen) {
425 continue;
426 }
427 const param_decl = try self.createAttachParamDecl(&fn_proto.params);
428 if (token.id == Token.Id.Keyword_comptime) {
429 param_decl.comptime_token = token;
430 token = self.getNextToken();
431 } else if (token.id == Token.Id.Keyword_noalias) {
432 param_decl.noalias_token = token;
433 token = self.getNextToken();
434 }
435 if (token.id == Token.Id.Identifier) {
436 const next_token = self.getNextToken();
437 if (next_token.id == Token.Id.Colon) {
438 param_decl.name_token = token;
439 token = self.getNextToken();
440 } else {
441 self.putBackToken(next_token);
442 }
443 }
444 if (token.id == Token.Id.Ellipsis3) {
445 param_decl.var_args_token = token;
446 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
447 continue;
448 } else {
449 self.putBackToken(token);
450 }
451
452 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
453 try stack.append(State.ParamDeclComma);
454 try stack.append(State {
455 .TypeExpr = DestPtr {.Field = &param_decl.type_node}
456 });
457 continue;
458 },
459
460 State.ParamDeclComma => {
461 const token = self.getNextToken();
462 switch (token.id) {
463 Token.Id.RParen => {
464 _ = stack.pop(); // pop off the ParamDecl
465 continue;
466 },
467 Token.Id.Comma => continue,
468 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
469 }
470 },
471
472 State.FnDef => |fn_proto| {
473 const token = self.getNextToken();
474 switch(token.id) {
475 Token.Id.LBrace => {
476 const block = try self.createBlock(token);
477 fn_proto.body_node = &block.base;
478 stack.append(State { .Block = block }) catch unreachable;
479 continue;
480 },
481 Token.Id.Semicolon => continue,
482 else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
483 }
484 },
485
486 State.Block => |block| {
487 const token = self.getNextToken();
488 switch (token.id) {
489 Token.Id.RBrace => {
490 block.end_token = token;
491 continue;
492 },
493 else => {
494 self.putBackToken(token);
495 stack.append(State { .Block = block }) catch unreachable;
496 try stack.append(State { .Statement = block });
497 continue;
498 },
499 }
500 },
501
502 State.Statement => |block| {
503 {
504 // Look for comptime var, comptime const
505 const comptime_token = self.getNextToken();
506 if (comptime_token.id == Token.Id.Keyword_comptime) {
507 const mut_token = self.getNextToken();
508 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
509 // TODO shouldn't need these casts
510 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
511 mut_token, (?Token)(comptime_token), (?Token)(null));
512 try stack.append(State { .VarDecl = var_decl });
513 continue;
514 }
515 self.putBackToken(mut_token);
516 }
517 self.putBackToken(comptime_token);
518 }
519 {
520 // Look for const, var
521 const mut_token = self.getNextToken();
522 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
523 // TODO shouldn't need these casts
524 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
525 mut_token, (?Token)(null), (?Token)(null));
526 try stack.append(State { .VarDecl = var_decl });
527 continue;
528 }
529 self.putBackToken(mut_token);
530 }
531
532 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
533 try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
534 continue;
535 },
536
537 // These are data, not control flow.
538 State.InfixOp => unreachable,
539 State.PrefixOp => unreachable,
540 State.Operand => unreachable,
541 }
542 @import("std").debug.panic("{}", @tagName(state));
543 //unreachable;
544 }
545 }
546
547 fn createRoot(self: &Parser) %&ast.NodeRoot {
548 const node = try self.allocator.create(ast.NodeRoot);
549
550 *node = ast.NodeRoot {
551 .base = ast.Node {.id = ast.Node.Id.Root},
552 .decls = ArrayList(&ast.Node).init(self.allocator),
553 };
554 return node;
555 }
556
557 fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
558 extern_token: &const ?Token) %&ast.NodeVarDecl
559 {
560 const node = try self.allocator.create(ast.NodeVarDecl);
561
562 *node = ast.NodeVarDecl {
563 .base = ast.Node {.id = ast.Node.Id.VarDecl},
564 .visib_token = *visib_token,
565 .mut_token = *mut_token,
566 .comptime_token = *comptime_token,
567 .extern_token = *extern_token,
568 .type_node = null,
569 .align_node = null,
570 .init_node = null,
571 .lib_name = null,
572 // initialized later
573 .name_token = undefined,
574 .eq_token = undefined,
575 };
576 return node;
577 }
578
579 fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
580 cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) %&ast.NodeFnProto
581 {
582 const node = try self.allocator.create(ast.NodeFnProto);
583
584 *node = ast.NodeFnProto {
585 .base = ast.Node {.id = ast.Node.Id.FnProto},
586 .visib_token = *visib_token,
587 .name_token = null,
588 .fn_token = *fn_token,
589 .params = ArrayList(&ast.Node).init(self.allocator),
590 .return_type = undefined,
591 .var_args_token = null,
592 .extern_token = *extern_token,
593 .inline_token = *inline_token,
594 .cc_token = *cc_token,
595 .body_node = null,
596 .lib_name = null,
597 .align_expr = null,
598 };
599 return node;
600 }
601
602 fn createParamDecl(self: &Parser) %&ast.NodeParamDecl {
603 const node = try self.allocator.create(ast.NodeParamDecl);
604
605 *node = ast.NodeParamDecl {
606 .base = ast.Node {.id = ast.Node.Id.ParamDecl},
607 .comptime_token = null,
608 .noalias_token = null,
609 .name_token = null,
610 .type_node = undefined,
611 .var_args_token = null,
612 };
613 return node;
614 }
615
616 fn createBlock(self: &Parser, begin_token: &const Token) %&ast.NodeBlock {
617 const node = try self.allocator.create(ast.NodeBlock);
618
619 *node = ast.NodeBlock {
620 .base = ast.Node {.id = ast.Node.Id.Block},
621 .begin_token = *begin_token,
622 .end_token = undefined,
623 .statements = ArrayList(&ast.Node).init(self.allocator),
624 };
625 return node;
626 }
627
628 fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) %&ast.NodeInfixOp {
629 const node = try self.allocator.create(ast.NodeInfixOp);
630
631 *node = ast.NodeInfixOp {
632 .base = ast.Node {.id = ast.Node.Id.InfixOp},
633 .op_token = *op_token,
634 .lhs = undefined,
635 .op = *op,
636 .rhs = undefined,
637 };
638 return node;
639 }
640
641 fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) %&ast.NodePrefixOp {
642 const node = try self.allocator.create(ast.NodePrefixOp);
643
644 *node = ast.NodePrefixOp {
645 .base = ast.Node {.id = ast.Node.Id.PrefixOp},
646 .op_token = *op_token,
647 .op = *op,
648 .rhs = undefined,
649 };
650 return node;
651 }
652
653 fn createIdentifier(self: &Parser, name_token: &const Token) %&ast.NodeIdentifier {
654 const node = try self.allocator.create(ast.NodeIdentifier);
655
656 *node = ast.NodeIdentifier {
657 .base = ast.Node {.id = ast.Node.Id.Identifier},
658 .name_token = *name_token,
659 };
660 return node;
661 }
662
663 fn createIntegerLiteral(self: &Parser, token: &const Token) %&ast.NodeIntegerLiteral {
664 const node = try self.allocator.create(ast.NodeIntegerLiteral);
665
666 *node = ast.NodeIntegerLiteral {
667 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
668 .token = *token,
669 };
670 return node;
671 }
672
673 fn createFloatLiteral(self: &Parser, token: &const Token) %&ast.NodeFloatLiteral {
674 const node = try self.allocator.create(ast.NodeFloatLiteral);
675
676 *node = ast.NodeFloatLiteral {
677 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
678 .token = *token,
679 };
680 return node;
681 }
682
683 fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) %&ast.NodeIdentifier {
684 const node = try self.createIdentifier(name_token);
685 try dest_ptr.store(&node.base);
686 return node;
687 }
688
689 fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) %&ast.NodeParamDecl {
690 const node = try self.createParamDecl();
691 try list.append(&node.base);
692 return node;
693 }
694
695 fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
696 extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
697 inline_token: &const ?Token) %&ast.NodeFnProto
698 {
699 const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
700 try list.append(&node.base);
701 return node;
702 }
703
704 fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
705 mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) %&ast.NodeVarDecl
706 {
707 const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
708 try list.append(&node.base);
709 return node;
710 }
711
712 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) error {
713 const loc = self.tokenizer.getTokenLocation(token);
714 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
715 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
716 {
717 var i: usize = 0;
718 while (i < loc.column) : (i += 1) {
719 warn(" ");
720 }
721 }
722 {
723 const caret_count = token.end - token.start;
724 var i: usize = 0;
725 while (i < caret_count) : (i += 1) {
726 warn("~");
727 }
728 }
729 warn("\n");
730 return error.ParseError;
731 }
732
733 fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) %void {
734 if (token.id != id) {
735 return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
736 }
737 }
738
739 fn eatToken(self: &Parser, id: @TagType(Token.Id)) %Token {
740 const token = self.getNextToken();
741 try self.expectToken(token, id);
742 return token;
743 }
744
745 fn putBackToken(self: &Parser, token: &const Token) void {
746 self.put_back_tokens[self.put_back_count] = *token;
747 self.put_back_count += 1;
748 }
749
750 fn getNextToken(self: &Parser) Token {
751 if (self.put_back_count != 0) {
752 const put_back_index = self.put_back_count - 1;
753 const put_back_token = self.put_back_tokens[put_back_index];
754 self.put_back_count = put_back_index;
755 return put_back_token;
756 } else {
757 return self.tokenizer.next();
758 }
759 }
760
761 const RenderAstFrame = struct {
762 node: &ast.Node,
763 indent: usize,
764 };
765
766 pub fn renderAst(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) %void {
767 var stack = self.initUtilityArrayList(RenderAstFrame);
768 defer self.deinitUtilityArrayList(stack);
769
770 try stack.append(RenderAstFrame {
771 .node = &root_node.base,
772 .indent = 0,
773 });
774
775 while (stack.popOrNull()) |frame| {
776 {
777 var i: usize = 0;
778 while (i < frame.indent) : (i += 1) {
779 try stream.print(" ");
780 }
781 }
782 try stream.print("{}\n", @tagName(frame.node.id));
783 var child_i: usize = 0;
784 while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
785 try stack.append(RenderAstFrame {
786 .node = child,
787 .indent = frame.indent + 2,
788 });
789 }
790 }
791 }
792
793 const RenderState = union(enum) {
794 TopLevelDecl: &ast.Node,
795 FnProtoRParen: &ast.NodeFnProto,
796 ParamDecl: &ast.Node,
797 Text: []const u8,
798 Expression: &ast.Node,
799 VarDecl: &ast.NodeVarDecl,
800 Statement: &ast.Node,
801 PrintIndent,
802 Indent: usize,
803 };
804
805 pub fn renderSource(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) %void {
806 var stack = self.initUtilityArrayList(RenderState);
807 defer self.deinitUtilityArrayList(stack);
808
809 {
810 var i = root_node.decls.len;
811 while (i != 0) {
812 i -= 1;
813 const decl = root_node.decls.items[i];
814 try stack.append(RenderState {.TopLevelDecl = decl});
815 }
816 }
817
818 const indent_delta = 4;
819 var indent: usize = 0;
820 while (stack.popOrNull()) |state| {
821 switch (state) {
822 RenderState.TopLevelDecl => |decl| {
823 switch (decl.id) {
824 ast.Node.Id.FnProto => {
825 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
826 if (fn_proto.visib_token) |visib_token| {
827 switch (visib_token.id) {
828 Token.Id.Keyword_pub => try stream.print("pub "),
829 Token.Id.Keyword_export => try stream.print("export "),
830 else => unreachable,
831 }
832 }
833 if (fn_proto.extern_token) |extern_token| {
834 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
835 }
836 try stream.print("fn");
837
838 if (fn_proto.name_token) |name_token| {
839 try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
840 }
841
842 try stream.print("(");
843
844 try stack.append(RenderState { .Text = "\n" });
845 if (fn_proto.body_node == null) {
846 try stack.append(RenderState { .Text = ";" });
847 }
848
849 try stack.append(RenderState { .FnProtoRParen = fn_proto});
850 var i = fn_proto.params.len;
851 while (i != 0) {
852 i -= 1;
853 const param_decl_node = fn_proto.params.items[i];
854 try stack.append(RenderState { .ParamDecl = param_decl_node});
855 if (i != 0) {
856 try stack.append(RenderState { .Text = ", " });
857 }
858 }
859 },
860 ast.Node.Id.VarDecl => {
861 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
862 try stack.append(RenderState { .Text = "\n"});
863 try stack.append(RenderState { .VarDecl = var_decl});
864
865 },
866 else => unreachable,
867 }
868 },
869
870 RenderState.VarDecl => |var_decl| {
871 if (var_decl.visib_token) |visib_token| {
872 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
873 }
874 if (var_decl.extern_token) |extern_token| {
875 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
876 if (var_decl.lib_name != null) {
877 @panic("TODO");
878 }
879 }
880 if (var_decl.comptime_token) |comptime_token| {
881 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
882 }
883 try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
884 try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
885
886 try stack.append(RenderState { .Text = ";" });
887 if (var_decl.init_node) |init_node| {
888 try stack.append(RenderState { .Expression = init_node });
889 try stack.append(RenderState { .Text = " = " });
890 }
891 if (var_decl.align_node) |align_node| {
892 try stack.append(RenderState { .Text = ")" });
893 try stack.append(RenderState { .Expression = align_node });
894 try stack.append(RenderState { .Text = " align(" });
895 }
896 if (var_decl.type_node) |type_node| {
897 try stream.print(": ");
898 try stack.append(RenderState { .Expression = type_node });
899 }
900 },
901
902 RenderState.ParamDecl => |base| {
903 const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
904 if (param_decl.comptime_token) |comptime_token| {
905 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
906 }
907 if (param_decl.noalias_token) |noalias_token| {
908 try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
909 }
910 if (param_decl.name_token) |name_token| {
911 try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
912 }
913 if (param_decl.var_args_token) |var_args_token| {
914 try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
915 } else {
916 try stack.append(RenderState { .Expression = param_decl.type_node});
917 }
918 },
919 RenderState.Text => |bytes| {
920 try stream.write(bytes);
921 },
922 RenderState.Expression => |base| switch (base.id) {
923 ast.Node.Id.Identifier => {
924 const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
925 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
926 },
927 ast.Node.Id.Block => {
928 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
929 try stream.write("{");
930 try stack.append(RenderState { .Text = "}"});
931 try stack.append(RenderState.PrintIndent);
932 try stack.append(RenderState { .Indent = indent});
933 try stack.append(RenderState { .Text = "\n"});
934 var i = block.statements.len;
935 while (i != 0) {
936 i -= 1;
937 const statement_node = block.statements.items[i];
938 try stack.append(RenderState { .Statement = statement_node});
939 try stack.append(RenderState.PrintIndent);
940 try stack.append(RenderState { .Indent = indent + indent_delta});
941 try stack.append(RenderState { .Text = "\n" });
942 }
943 },
944 ast.Node.Id.InfixOp => {
945 const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
946 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
947 switch (prefix_op_node.op) {
948 ast.NodeInfixOp.InfixOp.EqualEqual => {
949 try stack.append(RenderState { .Text = " == "});
950 },
951 ast.NodeInfixOp.InfixOp.BangEqual => {
952 try stack.append(RenderState { .Text = " != "});
953 },
954 else => unreachable,
955 }
956 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
957 },
958 ast.Node.Id.PrefixOp => {
959 const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
960 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
961 switch (prefix_op_node.op) {
962 ast.NodePrefixOp.PrefixOp.Return => {
963 try stream.write("return ");
964 },
965 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
966 try stream.write("&");
967 if (addr_of_info.volatile_token != null) {
968 try stack.append(RenderState { .Text = "volatile "});
969 }
970 if (addr_of_info.const_token != null) {
971 try stack.append(RenderState { .Text = "const "});
972 }
973 if (addr_of_info.align_expr) |align_expr| {
974 try stream.print("align(");
975 try stack.append(RenderState { .Text = ") "});
976 try stack.append(RenderState { .Expression = align_expr});
977 }
978 },
979 else => unreachable,
980 }
981 },
982 ast.Node.Id.IntegerLiteral => {
983 const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
984 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
985 },
986 ast.Node.Id.FloatLiteral => {
987 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
988 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
989 },
990 else => unreachable,
991 },
992 RenderState.FnProtoRParen => |fn_proto| {
993 try stream.print(")");
994 if (fn_proto.align_expr != null) {
995 @panic("TODO");
996 }
997 try stream.print(" ");
998 if (fn_proto.body_node) |body_node| {
999 try stack.append(RenderState { .Expression = body_node});
1000 try stack.append(RenderState { .Text = " "});
1001 }
1002 try stack.append(RenderState { .Expression = fn_proto.return_type});
1003 },
1004 RenderState.Statement => |base| {
1005 switch (base.id) {
1006 ast.Node.Id.VarDecl => {
1007 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
1008 try stack.append(RenderState { .VarDecl = var_decl});
1009 },
1010 else => {
1011 try stack.append(RenderState { .Text = ";"});
1012 try stack.append(RenderState { .Expression = base});
1013 },
1014 }
1015 },
1016 RenderState.Indent => |new_indent| indent = new_indent,
1017 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
1018 }
1019 }
1020 }
1021
1022 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
1023 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
1024 self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
1025 const typed_slice = ([]T)(self.utility_bytes);
1026 return ArrayList(T) {
1027 .allocator = self.allocator,
1028 .items = typed_slice,
1029 .len = 0,
1030 };
1031 }
1032
1033 fn deinitUtilityArrayList(self: &Parser, list: var) void {
1034 self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
1035 }
1036
1037};
1038
1039var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1040
1041fn testParse(source: []const u8, allocator: &mem.Allocator) %[]u8 {
1042 var padded_source: [0x100]u8 = undefined;
1043 std.mem.copy(u8, padded_source[0..source.len], source);
1044 padded_source[source.len + 0] = '\n';
1045 padded_source[source.len + 1] = '\n';
1046 padded_source[source.len + 2] = '\n';
1047
1048 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
1049 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1050 defer parser.deinit();
1051
1052 const tree = try parser.parse();
1053 defer tree.deinit();
1054
1055 var buffer = try std.Buffer.initSize(allocator, 0);
1056 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1057 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
1058 return buffer.toOwnedSlice();
1059}
1060
1061error TestFailed;
1062error NondeterministicMemoryUsage;
1063error MemoryLeakDetected;
1064
1065// TODO test for memory leaks
1066// TODO test for valid frees
1067fn testCanonical(source: []const u8) %void {
1068 const needed_alloc_count = x: {
1069 // Try it once with unlimited memory, make sure it works
1070 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1071 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1072 const result_source = try testParse(source, &failing_allocator.allocator);
1073 if (!mem.eql(u8, result_source, source)) {
1074 warn("\n====== expected this output: =========\n");
1075 warn("{}", source);
1076 warn("\n======== instead found this: =========\n");
1077 warn("{}", result_source);
1078 warn("\n======================================\n");
1079 return error.TestFailed;
1080 }
1081 failing_allocator.allocator.free(result_source);
1082 break :x failing_allocator.index;
1083 };
1084
1085 var fail_index: usize = 0;
1086 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1087 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1088 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1089 if (testParse(source, &failing_allocator.allocator)) |_| {
1090 return error.NondeterministicMemoryUsage;
1091 } else |err| {
1092 assert(err == error.OutOfMemory);
1093 // TODO make this pass
1094 //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1095 // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1096 // fail_index, needed_alloc_count,
1097 // failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1098 // failing_allocator.index, failing_allocator.deallocations);
1099 // return error.MemoryLeakDetected;
1100 //}
1101 }
1102 }
1103}
1104
1105test "zig fmt" {
1106 try testCanonical(
1107 \\extern fn puts(s: &const u8) c_int;
1108 \\
1109 );
1110
1111 try testCanonical(
1112 \\const a = b;
1113 \\pub const a = b;
1114 \\var a = b;
1115 \\pub var a = b;
1116 \\const a: i32 = b;
1117 \\pub const a: i32 = b;
1118 \\var a: i32 = b;
1119 \\pub var a: i32 = b;
1120 \\
1121 );
1122
1123 try testCanonical(
1124 \\extern var foo: c_int;
1125 \\
1126 );
1127
1128 try testCanonical(
1129 \\var foo: c_int align(1);
1130 \\
1131 );
1132
1133 try testCanonical(
1134 \\fn main(argc: c_int, argv: &&u8) c_int {
1135 \\ const a = b;
1136 \\}
1137 \\
1138 );
1139
1140 try testCanonical(
1141 \\fn foo(argc: c_int, argv: &&u8) c_int {
1142 \\ return 0;
1143 \\}
1144 \\
1145 );
1146
1147 try testCanonical(
1148 \\extern fn f1(s: &align(&u8) u8) c_int;
1149 \\
1150 );
1151
1152 try testCanonical(
1153 \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
1154 \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
1155 \\extern fn f3(s: &align(1) const volatile u8) c_int;
1156 \\
1157 );
1158
1159 try testCanonical(
1160 \\fn f1(a: bool, b: bool) bool {
1161 \\ a != b;
1162 \\ return a == b;
1163 \\}
1164 \\
1165 );
1166}
src-self-hosted/tokenizer.zig deleted-659
...@@ -1,659 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3
4pub const Token = struct {
5 id: Id,
6 start: usize,
7 end: usize,
8
9 const KeywordId = struct {
10 bytes: []const u8,
11 id: Id,
12 };
13
14 const keywords = []KeywordId {
15 KeywordId{.bytes="align", .id = Id.Keyword_align},
16 KeywordId{.bytes="and", .id = Id.Keyword_and},
17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
18 KeywordId{.bytes="break", .id = Id.Keyword_break},
19 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
20 KeywordId{.bytes="const", .id = Id.Keyword_const},
21 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
22 KeywordId{.bytes="defer", .id = Id.Keyword_defer},
23 KeywordId{.bytes="else", .id = Id.Keyword_else},
24 KeywordId{.bytes="enum", .id = Id.Keyword_enum},
25 KeywordId{.bytes="error", .id = Id.Keyword_error},
26 KeywordId{.bytes="export", .id = Id.Keyword_export},
27 KeywordId{.bytes="extern", .id = Id.Keyword_extern},
28 KeywordId{.bytes="false", .id = Id.Keyword_false},
29 KeywordId{.bytes="fn", .id = Id.Keyword_fn},
30 KeywordId{.bytes="for", .id = Id.Keyword_for},
31 KeywordId{.bytes="goto", .id = Id.Keyword_goto},
32 KeywordId{.bytes="if", .id = Id.Keyword_if},
33 KeywordId{.bytes="inline", .id = Id.Keyword_inline},
34 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
35 KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
36 KeywordId{.bytes="null", .id = Id.Keyword_null},
37 KeywordId{.bytes="or", .id = Id.Keyword_or},
38 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
39 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
40 KeywordId{.bytes="return", .id = Id.Keyword_return},
41 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
42 KeywordId{.bytes="struct", .id = Id.Keyword_struct},
43 KeywordId{.bytes="switch", .id = Id.Keyword_switch},
44 KeywordId{.bytes="test", .id = Id.Keyword_test},
45 KeywordId{.bytes="this", .id = Id.Keyword_this},
46 KeywordId{.bytes="true", .id = Id.Keyword_true},
47 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
48 KeywordId{.bytes="union", .id = Id.Keyword_union},
49 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
50 KeywordId{.bytes="use", .id = Id.Keyword_use},
51 KeywordId{.bytes="var", .id = Id.Keyword_var},
52 KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
53 KeywordId{.bytes="while", .id = Id.Keyword_while},
54 };
55
56 fn getKeyword(bytes: []const u8) ?Id {
57 for (keywords) |kw| {
58 if (mem.eql(u8, kw.bytes, bytes)) {
59 return kw.id;
60 }
61 }
62 return null;
63 }
64
65 const StrLitKind = enum {Normal, C};
66
67 pub const Id = union(enum) {
68 Invalid,
69 Identifier,
70 StringLiteral: StrLitKind,
71 Eof,
72 Builtin,
73 Bang,
74 Equal,
75 EqualEqual,
76 BangEqual,
77 LParen,
78 RParen,
79 Semicolon,
80 Percent,
81 LBrace,
82 RBrace,
83 Period,
84 Ellipsis2,
85 Ellipsis3,
86 Minus,
87 Arrow,
88 Colon,
89 Slash,
90 Comma,
91 Ampersand,
92 AmpersandEqual,
93 IntegerLiteral,
94 FloatLiteral,
95 Keyword_align,
96 Keyword_and,
97 Keyword_asm,
98 Keyword_break,
99 Keyword_comptime,
100 Keyword_const,
101 Keyword_continue,
102 Keyword_defer,
103 Keyword_else,
104 Keyword_enum,
105 Keyword_error,
106 Keyword_export,
107 Keyword_extern,
108 Keyword_false,
109 Keyword_fn,
110 Keyword_for,
111 Keyword_goto,
112 Keyword_if,
113 Keyword_inline,
114 Keyword_nakedcc,
115 Keyword_noalias,
116 Keyword_null,
117 Keyword_or,
118 Keyword_packed,
119 Keyword_pub,
120 Keyword_return,
121 Keyword_stdcallcc,
122 Keyword_struct,
123 Keyword_switch,
124 Keyword_test,
125 Keyword_this,
126 Keyword_true,
127 Keyword_undefined,
128 Keyword_union,
129 Keyword_unreachable,
130 Keyword_use,
131 Keyword_var,
132 Keyword_volatile,
133 Keyword_while,
134 };
135};
136
137pub const Tokenizer = struct {
138 buffer: []const u8,
139 index: usize,
140 pending_invalid_token: ?Token,
141
142 pub const Location = struct {
143 line: usize,
144 column: usize,
145 line_start: usize,
146 line_end: usize,
147 };
148
149 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
150 var loc = Location {
151 .line = 0,
152 .column = 0,
153 .line_start = 0,
154 .line_end = 0,
155 };
156 for (self.buffer) |c, i| {
157 if (i == token.start) {
158 loc.line_end = i;
159 while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
160 return loc;
161 }
162 if (c == '\n') {
163 loc.line += 1;
164 loc.column = 0;
165 loc.line_start = i + 1;
166 } else {
167 loc.column += 1;
168 }
169 }
170 return loc;
171 }
172
173 /// For debugging purposes
174 pub fn dump(self: &Tokenizer, token: &const Token) void {
175 std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
176 }
177
178 /// buffer must end with "\n\n\n". This is so that attempting to decode
179 /// a the 3 trailing bytes of a 4-byte utf8 sequence is never a buffer overflow.
180 pub fn init(buffer: []const u8) Tokenizer {
181 std.debug.assert(buffer[buffer.len - 1] == '\n');
182 std.debug.assert(buffer[buffer.len - 2] == '\n');
183 std.debug.assert(buffer[buffer.len - 3] == '\n');
184 return Tokenizer {
185 .buffer = buffer,
186 .index = 0,
187 .pending_invalid_token = null,
188 };
189 }
190
191 const State = enum {
192 Start,
193 Identifier,
194 Builtin,
195 C,
196 StringLiteral,
197 StringLiteralBackslash,
198 Equal,
199 Bang,
200 Minus,
201 Slash,
202 LineComment,
203 Zero,
204 IntegerLiteral,
205 IntegerLiteralWithRadix,
206 NumberDot,
207 FloatFraction,
208 FloatExponentUnsigned,
209 FloatExponentNumber,
210 Ampersand,
211 Period,
212 Period2,
213 };
214
215 pub fn next(self: &Tokenizer) Token {
216 if (self.pending_invalid_token) |token| {
217 self.pending_invalid_token = null;
218 return token;
219 }
220 var state = State.Start;
221 var result = Token {
222 .id = Token.Id.Eof,
223 .start = self.index,
224 .end = undefined,
225 };
226 while (self.index < self.buffer.len) : (self.index += 1) {
227 const c = self.buffer[self.index];
228 switch (state) {
229 State.Start => switch (c) {
230 ' ', '\n' => {
231 result.start = self.index + 1;
232 },
233 'c' => {
234 state = State.C;
235 result.id = Token.Id.Identifier;
236 },
237 '"' => {
238 state = State.StringLiteral;
239 result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
240 },
241 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
242 state = State.Identifier;
243 result.id = Token.Id.Identifier;
244 },
245 '@' => {
246 state = State.Builtin;
247 result.id = Token.Id.Builtin;
248 },
249 '=' => {
250 state = State.Equal;
251 },
252 '!' => {
253 state = State.Bang;
254 },
255 '(' => {
256 result.id = Token.Id.LParen;
257 self.index += 1;
258 break;
259 },
260 ')' => {
261 result.id = Token.Id.RParen;
262 self.index += 1;
263 break;
264 },
265 ';' => {
266 result.id = Token.Id.Semicolon;
267 self.index += 1;
268 break;
269 },
270 ',' => {
271 result.id = Token.Id.Comma;
272 self.index += 1;
273 break;
274 },
275 ':' => {
276 result.id = Token.Id.Colon;
277 self.index += 1;
278 break;
279 },
280 '%' => {
281 result.id = Token.Id.Percent;
282 self.index += 1;
283 break;
284 },
285 '{' => {
286 result.id = Token.Id.LBrace;
287 self.index += 1;
288 break;
289 },
290 '}' => {
291 result.id = Token.Id.RBrace;
292 self.index += 1;
293 break;
294 },
295 '.' => {
296 state = State.Period;
297 },
298 '-' => {
299 state = State.Minus;
300 },
301 '/' => {
302 state = State.Slash;
303 },
304 '&' => {
305 state = State.Ampersand;
306 },
307 '0' => {
308 state = State.Zero;
309 result.id = Token.Id.IntegerLiteral;
310 },
311 '1'...'9' => {
312 state = State.IntegerLiteral;
313 result.id = Token.Id.IntegerLiteral;
314 },
315 else => {
316 result.id = Token.Id.Invalid;
317 self.index += 1;
318 break;
319 },
320 },
321 State.Ampersand => switch (c) {
322 '=' => {
323 result.id = Token.Id.AmpersandEqual;
324 self.index += 1;
325 break;
326 },
327 else => {
328 result.id = Token.Id.Ampersand;
329 break;
330 },
331 },
332 State.Identifier => switch (c) {
333 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
334 else => {
335 if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
336 result.id = id;
337 }
338 break;
339 },
340 },
341 State.Builtin => switch (c) {
342 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
343 else => break,
344 },
345 State.C => switch (c) {
346 '\\' => @panic("TODO"),
347 '"' => {
348 state = State.StringLiteral;
349 result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
350 },
351 'a'...'z', 'A'...'Z', '_', '0'...'9' => {
352 state = State.Identifier;
353 },
354 else => break,
355 },
356 State.StringLiteral => switch (c) {
357 '\\' => {
358 state = State.StringLiteralBackslash;
359 },
360 '"' => {
361 self.index += 1;
362 break;
363 },
364 '\n' => break, // Look for this error later.
365 else => self.checkLiteralCharacter(),
366 },
367
368 State.StringLiteralBackslash => switch (c) {
369 '\n' => break, // Look for this error later.
370 else => {
371 state = State.StringLiteral;
372 },
373 },
374
375 State.Bang => switch (c) {
376 '=' => {
377 result.id = Token.Id.BangEqual;
378 self.index += 1;
379 break;
380 },
381 else => {
382 result.id = Token.Id.Bang;
383 break;
384 },
385 },
386
387 State.Equal => switch (c) {
388 '=' => {
389 result.id = Token.Id.EqualEqual;
390 self.index += 1;
391 break;
392 },
393 else => {
394 result.id = Token.Id.Equal;
395 break;
396 },
397 },
398
399 State.Minus => switch (c) {
400 '>' => {
401 result.id = Token.Id.Arrow;
402 self.index += 1;
403 break;
404 },
405 else => {
406 result.id = Token.Id.Minus;
407 break;
408 },
409 },
410
411 State.Period => switch (c) {
412 '.' => {
413 state = State.Period2;
414 },
415 else => {
416 result.id = Token.Id.Period;
417 break;
418 },
419 },
420
421 State.Period2 => switch (c) {
422 '.' => {
423 result.id = Token.Id.Ellipsis3;
424 self.index += 1;
425 break;
426 },
427 else => {
428 result.id = Token.Id.Ellipsis2;
429 break;
430 },
431 },
432
433 State.Slash => switch (c) {
434 '/' => {
435 result.id = undefined;
436 state = State.LineComment;
437 },
438 else => {
439 result.id = Token.Id.Slash;
440 break;
441 },
442 },
443 State.LineComment => switch (c) {
444 '\n' => {
445 state = State.Start;
446 result = Token {
447 .id = Token.Id.Eof,
448 .start = self.index + 1,
449 .end = undefined,
450 };
451 },
452 else => self.checkLiteralCharacter(),
453 },
454 State.Zero => switch (c) {
455 'b', 'o', 'x' => {
456 state = State.IntegerLiteralWithRadix;
457 },
458 else => {
459 // reinterpret as a normal number
460 self.index -= 1;
461 state = State.IntegerLiteral;
462 },
463 },
464 State.IntegerLiteral => switch (c) {
465 '.' => {
466 state = State.NumberDot;
467 },
468 'p', 'P', 'e', 'E' => {
469 state = State.FloatExponentUnsigned;
470 },
471 '0'...'9' => {},
472 else => break,
473 },
474 State.IntegerLiteralWithRadix => switch (c) {
475 '.' => {
476 state = State.NumberDot;
477 },
478 'p', 'P' => {
479 state = State.FloatExponentUnsigned;
480 },
481 '0'...'9', 'a'...'f', 'A'...'F' => {},
482 else => break,
483 },
484 State.NumberDot => switch (c) {
485 '.' => {
486 self.index -= 1;
487 state = State.Start;
488 break;
489 },
490 else => {
491 self.index -= 1;
492 result.id = Token.Id.FloatLiteral;
493 state = State.FloatFraction;
494 },
495 },
496 State.FloatFraction => switch (c) {
497 'p', 'P' => {
498 state = State.FloatExponentUnsigned;
499 },
500 '0'...'9', 'a'...'f', 'A'...'F' => {},
501 else => break,
502 },
503 State.FloatExponentUnsigned => switch (c) {
504 '+', '-' => {
505 state = State.FloatExponentNumber;
506 },
507 else => {
508 // reinterpret as a normal exponent number
509 self.index -= 1;
510 state = State.FloatExponentNumber;
511 }
512 },
513 State.FloatExponentNumber => switch (c) {
514 '0'...'9', 'a'...'f', 'A'...'F' => {},
515 else => break,
516 },
517 }
518 }
519 result.end = self.index;
520
521 if (result.id == Token.Id.Eof) {
522 if (self.pending_invalid_token) |token| {
523 self.pending_invalid_token = null;
524 return token;
525 }
526 }
527
528 return result;
529 }
530
531 pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 {
532 return self.buffer[token.start..token.end];
533 }
534
535 fn checkLiteralCharacter(self: &Tokenizer) void {
536 if (self.pending_invalid_token != null) return;
537 const invalid_length = self.getInvalidCharacterLength();
538 if (invalid_length == 0) return;
539 self.pending_invalid_token = Token {
540 .id = Token.Id.Invalid,
541 .start = self.index,
542 .end = self.index + invalid_length,
543 };
544 }
545
546 fn getInvalidCharacterLength(self: &Tokenizer) u3 {
547 const c0 = self.buffer[self.index];
548 if (c0 < 0x80) {
549 if (c0 < 0x20 or c0 == 0x7f) {
550 // ascii control codes are never allowed
551 // (note that \n was checked before we got here)
552 return 1;
553 }
554 // looks fine to me.
555 return 0;
556 } else {
557 // check utf8-encoded character.
558 const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1;
559 // the last 3 bytes in the buffer are guaranteed to be '\n',
560 // which means we don't need to do any bounds checking here.
561 const bytes = self.buffer[self.index..self.index + length];
562 switch (length) {
563 2 => {
564 const value = std.unicode.utf8Decode2(bytes) catch return length;
565 if (value == 0x85) return length; // U+0085 (NEL)
566 },
567 3 => {
568 const value = std.unicode.utf8Decode3(bytes) catch return length;
569 if (value == 0x2028) return length; // U+2028 (LS)
570 if (value == 0x2029) return length; // U+2029 (PS)
571 },
572 4 => {
573 _ = std.unicode.utf8Decode4(bytes) catch return length;
574 },
575 else => unreachable,
576 }
577 self.index += length - 1;
578 return 0;
579 }
580 }
581};
582
583
584
585test "tokenizer" {
586 testTokenize("test", []Token.Id {
587 Token.Id.Keyword_test,
588 });
589}
590
591test "tokenizer - invalid token characters" {
592 testTokenize("#", []Token.Id{Token.Id.Invalid});
593 testTokenize("`", []Token.Id{Token.Id.Invalid});
594}
595
596test "tokenizer - invalid literal/comment characters" {
597 testTokenize("\"\x00\"", []Token.Id {
598 Token.Id { .StringLiteral = Token.StrLitKind.Normal },
599 Token.Id.Invalid,
600 });
601 testTokenize("//\x00", []Token.Id {
602 Token.Id.Invalid,
603 });
604 testTokenize("//\x1f", []Token.Id {
605 Token.Id.Invalid,
606 });
607 testTokenize("//\x7f", []Token.Id {
608 Token.Id.Invalid,
609 });
610}
611
612test "tokenizer - utf8" {
613 testTokenize("//\xc2\x80", []Token.Id{});
614 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});
615}
616
617test "tokenizer - invalid utf8" {
618 testTokenize("//\x80", []Token.Id{Token.Id.Invalid});
619 testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});
620 testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});
621 testTokenize("//\xff", []Token.Id{Token.Id.Invalid});
622 testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});
623 testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});
624 testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});
625 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});
626}
627
628test "tokenizer - illegal unicode codepoints" {
629 // unicode newline characters.U+0085, U+2028, U+2029
630 testTokenize("//\xc2\x84", []Token.Id{});
631 testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});
632 testTokenize("//\xc2\x86", []Token.Id{});
633 testTokenize("//\xe2\x80\xa7", []Token.Id{});
634 testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});
635 testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});
636 testTokenize("//\xe2\x80\xaa", []Token.Id{});
637}
638
639fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
640 // (test authors, just make this bigger if you need it)
641 var padded_source: [0x100]u8 = undefined;
642 std.mem.copy(u8, padded_source[0..source.len], source);
643 padded_source[source.len + 0] = '\n';
644 padded_source[source.len + 1] = '\n';
645 padded_source[source.len + 2] = '\n';
646
647 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
648 for (expected_tokens) |expected_token_id| {
649 const token = tokenizer.next();
650 std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
651 switch (expected_token_id) {
652 Token.Id.StringLiteral => |expected_kind| {
653 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
654 },
655 else => {},
656 }
657 }
658 std.debug.assert(tokenizer.next().id == Token.Id.Eof);
659}
src/all_types.hpp+47-21
...@@ -236,7 +236,7 @@ struct ConstExprValue {...@@ -236,7 +236,7 @@ struct ConstExprValue {
236 TypeTableEntry *x_type;236 TypeTableEntry *x_type;
237 ConstExprValue *x_maybe;237 ConstExprValue *x_maybe;
238 ConstErrValue x_err_union;238 ConstErrValue x_err_union;
239 ErrorTableEntry *x_pure_err;239 ErrorTableEntry *x_err_set;
240 BigInt x_enum_tag;240 BigInt x_enum_tag;
241 ConstStructValue x_struct;241 ConstStructValue x_struct;
242 ConstUnionValue x_union;242 ConstUnionValue x_union;
...@@ -353,7 +353,6 @@ enum NodeType {...@@ -353,7 +353,6 @@ enum NodeType {
353 NodeTypeReturnExpr,353 NodeTypeReturnExpr,
354 NodeTypeDefer,354 NodeTypeDefer,
355 NodeTypeVariableDeclaration,355 NodeTypeVariableDeclaration,
356 NodeTypeErrorValueDecl,
357 NodeTypeTestDecl,356 NodeTypeTestDecl,
358 NodeTypeBinOpExpr,357 NodeTypeBinOpExpr,
359 NodeTypeUnwrapErrorExpr,358 NodeTypeUnwrapErrorExpr,
...@@ -393,6 +392,7 @@ enum NodeType {...@@ -393,6 +392,7 @@ enum NodeType {
393 NodeTypeVarLiteral,392 NodeTypeVarLiteral,
394 NodeTypeIfErrorExpr,393 NodeTypeIfErrorExpr,
395 NodeTypeTestExpr,394 NodeTypeTestExpr,
395 NodeTypeErrorSetDecl,
396};396};
397397
398struct AstNodeRoot {398struct AstNodeRoot {
...@@ -424,6 +424,8 @@ struct AstNodeFnProto {...@@ -424,6 +424,8 @@ struct AstNodeFnProto {
424 AstNode *align_expr;424 AstNode *align_expr;
425 // populated if the "section(S)" is present425 // populated if the "section(S)" is present
426 AstNode *section_expr;426 AstNode *section_expr;
427
428 bool auto_err_set;
427};429};
428430
429struct AstNodeFnDef {431struct AstNodeFnDef {
...@@ -486,12 +488,6 @@ struct AstNodeVariableDeclaration {...@@ -486,12 +488,6 @@ struct AstNodeVariableDeclaration {
486 AstNode *section_expr;488 AstNode *section_expr;
487};489};
488490
489struct AstNodeErrorValueDecl {
490 Buf *name;
491
492 ErrorTableEntry *err;
493};
494
495struct AstNodeTestDecl {491struct AstNodeTestDecl {
496 Buf *name;492 Buf *name;
497493
...@@ -514,8 +510,7 @@ enum BinOpType {...@@ -514,8 +510,7 @@ enum BinOpType {
514 BinOpTypeAssignBitAnd,510 BinOpTypeAssignBitAnd,
515 BinOpTypeAssignBitXor,511 BinOpTypeAssignBitXor,
516 BinOpTypeAssignBitOr,512 BinOpTypeAssignBitOr,
517 BinOpTypeAssignBoolAnd,513 BinOpTypeAssignMergeErrorSets,
518 BinOpTypeAssignBoolOr,
519 BinOpTypeBoolOr,514 BinOpTypeBoolOr,
520 BinOpTypeBoolAnd,515 BinOpTypeBoolAnd,
521 BinOpTypeCmpEq,516 BinOpTypeCmpEq,
...@@ -540,6 +535,8 @@ enum BinOpType {...@@ -540,6 +535,8 @@ enum BinOpType {
540 BinOpTypeUnwrapMaybe,535 BinOpTypeUnwrapMaybe,
541 BinOpTypeArrayCat,536 BinOpTypeArrayCat,
542 BinOpTypeArrayMult,537 BinOpTypeArrayMult,
538 BinOpTypeErrorUnion,
539 BinOpTypeMergeErrorSets,
543};540};
544541
545struct AstNodeBinOpExpr {542struct AstNodeBinOpExpr {
...@@ -563,6 +560,7 @@ enum CastOp {...@@ -563,6 +560,7 @@ enum CastOp {
563 CastOpResizeSlice,560 CastOpResizeSlice,
564 CastOpBytesToSlice,561 CastOpBytesToSlice,
565 CastOpNumLitToConcrete,562 CastOpNumLitToConcrete,
563 CastOpErrSet,
566};564};
567565
568struct AstNodeFnCallExpr {566struct AstNodeFnCallExpr {
...@@ -595,7 +593,6 @@ enum PrefixOp {...@@ -595,7 +593,6 @@ enum PrefixOp {
595 PrefixOpNegationWrap,593 PrefixOpNegationWrap,
596 PrefixOpDereference,594 PrefixOpDereference,
597 PrefixOpMaybe,595 PrefixOpMaybe,
598 PrefixOpError,
599 PrefixOpUnwrapMaybe,596 PrefixOpUnwrapMaybe,
600};597};
601598
...@@ -762,6 +759,10 @@ struct AstNodeContainerDecl {...@@ -762,6 +759,10 @@ struct AstNodeContainerDecl {
762 bool auto_enum; // union(enum)759 bool auto_enum; // union(enum)
763};760};
764761
762struct AstNodeErrorSetDecl {
763 ZigList<AstNode *> decls;
764};
765
765struct AstNodeStructField {766struct AstNodeStructField {
766 VisibMod visib_mod;767 VisibMod visib_mod;
767 Buf *name;768 Buf *name;
...@@ -858,7 +859,6 @@ struct AstNode {...@@ -858,7 +859,6 @@ struct AstNode {
858 AstNodeReturnExpr return_expr;859 AstNodeReturnExpr return_expr;
859 AstNodeDefer defer;860 AstNodeDefer defer;
860 AstNodeVariableDeclaration variable_declaration;861 AstNodeVariableDeclaration variable_declaration;
861 AstNodeErrorValueDecl error_value_decl;
862 AstNodeTestDecl test_decl;862 AstNodeTestDecl test_decl;
863 AstNodeBinOpExpr bin_op_expr;863 AstNodeBinOpExpr bin_op_expr;
864 AstNodeCatchExpr unwrap_err_expr;864 AstNodeCatchExpr unwrap_err_expr;
...@@ -899,6 +899,7 @@ struct AstNode {...@@ -899,6 +899,7 @@ struct AstNode {
899 AstNodeArrayType array_type;899 AstNodeArrayType array_type;
900 AstNodeErrorType error_type;900 AstNodeErrorType error_type;
901 AstNodeVarLiteral var_literal;901 AstNodeVarLiteral var_literal;
902 AstNodeErrorSetDecl err_set_decl;
902 } data;903 } data;
903};904};
904905
...@@ -993,8 +994,15 @@ struct TypeTableEntryMaybe {...@@ -993,8 +994,15 @@ struct TypeTableEntryMaybe {
993 TypeTableEntry *child_type;994 TypeTableEntry *child_type;
994};995};
995996
996struct TypeTableEntryError {997struct TypeTableEntryErrorUnion {
997 TypeTableEntry *child_type;998 TypeTableEntry *err_set_type;
999 TypeTableEntry *payload_type;
1000};
1001
1002struct TypeTableEntryErrorSet {
1003 uint32_t err_count;
1004 ErrorTableEntry **errors;
1005 FnTableEntry *infer_fn;
998};1006};
9991007
1000struct TypeTableEntryEnum {1008struct TypeTableEntryEnum {
...@@ -1097,7 +1105,7 @@ enum TypeTableEntryId {...@@ -1097,7 +1105,7 @@ enum TypeTableEntryId {
1097 TypeTableEntryIdNullLit,1105 TypeTableEntryIdNullLit,
1098 TypeTableEntryIdMaybe,1106 TypeTableEntryIdMaybe,
1099 TypeTableEntryIdErrorUnion,1107 TypeTableEntryIdErrorUnion,
1100 TypeTableEntryIdPureError,1108 TypeTableEntryIdErrorSet,
1101 TypeTableEntryIdEnum,1109 TypeTableEntryIdEnum,
1102 TypeTableEntryIdUnion,1110 TypeTableEntryIdUnion,
1103 TypeTableEntryIdFn,1111 TypeTableEntryIdFn,
...@@ -1126,7 +1134,8 @@ struct TypeTableEntry {...@@ -1126,7 +1134,8 @@ struct TypeTableEntry {
1126 TypeTableEntryArray array;1134 TypeTableEntryArray array;
1127 TypeTableEntryStruct structure;1135 TypeTableEntryStruct structure;
1128 TypeTableEntryMaybe maybe;1136 TypeTableEntryMaybe maybe;
1129 TypeTableEntryError error;1137 TypeTableEntryErrorUnion error_union;
1138 TypeTableEntryErrorSet error_set;
1130 TypeTableEntryEnum enumeration;1139 TypeTableEntryEnum enumeration;
1131 TypeTableEntryUnion unionation;1140 TypeTableEntryUnion unionation;
1132 TypeTableEntryFn fn;1141 TypeTableEntryFn fn;
...@@ -1136,7 +1145,6 @@ struct TypeTableEntry {...@@ -1136,7 +1145,6 @@ struct TypeTableEntry {
1136 // use these fields to make sure we don't duplicate type table entries for the same type1145 // use these fields to make sure we don't duplicate type table entries for the same type
1137 TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const]1146 TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const]
1138 TypeTableEntry *maybe_parent;1147 TypeTableEntry *maybe_parent;
1139 TypeTableEntry *error_parent;
1140 // If we generate a constant name value for this type, we memoize it here.1148 // If we generate a constant name value for this type, we memoize it here.
1141 // The type of this is array1149 // The type of this is array
1142 ConstExprValue *cached_const_name_val;1150 ConstExprValue *cached_const_name_val;
...@@ -1340,6 +1348,10 @@ struct TypeId {...@@ -1340,6 +1348,10 @@ struct TypeId {
1340 bool is_signed;1348 bool is_signed;
1341 uint32_t bit_count;1349 uint32_t bit_count;
1342 } integer;1350 } integer;
1351 struct {
1352 TypeTableEntry *err_set_type;
1353 TypeTableEntry *payload_type;
1354 } error_union;
1343 } data;1355 } data;
1344};1356};
13451357
...@@ -1481,7 +1493,7 @@ struct CodeGen {...@@ -1481,7 +1493,7 @@ struct CodeGen {
1481 TypeTableEntry *entry_undef;1493 TypeTableEntry *entry_undef;
1482 TypeTableEntry *entry_null;1494 TypeTableEntry *entry_null;
1483 TypeTableEntry *entry_var;1495 TypeTableEntry *entry_var;
1484 TypeTableEntry *entry_pure_error;1496 TypeTableEntry *entry_global_error_set;
1485 TypeTableEntry *entry_arg_tuple;1497 TypeTableEntry *entry_arg_tuple;
1486 } builtin_types;1498 } builtin_types;
14871499
...@@ -1570,7 +1582,6 @@ struct CodeGen {...@@ -1570,7 +1582,6 @@ struct CodeGen {
1570 LLVMValueRef return_address_fn_val;1582 LLVMValueRef return_address_fn_val;
1571 LLVMValueRef frame_address_fn_val;1583 LLVMValueRef frame_address_fn_val;
1572 bool error_during_imports;1584 bool error_during_imports;
1573 TypeTableEntry *err_tag_type;
15741585
1575 const char **clang_argv;1586 const char **clang_argv;
1576 size_t clang_argv_len;1587 size_t clang_argv_len;
...@@ -1584,7 +1595,9 @@ struct CodeGen {...@@ -1584,7 +1595,9 @@ struct CodeGen {
15841595
1585 bool each_lib_rpath;1596 bool each_lib_rpath;
15861597
1587 ZigList<AstNode *> error_decls;1598 TypeTableEntry *err_tag_type;
1599 ZigList<ZigLLVMDIEnumerator *> err_enumerators;
1600 ZigList<ErrorTableEntry *> errors_by_index;
1588 bool generate_error_name_table;1601 bool generate_error_name_table;
1589 LLVMValueRef err_name_table;1602 LLVMValueRef err_name_table;
1590 size_t largest_err_name_len;1603 size_t largest_err_name_len;
...@@ -1617,6 +1630,10 @@ struct CodeGen {...@@ -1617,6 +1630,10 @@ struct CodeGen {
1617 TypeTableEntry *align_amt_type;1630 TypeTableEntry *align_amt_type;
1618 TypeTableEntry *stack_trace_type;1631 TypeTableEntry *stack_trace_type;
1619 TypeTableEntry *ptr_to_stack_trace_type;1632 TypeTableEntry *ptr_to_stack_trace_type;
1633
1634 ZigList<ZigLLVMDIType **> error_di_types;
1635
1636 ZigList<Buf *> forbidden_libs;
1620};1637};
16211638
1622enum VarLinkage {1639enum VarLinkage {
...@@ -1653,6 +1670,7 @@ struct ErrorTableEntry {...@@ -1653,6 +1670,7 @@ struct ErrorTableEntry {
1653 Buf name;1670 Buf name;
1654 uint32_t value;1671 uint32_t value;
1655 AstNode *decl_node;1672 AstNode *decl_node;
1673 TypeTableEntry *set_with_only_this_in_it;
1656 // If we generate a constant error name value for this error, we memoize it here.1674 // If we generate a constant error name value for this error, we memoize it here.
1657 // The type of this is array1675 // The type of this is array
1658 ConstExprValue *cached_error_name_val;1676 ConstExprValue *cached_error_name_val;
...@@ -1920,6 +1938,7 @@ enum IrInstructionId {...@@ -1920,6 +1938,7 @@ enum IrInstructionId {
1920 IrInstructionIdArgType,1938 IrInstructionIdArgType,
1921 IrInstructionIdExport,1939 IrInstructionIdExport,
1922 IrInstructionIdErrorReturnTrace,1940 IrInstructionIdErrorReturnTrace,
1941 IrInstructionIdErrorUnion,
1923};1942};
19241943
1925struct IrInstruction {1944struct IrInstruction {
...@@ -1996,7 +2015,6 @@ enum IrUnOp {...@@ -1996,7 +2015,6 @@ enum IrUnOp {
1996 IrUnOpNegation,2015 IrUnOpNegation,
1997 IrUnOpNegationWrap,2016 IrUnOpNegationWrap,
1998 IrUnOpDereference,2017 IrUnOpDereference,
1999 IrUnOpError,
2000 IrUnOpMaybe,2018 IrUnOpMaybe,
2001};2019};
20022020
...@@ -2039,6 +2057,7 @@ enum IrBinOp {...@@ -2039,6 +2057,7 @@ enum IrBinOp {
2039 IrBinOpRemMod,2057 IrBinOpRemMod,
2040 IrBinOpArrayCat,2058 IrBinOpArrayCat,
2041 IrBinOpArrayMult,2059 IrBinOpArrayMult,
2060 IrBinOpMergeErrorSets,
2042};2061};
20432062
2044struct IrInstructionBinOp {2063struct IrInstructionBinOp {
...@@ -2750,6 +2769,13 @@ struct IrInstructionErrorReturnTrace {...@@ -2750,6 +2769,13 @@ struct IrInstructionErrorReturnTrace {
2750 IrInstruction base;2769 IrInstruction base;
2751};2770};
27522771
2772struct IrInstructionErrorUnion {
2773 IrInstruction base;
2774
2775 IrInstruction *err_set;
2776 IrInstruction *payload;
2777};
2778
2753static const size_t slice_ptr_index = 0;2779static const size_t slice_ptr_index = 0;
2754static const size_t slice_len_index = 1;2780static const size_t slice_len_index = 1;
27552781
src/analyze.cpp+169-201
...@@ -224,7 +224,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {...@@ -224,7 +224,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {
224 case TypeTableEntryIdNullLit:224 case TypeTableEntryIdNullLit:
225 case TypeTableEntryIdMaybe:225 case TypeTableEntryIdMaybe:
226 case TypeTableEntryIdErrorUnion:226 case TypeTableEntryIdErrorUnion:
227 case TypeTableEntryIdPureError:227 case TypeTableEntryIdErrorSet:
228 case TypeTableEntryIdFn:228 case TypeTableEntryIdFn:
229 case TypeTableEntryIdNamespace:229 case TypeTableEntryIdNamespace:
230 case TypeTableEntryIdBlock:230 case TypeTableEntryIdBlock:
...@@ -260,7 +260,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {...@@ -260,7 +260,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
260 case TypeTableEntryIdNullLit:260 case TypeTableEntryIdNullLit:
261 case TypeTableEntryIdMaybe:261 case TypeTableEntryIdMaybe:
262 case TypeTableEntryIdErrorUnion:262 case TypeTableEntryIdErrorUnion:
263 case TypeTableEntryIdPureError:263 case TypeTableEntryIdErrorSet:
264 case TypeTableEntryIdFn:264 case TypeTableEntryIdFn:
265 case TypeTableEntryIdNamespace:265 case TypeTableEntryIdNamespace:
266 case TypeTableEntryIdBlock:266 case TypeTableEntryIdBlock:
...@@ -514,29 +514,47 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -514,29 +514,47 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
514 }514 }
515}515}
516516
517TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {517TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type) {
518 if (child_type->error_parent)518 assert(err_set_type->id == TypeTableEntryIdErrorSet);
519 return child_type->error_parent;519 assert(!type_is_invalid(payload_type));
520
521 TypeId type_id = {};
522 type_id.id = TypeTableEntryIdErrorUnion;
523 type_id.data.error_union.err_set_type = err_set_type;
524 type_id.data.error_union.payload_type = payload_type;
525
526 auto existing_entry = g->type_table.maybe_get(type_id);
527 if (existing_entry) {
528 return existing_entry->value;
529 }
520530
521 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);531 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
522 entry->is_copyable = true;532 entry->is_copyable = true;
523 assert(child_type->type_ref);533 assert(payload_type->di_type);
524 assert(child_type->di_type);534 ensure_complete_type(g, payload_type);
525 ensure_complete_type(g, child_type);
526535
527 buf_resize(&entry->name, 0);536 buf_resize(&entry->name, 0);
528 buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name));537 buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
529538
530 entry->data.error.child_type = child_type;539 entry->data.error_union.err_set_type = err_set_type;
531540 entry->data.error_union.payload_type = payload_type;
532 if (!type_has_bits(child_type)) {
533 entry->type_ref = g->err_tag_type->type_ref;
534 entry->di_type = g->err_tag_type->di_type;
535541
542 if (!type_has_bits(payload_type)) {
543 if (type_has_bits(err_set_type)) {
544 entry->type_ref = err_set_type->type_ref;
545 entry->di_type = err_set_type->di_type;
546 g->error_di_types.append(&entry->di_type);
547 } else {
548 entry->zero_bits = true;
549 entry->di_type = g->builtin_types.entry_void->di_type;
550 }
551 } else if (!type_has_bits(err_set_type)) {
552 entry->type_ref = payload_type->type_ref;
553 entry->di_type = payload_type->di_type;
536 } else {554 } else {
537 LLVMTypeRef elem_types[] = {555 LLVMTypeRef elem_types[] = {
538 g->err_tag_type->type_ref,556 err_set_type->type_ref,
539 child_type->type_ref,557 payload_type->type_ref,
540 };558 };
541 entry->type_ref = LLVMStructType(elem_types, 2, false);559 entry->type_ref = LLVMStructType(elem_types, 2, false);
542560
...@@ -547,12 +565,12 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -547,12 +565,12 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
547 ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),565 ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),
548 compile_unit_scope, di_file, line);566 compile_unit_scope, di_file, line);
549567
550 uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref);568 uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_type->type_ref);
551 uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, g->err_tag_type->type_ref);569 uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_type->type_ref);
552 uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_err_index);570 uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_err_index);
553571
554 uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref);572 uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_type->type_ref);
555 uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref);573 uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_type->type_ref);
556 uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref,574 uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref,
557 err_union_payload_index);575 err_union_payload_index);
558576
...@@ -565,13 +583,13 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -565,13 +583,13 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
565 tag_debug_size_in_bits,583 tag_debug_size_in_bits,
566 tag_debug_align_in_bits,584 tag_debug_align_in_bits,
567 tag_offset_in_bits,585 tag_offset_in_bits,
568 0, child_type->di_type),586 0, err_set_type->di_type),
569 ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),587 ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
570 "value", di_file, line,588 "value", di_file, line,
571 value_debug_size_in_bits,589 value_debug_size_in_bits,
572 value_debug_align_in_bits,590 value_debug_align_in_bits,
573 value_offset_in_bits,591 value_offset_in_bits,
574 0, child_type->di_type),592 0, payload_type->di_type),
575 };593 };
576594
577 ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,595 ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
...@@ -587,7 +605,7 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -587,7 +605,7 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
587 entry->di_type = replacement_di_type;605 entry->di_type = replacement_di_type;
588 }606 }
589607
590 child_type->error_parent = entry;608 g->type_table.put(type_id, entry);
591 return entry;609 return entry;
592}610}
593611
...@@ -937,7 +955,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -937,7 +955,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
937 handle_is_ptr(fn_type_id->return_type);955 handle_is_ptr(fn_type_id->return_type);
938 bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&956 bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&
939 (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || 957 (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
940 fn_type_id->return_type->id == TypeTableEntryIdPureError);958 fn_type_id->return_type->id == TypeTableEntryIdErrorSet);
941 // +1 for maybe making the first argument the return value959 // +1 for maybe making the first argument the return value
942 // +1 for maybe last argument the error return trace960 // +1 for maybe last argument the error return trace
943 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(2 + fn_type_id->param_count);961 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(2 + fn_type_id->param_count);
...@@ -1177,7 +1195,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {...@@ -1177,7 +1195,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
1177 case TypeTableEntryIdUndefLit:1195 case TypeTableEntryIdUndefLit:
1178 case TypeTableEntryIdNullLit:1196 case TypeTableEntryIdNullLit:
1179 case TypeTableEntryIdErrorUnion:1197 case TypeTableEntryIdErrorUnion:
1180 case TypeTableEntryIdPureError:1198 case TypeTableEntryIdErrorSet:
1181 case TypeTableEntryIdNamespace:1199 case TypeTableEntryIdNamespace:
1182 case TypeTableEntryIdBlock:1200 case TypeTableEntryIdBlock:
1183 case TypeTableEntryIdBoundFn:1201 case TypeTableEntryIdBoundFn:
...@@ -1218,7 +1236,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {...@@ -1218,7 +1236,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1218 case TypeTableEntryIdUndefLit:1236 case TypeTableEntryIdUndefLit:
1219 case TypeTableEntryIdNullLit:1237 case TypeTableEntryIdNullLit:
1220 case TypeTableEntryIdErrorUnion:1238 case TypeTableEntryIdErrorUnion:
1221 case TypeTableEntryIdPureError:1239 case TypeTableEntryIdErrorSet:
1222 case TypeTableEntryIdNamespace:1240 case TypeTableEntryIdNamespace:
1223 case TypeTableEntryIdBlock:1241 case TypeTableEntryIdBlock:
1224 case TypeTableEntryIdBoundFn:1242 case TypeTableEntryIdBoundFn:
...@@ -1263,7 +1281,23 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {...@@ -1263,7 +1281,23 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1263 zig_unreachable();1281 zig_unreachable();
1264}1282}
12651283
1266static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {1284TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) {
1285 TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
1286 buf_resize(&err_set_type->name, 0);
1287 buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name));
1288 err_set_type->is_copyable = true;
1289 err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref;
1290 err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
1291 err_set_type->data.error_set.err_count = 0;
1292 err_set_type->data.error_set.errors = nullptr;
1293 err_set_type->data.error_set.infer_fn = fn_entry;
1294
1295 g->error_di_types.append(&err_set_type->di_type);
1296
1297 return err_set_type;
1298}
1299
1300static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) {
1267 assert(proto_node->type == NodeTypeFnProto);1301 assert(proto_node->type == NodeTypeFnProto);
1268 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;1302 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
12691303
...@@ -1359,7 +1393,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1359,7 +1393,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1359 case TypeTableEntryIdStruct:1393 case TypeTableEntryIdStruct:
1360 case TypeTableEntryIdMaybe:1394 case TypeTableEntryIdMaybe:
1361 case TypeTableEntryIdErrorUnion:1395 case TypeTableEntryIdErrorUnion:
1362 case TypeTableEntryIdPureError:1396 case TypeTableEntryIdErrorSet:
1363 case TypeTableEntryIdEnum:1397 case TypeTableEntryIdEnum:
1364 case TypeTableEntryIdUnion:1398 case TypeTableEntryIdUnion:
1365 case TypeTableEntryIdFn:1399 case TypeTableEntryIdFn:
...@@ -1382,13 +1416,19 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1382,13 +1416,19 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1382 }1416 }
1383 }1417 }
13841418
1385 fn_type_id.return_type = (fn_proto->return_type == nullptr) ?1419 TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
1386 g->builtin_types.entry_void : analyze_type_expr(g, child_scope, fn_proto->return_type);1420 if (type_is_invalid(specified_return_type)) {
13871421 fn_type_id.return_type = g->builtin_types.entry_invalid;
1388 if (type_is_invalid(fn_type_id.return_type)) {
1389 return g->builtin_types.entry_invalid;1422 return g->builtin_types.entry_invalid;
1390 }1423 }
13911424
1425 if (fn_proto->auto_err_set) {
1426 TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(g, fn_entry);
1427 fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type);
1428 } else {
1429 fn_type_id.return_type = specified_return_type;
1430 }
1431
1392 if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, fn_type_id.return_type)) {1432 if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, fn_type_id.return_type)) {
1393 add_node_error(g, fn_proto->return_type,1433 add_node_error(g, fn_proto->return_type,
1394 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",1434 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
...@@ -1434,7 +1474,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1434,7 +1474,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1434 case TypeTableEntryIdStruct:1474 case TypeTableEntryIdStruct:
1435 case TypeTableEntryIdMaybe:1475 case TypeTableEntryIdMaybe:
1436 case TypeTableEntryIdErrorUnion:1476 case TypeTableEntryIdErrorUnion:
1437 case TypeTableEntryIdPureError:1477 case TypeTableEntryIdErrorSet:
1438 case TypeTableEntryIdEnum:1478 case TypeTableEntryIdEnum:
1439 case TypeTableEntryIdUnion:1479 case TypeTableEntryIdUnion:
1440 case TypeTableEntryIdFn:1480 case TypeTableEntryIdFn:
...@@ -2756,7 +2796,8 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) {...@@ -2756,7 +2796,8 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) {
2756 return g->test_fn_type;2796 return g->test_fn_type;
27572797
2758 FnTypeId fn_type_id = {0};2798 FnTypeId fn_type_id = {0};
2759 fn_type_id.return_type = get_error_type(g, g->builtin_types.entry_void);2799 fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set,
2800 g->builtin_types.entry_void);
2760 g->test_fn_type = get_fn_type(g, &fn_type_id);2801 g->test_fn_type = get_fn_type(g, &fn_type_id);
2761 return g->test_fn_type;2802 return g->test_fn_type;
2762}2803}
...@@ -2824,7 +2865,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {...@@ -2824,7 +2865,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
28242865
2825 Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;2866 Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
28262867
2827 fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope);2868 fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry);
28282869
2829 if (fn_proto->section_expr != nullptr) {2870 if (fn_proto->section_expr != nullptr) {
2830 if (fn_table_entry->body_node == nullptr) {2871 if (fn_table_entry->body_node == nullptr) {
...@@ -2949,29 +2990,6 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope...@@ -2949,29 +2990,6 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope
2949 g->resolve_queue.append(&tld_fn->base);2990 g->resolve_queue.append(&tld_fn->base);
2950}2991}
29512992
2952static void preview_error_value_decl(CodeGen *g, AstNode *node) {
2953 assert(node->type == NodeTypeErrorValueDecl);
2954
2955 ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
2956
2957 err->decl_node = node;
2958 buf_init_from_buf(&err->name, node->data.error_value_decl.name);
2959
2960 auto existing_entry = g->error_table.maybe_get(&err->name);
2961 if (existing_entry) {
2962 // duplicate error definitions allowed and they get the same value
2963 err->value = existing_entry->value->value;
2964 } else {
2965 size_t error_value_count = g->error_decls.length;
2966 assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)g->err_tag_type->data.integral.bit_count));
2967 err->value = (uint32_t)error_value_count;
2968 g->error_decls.append(node);
2969 g->error_table.put(&err->name, err);
2970 }
2971
2972 node->data.error_value_decl.err = err;
2973}
2974
2975static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {2993static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
2976 assert(node->type == NodeTypeCompTime);2994 assert(node->type == NodeTypeCompTime);
29772995
...@@ -3045,10 +3063,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {...@@ -3045,10 +3063,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
3045 import->use_decls.append(node);3063 import->use_decls.append(node);
3046 break;3064 break;
3047 }3065 }
3048 case NodeTypeErrorValueDecl:
3049 // error value declarations do not depend on other top level decls
3050 preview_error_value_decl(g, node);
3051 break;
3052 case NodeTypeTestDecl:3066 case NodeTypeTestDecl:
3053 preview_test_decl(g, node, decls_scope);3067 preview_test_decl(g, node, decls_scope);
3054 break;3068 break;
...@@ -3097,6 +3111,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {...@@ -3097,6 +3111,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
3097 case NodeTypeVarLiteral:3111 case NodeTypeVarLiteral:
3098 case NodeTypeIfErrorExpr:3112 case NodeTypeIfErrorExpr:
3099 case NodeTypeTestExpr:3113 case NodeTypeTestExpr:
3114 case NodeTypeErrorSetDecl:
3100 zig_unreachable();3115 zig_unreachable();
3101 }3116 }
3102}3117}
...@@ -3147,7 +3162,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt...@@ -3147,7 +3162,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
3147 case TypeTableEntryIdStruct:3162 case TypeTableEntryIdStruct:
3148 case TypeTableEntryIdMaybe:3163 case TypeTableEntryIdMaybe:
3149 case TypeTableEntryIdErrorUnion:3164 case TypeTableEntryIdErrorUnion:
3150 case TypeTableEntryIdPureError:3165 case TypeTableEntryIdErrorSet:
3151 case TypeTableEntryIdEnum:3166 case TypeTableEntryIdEnum:
3152 case TypeTableEntryIdUnion:3167 case TypeTableEntryIdUnion:
3153 case TypeTableEntryIdFn:3168 case TypeTableEntryIdFn:
...@@ -3362,108 +3377,6 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so...@@ -3362,108 +3377,6 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so
3362 g->tld_ref_source_node_stack.pop();3377 g->tld_ref_source_node_stack.pop();
3363}3378}
33643379
3365bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) {
3366 if (expected_type == actual_type)
3367 return true;
3368
3369 // pointer const
3370 if (expected_type->id == TypeTableEntryIdPointer &&
3371 actual_type->id == TypeTableEntryIdPointer &&
3372 (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) &&
3373 (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) &&
3374 actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset &&
3375 actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count &&
3376 actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment)
3377 {
3378 return types_match_const_cast_only(expected_type->data.pointer.child_type,
3379 actual_type->data.pointer.child_type);
3380 }
3381
3382 // slice const
3383 if (is_slice(expected_type) && is_slice(actual_type)) {
3384 TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
3385 TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;
3386 if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) &&
3387 (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) &&
3388 actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset &&
3389 actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count &&
3390 actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment)
3391 {
3392 return types_match_const_cast_only(expected_ptr_type->data.pointer.child_type,
3393 actual_ptr_type->data.pointer.child_type);
3394 }
3395 }
3396
3397 // maybe
3398 if (expected_type->id == TypeTableEntryIdMaybe &&
3399 actual_type->id == TypeTableEntryIdMaybe)
3400 {
3401 return types_match_const_cast_only(
3402 expected_type->data.maybe.child_type,
3403 actual_type->data.maybe.child_type);
3404 }
3405
3406 // error
3407 if (expected_type->id == TypeTableEntryIdErrorUnion &&
3408 actual_type->id == TypeTableEntryIdErrorUnion)
3409 {
3410 return types_match_const_cast_only(
3411 expected_type->data.error.child_type,
3412 actual_type->data.error.child_type);
3413 }
3414
3415 // fn
3416 if (expected_type->id == TypeTableEntryIdFn &&
3417 actual_type->id == TypeTableEntryIdFn)
3418 {
3419 if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
3420 return false;
3421 }
3422 if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
3423 return false;
3424 }
3425 if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
3426 return false;
3427 }
3428 if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
3429 return false;
3430 }
3431 if (!expected_type->data.fn.is_generic &&
3432 actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable &&
3433 !types_match_const_cast_only(
3434 expected_type->data.fn.fn_type_id.return_type,
3435 actual_type->data.fn.fn_type_id.return_type))
3436 {
3437 return false;
3438 }
3439 if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
3440 return false;
3441 }
3442 if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
3443 return false;
3444 }
3445 assert(expected_type->data.fn.is_generic ||
3446 expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count);
3447 for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) {
3448 // note it's reversed for parameters
3449 FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
3450 FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
3451
3452 if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) {
3453 return false;
3454 }
3455
3456 if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
3457 return false;
3458 }
3459 }
3460 return true;
3461 }
3462
3463
3464 return false;
3465}
3466
3467Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {3380Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
3468 // we must resolve all the use decls3381 // we must resolve all the use decls
3469 ImportTableEntry *import = get_scope_import(scope);3382 ImportTableEntry *import = get_scope_import(scope);
...@@ -3625,7 +3538,7 @@ static bool is_container(TypeTableEntry *type_entry) {...@@ -3625,7 +3538,7 @@ static bool is_container(TypeTableEntry *type_entry) {
3625 case TypeTableEntryIdNullLit:3538 case TypeTableEntryIdNullLit:
3626 case TypeTableEntryIdMaybe:3539 case TypeTableEntryIdMaybe:
3627 case TypeTableEntryIdErrorUnion:3540 case TypeTableEntryIdErrorUnion:
3628 case TypeTableEntryIdPureError:3541 case TypeTableEntryIdErrorSet:
3629 case TypeTableEntryIdFn:3542 case TypeTableEntryIdFn:
3630 case TypeTableEntryIdNamespace:3543 case TypeTableEntryIdNamespace:
3631 case TypeTableEntryIdBlock:3544 case TypeTableEntryIdBlock:
...@@ -3673,7 +3586,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {...@@ -3673,7 +3586,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
3673 case TypeTableEntryIdNullLit:3586 case TypeTableEntryIdNullLit:
3674 case TypeTableEntryIdMaybe:3587 case TypeTableEntryIdMaybe:
3675 case TypeTableEntryIdErrorUnion:3588 case TypeTableEntryIdErrorUnion:
3676 case TypeTableEntryIdPureError:3589 case TypeTableEntryIdErrorSet:
3677 case TypeTableEntryIdFn:3590 case TypeTableEntryIdFn:
3678 case TypeTableEntryIdNamespace:3591 case TypeTableEntryIdNamespace:
3679 case TypeTableEntryIdBlock:3592 case TypeTableEntryIdBlock:
...@@ -3765,6 +3678,27 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari...@@ -3765,6 +3678,27 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari
3765 }3678 }
3766}3679}
37673680
3681static bool analyze_resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) {
3682 FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn;
3683 if (infer_fn != nullptr) {
3684 if (infer_fn->anal_state == FnAnalStateInvalid) {
3685 return false;
3686 } else if (infer_fn->anal_state == FnAnalStateReady) {
3687 analyze_fn_body(g, infer_fn);
3688 if (err_set_type->data.error_set.infer_fn != nullptr) {
3689 assert(g->errors.length != 0);
3690 return false;
3691 }
3692 } else {
3693 add_node_error(g, source_node,
3694 buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
3695 buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
3696 return false;
3697 }
3698 }
3699 return true;
3700}
3701
3768void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {3702void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {
3769 TypeTableEntry *fn_type = fn_table_entry->type_entry;3703 TypeTableEntry *fn_type = fn_table_entry->type_entry;
3770 assert(!fn_type->data.fn.is_generic);3704 assert(!fn_type->data.fn.is_generic);
...@@ -3774,14 +3708,49 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ...@@ -3774,14 +3708,49 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
3774 &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);3708 &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
3775 fn_table_entry->implicit_return_type = block_return_type;3709 fn_table_entry->implicit_return_type = block_return_type;
37763710
3777 if (block_return_type->id == TypeTableEntryIdInvalid ||3711 if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
3778 fn_table_entry->analyzed_executable.invalid)
3779 {
3780 assert(g->errors.length > 0);3712 assert(g->errors.length > 0);
3781 fn_table_entry->anal_state = FnAnalStateInvalid;3713 fn_table_entry->anal_state = FnAnalStateInvalid;
3782 return;3714 return;
3783 }3715 }
37843716
3717 if (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
3718 TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
3719 if (return_err_set_type->data.error_set.infer_fn != nullptr) {
3720 TypeTableEntry *inferred_err_set_type;
3721 if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) {
3722 inferred_err_set_type = fn_table_entry->implicit_return_type;
3723 } else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) {
3724 inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type;
3725 } else {
3726 add_node_error(g, return_type_node,
3727 buf_sprintf("function with inferred error set must return at least one possible error"));
3728 fn_table_entry->anal_state = FnAnalStateInvalid;
3729 return;
3730 }
3731
3732 if (inferred_err_set_type->data.error_set.infer_fn != nullptr) {
3733 if (!analyze_resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
3734 fn_table_entry->anal_state = FnAnalStateInvalid;
3735 return;
3736 }
3737 }
3738
3739 return_err_set_type->data.error_set.infer_fn = nullptr;
3740 if (type_is_global_error_set(inferred_err_set_type)) {
3741 return_err_set_type->data.error_set.err_count = UINT32_MAX;
3742 } else {
3743 return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
3744 if (inferred_err_set_type->data.error_set.err_count > 0) {
3745 return_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
3746 for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
3747 return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
3748 }
3749 }
3750 }
3751 }
3752 }
3753
3785 if (g->verbose_ir) {3754 if (g->verbose_ir) {
3786 fprintf(stderr, "{ // (analyzed)\n");3755 fprintf(stderr, "{ // (analyzed)\n");
3787 ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);3756 ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);
...@@ -3791,7 +3760,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ...@@ -3791,7 +3760,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
3791 fn_table_entry->anal_state = FnAnalStateComplete;3760 fn_table_entry->anal_state = FnAnalStateComplete;
3792}3761}
37933762
3794static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {3763void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
3795 assert(fn_table_entry->anal_state != FnAnalStateProbing);3764 assert(fn_table_entry->anal_state != FnAnalStateProbing);
3796 if (fn_table_entry->anal_state != FnAnalStateReady)3765 if (fn_table_entry->anal_state != FnAnalStateReady)
3797 return;3766 return;
...@@ -4022,7 +3991,8 @@ void semantic_analyze(CodeGen *g) {...@@ -4022,7 +3991,8 @@ void semantic_analyze(CodeGen *g) {
4022 for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {3991 for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
4023 Tld *tld = g->resolve_queue.at(g->resolve_queue_index);3992 Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
4024 bool pointer_only = false;3993 bool pointer_only = false;
4025 resolve_top_level_decl(g, tld, pointer_only, nullptr);3994 AstNode *source_node = nullptr;
3995 resolve_top_level_decl(g, tld, pointer_only, source_node);
4026 }3996 }
40273997
4028 for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {3998 for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
...@@ -4114,7 +4084,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {...@@ -4114,7 +4084,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
4114 case TypeTableEntryIdInt:4084 case TypeTableEntryIdInt:
4115 case TypeTableEntryIdFloat:4085 case TypeTableEntryIdFloat:
4116 case TypeTableEntryIdPointer:4086 case TypeTableEntryIdPointer:
4117 case TypeTableEntryIdPureError:4087 case TypeTableEntryIdErrorSet:
4118 case TypeTableEntryIdFn:4088 case TypeTableEntryIdFn:
4119 case TypeTableEntryIdEnum:4089 case TypeTableEntryIdEnum:
4120 return false;4090 return false;
...@@ -4122,7 +4092,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {...@@ -4122,7 +4092,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
4122 case TypeTableEntryIdStruct:4092 case TypeTableEntryIdStruct:
4123 return type_has_bits(type_entry);4093 return type_has_bits(type_entry);
4124 case TypeTableEntryIdErrorUnion:4094 case TypeTableEntryIdErrorUnion:
4125 return type_has_bits(type_entry->data.error.child_type);4095 return type_has_bits(type_entry->data.error_union.payload_type);
4126 case TypeTableEntryIdMaybe:4096 case TypeTableEntryIdMaybe:
4127 return type_has_bits(type_entry->data.maybe.child_type) &&4097 return type_has_bits(type_entry->data.maybe.child_type) &&
4128 type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&4098 type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
...@@ -4386,9 +4356,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {...@@ -4386,9 +4356,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
4386 case TypeTableEntryIdErrorUnion:4356 case TypeTableEntryIdErrorUnion:
4387 // TODO better hashing algorithm4357 // TODO better hashing algorithm
4388 return 3415065496;4358 return 3415065496;
4389 case TypeTableEntryIdPureError:4359 case TypeTableEntryIdErrorSet:
4390 // TODO better hashing algorithm4360 assert(const_val->data.x_err_set != nullptr);
4391 return 2630160122;4361 return const_val->data.x_err_set->value ^ 2630160122;
4392 case TypeTableEntryIdFn:4362 case TypeTableEntryIdFn:
4393 return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry);4363 return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry);
4394 case TypeTableEntryIdNamespace:4364 case TypeTableEntryIdNamespace:
...@@ -4515,7 +4485,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {...@@ -4515,7 +4485,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
4515 case TypeTableEntryIdMaybe:4485 case TypeTableEntryIdMaybe:
4516 case TypeTableEntryIdErrorUnion:4486 case TypeTableEntryIdErrorUnion:
4517 case TypeTableEntryIdEnum:4487 case TypeTableEntryIdEnum:
4518 case TypeTableEntryIdPureError:4488 case TypeTableEntryIdErrorSet:
4519 case TypeTableEntryIdFn:4489 case TypeTableEntryIdFn:
4520 case TypeTableEntryIdBool:4490 case TypeTableEntryIdBool:
4521 case TypeTableEntryIdInt:4491 case TypeTableEntryIdInt:
...@@ -4894,8 +4864,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {...@@ -4894,8 +4864,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
4894 return a->data.x_type == b->data.x_type;4864 return a->data.x_type == b->data.x_type;
4895 case TypeTableEntryIdVoid:4865 case TypeTableEntryIdVoid:
4896 return true;4866 return true;
4897 case TypeTableEntryIdPureError:4867 case TypeTableEntryIdErrorSet:
4898 return a->data.x_pure_err == b->data.x_pure_err;4868 return a->data.x_err_set->value == b->data.x_err_set->value;
4899 case TypeTableEntryIdFn:4869 case TypeTableEntryIdFn:
4900 return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry;4870 return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry;
4901 case TypeTableEntryIdBool:4871 case TypeTableEntryIdBool:
...@@ -5256,9 +5226,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {...@@ -5256,9 +5226,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
5256 buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name));5226 buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name));
5257 return;5227 return;
5258 }5228 }
5259 case TypeTableEntryIdPureError:5229 case TypeTableEntryIdErrorSet:
5260 {5230 {
5261 buf_appendf(buf, "(pure error constant)");5231 buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
5262 return;5232 return;
5263 }5233 }
5264 case TypeTableEntryIdArgTuple:5234 case TypeTableEntryIdArgTuple:
...@@ -5319,8 +5289,7 @@ uint32_t type_id_hash(TypeId x) {...@@ -5319,8 +5289,7 @@ uint32_t type_id_hash(TypeId x) {
5319 case TypeTableEntryIdUndefLit:5289 case TypeTableEntryIdUndefLit:
5320 case TypeTableEntryIdNullLit:5290 case TypeTableEntryIdNullLit:
5321 case TypeTableEntryIdMaybe:5291 case TypeTableEntryIdMaybe:
5322 case TypeTableEntryIdErrorUnion:5292 case TypeTableEntryIdErrorSet:
5323 case TypeTableEntryIdPureError:
5324 case TypeTableEntryIdEnum:5293 case TypeTableEntryIdEnum:
5325 case TypeTableEntryIdUnion:5294 case TypeTableEntryIdUnion:
5326 case TypeTableEntryIdFn:5295 case TypeTableEntryIdFn:
...@@ -5329,6 +5298,8 @@ uint32_t type_id_hash(TypeId x) {...@@ -5329,6 +5298,8 @@ uint32_t type_id_hash(TypeId x) {
5329 case TypeTableEntryIdBoundFn:5298 case TypeTableEntryIdBoundFn:
5330 case TypeTableEntryIdArgTuple:5299 case TypeTableEntryIdArgTuple:
5331 zig_unreachable();5300 zig_unreachable();
5301 case TypeTableEntryIdErrorUnion:
5302 return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
5332 case TypeTableEntryIdPointer:5303 case TypeTableEntryIdPointer:
5333 return hash_ptr(x.data.pointer.child_type) +5304 return hash_ptr(x.data.pointer.child_type) +
5334 (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +5305 (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
...@@ -5363,8 +5334,7 @@ bool type_id_eql(TypeId a, TypeId b) {...@@ -5363,8 +5334,7 @@ bool type_id_eql(TypeId a, TypeId b) {
5363 case TypeTableEntryIdUndefLit:5334 case TypeTableEntryIdUndefLit:
5364 case TypeTableEntryIdNullLit:5335 case TypeTableEntryIdNullLit:
5365 case TypeTableEntryIdMaybe:5336 case TypeTableEntryIdMaybe:
5366 case TypeTableEntryIdErrorUnion:5337 case TypeTableEntryIdErrorSet:
5367 case TypeTableEntryIdPureError:
5368 case TypeTableEntryIdEnum:5338 case TypeTableEntryIdEnum:
5369 case TypeTableEntryIdUnion:5339 case TypeTableEntryIdUnion:
5370 case TypeTableEntryIdFn:5340 case TypeTableEntryIdFn:
...@@ -5374,6 +5344,10 @@ bool type_id_eql(TypeId a, TypeId b) {...@@ -5374,6 +5344,10 @@ bool type_id_eql(TypeId a, TypeId b) {
5374 case TypeTableEntryIdArgTuple:5344 case TypeTableEntryIdArgTuple:
5375 case TypeTableEntryIdOpaque:5345 case TypeTableEntryIdOpaque:
5376 zig_unreachable();5346 zig_unreachable();
5347 case TypeTableEntryIdErrorUnion:
5348 return a.data.error_union.err_set_type == b.data.error_union.err_set_type &&
5349 a.data.error_union.payload_type == b.data.error_union.payload_type;
5350
5377 case TypeTableEntryIdPointer:5351 case TypeTableEntryIdPointer:
5378 return a.data.pointer.child_type == b.data.pointer.child_type &&5352 return a.data.pointer.child_type == b.data.pointer.child_type &&
5379 a.data.pointer.is_const == b.data.pointer.is_const &&5353 a.data.pointer.is_const == b.data.pointer.is_const &&
...@@ -5478,7 +5452,7 @@ static const TypeTableEntryId all_type_ids[] = {...@@ -5478,7 +5452,7 @@ static const TypeTableEntryId all_type_ids[] = {
5478 TypeTableEntryIdNullLit,5452 TypeTableEntryIdNullLit,
5479 TypeTableEntryIdMaybe,5453 TypeTableEntryIdMaybe,
5480 TypeTableEntryIdErrorUnion,5454 TypeTableEntryIdErrorUnion,
5481 TypeTableEntryIdPureError,5455 TypeTableEntryIdErrorSet,
5482 TypeTableEntryIdEnum,5456 TypeTableEntryIdEnum,
5483 TypeTableEntryIdUnion,5457 TypeTableEntryIdUnion,
5484 TypeTableEntryIdFn,5458 TypeTableEntryIdFn,
...@@ -5533,7 +5507,7 @@ size_t type_id_index(TypeTableEntryId id) {...@@ -5533,7 +5507,7 @@ size_t type_id_index(TypeTableEntryId id) {
5533 return 13;5507 return 13;
5534 case TypeTableEntryIdErrorUnion:5508 case TypeTableEntryIdErrorUnion:
5535 return 14;5509 return 14;
5536 case TypeTableEntryIdPureError:5510 case TypeTableEntryIdErrorSet:
5537 return 15;5511 return 15;
5538 case TypeTableEntryIdEnum:5512 case TypeTableEntryIdEnum:
5539 return 16;5513 return 16;
...@@ -5590,8 +5564,8 @@ const char *type_id_name(TypeTableEntryId id) {...@@ -5590,8 +5564,8 @@ const char *type_id_name(TypeTableEntryId id) {
5590 return "Nullable";5564 return "Nullable";
5591 case TypeTableEntryIdErrorUnion:5565 case TypeTableEntryIdErrorUnion:
5592 return "ErrorUnion";5566 return "ErrorUnion";
5593 case TypeTableEntryIdPureError:5567 case TypeTableEntryIdErrorSet:
5594 return "Error";5568 return "ErrorSet";
5595 case TypeTableEntryIdEnum:5569 case TypeTableEntryIdEnum:
5596 return "Enum";5570 return "Enum";
5597 case TypeTableEntryIdUnion:5571 case TypeTableEntryIdUnion:
...@@ -5640,17 +5614,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {...@@ -5640,17 +5614,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
5640 return link_lib;5614 return link_lib;
5641}5615}
56425616
5643void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name) {
5644 LinkLib *link_lib = add_link_lib(g, lib_name);
5645 for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
5646 Buf *existing_symbol_name = link_lib->symbols.at(i);
5647 if (buf_eql_buf(existing_symbol_name, symbol_name)) {
5648 return;
5649 }
5650 }
5651 link_lib->symbols.append(symbol_name);
5652}
5653
5654uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {5617uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
5655 type_ensure_zero_bits_known(g, type_entry);5618 type_ensure_zero_bits_known(g, type_entry);
5656 if (type_entry->zero_bits) return 0;5619 if (type_entry->zero_bits) return 0;
...@@ -5696,3 +5659,8 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {...@@ -5696,3 +5659,8 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
5696 return var_value;5659 return var_value;
5697}5660}
56985661
5662bool type_is_global_error_set(TypeTableEntry *err_set_type) {
5663 assert(err_set_type->id == TypeTableEntryIdErrorSet);
5664 assert(err_set_type->data.error_set.infer_fn == nullptr);
5665 return err_set_type->data.error_set.err_count == UINT32_MAX;
5666}
src/analyze.hpp+4-4
...@@ -30,7 +30,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type);...@@ -30,7 +30,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type);
30TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,30TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
31 AstNode *decl_node, const char *name, ContainerLayout layout);31 AstNode *decl_node, const char *name, ContainerLayout layout);
32TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);32TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
33TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type);33TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type);
34TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry);34TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry);
35TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name);35TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name);
36TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],36TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
...@@ -46,8 +46,6 @@ bool type_has_bits(TypeTableEntry *type_entry);...@@ -46,8 +46,6 @@ bool type_has_bits(TypeTableEntry *type_entry);
46ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);46ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
4747
4848
49// TODO move these over, these used to be static
50bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type);
51VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name);49VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name);
52Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);50Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);
53void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node);51void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node);
...@@ -58,6 +56,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt...@@ -58,6 +56,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
58TypeTableEntry *container_ref_type(TypeTableEntry *type_entry);56TypeTableEntry *container_ref_type(TypeTableEntry *type_entry);
59bool type_is_complete(TypeTableEntry *type_entry);57bool type_is_complete(TypeTableEntry *type_entry);
60bool type_is_invalid(TypeTableEntry *type_entry);58bool type_is_invalid(TypeTableEntry *type_entry);
59bool type_is_global_error_set(TypeTableEntry *err_set_type);
61bool type_has_zero_bits_known(TypeTableEntry *type_entry);60bool type_has_zero_bits_known(TypeTableEntry *type_entry);
62void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);61void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
63ScopeDecls *get_container_scope(TypeTableEntry *type_entry);62ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
...@@ -176,7 +175,6 @@ bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);...@@ -176,7 +175,6 @@ bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
176LinkLib *create_link_lib(Buf *name);175LinkLib *create_link_lib(Buf *name);
177bool calling_convention_does_first_arg_return(CallingConvention cc);176bool calling_convention_does_first_arg_return(CallingConvention cc);
178LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);177LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
179void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name);
180178
181uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry);179uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry);
182TypeTableEntry *get_align_amt_type(CodeGen *g);180TypeTableEntry *get_align_amt_type(CodeGen *g);
...@@ -188,6 +186,8 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G...@@ -188,6 +186,8 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
188186
189ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);187ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
190TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);188TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
189void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
191190
191TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry);
192192
193#endif193#endif
src/ast_render.cpp+26-7
...@@ -49,11 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) {...@@ -49,11 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) {
49 case BinOpTypeAssignBitAnd: return "&=";49 case BinOpTypeAssignBitAnd: return "&=";
50 case BinOpTypeAssignBitXor: return "^=";50 case BinOpTypeAssignBitXor: return "^=";
51 case BinOpTypeAssignBitOr: return "|=";51 case BinOpTypeAssignBitOr: return "|=";
52 case BinOpTypeAssignBoolAnd: return "&&=";52 case BinOpTypeAssignMergeErrorSets: return "||=";
53 case BinOpTypeAssignBoolOr: return "||=";
54 case BinOpTypeUnwrapMaybe: return "??";53 case BinOpTypeUnwrapMaybe: return "??";
55 case BinOpTypeArrayCat: return "++";54 case BinOpTypeArrayCat: return "++";
56 case BinOpTypeArrayMult: return "**";55 case BinOpTypeArrayMult: return "**";
56 case BinOpTypeErrorUnion: return "!";
57 case BinOpTypeMergeErrorSets: return "||";
57 }58 }
58 zig_unreachable();59 zig_unreachable();
59}60}
...@@ -67,7 +68,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {...@@ -67,7 +68,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
67 case PrefixOpBinNot: return "~";68 case PrefixOpBinNot: return "~";
68 case PrefixOpDereference: return "*";69 case PrefixOpDereference: return "*";
69 case PrefixOpMaybe: return "?";70 case PrefixOpMaybe: return "?";
70 case PrefixOpError: return "%";
71 case PrefixOpUnwrapMaybe: return "??";71 case PrefixOpUnwrapMaybe: return "??";
72 }72 }
73 zig_unreachable();73 zig_unreachable();
...@@ -174,8 +174,6 @@ static const char *node_type_str(NodeType node_type) {...@@ -174,8 +174,6 @@ static const char *node_type_str(NodeType node_type) {
174 return "Defer";174 return "Defer";
175 case NodeTypeVariableDeclaration:175 case NodeTypeVariableDeclaration:
176 return "VariableDeclaration";176 return "VariableDeclaration";
177 case NodeTypeErrorValueDecl:
178 return "ErrorValueDecl";
179 case NodeTypeTestDecl:177 case NodeTypeTestDecl:
180 return "TestDecl";178 return "TestDecl";
181 case NodeTypeIntLiteral:179 case NodeTypeIntLiteral:
...@@ -244,6 +242,8 @@ static const char *node_type_str(NodeType node_type) {...@@ -244,6 +242,8 @@ static const char *node_type_str(NodeType node_type) {
244 return "IfErrorExpr";242 return "IfErrorExpr";
245 case NodeTypeTestExpr:243 case NodeTypeTestExpr:
246 return "TestExpr";244 return "TestExpr";
245 case NodeTypeErrorSetDecl:
246 return "ErrorSetDecl";
247 }247 }
248 zig_unreachable();248 zig_unreachable();
249}249}
...@@ -396,7 +396,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -396,7 +396,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
396396
397 if (child->type == NodeTypeUse ||397 if (child->type == NodeTypeUse ||
398 child->type == NodeTypeVariableDeclaration ||398 child->type == NodeTypeVariableDeclaration ||
399 child->type == NodeTypeErrorValueDecl ||
400 child->type == NodeTypeFnProto)399 child->type == NodeTypeFnProto)
401 {400 {
402 fprintf(ar->f, ";");401 fprintf(ar->f, ";");
...@@ -452,6 +451,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -452,6 +451,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
452 AstNode *return_type_node = node->data.fn_proto.return_type;451 AstNode *return_type_node = node->data.fn_proto.return_type;
453 assert(return_type_node != nullptr);452 assert(return_type_node != nullptr);
454 fprintf(ar->f, " ");453 fprintf(ar->f, " ");
454 if (node->data.fn_proto.auto_err_set) {
455 fprintf(ar->f, "!");
456 }
455 render_node_grouped(ar, return_type_node);457 render_node_grouped(ar, return_type_node);
456 break;458 break;
457 }459 }
...@@ -1017,9 +1019,26 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -1017,9 +1019,26 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
1017 render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);1019 render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
1018 break;1020 break;
1019 }1021 }
1022 case NodeTypeErrorSetDecl:
1023 {
1024 fprintf(ar->f, "error {\n");
1025 ar->indent += ar->indent_size;
1026
1027 for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) {
1028 AstNode *field_node = node->data.err_set_decl.decls.at(i);
1029 assert(field_node->type == NodeTypeSymbol);
1030 print_indent(ar);
1031 print_symbol(ar, field_node->data.symbol_expr.symbol);
1032 fprintf(ar->f, ",\n");
1033 }
1034
1035 ar->indent -= ar->indent_size;
1036 print_indent(ar);
1037 fprintf(ar->f, "}");
1038 break;
1039 }
1020 case NodeTypeFnDecl:1040 case NodeTypeFnDecl:
1021 case NodeTypeParamDecl:1041 case NodeTypeParamDecl:
1022 case NodeTypeErrorValueDecl:
1023 case NodeTypeTestDecl:1042 case NodeTypeTestDecl:
1024 case NodeTypeStructField:1043 case NodeTypeStructField:
1025 case NodeTypeUse:1044 case NodeTypeUse:
src/codegen.cpp+181-100
...@@ -92,9 +92,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out...@@ -92,9 +92,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
92 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);92 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
93 buf_resize(&g->global_asm, 0);93 buf_resize(&g->global_asm, 0);
9494
95 // reserve index 0 to indicate no error
96 g->error_decls.append(nullptr);
97
98 if (root_src_path) {95 if (root_src_path) {
99 Buf *src_basename = buf_alloc();96 Buf *src_basename = buf_alloc();
100 Buf *src_dir = buf_alloc();97 Buf *src_dir = buf_alloc();
...@@ -256,6 +253,10 @@ LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) {...@@ -256,6 +253,10 @@ LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) {
256 return add_link_lib(g, name);253 return add_link_lib(g, name);
257}254}
258255
256void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) {
257 codegen->forbidden_libs.append(lib);
258}
259
259void codegen_add_framework(CodeGen *g, const char *framework) {260void codegen_add_framework(CodeGen *g, const char *framework) {
260 g->darwin_frameworks.append(buf_create_from_str(framework));261 g->darwin_frameworks.append(buf_create_from_str(framework));
261}262}
...@@ -282,9 +283,9 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) {...@@ -282,9 +283,9 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) {
282}283}
283284
284285
285static void render_const_val(CodeGen *g, ConstExprValue *const_val);286static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name);
286static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name);287static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name);
287static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val);288static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name);
288static void generate_error_name_table(CodeGen *g);289static void generate_error_name_table(CodeGen *g);
289290
290static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {291static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {
...@@ -410,7 +411,7 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e...@@ -410,7 +411,7 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e
410 }411 }
411 TypeTableEntry *fn_type = fn_table_entry->type_entry;412 TypeTableEntry *fn_type = fn_table_entry->type_entry;
412 TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;413 TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
413 if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {414 if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdErrorSet) {
414 return UINT32_MAX;415 return UINT32_MAX;
415 }416 }
416 bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type);417 bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type);
...@@ -873,7 +874,7 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {...@@ -873,7 +874,7 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) {
873 ConstExprValue *array_val = create_const_str_lit(g, buf_msg);874 ConstExprValue *array_val = create_const_str_lit(g, buf_msg);
874 init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true);875 init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true);
875876
876 render_const_val(g, val);877 render_const_val(g, val, "");
877 render_const_val_global(g, val, "");878 render_const_val_global(g, val, "");
878879
879 assert(val->global_refs->llvm_global);880 assert(val->global_refs->llvm_global);
...@@ -1412,7 +1413,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {...@@ -1412,7 +1413,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
1412 if (!instruction->llvm_value) {1413 if (!instruction->llvm_value) {
1413 assert(instruction->value.special != ConstValSpecialRuntime);1414 assert(instruction->value.special != ConstValSpecialRuntime);
1414 assert(instruction->value.type);1415 assert(instruction->value.type);
1415 render_const_val(g, &instruction->value);1416 render_const_val(g, &instruction->value, "");
1416 // we might have to do some pointer casting here due to the way union1417 // we might have to do some pointer casting here due to the way union
1417 // values are rendered with a type other than the one we expect1418 // values are rendered with a type other than the one we expect
1418 if (handle_is_ptr(instruction->value.type)) {1419 if (handle_is_ptr(instruction->value.type)) {
...@@ -1442,7 +1443,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns...@@ -1442,7 +1443,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
1442 is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;1443 is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
1443 // TODO: emit a branch to check if the return value is an error1444 // TODO: emit a branch to check if the return value is an error
1444 }1445 }
1445 } else if (return_type->id == TypeTableEntryIdPureError) {1446 } else if (return_type->id == TypeTableEntryIdErrorSet) {
1446 is_err_return = true;1447 is_err_return = true;
1447 }1448 }
1448 if (is_err_return) {1449 if (is_err_return) {
...@@ -1789,7 +1790,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1789,7 +1790,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
17891790
1790 assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy ||1791 assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy ||
1791 op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy ||1792 op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy ||
1792 op_id == IrBinOpBitShiftRightExact);1793 op_id == IrBinOpBitShiftRightExact ||
1794 (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet));
1793 TypeTableEntry *type_entry = op1->value.type;1795 TypeTableEntry *type_entry = op1->value.type;
17941796
1795 bool want_runtime_safety = bin_op_instruction->safety_check_on &&1797 bool want_runtime_safety = bin_op_instruction->safety_check_on &&
...@@ -1802,6 +1804,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1802,6 +1804,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
1802 case IrBinOpArrayCat:1804 case IrBinOpArrayCat:
1803 case IrBinOpArrayMult:1805 case IrBinOpArrayMult:
1804 case IrBinOpRemUnspecified:1806 case IrBinOpRemUnspecified:
1807 case IrBinOpMergeErrorSets:
1805 zig_unreachable();1808 zig_unreachable();
1806 case IrBinOpBoolOr:1809 case IrBinOpBoolOr:
1807 return LLVMBuildOr(g->builder, op1_value, op2_value, "");1810 return LLVMBuildOr(g->builder, op1_value, op2_value, "");
...@@ -1823,7 +1826,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1823,7 +1826,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
1823 } else if (type_entry->id == TypeTableEntryIdEnum) {1826 } else if (type_entry->id == TypeTableEntryIdEnum) {
1824 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);1827 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
1825 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");1828 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
1826 } else if (type_entry->id == TypeTableEntryIdPureError ||1829 } else if (type_entry->id == TypeTableEntryIdErrorSet ||
1827 type_entry->id == TypeTableEntryIdPointer ||1830 type_entry->id == TypeTableEntryIdPointer ||
1828 type_entry->id == TypeTableEntryIdBool)1831 type_entry->id == TypeTableEntryIdBool)
1829 {1832 {
...@@ -1955,6 +1958,54 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1955,6 +1958,54 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
1955 zig_unreachable();1958 zig_unreachable();
1956}1959}
19571960
1961static void add_error_range_check(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *int_type, LLVMValueRef target_val) {
1962 assert(err_set_type->id == TypeTableEntryIdErrorSet);
1963
1964 if (type_is_global_error_set(err_set_type)) {
1965 LLVMValueRef zero = LLVMConstNull(int_type->type_ref);
1966 LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, "");
1967 LLVMValueRef ok_bit;
1968
1969 BigInt biggest_possible_err_val = {0};
1970 eval_min_max_value_int(g, int_type, &biggest_possible_err_val, true);
1971
1972 if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) &&
1973 bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length)
1974 {
1975 ok_bit = neq_zero_bit;
1976 } else {
1977 LLVMValueRef error_value_count = LLVMConstInt(int_type->type_ref, g->errors_by_index.length, false);
1978 LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, "");
1979 ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, "");
1980 }
1981
1982 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
1983 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
1984
1985 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
1986
1987 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1988 gen_safety_crash(g, PanicMsgIdInvalidErrorCode);
1989
1990 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1991 } else {
1992 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
1993 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
1994
1995 uint32_t err_count = err_set_type->data.error_set.err_count;
1996 LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_val, fail_block, err_count);
1997 for (uint32_t i = 0; i < err_count; i += 1) {
1998 LLVMValueRef case_value = LLVMConstInt(g->err_tag_type->type_ref, err_set_type->data.error_set.errors[i]->value, false);
1999 LLVMAddCase(switch_instr, case_value, ok_block);
2000 }
2001
2002 LLVMPositionBuilderAtEnd(g->builder, fail_block);
2003 gen_safety_crash(g, PanicMsgIdInvalidErrorCode);
2004
2005 LLVMPositionBuilderAtEnd(g->builder, ok_block);
2006 }
2007}
2008
1958static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,2009static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
1959 IrInstructionCast *cast_instruction)2010 IrInstructionCast *cast_instruction)
1960{2011{
...@@ -2078,6 +2129,11 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,...@@ -2078,6 +2129,11 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
2078 assert(wanted_type->id == TypeTableEntryIdInt);2129 assert(wanted_type->id == TypeTableEntryIdInt);
2079 assert(actual_type->id == TypeTableEntryIdBool);2130 assert(actual_type->id == TypeTableEntryIdBool);
2080 return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");2131 return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
2132 case CastOpErrSet:
2133 if (ir_want_runtime_safety(g, &cast_instruction->base)) {
2134 add_error_range_check(g, wanted_type, g->err_tag_type, expr_val);
2135 }
2136 return expr_val;
2081 }2137 }
2082 zig_unreachable();2138 zig_unreachable();
2083}2139}
...@@ -2139,7 +2195,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,...@@ -2139,7 +2195,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
21392195
2140static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) {2196static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) {
2141 TypeTableEntry *wanted_type = instruction->base.value.type;2197 TypeTableEntry *wanted_type = instruction->base.value.type;
2142 assert(wanted_type->id == TypeTableEntryIdPureError);2198 assert(wanted_type->id == TypeTableEntryIdErrorSet);
21432199
2144 TypeTableEntry *actual_type = instruction->target->value.type;2200 TypeTableEntry *actual_type = instruction->target->value.type;
2145 assert(actual_type->id == TypeTableEntryIdInt);2201 assert(actual_type->id == TypeTableEntryIdInt);
...@@ -2148,32 +2204,7 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I...@@ -2148,32 +2204,7 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I
2148 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);2204 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
21492205
2150 if (ir_want_runtime_safety(g, &instruction->base)) {2206 if (ir_want_runtime_safety(g, &instruction->base)) {
2151 LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);2207 add_error_range_check(g, wanted_type, actual_type, target_val);
2152 LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, "");
2153 LLVMValueRef ok_bit;
2154
2155 BigInt biggest_possible_err_val = {0};
2156 eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true);
2157
2158 if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) &&
2159 bigint_as_unsigned(&biggest_possible_err_val) < g->error_decls.length)
2160 {
2161 ok_bit = neq_zero_bit;
2162 } else {
2163 LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false);
2164 LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, "");
2165 ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, "");
2166 }
2167
2168 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
2169 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
2170
2171 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
2172
2173 LLVMPositionBuilderAtEnd(g->builder, fail_block);
2174 gen_safety_crash(g, PanicMsgIdInvalidErrorCode);
2175
2176 LLVMPositionBuilderAtEnd(g->builder, ok_block);
2177 }2208 }
21782209
2179 return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val);2210 return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val);
...@@ -2187,15 +2218,18 @@ static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, I...@@ -2187,15 +2218,18 @@ static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, I
2187 TypeTableEntry *actual_type = instruction->target->value.type;2218 TypeTableEntry *actual_type = instruction->target->value.type;
2188 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);2219 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
21892220
2190 if (actual_type->id == TypeTableEntryIdPureError) {2221 if (actual_type->id == TypeTableEntryIdErrorSet) {
2191 return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),2222 return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
2192 g->err_tag_type, wanted_type, target_val);2223 g->err_tag_type, wanted_type, target_val);
2193 } else if (actual_type->id == TypeTableEntryIdErrorUnion) {2224 } else if (actual_type->id == TypeTableEntryIdErrorUnion) {
2194 if (!type_has_bits(actual_type->data.error.child_type)) {2225 // this should have been a compile time constant
2226 assert(type_has_bits(actual_type->data.error_union.err_set_type));
2227
2228 if (!type_has_bits(actual_type->data.error_union.payload_type)) {
2195 return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),2229 return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
2196 g->err_tag_type, wanted_type, target_val);2230 g->err_tag_type, wanted_type, target_val);
2197 } else {2231 } else {
2198 zig_panic("TODO");2232 zig_panic("TODO err to int when error union payload type not void");
2199 }2233 }
2200 } else {2234 } else {
2201 zig_unreachable();2235 zig_unreachable();
...@@ -2235,7 +2269,6 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst...@@ -2235,7 +2269,6 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
22352269
2236 switch (op_id) {2270 switch (op_id) {
2237 case IrUnOpInvalid:2271 case IrUnOpInvalid:
2238 case IrUnOpError:
2239 case IrUnOpMaybe:2272 case IrUnOpMaybe:
2240 case IrUnOpDereference:2273 case IrUnOpDereference:
2241 zig_unreachable();2274 zig_unreachable();
...@@ -2489,7 +2522,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -2489,7 +2522,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
2489 TypeTableEntry *src_return_type = fn_type_id->return_type;2522 TypeTableEntry *src_return_type = fn_type_id->return_type;
2490 bool ret_has_bits = type_has_bits(src_return_type);2523 bool ret_has_bits = type_has_bits(src_return_type);
2491 bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);2524 bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
2492 bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError);2525 bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdErrorSet);
2493 size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);2526 size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
2494 bool is_var_args = fn_type_id->is_var_args;2527 bool is_var_args = fn_type_id->is_var_args;
2495 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);2528 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
...@@ -2907,7 +2940,7 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru...@@ -2907,7 +2940,7 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru
2907static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrInstructionErrName *instruction) {2940static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrInstructionErrName *instruction) {
2908 assert(g->generate_error_name_table);2941 assert(g->generate_error_name_table);
29092942
2910 if (g->error_decls.length == 1) {2943 if (g->errors_by_index.length == 1) {
2911 LLVMBuildUnreachable(g->builder);2944 LLVMBuildUnreachable(g->builder);
2912 return nullptr;2945 return nullptr;
2913 }2946 }
...@@ -2915,7 +2948,7 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI...@@ -2915,7 +2948,7 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
2915 LLVMValueRef err_val = ir_llvm_value(g, instruction->value);2948 LLVMValueRef err_val = ir_llvm_value(g, instruction->value);
2916 if (ir_want_runtime_safety(g, &instruction->base)) {2949 if (ir_want_runtime_safety(g, &instruction->base)) {
2917 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));2950 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));
2918 LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);2951 LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->errors_by_index.length, false);
2919 add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);2952 add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
2920 }2953 }
29212954
...@@ -3393,11 +3426,11 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable,...@@ -3393,11 +3426,11 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable,
33933426
3394static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) {3427static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) {
3395 TypeTableEntry *err_union_type = instruction->value->value.type;3428 TypeTableEntry *err_union_type = instruction->value->value.type;
3396 TypeTableEntry *child_type = err_union_type->data.error.child_type;3429 TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type;
3397 LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->value);3430 LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->value);
33983431
3399 LLVMValueRef err_val;3432 LLVMValueRef err_val;
3400 if (type_has_bits(child_type)) {3433 if (type_has_bits(payload_type)) {
3401 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");3434 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
3402 err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");3435 err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");
3403 } else {3436 } else {
...@@ -3412,11 +3445,11 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab...@@ -3412,11 +3445,11 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
3412 TypeTableEntry *ptr_type = instruction->value->value.type;3445 TypeTableEntry *ptr_type = instruction->value->value.type;
3413 assert(ptr_type->id == TypeTableEntryIdPointer);3446 assert(ptr_type->id == TypeTableEntryIdPointer);
3414 TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type;3447 TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type;
3415 TypeTableEntry *child_type = err_union_type->data.error.child_type;3448 TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type;
3416 LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);3449 LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
3417 LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);3450 LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
34183451
3419 if (type_has_bits(child_type)) {3452 if (type_has_bits(payload_type)) {
3420 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");3453 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
3421 return gen_load_untyped(g, err_val_ptr, 0, false, "");3454 return gen_load_untyped(g, err_val_ptr, 0, false, "");
3422 } else {3455 } else {
...@@ -3428,13 +3461,17 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu...@@ -3428,13 +3461,17 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
3428 TypeTableEntry *ptr_type = instruction->value->value.type;3461 TypeTableEntry *ptr_type = instruction->value->value.type;
3429 assert(ptr_type->id == TypeTableEntryIdPointer);3462 assert(ptr_type->id == TypeTableEntryIdPointer);
3430 TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type;3463 TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type;
3431 TypeTableEntry *child_type = err_union_type->data.error.child_type;3464 TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type;
3432 LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);3465 LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
3433 LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);3466 LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
34343467
3435 if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->error_decls.length > 1) {3468 if (!type_has_bits(err_union_type->data.error_union.err_set_type)) {
3469 return err_union_handle;
3470 }
3471
3472 if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) {
3436 LLVMValueRef err_val;3473 LLVMValueRef err_val;
3437 if (type_has_bits(child_type)) {3474 if (type_has_bits(payload_type)) {
3438 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");3475 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
3439 err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");3476 err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");
3440 } else {3477 } else {
...@@ -3452,7 +3489,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu...@@ -3452,7 +3489,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
3452 LLVMPositionBuilderAtEnd(g->builder, ok_block);3489 LLVMPositionBuilderAtEnd(g->builder, ok_block);
3453 }3490 }
34543491
3455 if (type_has_bits(child_type)) {3492 if (type_has_bits(payload_type)) {
3456 return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, "");3493 return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, "");
3457 } else {3494 } else {
3458 return nullptr;3495 return nullptr;
...@@ -3493,10 +3530,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable...@@ -3493,10 +3530,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable
34933530
3494 assert(wanted_type->id == TypeTableEntryIdErrorUnion);3531 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
34953532
3496 TypeTableEntry *child_type = wanted_type->data.error.child_type;3533 TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type;
3534 TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type;
3535
3497 LLVMValueRef err_val = ir_llvm_value(g, instruction->value);3536 LLVMValueRef err_val = ir_llvm_value(g, instruction->value);
34983537
3499 if (!type_has_bits(child_type))3538 if (!type_has_bits(payload_type) || !type_has_bits(err_set_type))
3500 return err_val;3539 return err_val;
35013540
3502 assert(instruction->tmp_ptr);3541 assert(instruction->tmp_ptr);
...@@ -3512,11 +3551,16 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa...@@ -3512,11 +3551,16 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
35123551
3513 assert(wanted_type->id == TypeTableEntryIdErrorUnion);3552 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
35143553
3515 TypeTableEntry *child_type = wanted_type->data.error.child_type;3554 TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type;
3555 TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type;
3556
3557 if (!type_has_bits(err_set_type)) {
3558 return ir_llvm_value(g, instruction->value);
3559 }
35163560
3517 LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);3561 LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
35183562
3519 if (!type_has_bits(child_type))3563 if (!type_has_bits(payload_type))
3520 return ok_err_val;3564 return ok_err_val;
35213565
3522 assert(instruction->tmp_ptr);3566 assert(instruction->tmp_ptr);
...@@ -3527,7 +3571,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa...@@ -3527,7 +3571,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
3527 gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false);3571 gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false);
35283572
3529 LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, "");3573 LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, "");
3530 gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, child_type, false), payload_val);3574 gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val);
35313575
3532 return instruction->tmp_ptr;3576 return instruction->tmp_ptr;
3533}3577}
...@@ -3700,6 +3744,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -3700,6 +3744,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
3700 case IrInstructionIdArgType:3744 case IrInstructionIdArgType:
3701 case IrInstructionIdTagType:3745 case IrInstructionIdTagType:
3702 case IrInstructionIdExport:3746 case IrInstructionIdExport:
3747 case IrInstructionIdErrorUnion:
3703 zig_unreachable();3748 zig_unreachable();
3704 case IrInstructionIdReturn:3749 case IrInstructionIdReturn:
3705 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);3750 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
...@@ -3847,7 +3892,7 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *ar...@@ -3847,7 +3892,7 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *ar
3847static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {3892static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
3848 switch (parent->id) {3893 switch (parent->id) {
3849 case ConstParentIdNone:3894 case ConstParentIdNone:
3850 render_const_val(g, val);3895 render_const_val(g, val, "");
3851 render_const_val_global(g, val, "");3896 render_const_val_global(g, val, "");
3852 return val->global_refs->llvm_global;3897 return val->global_refs->llvm_global;
3853 case ConstParentIdStruct:3898 case ConstParentIdStruct:
...@@ -3933,7 +3978,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con...@@ -3933,7 +3978,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
3933 case TypeTableEntryIdUndefLit:3978 case TypeTableEntryIdUndefLit:
3934 case TypeTableEntryIdNullLit:3979 case TypeTableEntryIdNullLit:
3935 case TypeTableEntryIdErrorUnion:3980 case TypeTableEntryIdErrorUnion:
3936 case TypeTableEntryIdPureError:3981 case TypeTableEntryIdErrorSet:
3937 case TypeTableEntryIdNamespace:3982 case TypeTableEntryIdNamespace:
3938 case TypeTableEntryIdBlock:3983 case TypeTableEntryIdBlock:
3939 case TypeTableEntryIdBoundFn:3984 case TypeTableEntryIdBoundFn:
...@@ -3946,17 +3991,17 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con...@@ -3946,17 +3991,17 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
3946 case TypeTableEntryIdEnum:3991 case TypeTableEntryIdEnum:
3947 {3992 {
3948 assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr);3993 assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr);
3949 LLVMValueRef int_val = gen_const_val(g, const_val);3994 LLVMValueRef int_val = gen_const_val(g, const_val, "");
3950 return LLVMConstZExt(int_val, big_int_type_ref);3995 return LLVMConstZExt(int_val, big_int_type_ref);
3951 }3996 }
3952 case TypeTableEntryIdInt:3997 case TypeTableEntryIdInt:
3953 {3998 {
3954 LLVMValueRef int_val = gen_const_val(g, const_val);3999 LLVMValueRef int_val = gen_const_val(g, const_val, "");
3955 return LLVMConstZExt(int_val, big_int_type_ref);4000 return LLVMConstZExt(int_val, big_int_type_ref);
3956 }4001 }
3957 case TypeTableEntryIdFloat:4002 case TypeTableEntryIdFloat:
3958 {4003 {
3959 LLVMValueRef float_val = gen_const_val(g, const_val);4004 LLVMValueRef float_val = gen_const_val(g, const_val, "");
3960 LLVMValueRef int_val = LLVMConstFPToUI(float_val,4005 LLVMValueRef int_val = LLVMConstFPToUI(float_val,
3961 LLVMIntType((unsigned)type_entry->data.floating.bit_count));4006 LLVMIntType((unsigned)type_entry->data.floating.bit_count));
3962 return LLVMConstZExt(int_val, big_int_type_ref);4007 return LLVMConstZExt(int_val, big_int_type_ref);
...@@ -3965,7 +4010,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con...@@ -3965,7 +4010,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
3965 case TypeTableEntryIdFn:4010 case TypeTableEntryIdFn:
3966 case TypeTableEntryIdMaybe:4011 case TypeTableEntryIdMaybe:
3967 {4012 {
3968 LLVMValueRef ptr_val = gen_const_val(g, const_val);4013 LLVMValueRef ptr_val = gen_const_val(g, const_val, "");
3969 LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref);4014 LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref);
3970 return LLVMConstZExt(ptr_size_int_val, big_int_type_ref);4015 return LLVMConstZExt(ptr_size_int_val, big_int_type_ref);
3971 }4016 }
...@@ -4010,7 +4055,7 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef...@@ -4010,7 +4055,7 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
4010 return LLVMTypeOf(val) != type_entry->type_ref;4055 return LLVMTypeOf(val) != type_entry->type_ref;
4011}4056}
40124057
4013static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {4058static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
4014 TypeTableEntry *type_entry = const_val->type;4059 TypeTableEntry *type_entry = const_val->type;
4015 assert(!type_entry->zero_bits);4060 assert(!type_entry->zero_bits);
40164061
...@@ -4026,10 +4071,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4026,10 +4071,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4026 switch (type_entry->id) {4071 switch (type_entry->id) {
4027 case TypeTableEntryIdInt:4072 case TypeTableEntryIdInt:
4028 return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);4073 return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);
4029 case TypeTableEntryIdPureError:4074 case TypeTableEntryIdErrorSet:
4030 assert(const_val->data.x_pure_err);4075 assert(const_val->data.x_err_set != nullptr);
4031 return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref,4076 return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref,
4032 const_val->data.x_pure_err->value, false);4077 const_val->data.x_err_set->value, false);
4033 case TypeTableEntryIdFloat:4078 case TypeTableEntryIdFloat:
4034 switch (type_entry->data.floating.bit_count) {4079 switch (type_entry->data.floating.bit_count) {
4035 case 32:4080 case 32:
...@@ -4063,7 +4108,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4063,7 +4108,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4063 child_type->id == TypeTableEntryIdFn)4108 child_type->id == TypeTableEntryIdFn)
4064 {4109 {
4065 if (const_val->data.x_maybe) {4110 if (const_val->data.x_maybe) {
4066 return gen_const_val(g, const_val->data.x_maybe);4111 return gen_const_val(g, const_val->data.x_maybe, "");
4067 } else {4112 } else {
4068 return LLVMConstNull(child_type->type_ref);4113 return LLVMConstNull(child_type->type_ref);
4069 }4114 }
...@@ -4072,7 +4117,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4072,7 +4117,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4072 LLVMValueRef maybe_val;4117 LLVMValueRef maybe_val;
4073 bool make_unnamed_struct;4118 bool make_unnamed_struct;
4074 if (const_val->data.x_maybe) {4119 if (const_val->data.x_maybe) {
4075 child_val = gen_const_val(g, const_val->data.x_maybe);4120 child_val = gen_const_val(g, const_val->data.x_maybe, "");
4076 maybe_val = LLVMConstAllOnes(LLVMInt1Type());4121 maybe_val = LLVMConstAllOnes(LLVMInt1Type());
40774122
4078 make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val);4123 make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val);
...@@ -4116,7 +4161,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4116,7 +4161,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
41164161
4117 if (src_field_index + 1 == src_field_index_end) {4162 if (src_field_index + 1 == src_field_index_end) {
4118 ConstExprValue *field_val = &const_val->data.x_struct.fields[src_field_index];4163 ConstExprValue *field_val = &const_val->data.x_struct.fields[src_field_index];
4119 LLVMValueRef val = gen_const_val(g, field_val);4164 LLVMValueRef val = gen_const_val(g, field_val, "");
4120 fields[type_struct_field->gen_index] = val;4165 fields[type_struct_field->gen_index] = val;
4121 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);4166 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
4122 } else {4167 } else {
...@@ -4156,7 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4156,7 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4156 continue;4201 continue;
4157 }4202 }
4158 ConstExprValue *field_val = &const_val->data.x_struct.fields[i];4203 ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
4159 LLVMValueRef val = gen_const_val(g, field_val);4204 LLVMValueRef val = gen_const_val(g, field_val, "");
4160 fields[type_struct_field->gen_index] = val;4205 fields[type_struct_field->gen_index] = val;
4161 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);4206 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
4162 }4207 }
...@@ -4180,7 +4225,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4180,7 +4225,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4180 bool make_unnamed_struct = false;4225 bool make_unnamed_struct = false;
4181 for (uint64_t i = 0; i < len; i += 1) {4226 for (uint64_t i = 0; i < len; i += 1) {
4182 ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];4227 ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
4183 LLVMValueRef val = gen_const_val(g, elem_value);4228 LLVMValueRef val = gen_const_val(g, elem_value, "");
4184 values[i] = val;4229 values[i] = val;
4185 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);4230 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
4186 }4231 }
...@@ -4215,7 +4260,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4215,7 +4260,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4215 } else {4260 } else {
4216 uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);4261 uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);
4217 uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes;4262 uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes;
4218 LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value);4263 LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value, "");
4219 make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||4264 make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||
4220 payload_value->type != type_entry->data.unionation.most_aligned_union_member;4265 payload_value->type != type_entry->data.unionation.most_aligned_union_member;
42214266
...@@ -4260,7 +4305,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4260,7 +4305,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4260 return fn_llvm_value(g, const_val->data.x_fn.fn_entry);4305 return fn_llvm_value(g, const_val->data.x_fn.fn_entry);
4261 case TypeTableEntryIdPointer:4306 case TypeTableEntryIdPointer:
4262 {4307 {
4263 render_const_val_global(g, const_val, "");4308 render_const_val_global(g, const_val, name);
4264 switch (const_val->data.x_ptr.special) {4309 switch (const_val->data.x_ptr.special) {
4265 case ConstPtrSpecialInvalid:4310 case ConstPtrSpecialInvalid:
4266 case ConstPtrSpecialDiscard:4311 case ConstPtrSpecialDiscard:
...@@ -4268,7 +4313,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4268,7 +4313,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4268 case ConstPtrSpecialRef:4313 case ConstPtrSpecialRef:
4269 {4314 {
4270 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;4315 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
4271 render_const_val(g, pointee);4316 render_const_val(g, pointee, "");
4272 render_const_val_global(g, pointee, "");4317 render_const_val_global(g, pointee, "");
4273 ConstExprValue *other_val = pointee;4318 ConstExprValue *other_val = pointee;
4274 const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);4319 const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
...@@ -4330,22 +4375,27 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4330,22 +4375,27 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4330 }4375 }
4331 case TypeTableEntryIdErrorUnion:4376 case TypeTableEntryIdErrorUnion:
4332 {4377 {
4333 TypeTableEntry *child_type = type_entry->data.error.child_type;4378 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
4334 if (!type_has_bits(child_type)) {4379 TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
4380 if (!type_has_bits(payload_type)) {
4381 assert(type_has_bits(err_set_type));
4335 uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0;4382 uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0;
4336 return LLVMConstInt(g->err_tag_type->type_ref, value, false);4383 return LLVMConstInt(g->err_tag_type->type_ref, value, false);
4384 } else if (!type_has_bits(err_set_type)) {
4385 assert(type_has_bits(payload_type));
4386 return gen_const_val(g, const_val->data.x_err_union.payload, "");
4337 } else {4387 } else {
4338 LLVMValueRef err_tag_value;4388 LLVMValueRef err_tag_value;
4339 LLVMValueRef err_payload_value;4389 LLVMValueRef err_payload_value;
4340 bool make_unnamed_struct;4390 bool make_unnamed_struct;
4341 if (const_val->data.x_err_union.err) {4391 if (const_val->data.x_err_union.err) {
4342 err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false);4392 err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false);
4343 err_payload_value = LLVMConstNull(child_type->type_ref);4393 err_payload_value = LLVMConstNull(payload_type->type_ref);
4344 make_unnamed_struct = false;4394 make_unnamed_struct = false;
4345 } else {4395 } else {
4346 err_tag_value = LLVMConstNull(g->err_tag_type->type_ref);4396 err_tag_value = LLVMConstNull(g->err_tag_type->type_ref);
4347 ConstExprValue *payload_val = const_val->data.x_err_union.payload;4397 ConstExprValue *payload_val = const_val->data.x_err_union.payload;
4348 err_payload_value = gen_const_val(g, payload_val);4398 err_payload_value = gen_const_val(g, payload_val, "");
4349 make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value);4399 make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value);
4350 }4400 }
4351 LLVMValueRef fields[] = {4401 LLVMValueRef fields[] = {
...@@ -4380,11 +4430,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -4380,11 +4430,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4380 zig_unreachable();4430 zig_unreachable();
4381}4431}
43824432
4383static void render_const_val(CodeGen *g, ConstExprValue *const_val) {4433static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
4384 if (!const_val->global_refs)4434 if (!const_val->global_refs)
4385 const_val->global_refs = allocate<ConstGlobalRefs>(1);4435 const_val->global_refs = allocate<ConstGlobalRefs>(1);
4386 if (!const_val->global_refs->llvm_value)4436 if (!const_val->global_refs->llvm_value)
4387 const_val->global_refs->llvm_value = gen_const_val(g, const_val);4437 const_val->global_refs->llvm_value = gen_const_val(g, const_val, name);
43884438
4389 if (const_val->global_refs->llvm_global)4439 if (const_val->global_refs->llvm_global)
4390 LLVMSetInitializer(const_val->global_refs->llvm_global, const_val->global_refs->llvm_value);4440 LLVMSetInitializer(const_val->global_refs->llvm_global, const_val->global_refs->llvm_value);
...@@ -4410,21 +4460,20 @@ static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const...@@ -4410,21 +4460,20 @@ static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const
4410}4460}
44114461
4412static void generate_error_name_table(CodeGen *g) {4462static void generate_error_name_table(CodeGen *g) {
4413 if (g->err_name_table != nullptr || !g->generate_error_name_table || g->error_decls.length == 1) {4463 if (g->err_name_table != nullptr || !g->generate_error_name_table || g->errors_by_index.length == 1) {
4414 return;4464 return;
4415 }4465 }
44164466
4417 assert(g->error_decls.length > 0);4467 assert(g->errors_by_index.length > 0);
44184468
4419 TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);4469 TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
4420 TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);4470 TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);
44214471
4422 LLVMValueRef *values = allocate<LLVMValueRef>(g->error_decls.length);4472 LLVMValueRef *values = allocate<LLVMValueRef>(g->errors_by_index.length);
4423 values[0] = LLVMGetUndef(str_type->type_ref);4473 values[0] = LLVMGetUndef(str_type->type_ref);
4424 for (size_t i = 1; i < g->error_decls.length; i += 1) {4474 for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
4425 AstNode *error_decl_node = g->error_decls.at(i);4475 ErrorTableEntry *err_entry = g->errors_by_index.at(i);
4426 assert(error_decl_node->type == NodeTypeErrorValueDecl);4476 Buf *name = &err_entry->name;
4427 Buf *name = error_decl_node->data.error_value_decl.name;
44284477
4429 g->largest_err_name_len = max(g->largest_err_name_len, buf_len(name));4478 g->largest_err_name_len = max(g->largest_err_name_len, buf_len(name));
44304479
...@@ -4443,7 +4492,7 @@ static void generate_error_name_table(CodeGen *g) {...@@ -4443,7 +4492,7 @@ static void generate_error_name_table(CodeGen *g) {
4443 values[i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);4492 values[i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
4444 }4493 }
44454494
4446 LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)g->error_decls.length);4495 LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)g->errors_by_index.length);
44474496
4448 g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init),4497 g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init),
4449 buf_ptr(get_mangled_name(g, buf_create_from_str("__zig_err_name_table"), false)));4498 buf_ptr(get_mangled_name(g, buf_create_from_str("__zig_err_name_table"), false)));
...@@ -4575,6 +4624,28 @@ static void do_code_gen(CodeGen *g) {...@@ -4575,6 +4624,28 @@ static void do_code_gen(CodeGen *g) {
45754624
4576 codegen_add_time_event(g, "Code Generation");4625 codegen_add_time_event(g, "Code Generation");
45774626
4627 {
4628 // create debug type for error sets
4629 assert(g->err_enumerators.length == g->errors_by_index.length);
4630 uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref);
4631 uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, g->err_tag_type->type_ref);
4632 ZigLLVMDIFile *err_set_di_file = nullptr;
4633 ZigLLVMDIType *err_set_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
4634 ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&g->builtin_types.entry_global_error_set->name),
4635 err_set_di_file, 0,
4636 tag_debug_size_in_bits,
4637 tag_debug_align_in_bits,
4638 g->err_enumerators.items, g->err_enumerators.length,
4639 g->err_tag_type->di_type, "");
4640 ZigLLVMReplaceTemporary(g->dbuilder, g->builtin_types.entry_global_error_set->di_type, err_set_di_type);
4641 g->builtin_types.entry_global_error_set->di_type = err_set_di_type;
4642
4643 for (size_t i = 0; i < g->error_di_types.length; i += 1) {
4644 ZigLLVMDIType **di_type_ptr = g->error_di_types.at(i);
4645 *di_type_ptr = err_set_di_type;
4646 }
4647 }
4648
4578 generate_error_name_table(g);4649 generate_error_name_table(g);
4579 generate_enum_name_tables(g);4650 generate_enum_name_tables(g);
45804651
...@@ -4592,7 +4663,7 @@ static void do_code_gen(CodeGen *g) {...@@ -4592,7 +4663,7 @@ static void do_code_gen(CodeGen *g) {
4592 coerced_value.special = ConstValSpecialStatic;4663 coerced_value.special = ConstValSpecialStatic;
4593 coerced_value.type = var_type;4664 coerced_value.type = var_type;
4594 coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat);4665 coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat);
4595 LLVMValueRef init_val = gen_const_val(g, &coerced_value);4666 LLVMValueRef init_val = gen_const_val(g, &coerced_value, "");
4596 gen_global_var(g, var, init_val, var_type);4667 gen_global_var(g, var, init_val, var_type);
4597 continue;4668 continue;
4598 }4669 }
...@@ -4626,8 +4697,9 @@ static void do_code_gen(CodeGen *g) {...@@ -4626,8 +4697,9 @@ static void do_code_gen(CodeGen *g) {
4626 LLVMSetAlignment(global_value, var->align_bytes);4697 LLVMSetAlignment(global_value, var->align_bytes);
4627 } else {4698 } else {
4628 bool exported = (var->linkage == VarLinkageExport);4699 bool exported = (var->linkage == VarLinkageExport);
4629 render_const_val(g, var->value);4700 const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));
4630 render_const_val_global(g, var->value, buf_ptr(get_mangled_name(g, &var->name, exported)));4701 render_const_val(g, var->value, mangled_name);
4702 render_const_val_global(g, var->value, mangled_name);
4631 global_value = var->value->global_refs->llvm_global;4703 global_value = var->value->global_refs->llvm_global;
46324704
4633 if (exported) {4705 if (exported) {
...@@ -5176,16 +5248,24 @@ static void define_builtin_types(CodeGen *g) {...@@ -5176,16 +5248,24 @@ static void define_builtin_types(CodeGen *g) {
5176 }5248 }
51775249
5178 {5250 {
5179 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);5251 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorSet);
5180 buf_init_from_str(&entry->name, "error");5252 buf_init_from_str(&entry->name, "error");
5253 entry->data.error_set.err_count = UINT32_MAX;
51815254
5182 // TODO allow overriding this type and keep track of max value and emit an5255 // TODO allow overriding this type and keep track of max value and emit an
5183 // error if there are too many errors declared5256 // error if there are too many errors declared
5184 g->err_tag_type = g->builtin_types.entry_u16;5257 g->err_tag_type = g->builtin_types.entry_u16;
51855258
5186 g->builtin_types.entry_pure_error = entry;5259 g->builtin_types.entry_global_error_set = entry;
5187 entry->type_ref = g->err_tag_type->type_ref;5260 entry->type_ref = g->err_tag_type->type_ref;
5188 entry->di_type = g->err_tag_type->di_type;5261
5262 entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
5263 ZigLLVMTag_DW_enumeration_type(), "error",
5264 ZigLLVMCompileUnitToScope(g->compile_unit), nullptr, 0);
5265
5266 // reserve index 0 to indicate no error
5267 g->err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0));
5268 g->errors_by_index.append(nullptr);
51895269
5190 g->primitive_type_table.put(&entry->name, entry);5270 g->primitive_type_table.put(&entry->name, entry);
5191 }5271 }
...@@ -5815,7 +5895,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry...@@ -5815,7 +5895,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry
5815 case TypeTableEntryIdBoundFn:5895 case TypeTableEntryIdBoundFn:
5816 case TypeTableEntryIdArgTuple:5896 case TypeTableEntryIdArgTuple:
5817 case TypeTableEntryIdErrorUnion:5897 case TypeTableEntryIdErrorUnion:
5818 case TypeTableEntryIdPureError:5898 case TypeTableEntryIdErrorSet:
5819 zig_unreachable();5899 zig_unreachable();
5820 case TypeTableEntryIdVoid:5900 case TypeTableEntryIdVoid:
5821 case TypeTableEntryIdUnreachable:5901 case TypeTableEntryIdUnreachable:
...@@ -5988,7 +6068,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf...@@ -5988,7 +6068,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
5988 return;6068 return;
5989 }6069 }
5990 case TypeTableEntryIdErrorUnion:6070 case TypeTableEntryIdErrorUnion:
5991 case TypeTableEntryIdPureError:6071 case TypeTableEntryIdErrorSet:
5992 case TypeTableEntryIdFn:6072 case TypeTableEntryIdFn:
5993 zig_panic("TODO implement get_c_type for more types");6073 zig_panic("TODO implement get_c_type for more types");
5994 case TypeTableEntryIdInvalid:6074 case TypeTableEntryIdInvalid:
...@@ -6155,7 +6235,7 @@ static void gen_h_file(CodeGen *g) {...@@ -6155,7 +6235,7 @@ static void gen_h_file(CodeGen *g) {
6155 case TypeTableEntryIdUndefLit:6235 case TypeTableEntryIdUndefLit:
6156 case TypeTableEntryIdNullLit:6236 case TypeTableEntryIdNullLit:
6157 case TypeTableEntryIdErrorUnion:6237 case TypeTableEntryIdErrorUnion:
6158 case TypeTableEntryIdPureError:6238 case TypeTableEntryIdErrorSet:
6159 case TypeTableEntryIdNamespace:6239 case TypeTableEntryIdNamespace:
6160 case TypeTableEntryIdBlock:6240 case TypeTableEntryIdBlock:
6161 case TypeTableEntryIdBoundFn:6241 case TypeTableEntryIdBoundFn:
...@@ -6265,3 +6345,4 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,...@@ -6265,3 +6345,4 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
6265 }6345 }
6266 return pkg;6346 return pkg;
6267}6347}
6348
src/codegen.hpp+1
...@@ -36,6 +36,7 @@ void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir);...@@ -36,6 +36,7 @@ void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir);
36void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);36void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);
37void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);37void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);
38void codegen_add_lib_dir(CodeGen *codegen, const char *dir);38void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
39void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib);
39LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);40LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);
40void codegen_add_framework(CodeGen *codegen, const char *name);41void codegen_add_framework(CodeGen *codegen, const char *name);
41void codegen_add_rpath(CodeGen *codegen, const char *name);42void codegen_add_rpath(CodeGen *codegen, const char *name);
src/ir.cpp+1434-232
...@@ -45,6 +45,59 @@ static LVal make_lval_addr(bool is_const, bool is_volatile) {...@@ -45,6 +45,59 @@ static LVal make_lval_addr(bool is_const, bool is_volatile) {
45 return { true, is_const, is_volatile };45 return { true, is_const, is_volatile };
46}46}
4747
48enum ConstCastResultId {
49 ConstCastResultIdOk,
50 ConstCastResultIdErrSet,
51 ConstCastResultIdErrSetGlobal,
52 ConstCastResultIdPointerChild,
53 ConstCastResultIdSliceChild,
54 ConstCastResultIdNullableChild,
55 ConstCastResultIdErrorUnionPayload,
56 ConstCastResultIdErrorUnionErrorSet,
57 ConstCastResultIdFnAlign,
58 ConstCastResultIdFnCC,
59 ConstCastResultIdFnVarArgs,
60 ConstCastResultIdFnIsGeneric,
61 ConstCastResultIdFnReturnType,
62 ConstCastResultIdFnArgCount,
63 ConstCastResultIdFnGenericArgCount,
64 ConstCastResultIdFnArg,
65 ConstCastResultIdFnArgNoAlias,
66 ConstCastResultIdType,
67 ConstCastResultIdUnresolvedInferredErrSet,
68};
69
70struct ConstCastErrSetMismatch {
71 ZigList<ErrorTableEntry *> missing_errors;
72};
73
74struct ConstCastOnly;
75
76struct ConstCastArg {
77 size_t arg_index;
78 ConstCastOnly *child;
79};
80
81struct ConstCastArgNoAlias {
82 size_t arg_index;
83};
84
85struct ConstCastOnly {
86 ConstCastResultId id;
87 union {
88 ConstCastErrSetMismatch error_set;
89 ConstCastOnly *pointer_child;
90 ConstCastOnly *slice_child;
91 ConstCastOnly *nullable_child;
92 ConstCastOnly *error_union_payload;
93 ConstCastOnly *error_union_error_set;
94 ConstCastOnly *return_type;
95 ConstCastArg fn_arg;
96 ConstCastArgNoAlias arg_no_alias;
97 } data;
98};
99
100
48static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);101static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
49static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval);102static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval);
50static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);103static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
...@@ -580,6 +633,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace...@@ -580,6 +633,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace
580 return IrInstructionIdErrorReturnTrace;633 return IrInstructionIdErrorReturnTrace;
581}634}
582635
636static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) {
637 return IrInstructionIdErrorUnion;
638}
639
583template<typename T>640template<typename T>
584static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {641static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
585 T *special_instruction = allocate<T>(1);642 T *special_instruction = allocate<T>(1);
...@@ -2326,6 +2383,19 @@ static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope,...@@ -2326,6 +2383,19 @@ static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope,
2326 return &instruction->base;2383 return &instruction->base;
2327}2384}
23282385
2386static IrInstruction *ir_build_error_union(IrBuilder *irb, Scope *scope, AstNode *source_node,
2387 IrInstruction *err_set, IrInstruction *payload)
2388{
2389 IrInstructionErrorUnion *instruction = ir_build_instruction<IrInstructionErrorUnion>(irb, scope, source_node);
2390 instruction->err_set = err_set;
2391 instruction->payload = payload;
2392
2393 ir_ref_instruction(err_set, irb->current_basic_block);
2394 ir_ref_instruction(payload, irb->current_basic_block);
2395
2396 return &instruction->base;
2397}
2398
2329static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {2399static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
2330 results[ReturnKindUnconditional] = 0;2400 results[ReturnKindUnconditional] = 0;
2331 results[ReturnKindError] = 0;2401 results[ReturnKindError] = 0;
...@@ -2800,6 +2870,23 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As...@@ -2800,6 +2870,23 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
2800 return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);2870 return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
2801}2871}
28022872
2873static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
2874 assert(node->type == NodeTypeBinOpExpr);
2875
2876 AstNode *op1_node = node->data.bin_op_expr.op1;
2877 AstNode *op2_node = node->data.bin_op_expr.op2;
2878
2879 IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope);
2880 if (err_set == irb->codegen->invalid_instruction)
2881 return irb->codegen->invalid_instruction;
2882
2883 IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope);
2884 if (payload == irb->codegen->invalid_instruction)
2885 return irb->codegen->invalid_instruction;
2886
2887 return ir_build_error_union(irb, parent_scope, node, err_set, payload);
2888}
2889
2803static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) {2890static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) {
2804 assert(node->type == NodeTypeBinOpExpr);2891 assert(node->type == NodeTypeBinOpExpr);
28052892
...@@ -2835,10 +2922,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -2835,10 +2922,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
2835 return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);2922 return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);
2836 case BinOpTypeAssignBitOr:2923 case BinOpTypeAssignBitOr:
2837 return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);2924 return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);
2838 case BinOpTypeAssignBoolAnd:2925 case BinOpTypeAssignMergeErrorSets:
2839 return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd);2926 return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets);
2840 case BinOpTypeAssignBoolOr:
2841 return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr);
2842 case BinOpTypeBoolOr:2927 case BinOpTypeBoolOr:
2843 return ir_gen_bool_or(irb, scope, node);2928 return ir_gen_bool_or(irb, scope, node);
2844 case BinOpTypeBoolAnd:2929 case BinOpTypeBoolAnd:
...@@ -2885,8 +2970,12 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -2885,8 +2970,12 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
2885 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);2970 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
2886 case BinOpTypeArrayMult:2971 case BinOpTypeArrayMult:
2887 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);2972 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
2973 case BinOpTypeMergeErrorSets:
2974 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
2888 case BinOpTypeUnwrapMaybe:2975 case BinOpTypeUnwrapMaybe:
2889 return ir_gen_maybe_ok_or(irb, scope, node);2976 return ir_gen_maybe_ok_or(irb, scope, node);
2977 case BinOpTypeErrorUnion:
2978 return ir_gen_error_union(irb, scope, node);
2890 }2979 }
2891 zig_unreachable();2980 zig_unreachable();
2892}2981}
...@@ -3990,8 +4079,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod...@@ -3990,8 +4079,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
3990 return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval);4079 return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval);
3991 case PrefixOpMaybe:4080 case PrefixOpMaybe:
3992 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval);4081 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval);
3993 case PrefixOpError:
3994 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpError), lval);
3995 case PrefixOpUnwrapMaybe:4082 case PrefixOpUnwrapMaybe:
3996 return ir_gen_maybe_assert_ok(irb, scope, node, lval);4083 return ir_gen_maybe_assert_ok(irb, scope, node, lval);
3997 }4084 }
...@@ -4713,12 +4800,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4713,12 +4800,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
4713 IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse");4800 IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse");
4714 IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd");4801 IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
47154802
4716 IrInstruction *is_comptime;4803 bool force_comptime = ir_should_inline(irb->exec, scope);
4717 if (ir_should_inline(irb->exec, scope)) {4804 IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
4718 is_comptime = ir_build_const_bool(irb, scope, node, true);
4719 } else {
4720 is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
4721 }
4722 ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);4805 ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
47234806
4724 ir_set_cursor_at_end_and_append_block(irb, ok_block);4807 ir_set_cursor_at_end_and_append_block(irb, ok_block);
...@@ -4727,8 +4810,9 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -4727,8 +4810,9 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
4727 if (var_symbol) {4810 if (var_symbol) {
4728 IrInstruction *var_type = nullptr;4811 IrInstruction *var_type = nullptr;
4729 bool is_shadowable = false;4812 bool is_shadowable = false;
4813 IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, err_val);
4730 VariableTableEntry *var = ir_create_var(irb, node, scope,4814 VariableTableEntry *var = ir_create_var(irb, node, scope,
4731 var_symbol, var_is_const, var_is_const, is_shadowable, is_comptime);4815 var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime);
47324816
4733 IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false);4817 IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false);
4734 IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value);4818 IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value);
...@@ -5165,7 +5249,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast...@@ -5165,7 +5249,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
51655249
5166static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {5250static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
5167 assert(node->type == NodeTypeErrorType);5251 assert(node->type == NodeTypeErrorType);
5168 return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error);5252 return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set);
5169}5253}
51705254
5171static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {5255static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
...@@ -5249,8 +5333,6 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN...@@ -5249,8 +5333,6 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
5249 Scope *err_scope;5333 Scope *err_scope;
5250 if (var_node) {5334 if (var_node) {
5251 assert(var_node->type == NodeTypeSymbol);5335 assert(var_node->type == NodeTypeSymbol);
5252 IrInstruction *var_type = ir_build_const_type(irb, parent_scope, node,
5253 irb->codegen->builtin_types.entry_pure_error);
5254 Buf *var_name = var_node->data.symbol_expr.symbol;5336 Buf *var_name = var_node->data.symbol_expr.symbol;
5255 bool is_const = true;5337 bool is_const = true;
5256 bool is_shadowable = false;5338 bool is_shadowable = false;
...@@ -5258,7 +5340,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN...@@ -5258,7 +5340,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
5258 is_const, is_const, is_shadowable, is_comptime);5340 is_const, is_const, is_shadowable, is_comptime);
5259 err_scope = var->child_scope;5341 err_scope = var->child_scope;
5260 IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);5342 IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
5261 ir_build_var_decl(irb, err_scope, var_node, var, var_type, nullptr, err_val);5343 ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val);
5262 } else {5344 } else {
5263 err_scope = parent_scope;5345 err_scope = parent_scope;
5264 }5346 }
...@@ -5348,6 +5430,135 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,...@@ -5348,6 +5430,135 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
5348 return ir_build_const_type(irb, parent_scope, node, container_type);5430 return ir_build_const_type(irb, parent_scope, node, container_type);
5349}5431}
53505432
5433// errors should be populated with set1's values
5434static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, TypeTableEntry *set1, TypeTableEntry *set2) {
5435 assert(set1->id == TypeTableEntryIdErrorSet);
5436 assert(set2->id == TypeTableEntryIdErrorSet);
5437
5438 TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
5439 buf_resize(&err_set_type->name, 0);
5440 buf_appendf(&err_set_type->name, "error{");
5441
5442 for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) {
5443 assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]);
5444 }
5445
5446 uint32_t count = set1->data.error_set.err_count;
5447 for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
5448 ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
5449 if (errors[error_entry->value] == nullptr) {
5450 count += 1;
5451 }
5452 }
5453
5454 err_set_type->is_copyable = true;
5455 err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref;
5456 err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
5457 err_set_type->data.error_set.err_count = count;
5458 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(count);
5459
5460 for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
5461 ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
5462 buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
5463 err_set_type->data.error_set.errors[i] = error_entry;
5464 }
5465
5466 uint32_t index = set1->data.error_set.err_count;
5467 for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
5468 ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
5469 if (errors[error_entry->value] == nullptr) {
5470 errors[error_entry->value] = error_entry;
5471 buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
5472 err_set_type->data.error_set.errors[index] = error_entry;
5473 index += 1;
5474 }
5475 }
5476 assert(index == count);
5477 assert(count != 0);
5478
5479 buf_appendf(&err_set_type->name, "}");
5480
5481 g->error_di_types.append(&err_set_type->di_type);
5482
5483 return err_set_type;
5484
5485}
5486
5487static TypeTableEntry *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node,
5488 ErrorTableEntry *err_entry)
5489{
5490 TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
5491 buf_resize(&err_set_type->name, 0);
5492 buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name));
5493 err_set_type->is_copyable = true;
5494 err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref;
5495 err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
5496 err_set_type->data.error_set.err_count = 1;
5497 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
5498
5499 g->error_di_types.append(&err_set_type->di_type);
5500
5501 err_set_type->data.error_set.errors[0] = err_entry;
5502
5503 return err_set_type;
5504}
5505
5506static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
5507 assert(node->type == NodeTypeErrorSetDecl);
5508
5509 uint32_t err_count = node->data.err_set_decl.decls.length;
5510
5511 Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node);
5512 TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
5513 buf_init_from_buf(&err_set_type->name, type_name);
5514 err_set_type->is_copyable = true;
5515 err_set_type->data.error_set.err_count = err_count;
5516
5517 if (err_count == 0) {
5518 err_set_type->zero_bits = true;
5519 err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type;
5520 } else {
5521 err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
5522 err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
5523 irb->codegen->error_di_types.append(&err_set_type->di_type);
5524 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
5525 }
5526
5527 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
5528
5529 for (uint32_t i = 0; i < err_count; i += 1) {
5530 AstNode *symbol_node = node->data.err_set_decl.decls.at(i);
5531 assert(symbol_node->type == NodeTypeSymbol);
5532 Buf *err_name = symbol_node->data.symbol_expr.symbol;
5533 ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
5534 err->decl_node = symbol_node;
5535 buf_init_from_buf(&err->name, err_name);
5536
5537 auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
5538 if (existing_entry) {
5539 err->value = existing_entry->value->value;
5540 } else {
5541 size_t error_value_count = irb->codegen->errors_by_index.length;
5542 assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
5543 err->value = error_value_count;
5544 irb->codegen->errors_by_index.append(err);
5545 irb->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(irb->codegen->dbuilder,
5546 buf_ptr(err_name), error_value_count));
5547 }
5548 err_set_type->data.error_set.errors[i] = err;
5549
5550 ErrorTableEntry *prev_err = errors[err->value];
5551 if (prev_err != nullptr) {
5552 ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
5553 add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here"));
5554 return irb->codegen->invalid_instruction;
5555 }
5556 errors[err->value] = err;
5557 }
5558 free(errors);
5559 return ir_build_const_type(irb, parent_scope, node, err_set_type);
5560}
5561
5351static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {5562static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
5352 assert(node->type == NodeTypeFnProto);5563 assert(node->type == NodeTypeFnProto);
53535564
...@@ -5401,7 +5612,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -5401,7 +5612,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
5401 case NodeTypeStructField:5612 case NodeTypeStructField:
5402 case NodeTypeFnDef:5613 case NodeTypeFnDef:
5403 case NodeTypeFnDecl:5614 case NodeTypeFnDecl:
5404 case NodeTypeErrorValueDecl:
5405 case NodeTypeTestDecl:5615 case NodeTypeTestDecl:
5406 zig_unreachable();5616 zig_unreachable();
5407 case NodeTypeBlock:5617 case NodeTypeBlock:
...@@ -5482,6 +5692,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -5482,6 +5692,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
5482 return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);5692 return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
5483 case NodeTypeFnProto:5693 case NodeTypeFnProto:
5484 return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);5694 return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);
5695 case NodeTypeErrorSetDecl:
5696 return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval);
5485 }5697 }
5486 zig_unreachable();5698 zig_unreachable();
5487}5699}
...@@ -6287,6 +6499,274 @@ static bool slice_is_const(TypeTableEntry *type) {...@@ -6287,6 +6499,274 @@ static bool slice_is_const(TypeTableEntry *type) {
6287 return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;6499 return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
6288}6500}
62896501
6502static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) {
6503 assert(err_set_type->id == TypeTableEntryIdErrorSet);
6504 FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn;
6505 if (infer_fn != nullptr) {
6506 if (infer_fn->anal_state == FnAnalStateInvalid) {
6507 return false;
6508 } else if (infer_fn->anal_state == FnAnalStateReady) {
6509 analyze_fn_body(ira->codegen, infer_fn);
6510 if (err_set_type->data.error_set.infer_fn != nullptr) {
6511 assert(ira->codegen->errors.length != 0);
6512 return false;
6513 }
6514 } else {
6515 ir_add_error_node(ira, source_node,
6516 buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
6517 buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
6518 return false;
6519 }
6520 }
6521 return true;
6522}
6523
6524static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry *set1, TypeTableEntry *set2,
6525 AstNode *source_node)
6526{
6527 assert(set1->id == TypeTableEntryIdErrorSet);
6528 assert(set2->id == TypeTableEntryIdErrorSet);
6529
6530 if (!resolve_inferred_error_set(ira, set1, source_node)) {
6531 return ira->codegen->builtin_types.entry_invalid;
6532 }
6533 if (!resolve_inferred_error_set(ira, set2, source_node)) {
6534 return ira->codegen->builtin_types.entry_invalid;
6535 }
6536 if (type_is_global_error_set(set1)) {
6537 return set2;
6538 }
6539 if (type_is_global_error_set(set2)) {
6540 return set1;
6541 }
6542 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
6543 for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
6544 ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
6545 assert(errors[error_entry->value] == nullptr);
6546 errors[error_entry->value] = error_entry;
6547 }
6548 ZigList<ErrorTableEntry *> intersection_list = {};
6549
6550 TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
6551 buf_resize(&err_set_type->name, 0);
6552 buf_appendf(&err_set_type->name, "error{");
6553
6554 for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
6555 ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
6556 ErrorTableEntry *existing_entry = errors[error_entry->value];
6557 if (existing_entry != nullptr) {
6558 intersection_list.append(existing_entry);
6559 buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name));
6560 }
6561 }
6562 free(errors);
6563
6564 err_set_type->is_copyable = true;
6565 err_set_type->type_ref = ira->codegen->builtin_types.entry_global_error_set->type_ref;
6566 err_set_type->di_type = ira->codegen->builtin_types.entry_global_error_set->di_type;
6567 err_set_type->data.error_set.err_count = intersection_list.length;
6568 err_set_type->data.error_set.errors = intersection_list.items;
6569 err_set_type->zero_bits = intersection_list.length == 0;
6570
6571 buf_appendf(&err_set_type->name, "}");
6572
6573 ira->codegen->error_di_types.append(&err_set_type->di_type);
6574
6575 return err_set_type;
6576}
6577
6578
6579static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type,
6580 TypeTableEntry *actual_type, AstNode *source_node)
6581{
6582 CodeGen *g = ira->codegen;
6583 ConstCastOnly result = {};
6584 result.id = ConstCastResultIdOk;
6585
6586 if (expected_type == actual_type)
6587 return result;
6588
6589 // pointer const
6590 if (expected_type->id == TypeTableEntryIdPointer &&
6591 actual_type->id == TypeTableEntryIdPointer &&
6592 (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) &&
6593 (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) &&
6594 actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset &&
6595 actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count &&
6596 actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment)
6597 {
6598 ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node);
6599 if (child.id != ConstCastResultIdOk) {
6600 result.id = ConstCastResultIdPointerChild;
6601 result.data.pointer_child = allocate_nonzero<ConstCastOnly>(1);
6602 *result.data.pointer_child = child;
6603 }
6604 return result;
6605 }
6606
6607 // slice const
6608 if (is_slice(expected_type) && is_slice(actual_type)) {
6609 TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
6610 TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;
6611 if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) &&
6612 (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) &&
6613 actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset &&
6614 actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count &&
6615 actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment)
6616 {
6617 ConstCastOnly child = types_match_const_cast_only(ira, expected_ptr_type->data.pointer.child_type,
6618 actual_ptr_type->data.pointer.child_type, source_node);
6619 if (child.id != ConstCastResultIdOk) {
6620 result.id = ConstCastResultIdSliceChild;
6621 result.data.slice_child = allocate_nonzero<ConstCastOnly>(1);
6622 *result.data.slice_child = child;
6623 }
6624 return result;
6625 }
6626 }
6627
6628 // maybe
6629 if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) {
6630 ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node);
6631 if (child.id != ConstCastResultIdOk) {
6632 result.id = ConstCastResultIdNullableChild;
6633 result.data.nullable_child = allocate_nonzero<ConstCastOnly>(1);
6634 *result.data.nullable_child = child;
6635 }
6636 return result;
6637 }
6638
6639 // error union
6640 if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) {
6641 ConstCastOnly payload_child = types_match_const_cast_only(ira, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type, source_node);
6642 if (payload_child.id != ConstCastResultIdOk) {
6643 result.id = ConstCastResultIdErrorUnionPayload;
6644 result.data.error_union_payload = allocate_nonzero<ConstCastOnly>(1);
6645 *result.data.error_union_payload = payload_child;
6646 return result;
6647 }
6648 ConstCastOnly error_set_child = types_match_const_cast_only(ira, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node);
6649 if (error_set_child.id != ConstCastResultIdOk) {
6650 result.id = ConstCastResultIdErrorUnionErrorSet;
6651 result.data.error_union_error_set = allocate_nonzero<ConstCastOnly>(1);
6652 *result.data.error_union_error_set = error_set_child;
6653 return result;
6654 }
6655 return result;
6656 }
6657
6658 // error set
6659 if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) {
6660 TypeTableEntry *contained_set = actual_type;
6661 TypeTableEntry *container_set = expected_type;
6662
6663 // if the container set is inferred, then this will always work.
6664 if (container_set->data.error_set.infer_fn != nullptr) {
6665 return result;
6666 }
6667 // if the container set is the global one, it will always work.
6668 if (type_is_global_error_set(container_set)) {
6669 return result;
6670 }
6671
6672 if (!resolve_inferred_error_set(ira, contained_set, source_node)) {
6673 result.id = ConstCastResultIdUnresolvedInferredErrSet;
6674 return result;
6675 }
6676
6677 if (type_is_global_error_set(contained_set)) {
6678 result.id = ConstCastResultIdErrSetGlobal;
6679 return result;
6680 }
6681
6682 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length);
6683 for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
6684 ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
6685 assert(errors[error_entry->value] == nullptr);
6686 errors[error_entry->value] = error_entry;
6687 }
6688 for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) {
6689 ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i];
6690 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
6691 if (error_entry == nullptr) {
6692 if (result.id == ConstCastResultIdOk) {
6693 result.id = ConstCastResultIdErrSet;
6694 }
6695 result.data.error_set.missing_errors.append(contained_error_entry);
6696 }
6697 }
6698 free(errors);
6699 return result;
6700 }
6701
6702 // fn
6703 if (expected_type->id == TypeTableEntryIdFn &&
6704 actual_type->id == TypeTableEntryIdFn)
6705 {
6706 if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
6707 result.id = ConstCastResultIdFnAlign;
6708 return result;
6709 }
6710 if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
6711 result.id = ConstCastResultIdFnCC;
6712 return result;
6713 }
6714 if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
6715 result.id = ConstCastResultIdFnVarArgs;
6716 return result;
6717 }
6718 if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
6719 result.id = ConstCastResultIdFnIsGeneric;
6720 return result;
6721 }
6722 if (!expected_type->data.fn.is_generic &&
6723 actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable)
6724 {
6725 ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node);
6726 if (child.id != ConstCastResultIdOk) {
6727 result.id = ConstCastResultIdFnReturnType;
6728 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
6729 *result.data.return_type = child;
6730 }
6731 return result;
6732 }
6733 if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
6734 result.id = ConstCastResultIdFnArgCount;
6735 return result;
6736 }
6737 if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
6738 result.id = ConstCastResultIdFnGenericArgCount;
6739 return result;
6740 }
6741 assert(expected_type->data.fn.is_generic ||
6742 expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count);
6743 for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) {
6744 // note it's reversed for parameters
6745 FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
6746 FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
6747
6748 ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node);
6749 if (arg_child.id != ConstCastResultIdOk) {
6750 result.id = ConstCastResultIdFnArg;
6751 result.data.fn_arg.arg_index = i;
6752 result.data.fn_arg.child = allocate_nonzero<ConstCastOnly>(1);
6753 *result.data.fn_arg.child = arg_child;
6754 return result;
6755 }
6756
6757 if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
6758 result.id = ConstCastResultIdFnArgNoAlias;
6759 result.data.arg_no_alias.arg_index = i;
6760 return result;
6761 }
6762 }
6763 return result;
6764 }
6765
6766 result.id = ConstCastResultIdType;
6767 return result;
6768}
6769
6290enum ImplicitCastMatchResult {6770enum ImplicitCastMatchResult {
6291 ImplicitCastMatchResultNo,6771 ImplicitCastMatchResultNo,
6292 ImplicitCastMatchResultYes,6772 ImplicitCastMatchResultYes,
...@@ -6296,10 +6776,46 @@ enum ImplicitCastMatchResult {...@@ -6296,10 +6776,46 @@ enum ImplicitCastMatchResult {
6296static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type,6776static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type,
6297 TypeTableEntry *actual_type, IrInstruction *value)6777 TypeTableEntry *actual_type, IrInstruction *value)
6298{6778{
6299 if (types_match_const_cast_only(expected_type, actual_type)) {6779 AstNode *source_node = value->source_node;
6780 ConstCastOnly const_cast_result = types_match_const_cast_only(ira, expected_type, actual_type, source_node);
6781 if (const_cast_result.id == ConstCastResultIdOk) {
6300 return ImplicitCastMatchResultYes;6782 return ImplicitCastMatchResultYes;
6301 }6783 }
63026784
6785 // if we got here with error sets, make an error showing the incompatibilities
6786 ZigList<ErrorTableEntry *> *missing_errors = nullptr;
6787 if (const_cast_result.id == ConstCastResultIdErrSet) {
6788 missing_errors = &const_cast_result.data.error_set.missing_errors;
6789 }
6790 if (const_cast_result.id == ConstCastResultIdErrorUnionErrorSet) {
6791 if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSet) {
6792 missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors;
6793 } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) {
6794 ErrorMsg *msg = ir_add_error(ira, value,
6795 buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
6796 add_error_note(ira->codegen, msg, value->source_node,
6797 buf_sprintf("unable to cast global error set into smaller set"));
6798 return ImplicitCastMatchResultReportedError;
6799 }
6800 } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) {
6801 ErrorMsg *msg = ir_add_error(ira, value,
6802 buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
6803 add_error_note(ira->codegen, msg, value->source_node,
6804 buf_sprintf("unable to cast global error set into smaller set"));
6805 return ImplicitCastMatchResultReportedError;
6806 }
6807 if (missing_errors != nullptr) {
6808 ErrorMsg *msg = ir_add_error(ira, value,
6809 buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
6810 for (size_t i = 0; i < missing_errors->length; i += 1) {
6811 ErrorTableEntry *error_entry = missing_errors->at(i);
6812 add_error_note(ira->codegen, msg, error_entry->decl_node,
6813 buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name)));
6814 }
6815
6816 return ImplicitCastMatchResultReportedError;
6817 }
6818
6303 // implicit conversion from anything to var6819 // implicit conversion from anything to var
6304 if (expected_type->id == TypeTableEntryIdVar) {6820 if (expected_type->id == TypeTableEntryIdVar) {
6305 return ImplicitCastMatchResultYes;6821 return ImplicitCastMatchResultYes;
...@@ -6319,25 +6835,25 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6319,25 +6835,25 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6319 return ImplicitCastMatchResultYes;6835 return ImplicitCastMatchResultYes;
6320 }6836 }
63216837
6322 // implicit T to %T6838 // implicit T to U!T
6323 if (expected_type->id == TypeTableEntryIdErrorUnion &&6839 if (expected_type->id == TypeTableEntryIdErrorUnion &&
6324 ir_types_match_with_implicit_cast(ira, expected_type->data.error.child_type, actual_type, value))6840 ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type, actual_type, value))
6325 {6841 {
6326 return ImplicitCastMatchResultYes;6842 return ImplicitCastMatchResultYes;
6327 }6843 }
63286844
6329 // implicit conversion from pure error to error union type6845 // implicit conversion from error set to error union type
6330 if (expected_type->id == TypeTableEntryIdErrorUnion &&6846 if (expected_type->id == TypeTableEntryIdErrorUnion &&
6331 actual_type->id == TypeTableEntryIdPureError)6847 actual_type->id == TypeTableEntryIdErrorSet)
6332 {6848 {
6333 return ImplicitCastMatchResultYes;6849 return ImplicitCastMatchResultYes;
6334 }6850 }
63356851
6336 // implicit conversion from T to %?T6852 // implicit conversion from T to U!?T
6337 if (expected_type->id == TypeTableEntryIdErrorUnion &&6853 if (expected_type->id == TypeTableEntryIdErrorUnion &&
6338 expected_type->data.error.child_type->id == TypeTableEntryIdMaybe &&6854 expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
6339 ir_types_match_with_implicit_cast(ira,6855 ir_types_match_with_implicit_cast(ira,
6340 expected_type->data.error.child_type->data.maybe.child_type,6856 expected_type->data.error_union.payload_type->data.maybe.child_type,
6341 actual_type, value))6857 actual_type, value))
6342 {6858 {
6343 return ImplicitCastMatchResultYes;6859 return ImplicitCastMatchResultYes;
...@@ -6374,7 +6890,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6374,7 +6890,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6374 assert(ptr_type->id == TypeTableEntryIdPointer);6890 assert(ptr_type->id == TypeTableEntryIdPointer);
63756891
6376 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&6892 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
6377 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))6893 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6378 {6894 {
6379 return ImplicitCastMatchResultYes;6895 return ImplicitCastMatchResultYes;
6380 }6896 }
...@@ -6392,7 +6908,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6392,7 +6908,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6392 TypeTableEntry *array_type = actual_type->data.pointer.child_type;6908 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
63936909
6394 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&6910 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
6395 types_match_const_cast_only(ptr_type->data.pointer.child_type, array_type->data.array.child_type))6911 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6396 {6912 {
6397 return ImplicitCastMatchResultYes;6913 return ImplicitCastMatchResultYes;
6398 }6914 }
...@@ -6408,7 +6924,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6408,7 +6924,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6408 expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;6924 expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
6409 assert(ptr_type->id == TypeTableEntryIdPointer);6925 assert(ptr_type->id == TypeTableEntryIdPointer);
6410 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&6926 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
6411 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))6927 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6412 {6928 {
6413 return ImplicitCastMatchResultYes;6929 return ImplicitCastMatchResultYes;
6414 }6930 }
...@@ -6423,7 +6939,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6423,7 +6939,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6423 expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;6939 expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
6424 assert(ptr_type->id == TypeTableEntryIdPointer);6940 assert(ptr_type->id == TypeTableEntryIdPointer);
6425 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&6941 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
6426 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))6942 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6427 {6943 {
6428 return ImplicitCastMatchResultYes;6944 return ImplicitCastMatchResultYes;
6429 }6945 }
...@@ -6503,7 +7019,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6503,7 +7019,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6503 // implicitly take a const pointer to something7019 // implicitly take a const pointer to something
6504 if (!type_requires_comptime(actual_type)) {7020 if (!type_requires_comptime(actual_type)) {
6505 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);7021 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
6506 if (types_match_const_cast_only(expected_type, const_ptr_actual)) {7022 if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) {
6507 return ImplicitCastMatchResultYes;7023 return ImplicitCastMatchResultYes;
6508 }7024 }
6509 }7025 }
...@@ -6511,13 +7027,39 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -6511,13 +7027,39 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
6511 return ImplicitCastMatchResultNo;7027 return ImplicitCastMatchResultNo;
6512}7028}
65137029
7030static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) {
7031 size_t old_errors_count = *errors_count;
7032 *errors_count = g->errors_by_index.length;
7033 *errors = reallocate(*errors, old_errors_count, *errors_count);
7034}
7035
6514static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {7036static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
6515 assert(instruction_count >= 1);7037 assert(instruction_count >= 1);
6516 IrInstruction *prev_inst = instructions[0];7038 IrInstruction *prev_inst = instructions[0];
6517 if (type_is_invalid(prev_inst->value.type)) {7039 if (type_is_invalid(prev_inst->value.type)) {
6518 return ira->codegen->builtin_types.entry_invalid;7040 return ira->codegen->builtin_types.entry_invalid;
6519 }7041 }
6520 bool any_are_pure_error = (prev_inst->value.type->id == TypeTableEntryIdPureError);7042 ErrorTableEntry **errors = nullptr;
7043 size_t errors_count = 0;
7044 TypeTableEntry *err_set_type = nullptr;
7045 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
7046 if (type_is_global_error_set(prev_inst->value.type)) {
7047 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
7048 } else {
7049 err_set_type = prev_inst->value.type;
7050 if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) {
7051 return ira->codegen->builtin_types.entry_invalid;
7052 }
7053 update_errors_helper(ira->codegen, &errors, &errors_count);
7054
7055 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7056 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
7057 assert(errors[error_entry->value] == nullptr);
7058 errors[error_entry->value] = error_entry;
7059 }
7060 }
7061 }
7062
6521 bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit);7063 bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit);
6522 bool convert_to_const_slice = false;7064 bool convert_to_const_slice = false;
6523 for (size_t i = 1; i < instruction_count; i += 1) {7065 for (size_t i = 1; i < instruction_count; i += 1) {
...@@ -6538,34 +7080,280 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6538,34 +7080,280 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
6538 continue;7080 continue;
6539 }7081 }
65407082
6541 if (prev_type->id == TypeTableEntryIdPureError) {7083 if (prev_type->id == TypeTableEntryIdNullLit) {
6542 prev_inst = cur_inst;7084 prev_inst = cur_inst;
6543 continue;7085 continue;
6544 }7086 }
65457087
6546 if (prev_type->id == TypeTableEntryIdNullLit) {7088 if (cur_type->id == TypeTableEntryIdNullLit) {
6547 prev_inst = cur_inst;7089 any_are_null = true;
6548 continue;7090 continue;
6549 }7091 }
65507092
6551 if (cur_type->id == TypeTableEntryIdPureError) {7093 if (prev_type->id == TypeTableEntryIdErrorSet) {
7094 assert(err_set_type != nullptr);
7095 if (cur_type->id == TypeTableEntryIdErrorSet) {
7096 if (type_is_global_error_set(err_set_type)) {
7097 continue;
7098 }
7099 if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
7100 return ira->codegen->builtin_types.entry_invalid;
7101 }
7102 if (type_is_global_error_set(cur_type)) {
7103 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
7104 prev_inst = cur_inst;
7105 continue;
7106 }
7107
7108 // number of declared errors might have increased now
7109 update_errors_helper(ira->codegen, &errors, &errors_count);
7110
7111 // if err_set_type is a superset of cur_type, keep err_set_type.
7112 // if cur_type is a superset of err_set_type, switch err_set_type to cur_type
7113 bool prev_is_superset = true;
7114 for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
7115 ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
7116 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
7117 if (error_entry == nullptr) {
7118 prev_is_superset = false;
7119 break;
7120 }
7121 }
7122 if (prev_is_superset) {
7123 continue;
7124 }
7125
7126 // unset everything in errors
7127 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7128 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
7129 errors[error_entry->value] = nullptr;
7130 }
7131 for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
7132 assert(errors[i] == nullptr);
7133 }
7134 for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
7135 ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i];
7136 assert(errors[error_entry->value] == nullptr);
7137 errors[error_entry->value] = error_entry;
7138 }
7139 bool cur_is_superset = true;
7140 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7141 ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
7142 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
7143 if (error_entry == nullptr) {
7144 cur_is_superset = false;
7145 break;
7146 }
7147 }
7148 if (cur_is_superset) {
7149 err_set_type = cur_type;
7150 prev_inst = cur_inst;
7151 assert(errors != nullptr);
7152 continue;
7153 }
7154
7155 // neither of them are supersets. so we invent a new error set type that is a union of both of them
7156 err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type);
7157 assert(errors != nullptr);
7158 continue;
7159 } else if (cur_type->id == TypeTableEntryIdErrorUnion) {
7160 if (type_is_global_error_set(err_set_type)) {
7161 prev_inst = cur_inst;
7162 continue;
7163 }
7164 TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
7165 if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
7166 return ira->codegen->builtin_types.entry_invalid;
7167 }
7168 if (type_is_global_error_set(cur_err_set_type)) {
7169 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
7170 prev_inst = cur_inst;
7171 continue;
7172 }
7173
7174 update_errors_helper(ira->codegen, &errors, &errors_count);
7175
7176 // test if err_set_type is a subset of cur_type's error set
7177 // unset everything in errors
7178 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7179 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
7180 errors[error_entry->value] = nullptr;
7181 }
7182 for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
7183 assert(errors[i] == nullptr);
7184 }
7185 for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
7186 ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
7187 assert(errors[error_entry->value] == nullptr);
7188 errors[error_entry->value] = error_entry;
7189 }
7190 bool cur_is_superset = true;
7191 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7192 ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
7193 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
7194 if (error_entry == nullptr) {
7195 cur_is_superset = false;
7196 break;
7197 }
7198 }
7199 if (cur_is_superset) {
7200 err_set_type = cur_err_set_type;
7201 prev_inst = cur_inst;
7202 assert(errors != nullptr);
7203 continue;
7204 }
7205
7206 // not a subset. invent new error set type, union of both of them
7207 err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type);
7208 prev_inst = cur_inst;
7209 assert(errors != nullptr);
7210 continue;
7211 } else {
7212 prev_inst = cur_inst;
7213 continue;
7214 }
7215 }
7216
7217 if (cur_type->id == TypeTableEntryIdErrorSet) {
6552 if (prev_type->id == TypeTableEntryIdArray) {7218 if (prev_type->id == TypeTableEntryIdArray) {
6553 convert_to_const_slice = true;7219 convert_to_const_slice = true;
6554 }7220 }
6555 any_are_pure_error = true;7221 if (type_is_global_error_set(cur_type)) {
7222 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
7223 continue;
7224 }
7225 if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) {
7226 continue;
7227 }
7228 if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
7229 return ira->codegen->builtin_types.entry_invalid;
7230 }
7231
7232 update_errors_helper(ira->codegen, &errors, &errors_count);
7233
7234 if (err_set_type == nullptr) {
7235 if (prev_type->id == TypeTableEntryIdErrorUnion) {
7236 err_set_type = prev_type->data.error_union.err_set_type;
7237 } else {
7238 err_set_type = cur_type;
7239 }
7240 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7241 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
7242 assert(errors[error_entry->value] == nullptr);
7243 errors[error_entry->value] = error_entry;
7244 }
7245 if (err_set_type == cur_type) {
7246 continue;
7247 }
7248 }
7249 // check if the cur type error set is a subset
7250 bool prev_is_superset = true;
7251 for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
7252 ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
7253 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
7254 if (error_entry == nullptr) {
7255 prev_is_superset = false;
7256 break;
7257 }
7258 }
7259 if (prev_is_superset) {
7260 continue;
7261 }
7262 // not a subset. invent new error set type, union of both of them
7263 err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type);
7264 assert(errors != nullptr);
6556 continue;7265 continue;
6557 }7266 }
65587267
6559 if (cur_type->id == TypeTableEntryIdNullLit) {7268 if (prev_type->id == TypeTableEntryIdErrorUnion && cur_type->id == TypeTableEntryIdErrorUnion) {
6560 any_are_null = true;7269 TypeTableEntry *prev_payload_type = prev_type->data.error_union.payload_type;
6561 continue;7270 TypeTableEntry *cur_payload_type = cur_type->data.error_union.payload_type;
7271
7272 bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type,
7273 source_node).id == ConstCastResultIdOk;
7274 bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type,
7275 source_node).id == ConstCastResultIdOk;
7276
7277 if (const_cast_prev || const_cast_cur) {
7278 if (const_cast_cur) {
7279 prev_inst = cur_inst;
7280 }
7281
7282 TypeTableEntry *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type;
7283 TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
7284
7285 if (!resolve_inferred_error_set(ira, prev_err_set_type, cur_inst->source_node)) {
7286 return ira->codegen->builtin_types.entry_invalid;
7287 }
7288
7289 if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
7290 return ira->codegen->builtin_types.entry_invalid;
7291 }
7292
7293 if (type_is_global_error_set(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) {
7294 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
7295 continue;
7296 }
7297
7298 update_errors_helper(ira->codegen, &errors, &errors_count);
7299
7300 if (err_set_type == nullptr) {
7301 err_set_type = prev_err_set_type;
7302 for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
7303 ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i];
7304 assert(errors[error_entry->value] == nullptr);
7305 errors[error_entry->value] = error_entry;
7306 }
7307 }
7308 bool prev_is_superset = true;
7309 for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
7310 ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i];
7311 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
7312 if (error_entry == nullptr) {
7313 prev_is_superset = false;
7314 break;
7315 }
7316 }
7317 if (prev_is_superset) {
7318 continue;
7319 }
7320 // unset all the errors
7321 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
7322 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
7323 errors[error_entry->value] = nullptr;
7324 }
7325 for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
7326 assert(errors[i] == nullptr);
7327 }
7328 for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
7329 ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
7330 assert(errors[error_entry->value] == nullptr);
7331 errors[error_entry->value] = error_entry;
7332 }
7333 bool cur_is_superset = true;
7334 for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
7335 ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i];
7336 ErrorTableEntry *error_entry = errors[contained_error_entry->value];
7337 if (error_entry == nullptr) {
7338 cur_is_superset = false;
7339 break;
7340 }
7341 }
7342 if (cur_is_superset) {
7343 err_set_type = cur_err_set_type;
7344 continue;
7345 }
7346
7347 err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type);
7348 continue;
7349 }
6562 }7350 }
65637351
6564 if (types_match_const_cast_only(prev_type, cur_type)) {7352 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {
6565 continue;7353 continue;
6566 }7354 }
65677355
6568 if (types_match_const_cast_only(cur_type, prev_type)) {7356 if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) {
6569 prev_inst = cur_inst;7357 prev_inst = cur_inst;
6570 continue;7358 continue;
6571 }7359 }
...@@ -6588,26 +7376,41 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6588,26 +7376,41 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
6588 }7376 }
65897377
6590 if (prev_type->id == TypeTableEntryIdErrorUnion &&7378 if (prev_type->id == TypeTableEntryIdErrorUnion &&
6591 types_match_const_cast_only(prev_type->data.error.child_type, cur_type))7379 types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node).id == ConstCastResultIdOk)
6592 {7380 {
6593 continue;7381 continue;
6594 }7382 }
65957383
6596 if (cur_type->id == TypeTableEntryIdErrorUnion &&7384 if (cur_type->id == TypeTableEntryIdErrorUnion &&
6597 types_match_const_cast_only(cur_type->data.error.child_type, prev_type))7385 types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk)
6598 {7386 {
7387 if (err_set_type != nullptr) {
7388 TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
7389 if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
7390 return ira->codegen->builtin_types.entry_invalid;
7391 }
7392 if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) {
7393 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
7394 prev_inst = cur_inst;
7395 continue;
7396 }
7397
7398 update_errors_helper(ira->codegen, &errors, &errors_count);
7399
7400 err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type);
7401 }
6599 prev_inst = cur_inst;7402 prev_inst = cur_inst;
6600 continue;7403 continue;
6601 }7404 }
66027405
6603 if (prev_type->id == TypeTableEntryIdMaybe &&7406 if (prev_type->id == TypeTableEntryIdMaybe &&
6604 types_match_const_cast_only(prev_type->data.maybe.child_type, cur_type))7407 types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk)
6605 {7408 {
6606 continue;7409 continue;
6607 }7410 }
66087411
6609 if (cur_type->id == TypeTableEntryIdMaybe &&7412 if (cur_type->id == TypeTableEntryIdMaybe &&
6610 types_match_const_cast_only(cur_type->data.maybe.child_type, prev_type))7413 types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk)
6611 {7414 {
6612 prev_inst = cur_inst;7415 prev_inst = cur_inst;
6613 continue;7416 continue;
...@@ -6645,7 +7448,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6645,7 +7448,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
66457448
6646 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&7449 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
6647 cur_type->data.array.len != prev_type->data.array.len &&7450 cur_type->data.array.len != prev_type->data.array.len &&
6648 types_match_const_cast_only(cur_type->data.array.child_type, prev_type->data.array.child_type))7451 types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6649 {7452 {
6650 convert_to_const_slice = true;7453 convert_to_const_slice = true;
6651 prev_inst = cur_inst;7454 prev_inst = cur_inst;
...@@ -6654,7 +7457,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6654,7 +7457,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
66547457
6655 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&7458 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
6656 cur_type->data.array.len != prev_type->data.array.len &&7459 cur_type->data.array.len != prev_type->data.array.len &&
6657 types_match_const_cast_only(prev_type->data.array.child_type, cur_type->data.array.child_type))7460 types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6658 {7461 {
6659 convert_to_const_slice = true;7462 convert_to_const_slice = true;
6660 continue;7463 continue;
...@@ -6663,8 +7466,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6663,8 +7466,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
6663 if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&7466 if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&
6664 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||7467 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
6665 cur_type->data.array.len == 0) &&7468 cur_type->data.array.len == 0) &&
6666 types_match_const_cast_only(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,7469 types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
6667 cur_type->data.array.child_type))7470 cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6668 {7471 {
6669 convert_to_const_slice = false;7472 convert_to_const_slice = false;
6670 continue;7473 continue;
...@@ -6673,8 +7476,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6673,8 +7476,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
6673 if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&7476 if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&
6674 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||7477 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
6675 prev_type->data.array.len == 0) &&7478 prev_type->data.array.len == 0) &&
6676 types_match_const_cast_only(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,7479 types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
6677 prev_type->data.array.child_type))7480 prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
6678 {7481 {
6679 prev_inst = cur_inst;7482 prev_inst = cur_inst;
6680 convert_to_const_slice = false;7483 convert_to_const_slice = false;
...@@ -6714,30 +7517,37 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -6714,30 +7517,37 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
67147517
6715 return ira->codegen->builtin_types.entry_invalid;7518 return ira->codegen->builtin_types.entry_invalid;
6716 }7519 }
7520
7521 free(errors);
7522
6717 if (convert_to_const_slice) {7523 if (convert_to_const_slice) {
6718 assert(prev_inst->value.type->id == TypeTableEntryIdArray);7524 assert(prev_inst->value.type->id == TypeTableEntryIdArray);
6719 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true);7525 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true);
6720 TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type);7526 TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type);
6721 if (any_are_pure_error) {7527 if (err_set_type != nullptr) {
6722 return get_error_type(ira->codegen, slice_type);7528 return get_error_union_type(ira->codegen, err_set_type, slice_type);
6723 } else {7529 } else {
6724 return slice_type;7530 return slice_type;
6725 }7531 }
6726 } else if (any_are_pure_error && prev_inst->value.type->id != TypeTableEntryIdPureError) {7532 } else if (err_set_type != nullptr) {
6727 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||7533 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
6728 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)7534 return err_set_type;
6729 {
6730 ir_add_error_node(ira, source_node,
6731 buf_sprintf("unable to make error union out of number literal"));
6732 return ira->codegen->builtin_types.entry_invalid;
6733 } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) {
6734 ir_add_error_node(ira, source_node,
6735 buf_sprintf("unable to make error union out of null literal"));
6736 return ira->codegen->builtin_types.entry_invalid;
6737 } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
6738 return prev_inst->value.type;
6739 } else {7535 } else {
6740 return get_error_type(ira->codegen, prev_inst->value.type);7536 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
7537 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
7538 {
7539 ir_add_error_node(ira, source_node,
7540 buf_sprintf("unable to make error union out of number literal"));
7541 return ira->codegen->builtin_types.entry_invalid;
7542 } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) {
7543 ir_add_error_node(ira, source_node,
7544 buf_sprintf("unable to make error union out of null literal"));
7545 return ira->codegen->builtin_types.entry_invalid;
7546 } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
7547 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
7548 } else {
7549 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
7550 }
6741 }7551 }
6742 } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) {7552 } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) {
6743 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||7553 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
...@@ -6783,6 +7593,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,...@@ -6783,6 +7593,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
6783 switch (cast_op) {7593 switch (cast_op) {
6784 case CastOpNoCast:7594 case CastOpNoCast:
6785 zig_unreachable();7595 zig_unreachable();
7596 case CastOpErrSet:
7597 zig_panic("TODO");
6786 case CastOpNoop:7598 case CastOpNoop:
6787 {7599 {
6788 copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic);7600 copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic);
...@@ -7213,7 +8025,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction...@@ -7213,7 +8025,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction
7213 assert(wanted_type->id == TypeTableEntryIdErrorUnion);8025 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
72148026
7215 if (instr_is_comptime(value)) {8027 if (instr_is_comptime(value)) {
7216 TypeTableEntry *payload_type = wanted_type->data.error.child_type;8028 TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type;
7217 IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);8029 IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
7218 if (type_is_invalid(casted_payload->value.type))8030 if (type_is_invalid(casted_payload->value.type))
7219 return ira->codegen->invalid_instruction;8031 return ira->codegen->invalid_instruction;
...@@ -7238,19 +8050,64 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction...@@ -7238,19 +8050,64 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction
7238 return result;8050 return result;
7239}8051}
72408052
7241static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {8053static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
7242 assert(wanted_type->id == TypeTableEntryIdErrorUnion);8054 TypeTableEntry *wanted_type)
8055{
8056 assert(value->value.type->id == TypeTableEntryIdErrorSet);
8057 assert(wanted_type->id == TypeTableEntryIdErrorSet);
72438058
7244 if (instr_is_comptime(value)) {8059 if (instr_is_comptime(value)) {
7245 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);8060 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
7246 if (!val)8061 if (!val)
7247 return ira->codegen->invalid_instruction;8062 return ira->codegen->invalid_instruction;
72488063
8064 if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) {
8065 return ira->codegen->invalid_instruction;
8066 }
8067 if (!type_is_global_error_set(wanted_type)) {
8068 bool subset = false;
8069 for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
8070 if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) {
8071 subset = true;
8072 break;
8073 }
8074 }
8075 if (!subset) {
8076 ir_add_error(ira, source_instr,
8077 buf_sprintf("error.%s not a member of error set '%s'",
8078 buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name)));
8079 return ira->codegen->invalid_instruction;
8080 }
8081 }
8082
7249 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,8083 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
7250 source_instr->scope, source_instr->source_node);8084 source_instr->scope, source_instr->source_node);
7251 const_instruction->base.value.type = wanted_type;8085 const_instruction->base.value.type = wanted_type;
7252 const_instruction->base.value.special = ConstValSpecialStatic;8086 const_instruction->base.value.special = ConstValSpecialStatic;
7253 const_instruction->base.value.data.x_err_union.err = val->data.x_pure_err;8087 const_instruction->base.value.data.x_err_set = val->data.x_err_set;
8088 return &const_instruction->base;
8089 }
8090
8091 IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet);
8092 result->value.type = wanted_type;
8093 return result;
8094}
8095
8096static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
8097 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
8098
8099 IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type);
8100
8101 if (instr_is_comptime(casted_value)) {
8102 ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad);
8103 if (!val)
8104 return ira->codegen->invalid_instruction;
8105
8106 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
8107 source_instr->scope, source_instr->source_node);
8108 const_instruction->base.value.type = wanted_type;
8109 const_instruction->base.value.special = ConstValSpecialStatic;
8110 const_instruction->base.value.data.x_err_union.err = val->data.x_err_set;
7254 const_instruction->base.value.data.x_err_union.payload = nullptr;8111 const_instruction->base.value.data.x_err_union.payload = nullptr;
7255 return &const_instruction->base;8112 return &const_instruction->base;
7256 }8113 }
...@@ -7630,36 +8487,68 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction...@@ -7630,36 +8487,68 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction
7630 return result;8487 return result;
7631}8488}
76328489
7633static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) {8490static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
8491 TypeTableEntry *wanted_type)
8492{
7634 assert(target->value.type->id == TypeTableEntryIdInt);8493 assert(target->value.type->id == TypeTableEntryIdInt);
7635 assert(!target->value.type->data.integral.is_signed);8494 assert(!target->value.type->data.integral.is_signed);
8495 assert(wanted_type->id == TypeTableEntryIdErrorSet);
76368496
7637 if (instr_is_comptime(target)) {8497 if (instr_is_comptime(target)) {
7638 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);8498 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
7639 if (!val)8499 if (!val)
7640 return ira->codegen->invalid_instruction;8500 return ira->codegen->invalid_instruction;
76418501
7642 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,8502 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
7643 source_instr->source_node, ira->codegen->builtin_types.entry_pure_error);8503 source_instr->source_node, wanted_type);
8504
8505 if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) {
8506 return ira->codegen->invalid_instruction;
8507 }
8508
8509 if (type_is_global_error_set(wanted_type)) {
8510 BigInt err_count;
8511 bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length);
8512
8513 if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
8514 Buf *val_buf = buf_alloc();
8515 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
8516 ir_add_error(ira, source_instr,
8517 buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
8518 return ira->codegen->invalid_instruction;
8519 }
8520
8521 size_t index = bigint_as_unsigned(&val->data.x_bigint);
8522 result->value.data.x_err_set = ira->codegen->errors_by_index.at(index);
8523 return result;
8524 } else {
8525 ErrorTableEntry *err = nullptr;
8526 BigInt err_int;
8527
8528 for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
8529 ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i];
8530 bigint_init_unsigned(&err_int, this_err->value);
8531 if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) {
8532 err = this_err;
8533 break;
8534 }
8535 }
76448536
7645 BigInt err_count;8537 if (err == nullptr) {
7646 bigint_init_unsigned(&err_count, ira->codegen->error_decls.length);8538 Buf *val_buf = buf_alloc();
7647 if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {8539 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
7648 Buf *val_buf = buf_alloc();8540 ir_add_error(ira, source_instr,
7649 bigint_append_buf(val_buf, &val->data.x_bigint, 10);8541 buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name)));
7650 ir_add_error(ira, source_instr,8542 return ira->codegen->invalid_instruction;
7651 buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));8543 }
7652 return ira->codegen->invalid_instruction;
7653 }
76548544
7655 size_t index = bigint_as_unsigned(&val->data.x_bigint);8545 result->value.data.x_err_set = err;
7656 AstNode *error_decl_node = ira->codegen->error_decls.at(index);8546 return result;
7657 result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err;8547 }
7658 return result;
7659 }8548 }
76608549
7661 IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target);8550 IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
7662 result->value.type = ira->codegen->builtin_types.entry_pure_error;8551 result->value.type = wanted_type;
7663 return result;8552 return result;
7664}8553}
76658554
...@@ -7681,8 +8570,8 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc...@@ -7681,8 +8570,8 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
7681 ErrorTableEntry *err;8570 ErrorTableEntry *err;
7682 if (err_type->id == TypeTableEntryIdErrorUnion) {8571 if (err_type->id == TypeTableEntryIdErrorUnion) {
7683 err = val->data.x_err_union.err;8572 err = val->data.x_err_union.err;
7684 } else if (err_type->id == TypeTableEntryIdPureError) {8573 } else if (err_type->id == TypeTableEntryIdErrorSet) {
7685 err = val->data.x_pure_err;8574 err = val->data.x_err_set;
7686 } else {8575 } else {
7687 zig_unreachable();8576 zig_unreachable();
7688 }8577 }
...@@ -7702,8 +8591,36 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc...@@ -7702,8 +8591,36 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
7702 return result;8591 return result;
7703 }8592 }
77048593
8594 TypeTableEntry *err_set_type;
8595 if (err_type->id == TypeTableEntryIdErrorUnion) {
8596 err_set_type = err_type->data.error_union.err_set_type;
8597 } else if (err_type->id == TypeTableEntryIdErrorSet) {
8598 err_set_type = err_type;
8599 } else {
8600 zig_unreachable();
8601 }
8602 if (!type_is_global_error_set(err_set_type)) {
8603 if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) {
8604 return ira->codegen->invalid_instruction;
8605 }
8606 if (err_set_type->data.error_set.err_count == 0) {
8607 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8608 source_instr->source_node, wanted_type);
8609 result->value.type = wanted_type;
8610 bigint_init_unsigned(&result->value.data.x_bigint, 0);
8611 return result;
8612 } else if (err_set_type->data.error_set.err_count == 1) {
8613 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8614 source_instr->source_node, wanted_type);
8615 result->value.type = wanted_type;
8616 ErrorTableEntry *err = err_set_type->data.error_set.errors[0];
8617 bigint_init_unsigned(&result->value.data.x_bigint, err->value);
8618 return result;
8619 }
8620 }
8621
7705 BigInt bn;8622 BigInt bn;
7706 bigint_init_unsigned(&bn, ira->codegen->error_decls.length);8623 bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length);
7707 if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {8624 if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
7708 ir_add_error_node(ira, source_instr->source_node,8625 ir_add_error_node(ira, source_instr->source_node,
7709 buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));8626 buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
...@@ -7719,6 +8636,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7719,6 +8636,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7719 TypeTableEntry *wanted_type, IrInstruction *value)8636 TypeTableEntry *wanted_type, IrInstruction *value)
7720{8637{
7721 TypeTableEntry *actual_type = value->value.type;8638 TypeTableEntry *actual_type = value->value.type;
8639 AstNode *source_node = source_instr->source_node;
77228640
7723 if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {8641 if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
7724 return ira->codegen->invalid_instruction;8642 return ira->codegen->invalid_instruction;
...@@ -7728,7 +8646,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7728,7 +8646,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7728 return value;8646 return value;
77298647
7730 // explicit match or non-const to const8648 // explicit match or non-const to const
7731 if (types_match_const_cast_only(wanted_type, actual_type)) {8649 if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) {
7732 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);8650 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
7733 }8651 }
77348652
...@@ -7748,6 +8666,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7748,6 +8666,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7748 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);8666 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
7749 }8667 }
77508668
8669 // explicit error set cast
8670 if (wanted_type->id == TypeTableEntryIdErrorSet &&
8671 actual_type->id == TypeTableEntryIdErrorSet)
8672 {
8673 return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type);
8674 }
8675
7751 // explicit cast from int to float8676 // explicit cast from int to float
7752 if (wanted_type->id == TypeTableEntryIdFloat &&8677 if (wanted_type->id == TypeTableEntryIdFloat &&
7753 actual_type->id == TypeTableEntryIdInt)8678 actual_type->id == TypeTableEntryIdInt)
...@@ -7767,7 +8692,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7767,7 +8692,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7767 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;8692 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
7768 assert(ptr_type->id == TypeTableEntryIdPointer);8693 assert(ptr_type->id == TypeTableEntryIdPointer);
7769 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8694 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
7770 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))8695 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
7771 {8696 {
7772 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);8697 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
7773 }8698 }
...@@ -7785,7 +8710,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7785,7 +8710,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7785 TypeTableEntry *array_type = actual_type->data.pointer.child_type;8710 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
77868711
7787 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&8712 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
7788 types_match_const_cast_only(ptr_type->data.pointer.child_type, array_type->data.array.child_type))8713 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
7789 {8714 {
7790 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);8715 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
7791 }8716 }
...@@ -7801,7 +8726,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7801,7 +8726,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7801 wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;8726 wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
7802 assert(ptr_type->id == TypeTableEntryIdPointer);8727 assert(ptr_type->id == TypeTableEntryIdPointer);
7803 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8728 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
7804 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))8729 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
7805 {8730 {
7806 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);8731 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
7807 if (type_is_invalid(cast1->value.type))8732 if (type_is_invalid(cast1->value.type))
...@@ -7824,7 +8749,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7824,7 +8749,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7824 wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;8749 wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
7825 assert(ptr_type->id == TypeTableEntryIdPointer);8750 assert(ptr_type->id == TypeTableEntryIdPointer);
7826 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8751 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
7827 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))8752 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
7828 {8753 {
7829 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);8754 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
7830 if (type_is_invalid(cast1->value.type))8755 if (type_is_invalid(cast1->value.type))
...@@ -7886,7 +8811,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7886,7 +8811,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
78868811
7887 // explicit cast from child type of maybe type to maybe type8812 // explicit cast from child type of maybe type to maybe type
7888 if (wanted_type->id == TypeTableEntryIdMaybe) {8813 if (wanted_type->id == TypeTableEntryIdMaybe) {
7889 if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {8814 if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) {
7890 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);8815 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
7891 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||8816 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
7892 actual_type->id == TypeTableEntryIdNumLitFloat)8817 actual_type->id == TypeTableEntryIdNumLitFloat)
...@@ -7908,12 +8833,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7908,12 +8833,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
79088833
7909 // explicit cast from child type of error type to error type8834 // explicit cast from child type of error type to error type
7910 if (wanted_type->id == TypeTableEntryIdErrorUnion) {8835 if (wanted_type->id == TypeTableEntryIdErrorUnion) {
7911 if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {8836 if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) {
7912 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);8837 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
7913 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||8838 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
7914 actual_type->id == TypeTableEntryIdNumLitFloat)8839 actual_type->id == TypeTableEntryIdNumLitFloat)
7915 {8840 {
7916 if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type, true)) {8841 if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
7917 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);8842 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
7918 } else {8843 } else {
7919 return ira->codegen->invalid_instruction;8844 return ira->codegen->invalid_instruction;
...@@ -7923,16 +8848,16 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7923,16 +8848,16 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
79238848
7924 // explicit cast from [N]T to %[]const T8849 // explicit cast from [N]T to %[]const T
7925 if (wanted_type->id == TypeTableEntryIdErrorUnion &&8850 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7926 is_slice(wanted_type->data.error.child_type) &&8851 is_slice(wanted_type->data.error_union.payload_type) &&
7927 actual_type->id == TypeTableEntryIdArray)8852 actual_type->id == TypeTableEntryIdArray)
7928 {8853 {
7929 TypeTableEntry *ptr_type =8854 TypeTableEntry *ptr_type =
7930 wanted_type->data.error.child_type->data.structure.fields[slice_ptr_index].type_entry;8855 wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
7931 assert(ptr_type->id == TypeTableEntryIdPointer);8856 assert(ptr_type->id == TypeTableEntryIdPointer);
7932 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8857 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
7933 types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))8858 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
7934 {8859 {
7935 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error.child_type, value);8860 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
7936 if (type_is_invalid(cast1->value.type))8861 if (type_is_invalid(cast1->value.type))
7937 return ira->codegen->invalid_instruction;8862 return ira->codegen->invalid_instruction;
79388863
...@@ -7944,25 +8869,25 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -7944,25 +8869,25 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
7944 }8869 }
7945 }8870 }
79468871
7947 // explicit cast from pure error to error union type8872 // explicit cast from error set to error union type
7948 if (wanted_type->id == TypeTableEntryIdErrorUnion &&8873 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7949 actual_type->id == TypeTableEntryIdPureError)8874 actual_type->id == TypeTableEntryIdErrorSet)
7950 {8875 {
7951 return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);8876 return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
7952 }8877 }
79538878
7954 // explicit cast from T to %?T8879 // explicit cast from T to %?T
7955 if (wanted_type->id == TypeTableEntryIdErrorUnion &&8880 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7956 wanted_type->data.error.child_type->id == TypeTableEntryIdMaybe &&8881 wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
7957 actual_type->id != TypeTableEntryIdMaybe)8882 actual_type->id != TypeTableEntryIdMaybe)
7958 {8883 {
7959 TypeTableEntry *wanted_child_type = wanted_type->data.error.child_type->data.maybe.child_type;8884 TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
7960 if (types_match_const_cast_only(wanted_child_type, actual_type) ||8885 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk ||
7961 actual_type->id == TypeTableEntryIdNullLit ||8886 actual_type->id == TypeTableEntryIdNullLit ||
7962 actual_type->id == TypeTableEntryIdNumLitInt ||8887 actual_type->id == TypeTableEntryIdNumLitInt ||
7963 actual_type->id == TypeTableEntryIdNumLitFloat)8888 actual_type->id == TypeTableEntryIdNumLitFloat)
7964 {8889 {
7965 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error.child_type, value);8890 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
7966 if (type_is_invalid(cast1->value.type))8891 if (type_is_invalid(cast1->value.type))
7967 return ira->codegen->invalid_instruction;8892 return ira->codegen->invalid_instruction;
79688893
...@@ -8031,21 +8956,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -8031,21 +8956,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
8031 return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);8956 return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
8032 }8957 }
80338958
8034 // explicit cast from %void to integer type which can fit it8959 // explicit cast from T!void to integer type which can fit it
8035 bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&8960 bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
8036 !type_has_bits(actual_type->data.error.child_type);8961 !type_has_bits(actual_type->data.error_union.payload_type);
8037 bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError;8962 bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet;
8038 if ((actual_type_is_void_err || actual_type_is_pure_err) &&8963 if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) {
8039 wanted_type->id == TypeTableEntryIdInt)
8040 {
8041 return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);8964 return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);
8042 }8965 }
80438966
8044 // explicit cast from integer to pure error8967 // explicit cast from integer to error set
8045 if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt &&8968 if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt &&
8046 !actual_type->data.integral.is_signed)8969 !actual_type->data.integral.is_signed)
8047 {8970 {
8048 return ir_analyze_int_to_err(ira, source_instr, value);8971 return ir_analyze_int_to_err(ira, source_instr, value, wanted_type);
8049 }8972 }
80508973
8051 // explicit cast from integer to enum type with no payload8974 // explicit cast from integer to enum type with no payload
...@@ -8109,7 +9032,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -8109,7 +9032,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
8109 // explicit cast from something to const pointer of it9032 // explicit cast from something to const pointer of it
8110 if (!type_requires_comptime(actual_type)) {9033 if (!type_requires_comptime(actual_type)) {
8111 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);9034 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
8112 if (types_match_const_cast_only(wanted_type, const_ptr_actual)) {9035 if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) {
8113 return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);9036 return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
8114 }9037 }
8115 }9038 }
...@@ -8471,6 +9394,7 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {...@@ -8471,6 +9394,7 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
8471static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {9394static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
8472 IrInstruction *op1 = bin_op_instruction->op1->other;9395 IrInstruction *op1 = bin_op_instruction->op1->other;
8473 IrInstruction *op2 = bin_op_instruction->op2->other;9396 IrInstruction *op2 = bin_op_instruction->op2->other;
9397 AstNode *source_node = bin_op_instruction->base.source_node;
84749398
8475 IrBinOp op_id = bin_op_instruction->op_id;9399 IrBinOp op_id = bin_op_instruction->op_id;
8476 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);9400 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
...@@ -8503,7 +9427,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -8503,7 +9427,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
8503 }9427 }
85049428
8505 IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,9429 IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
8506 bin_op_instruction->base.source_node, maybe_op);9430 source_node, maybe_op);
8507 is_non_null->value.type = ira->codegen->builtin_types.entry_bool;9431 is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
85089432
8509 if (op_id == IrBinOpCmpEq) {9433 if (op_id == IrBinOpCmpEq) {
...@@ -8514,8 +9438,88 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -8514,8 +9438,88 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
8514 return ira->codegen->builtin_types.entry_bool;9438 return ira->codegen->builtin_types.entry_bool;
8515 }9439 }
85169440
9441 if (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet) {
9442 if (!is_equality_cmp) {
9443 ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors"));
9444 return ira->codegen->builtin_types.entry_invalid;
9445 }
9446 TypeTableEntry *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node);
9447 if (type_is_invalid(intersect_type)) {
9448 return ira->codegen->builtin_types.entry_invalid;
9449 }
9450
9451 if (!resolve_inferred_error_set(ira, intersect_type, source_node)) {
9452 return ira->codegen->builtin_types.entry_invalid;
9453 }
9454
9455 // exception if one of the operators has the type of the empty error set, we allow the comparison
9456 // (and make it comptime known)
9457 // this is a function which is evaluated at comptime and returns an inferred error set will have an empty
9458 // error set.
9459 if (op1->value.type->data.error_set.err_count == 0 || op2->value.type->data.error_set.err_count == 0) {
9460 bool are_equal = false;
9461 bool answer;
9462 if (op_id == IrBinOpCmpEq) {
9463 answer = are_equal;
9464 } else if (op_id == IrBinOpCmpNotEq) {
9465 answer = !are_equal;
9466 } else {
9467 zig_unreachable();
9468 }
9469 ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
9470 out_val->data.x_bool = answer;
9471 return ira->codegen->builtin_types.entry_bool;
9472 }
9473
9474 if (!type_is_global_error_set(intersect_type)) {
9475 if (intersect_type->data.error_set.err_count == 0) {
9476 ir_add_error_node(ira, source_node,
9477 buf_sprintf("error sets '%s' and '%s' have no common errors",
9478 buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name)));
9479 return ira->codegen->builtin_types.entry_invalid;
9480 }
9481 if (op1->value.type->data.error_set.err_count == 1 && op2->value.type->data.error_set.err_count == 1) {
9482 bool are_equal = true;
9483 bool answer;
9484 if (op_id == IrBinOpCmpEq) {
9485 answer = are_equal;
9486 } else if (op_id == IrBinOpCmpNotEq) {
9487 answer = !are_equal;
9488 } else {
9489 zig_unreachable();
9490 }
9491 ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
9492 out_val->data.x_bool = answer;
9493 return ira->codegen->builtin_types.entry_bool;
9494 }
9495 }
9496
9497 ConstExprValue *op1_val = &op1->value;
9498 ConstExprValue *op2_val = &op2->value;
9499 if (value_is_comptime(op1_val) && value_is_comptime(op2_val)) {
9500 bool answer;
9501 bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value;
9502 if (op_id == IrBinOpCmpEq) {
9503 answer = are_equal;
9504 } else if (op_id == IrBinOpCmpNotEq) {
9505 answer = !are_equal;
9506 } else {
9507 zig_unreachable();
9508 }
9509
9510 ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
9511 out_val->data.x_bool = answer;
9512 return ira->codegen->builtin_types.entry_bool;
9513 }
9514
9515 ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id,
9516 op1, op2, bin_op_instruction->safety_check_on);
9517
9518 return ira->codegen->builtin_types.entry_bool;
9519 }
9520
8517 IrInstruction *instructions[] = {op1, op2};9521 IrInstruction *instructions[] = {op1, op2};
8518 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);9522 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2);
8519 if (type_is_invalid(resolved_type))9523 if (type_is_invalid(resolved_type))
8520 return resolved_type;9524 return resolved_type;
8521 type_ensure_zero_bits_known(ira->codegen, resolved_type);9525 type_ensure_zero_bits_known(ira->codegen, resolved_type);
...@@ -8523,7 +9527,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -8523,7 +9527,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
8523 return resolved_type;9527 return resolved_type;
85249528
85259529
8526 AstNode *source_node = bin_op_instruction->base.source_node;
8527 switch (resolved_type->id) {9530 switch (resolved_type->id) {
8528 case TypeTableEntryIdInvalid:9531 case TypeTableEntryIdInvalid:
8529 zig_unreachable(); // handled above9532 zig_unreachable(); // handled above
...@@ -8538,7 +9541,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -8538,7 +9541,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
8538 case TypeTableEntryIdMetaType:9541 case TypeTableEntryIdMetaType:
8539 case TypeTableEntryIdVoid:9542 case TypeTableEntryIdVoid:
8540 case TypeTableEntryIdPointer:9543 case TypeTableEntryIdPointer:
8541 case TypeTableEntryIdPureError:9544 case TypeTableEntryIdErrorSet:
8542 case TypeTableEntryIdFn:9545 case TypeTableEntryIdFn:
8543 case TypeTableEntryIdOpaque:9546 case TypeTableEntryIdOpaque:
8544 case TypeTableEntryIdNamespace:9547 case TypeTableEntryIdNamespace:
...@@ -8692,6 +9695,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,...@@ -8692,6 +9695,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,
8692 case IrBinOpArrayCat:9695 case IrBinOpArrayCat:
8693 case IrBinOpArrayMult:9696 case IrBinOpArrayMult:
8694 case IrBinOpRemUnspecified:9697 case IrBinOpRemUnspecified:
9698 case IrBinOpMergeErrorSets:
8695 zig_unreachable();9699 zig_unreachable();
8696 case IrBinOpBinOr:9700 case IrBinOpBinOr:
8697 assert(is_int);9701 assert(is_int);
...@@ -9264,6 +10268,46 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp...@@ -9264,6 +10268,46 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp
9264 return get_array_type(ira->codegen, child_type, new_array_len);10268 return get_array_type(ira->codegen, child_type, new_array_len);
9265}10269}
926610270
10271static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) {
10272 TypeTableEntry *op1_type = ir_resolve_type(ira, instruction->op1->other);
10273 if (type_is_invalid(op1_type))
10274 return ira->codegen->builtin_types.entry_invalid;
10275
10276 TypeTableEntry *op2_type = ir_resolve_type(ira, instruction->op2->other);
10277 if (type_is_invalid(op2_type))
10278 return ira->codegen->builtin_types.entry_invalid;
10279
10280 if (type_is_global_error_set(op1_type) ||
10281 type_is_global_error_set(op2_type))
10282 {
10283 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
10284 out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set;
10285 return ira->codegen->builtin_types.entry_type;
10286 }
10287
10288 if (!resolve_inferred_error_set(ira, op1_type, instruction->op1->other->source_node)) {
10289 return ira->codegen->builtin_types.entry_invalid;
10290 }
10291
10292 if (!resolve_inferred_error_set(ira, op2_type, instruction->op2->other->source_node)) {
10293 return ira->codegen->builtin_types.entry_invalid;
10294 }
10295
10296 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
10297 for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
10298 ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
10299 assert(errors[error_entry->value] == nullptr);
10300 errors[error_entry->value] = error_entry;
10301 }
10302 TypeTableEntry *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type);
10303 free(errors);
10304
10305
10306 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
10307 out_val->data.x_type = result_type;
10308 return ira->codegen->builtin_types.entry_type;
10309}
10310
9267static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {10311static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
9268 IrBinOp op_id = bin_op_instruction->op_id;10312 IrBinOp op_id = bin_op_instruction->op_id;
9269 switch (op_id) {10313 switch (op_id) {
...@@ -9305,6 +10349,8 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi...@@ -9305,6 +10349,8 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
9305 return ir_analyze_array_cat(ira, bin_op_instruction);10349 return ir_analyze_array_cat(ira, bin_op_instruction);
9306 case IrBinOpArrayMult:10350 case IrBinOpArrayMult:
9307 return ir_analyze_array_mult(ira, bin_op_instruction);10351 return ir_analyze_array_mult(ira, bin_op_instruction);
10352 case IrBinOpMergeErrorSets:
10353 return ir_analyze_merge_error_sets(ira, bin_op_instruction);
9308 }10354 }
9309 zig_unreachable();10355 zig_unreachable();
9310}10356}
...@@ -9326,7 +10372,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {...@@ -9326,7 +10372,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
9326 case TypeTableEntryIdInt:10372 case TypeTableEntryIdInt:
9327 case TypeTableEntryIdFloat:10373 case TypeTableEntryIdFloat:
9328 case TypeTableEntryIdVoid:10374 case TypeTableEntryIdVoid:
9329 case TypeTableEntryIdPureError:10375 case TypeTableEntryIdErrorSet:
9330 case TypeTableEntryIdFn:10376 case TypeTableEntryIdFn:
9331 return VarClassRequiredAny;10377 return VarClassRequiredAny;
9332 case TypeTableEntryIdNumLitFloat:10378 case TypeTableEntryIdNumLitFloat:
...@@ -9352,7 +10398,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {...@@ -9352,7 +10398,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
9352 case TypeTableEntryIdMaybe:10398 case TypeTableEntryIdMaybe:
9353 return get_var_class_required(type_entry->data.maybe.child_type);10399 return get_var_class_required(type_entry->data.maybe.child_type);
9354 case TypeTableEntryIdErrorUnion:10400 case TypeTableEntryIdErrorUnion:
9355 return get_var_class_required(type_entry->data.error.child_type);10401 return get_var_class_required(type_entry->data.error_union.payload_type);
935610402
9357 case TypeTableEntryIdStruct:10403 case TypeTableEntryIdStruct:
9358 case TypeTableEntryIdEnum:10404 case TypeTableEntryIdEnum:
...@@ -9587,7 +10633,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi...@@ -9587,7 +10633,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
9587 case TypeTableEntryIdNullLit:10633 case TypeTableEntryIdNullLit:
9588 case TypeTableEntryIdMaybe:10634 case TypeTableEntryIdMaybe:
9589 case TypeTableEntryIdErrorUnion:10635 case TypeTableEntryIdErrorUnion:
9590 case TypeTableEntryIdPureError:10636 case TypeTableEntryIdErrorSet:
9591 case TypeTableEntryIdNamespace:10637 case TypeTableEntryIdNamespace:
9592 case TypeTableEntryIdBlock:10638 case TypeTableEntryIdBlock:
9593 case TypeTableEntryIdBoundFn:10639 case TypeTableEntryIdBoundFn:
...@@ -9610,7 +10656,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi...@@ -9610,7 +10656,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
9610 case TypeTableEntryIdNullLit:10656 case TypeTableEntryIdNullLit:
9611 case TypeTableEntryIdMaybe:10657 case TypeTableEntryIdMaybe:
9612 case TypeTableEntryIdErrorUnion:10658 case TypeTableEntryIdErrorUnion:
9613 case TypeTableEntryIdPureError:10659 case TypeTableEntryIdErrorSet:
9614 zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));10660 zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
9615 case TypeTableEntryIdNamespace:10661 case TypeTableEntryIdNamespace:
9616 case TypeTableEntryIdBlock:10662 case TypeTableEntryIdBlock:
...@@ -9644,6 +10690,31 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,...@@ -9644,6 +10690,31 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
9644 return nullable_type;10690 return nullable_type;
9645}10691}
964610692
10693static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira,
10694 IrInstructionErrorUnion *instruction)
10695{
10696 TypeTableEntry *err_set_type = ir_resolve_type(ira, instruction->err_set->other);
10697 if (type_is_invalid(err_set_type))
10698 return ira->codegen->builtin_types.entry_invalid;
10699
10700 TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload->other);
10701 if (type_is_invalid(payload_type))
10702 return ira->codegen->builtin_types.entry_invalid;
10703
10704 if (err_set_type->id != TypeTableEntryIdErrorSet) {
10705 ir_add_error(ira, instruction->err_set->other,
10706 buf_sprintf("expected error set type, found type '%s'",
10707 buf_ptr(&err_set_type->name)));
10708 return ira->codegen->builtin_types.entry_invalid;
10709 }
10710
10711 TypeTableEntry *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
10712
10713 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
10714 out_val->data.x_type = result_type;
10715 return ira->codegen->builtin_types.entry_type;
10716}
10717
9647static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,10718static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
9648 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)10719 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
9649{10720{
...@@ -9926,9 +10997,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -9926,9 +10997,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
9926 }10997 }
992710998
9928 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;10999 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
9929 TypeTableEntry *return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);11000 TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);
9930 if (type_is_invalid(return_type))11001 if (type_is_invalid(specified_return_type))
9931 return ira->codegen->builtin_types.entry_invalid;11002 return ira->codegen->builtin_types.entry_invalid;
11003 TypeTableEntry *return_type;
11004 TypeTableEntry *inferred_err_set_type = nullptr;
11005 if (fn_proto_node->data.fn_proto.auto_err_set) {
11006 inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry);
11007 return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
11008 } else {
11009 return_type = specified_return_type;
11010 }
993211011
9933 IrInstruction *result;11012 IrInstruction *result;
993411013
...@@ -9942,6 +11021,23 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -9942,6 +11021,23 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
9942 ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,11021 ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
9943 nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);11022 nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
994411023
11024 if (inferred_err_set_type != nullptr) {
11025 inferred_err_set_type->data.error_set.infer_fn = nullptr;
11026 if (result->value.type->id == TypeTableEntryIdErrorUnion) {
11027 if (result->value.data.x_err_union.err != nullptr) {
11028 inferred_err_set_type->data.error_set.err_count = 1;
11029 inferred_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
11030 inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err;
11031 }
11032 TypeTableEntry *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type;
11033 inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count;
11034 inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors;
11035 } else if (result->value.type->id == TypeTableEntryIdErrorSet) {
11036 inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count;
11037 inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors;
11038 }
11039 }
11040
9945 ira->codegen->memoized_fn_eval_table.put(exec_scope, result);11041 ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
994611042
9947 if (type_is_invalid(result->value.type))11043 if (type_is_invalid(result->value.type))
...@@ -10092,12 +11188,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -10092,12 +11188,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1009211188
10093 {11189 {
10094 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;11190 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
10095 TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);11191 TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);
10096 if (type_is_invalid(return_type))11192 if (type_is_invalid(specified_return_type))
10097 return ira->codegen->builtin_types.entry_invalid;11193 return ira->codegen->builtin_types.entry_invalid;
10098 inst_fn_type_id.return_type = return_type;11194 if (fn_proto_node->data.fn_proto.auto_err_set) {
11195 TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn);
11196 inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
11197 } else {
11198 inst_fn_type_id.return_type = specified_return_type;
11199 }
1009911200
10100 if (type_requires_comptime(return_type)) {11201 if (type_requires_comptime(specified_return_type)) {
10101 // Throw out our work and call the function as if it were comptime.11202 // Throw out our work and call the function as if it were comptime.
10102 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);11203 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
10103 }11204 }
...@@ -10128,7 +11229,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -10128,7 +11229,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
10128 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;11229 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
10129 ir_add_alloca(ira, new_call_instruction, return_type);11230 ir_add_alloca(ira, new_call_instruction, return_type);
1013011231
10131 if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {11232 if (return_type->id == TypeTableEntryIdErrorSet || return_type->id == TypeTableEntryIdErrorUnion) {
10132 parent_fn_entry->calls_errorable_function = true;11233 parent_fn_entry->calls_errorable_function = true;
10133 }11234 }
1013411235
...@@ -10138,7 +11239,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -10138,7 +11239,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
10138 FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);11239 FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
10139 assert(fn_type_id->return_type != nullptr);11240 assert(fn_type_id->return_type != nullptr);
10140 assert(parent_fn_entry != nullptr);11241 assert(parent_fn_entry != nullptr);
10141 if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {11242 if (fn_type_id->return_type->id == TypeTableEntryIdErrorSet || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
10142 parent_fn_entry->calls_errorable_function = true;11243 parent_fn_entry->calls_errorable_function = true;
10143 }11244 }
1014411245
...@@ -10257,58 +11358,6 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction...@@ -10257,58 +11358,6 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
10257 }11358 }
10258}11359}
1025911360
10260static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
10261 assert(un_op_instruction->op_id == IrUnOpError);
10262 IrInstruction *value = un_op_instruction->value->other;
10263
10264 TypeTableEntry *meta_type = ir_resolve_type(ira, value);
10265 if (type_is_invalid(meta_type))
10266 return ira->codegen->builtin_types.entry_invalid;
10267
10268
10269 switch (meta_type->id) {
10270 case TypeTableEntryIdInvalid: // handled above
10271 zig_unreachable();
10272
10273 case TypeTableEntryIdVoid:
10274 case TypeTableEntryIdBool:
10275 case TypeTableEntryIdInt:
10276 case TypeTableEntryIdFloat:
10277 case TypeTableEntryIdPointer:
10278 case TypeTableEntryIdArray:
10279 case TypeTableEntryIdStruct:
10280 case TypeTableEntryIdMaybe:
10281 case TypeTableEntryIdErrorUnion:
10282 case TypeTableEntryIdPureError:
10283 case TypeTableEntryIdEnum:
10284 case TypeTableEntryIdUnion:
10285 case TypeTableEntryIdFn:
10286 case TypeTableEntryIdBoundFn:
10287 {
10288 ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base);
10289 TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type);
10290 out_val->data.x_type = result_type;
10291 return ira->codegen->builtin_types.entry_type;
10292 }
10293 case TypeTableEntryIdMetaType:
10294 case TypeTableEntryIdNumLitFloat:
10295 case TypeTableEntryIdNumLitInt:
10296 case TypeTableEntryIdUndefLit:
10297 case TypeTableEntryIdNullLit:
10298 case TypeTableEntryIdNamespace:
10299 case TypeTableEntryIdBlock:
10300 case TypeTableEntryIdUnreachable:
10301 case TypeTableEntryIdVar:
10302 case TypeTableEntryIdArgTuple:
10303 case TypeTableEntryIdOpaque:
10304 ir_add_error_node(ira, un_op_instruction->base.source_node,
10305 buf_sprintf("unable to wrap type '%s' in error type", buf_ptr(&meta_type->name)));
10306 return ira->codegen->builtin_types.entry_invalid;
10307 }
10308 zig_unreachable();
10309}
10310
10311
10312static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {11361static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
10313 IrInstruction *value = un_op_instruction->value->other;11362 IrInstruction *value = un_op_instruction->value->other;
1031411363
...@@ -10364,7 +11413,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op...@@ -10364,7 +11413,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
10364 case TypeTableEntryIdNullLit:11413 case TypeTableEntryIdNullLit:
10365 case TypeTableEntryIdMaybe:11414 case TypeTableEntryIdMaybe:
10366 case TypeTableEntryIdErrorUnion:11415 case TypeTableEntryIdErrorUnion:
10367 case TypeTableEntryIdPureError:11416 case TypeTableEntryIdErrorSet:
10368 case TypeTableEntryIdEnum:11417 case TypeTableEntryIdEnum:
10369 case TypeTableEntryIdUnion:11418 case TypeTableEntryIdUnion:
10370 case TypeTableEntryIdFn:11419 case TypeTableEntryIdFn:
...@@ -10474,8 +11523,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio...@@ -10474,8 +11523,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
10474 return ir_analyze_dereference(ira, un_op_instruction);11523 return ir_analyze_dereference(ira, un_op_instruction);
10475 case IrUnOpMaybe:11524 case IrUnOpMaybe:
10476 return ir_analyze_maybe(ira, un_op_instruction);11525 return ir_analyze_maybe(ira, un_op_instruction);
10477 case IrUnOpError:
10478 return ir_analyze_unary_prefix_op_err(ira, un_op_instruction);
10479 }11526 }
10480 zig_unreachable();11527 zig_unreachable();
10481}11528}
...@@ -10633,6 +11680,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP...@@ -10633,6 +11680,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
10633 IrInstruction *branch_instruction = predecessor->instruction_list.pop();11680 IrInstruction *branch_instruction = predecessor->instruction_list.pop();
10634 ir_set_cursor_at_end(&ira->new_irb, predecessor);11681 ir_set_cursor_at_end(&ira->new_irb, predecessor);
10635 IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type);11682 IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type);
11683 if (casted_value == ira->codegen->invalid_instruction) {
11684 return ira->codegen->builtin_types.entry_invalid;
11685 }
10636 new_incoming_values.items[i] = casted_value;11686 new_incoming_values.items[i] = casted_value;
10637 predecessor->instruction_list.append(branch_instruction);11687 predecessor->instruction_list.append(branch_instruction);
1063811688
...@@ -11048,6 +12098,25 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field...@@ -11048,6 +12098,25 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
11048 }12098 }
11049}12099}
1105012100
12101static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
12102 LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);
12103 for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
12104 Buf *existing_symbol_name = link_lib->symbols.at(i);
12105 if (buf_eql_buf(existing_symbol_name, symbol_name)) {
12106 return;
12107 }
12108 }
12109 for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) {
12110 Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i);
12111 if (buf_eql_buf(lib_name, forbidden_lib_name)) {
12112 ir_add_error_node(ira, source_node,
12113 buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name)));
12114 }
12115 }
12116 link_lib->symbols.append(symbol_name);
12117}
12118
12119
11051static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {12120static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {
11052 bool pointer_only = false;12121 bool pointer_only = false;
11053 resolve_top_level_decl(ira->codegen, tld, pointer_only, source_instruction->source_node);12122 resolve_top_level_decl(ira->codegen, tld, pointer_only, source_instruction->source_node);
...@@ -11063,7 +12132,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source...@@ -11063,7 +12132,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
11063 TldVar *tld_var = (TldVar *)tld;12132 TldVar *tld_var = (TldVar *)tld;
11064 VariableTableEntry *var = tld_var->var;12133 VariableTableEntry *var = tld_var->var;
11065 if (tld_var->extern_lib_name != nullptr) {12134 if (tld_var->extern_lib_name != nullptr) {
11066 add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name);12135 add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node);
11067 }12136 }
1106812137
11069 return ir_analyze_var_ptr(ira, source_instruction, var, false, false);12138 return ir_analyze_var_ptr(ira, source_instruction, var, false, false);
...@@ -11085,7 +12154,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source...@@ -11085,7 +12154,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
11085 const_val->data.x_fn.fn_entry = fn_entry;12154 const_val->data.x_fn.fn_entry = fn_entry;
1108612155
11087 if (tld_fn->extern_lib_name != nullptr) {12156 if (tld_fn->extern_lib_name != nullptr) {
11088 add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name);12157 add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node);
11089 }12158 }
1109012159
11091 bool ptr_is_const = true;12160 bool ptr_is_const = true;
...@@ -11097,6 +12166,17 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source...@@ -11097,6 +12166,17 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
11097 zig_unreachable();12166 zig_unreachable();
11098}12167}
1109912168
12169static ErrorTableEntry *find_err_table_entry(TypeTableEntry *err_set_type, Buf *field_name) {
12170 assert(err_set_type->id == TypeTableEntryIdErrorSet);
12171 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
12172 ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i];
12173 if (buf_eql_buf(&err_table_entry->name, field_name)) {
12174 return err_table_entry;
12175 }
12176 }
12177 return nullptr;
12178}
12179
11100static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {12180static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
11101 IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;12181 IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
11102 if (type_is_invalid(container_ptr->value.type))12182 if (type_is_invalid(container_ptr->value.type))
...@@ -11238,23 +12318,52 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -11238,23 +12318,52 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
11238 buf_sprintf("container '%s' has no member called '%s'",12318 buf_sprintf("container '%s' has no member called '%s'",
11239 buf_ptr(&child_type->name), buf_ptr(field_name)));12319 buf_ptr(&child_type->name), buf_ptr(field_name)));
11240 return ira->codegen->builtin_types.entry_invalid;12320 return ira->codegen->builtin_types.entry_invalid;
11241 } else if (child_type->id == TypeTableEntryIdPureError) {12321 } else if (child_type->id == TypeTableEntryIdErrorSet) {
11242 auto err_table_entry = ira->codegen->error_table.maybe_get(field_name);12322 ErrorTableEntry *err_entry;
11243 if (err_table_entry) {12323 TypeTableEntry *err_set_type;
11244 ConstExprValue *const_val = create_const_vals(1);12324 if (type_is_global_error_set(child_type)) {
11245 const_val->special = ConstValSpecialStatic;12325 auto existing_entry = ira->codegen->error_table.maybe_get(field_name);
11246 const_val->type = child_type;12326 if (existing_entry) {
11247 const_val->data.x_pure_err = err_table_entry->value;12327 err_entry = existing_entry->value;
1124812328 } else {
11249 bool ptr_is_const = true;12329 err_entry = allocate<ErrorTableEntry>(1);
11250 bool ptr_is_volatile = false;12330 err_entry->decl_node = field_ptr_instruction->base.source_node;
11251 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,12331 buf_init_from_buf(&err_entry->name, field_name);
11252 child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);12332 size_t error_value_count = ira->codegen->errors_by_index.length;
12333 assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
12334 err_entry->value = error_value_count;
12335 ira->codegen->errors_by_index.append(err_entry);
12336 ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder,
12337 buf_ptr(field_name), error_value_count));
12338 ira->codegen->error_table.put(field_name, err_entry);
12339 }
12340 if (err_entry->set_with_only_this_in_it == nullptr) {
12341 err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen,
12342 field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node,
12343 err_entry);
12344 }
12345 err_set_type = err_entry->set_with_only_this_in_it;
12346 } else {
12347 if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) {
12348 return ira->codegen->builtin_types.entry_invalid;
12349 }
12350 err_entry = find_err_table_entry(child_type, field_name);
12351 if (err_entry == nullptr) {
12352 ir_add_error(ira, &field_ptr_instruction->base,
12353 buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name)));
12354 return ira->codegen->builtin_types.entry_invalid;
12355 }
12356 err_set_type = child_type;
11253 }12357 }
12358 ConstExprValue *const_val = create_const_vals(1);
12359 const_val->special = ConstValSpecialStatic;
12360 const_val->type = err_set_type;
12361 const_val->data.x_err_set = err_entry;
1125412362
11255 ir_add_error(ira, &field_ptr_instruction->base,12363 bool ptr_is_const = true;
11256 buf_sprintf("use of undeclared error value '%s'", buf_ptr(field_name)));12364 bool ptr_is_volatile = false;
11257 return ira->codegen->builtin_types.entry_invalid;12365 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
12366 err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
11258 } else if (child_type->id == TypeTableEntryIdInt) {12367 } else if (child_type->id == TypeTableEntryIdInt) {
11259 if (buf_eql_str(field_name, "bit_count")) {12368 if (buf_eql_str(field_name, "bit_count")) {
11260 bool ptr_is_const = true;12369 bool ptr_is_const = true;
...@@ -11337,11 +12446,18 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -11337,11 +12446,18 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
11337 return ira->codegen->builtin_types.entry_invalid;12446 return ira->codegen->builtin_types.entry_invalid;
11338 }12447 }
11339 } else if (child_type->id == TypeTableEntryIdErrorUnion) {12448 } else if (child_type->id == TypeTableEntryIdErrorUnion) {
11340 if (buf_eql_str(field_name, "Child")) {12449 if (buf_eql_str(field_name, "Payload")) {
12450 bool ptr_is_const = true;
12451 bool ptr_is_volatile = false;
12452 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
12453 create_const_type(ira->codegen, child_type->data.error_union.payload_type),
12454 ira->codegen->builtin_types.entry_type,
12455 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
12456 } else if (buf_eql_str(field_name, "ErrorSet")) {
11341 bool ptr_is_const = true;12457 bool ptr_is_const = true;
11342 bool ptr_is_volatile = false;12458 bool ptr_is_volatile = false;
11343 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,12459 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
11344 create_const_type(ira->codegen, child_type->data.error.child_type),12460 create_const_type(ira->codegen, child_type->data.error_union.err_set_type),
11345 ira->codegen->builtin_types.entry_type,12461 ira->codegen->builtin_types.entry_type,
11346 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);12462 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
11347 } else {12463 } else {
...@@ -11528,7 +12644,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi...@@ -11528,7 +12644,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
11528 case TypeTableEntryIdStruct:12644 case TypeTableEntryIdStruct:
11529 case TypeTableEntryIdMaybe:12645 case TypeTableEntryIdMaybe:
11530 case TypeTableEntryIdErrorUnion:12646 case TypeTableEntryIdErrorUnion:
11531 case TypeTableEntryIdPureError:12647 case TypeTableEntryIdErrorSet:
11532 case TypeTableEntryIdEnum:12648 case TypeTableEntryIdEnum:
11533 case TypeTableEntryIdUnion:12649 case TypeTableEntryIdUnion:
11534 case TypeTableEntryIdFn:12650 case TypeTableEntryIdFn:
...@@ -11795,7 +12911,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,...@@ -11795,7 +12911,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
11795 case TypeTableEntryIdNumLitInt:12911 case TypeTableEntryIdNumLitInt:
11796 case TypeTableEntryIdMaybe:12912 case TypeTableEntryIdMaybe:
11797 case TypeTableEntryIdErrorUnion:12913 case TypeTableEntryIdErrorUnion:
11798 case TypeTableEntryIdPureError:12914 case TypeTableEntryIdErrorSet:
11799 case TypeTableEntryIdEnum:12915 case TypeTableEntryIdEnum:
11800 case TypeTableEntryIdUnion:12916 case TypeTableEntryIdUnion:
11801 case TypeTableEntryIdFn:12917 case TypeTableEntryIdFn:
...@@ -11903,7 +13019,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,...@@ -11903,7 +13019,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
11903 case TypeTableEntryIdNumLitInt:13019 case TypeTableEntryIdNumLitInt:
11904 case TypeTableEntryIdMaybe:13020 case TypeTableEntryIdMaybe:
11905 case TypeTableEntryIdErrorUnion:13021 case TypeTableEntryIdErrorUnion:
11906 case TypeTableEntryIdPureError:13022 case TypeTableEntryIdErrorSet:
11907 case TypeTableEntryIdEnum:13023 case TypeTableEntryIdEnum:
11908 case TypeTableEntryIdUnion:13024 case TypeTableEntryIdUnion:
11909 case TypeTableEntryIdFn:13025 case TypeTableEntryIdFn:
...@@ -11956,7 +13072,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,...@@ -11956,7 +13072,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
11956 case TypeTableEntryIdStruct:13072 case TypeTableEntryIdStruct:
11957 case TypeTableEntryIdMaybe:13073 case TypeTableEntryIdMaybe:
11958 case TypeTableEntryIdErrorUnion:13074 case TypeTableEntryIdErrorUnion:
11959 case TypeTableEntryIdPureError:13075 case TypeTableEntryIdErrorSet:
11960 case TypeTableEntryIdEnum:13076 case TypeTableEntryIdEnum:
11961 case TypeTableEntryIdUnion:13077 case TypeTableEntryIdUnion:
11962 case TypeTableEntryIdFn:13078 case TypeTableEntryIdFn:
...@@ -12291,7 +13407,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -12291,7 +13407,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
12291 case TypeTableEntryIdPointer:13407 case TypeTableEntryIdPointer:
12292 case TypeTableEntryIdFn:13408 case TypeTableEntryIdFn:
12293 case TypeTableEntryIdNamespace:13409 case TypeTableEntryIdNamespace:
12294 case TypeTableEntryIdPureError:13410 case TypeTableEntryIdErrorSet:
12295 if (pointee_val) {13411 if (pointee_val) {
12296 ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);13412 ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
12297 copy_const_val(out_val, pointee_val, true);13413 copy_const_val(out_val, pointee_val, true);
...@@ -12361,8 +13477,6 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -12361,8 +13477,6 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
12361 return target_type;13477 return target_type;
12362 }13478 }
12363 case TypeTableEntryIdErrorUnion:13479 case TypeTableEntryIdErrorUnion:
12364 // see https://github.com/andrewrk/zig/issues/632
12365 zig_panic("TODO switch on error union");
12366 case TypeTableEntryIdUnreachable:13480 case TypeTableEntryIdUnreachable:
12367 case TypeTableEntryIdArray:13481 case TypeTableEntryIdArray:
12368 case TypeTableEntryIdStruct:13482 case TypeTableEntryIdStruct:
...@@ -12887,7 +14001,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_...@@ -12887,7 +14001,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
12887 case TypeTableEntryIdNullLit:14001 case TypeTableEntryIdNullLit:
12888 case TypeTableEntryIdMaybe:14002 case TypeTableEntryIdMaybe:
12889 case TypeTableEntryIdErrorUnion:14003 case TypeTableEntryIdErrorUnion:
12890 case TypeTableEntryIdPureError:14004 case TypeTableEntryIdErrorSet:
12891 case TypeTableEntryIdUnion:14005 case TypeTableEntryIdUnion:
12892 case TypeTableEntryIdFn:14006 case TypeTableEntryIdFn:
12893 case TypeTableEntryIdNamespace:14007 case TypeTableEntryIdNamespace:
...@@ -12975,7 +14089,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc...@@ -12975,7 +14089,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
12975 TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);14089 TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
12976 TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type);14090 TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type);
12977 if (casted_value->value.special == ConstValSpecialStatic) {14091 if (casted_value->value.special == ConstValSpecialStatic) {
12978 ErrorTableEntry *err = casted_value->value.data.x_pure_err;14092 ErrorTableEntry *err = casted_value->value.data.x_err_set;
12979 if (!err->cached_error_name_val) {14093 if (!err->cached_error_name_val) {
12980 ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name);14094 ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name);
12981 err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true);14095 err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true);
...@@ -13956,6 +15070,15 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns...@@ -13956,6 +15070,15 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
13956 result = container_type->data.structure.src_field_count;15070 result = container_type->data.structure.src_field_count;
13957 } else if (container_type->id == TypeTableEntryIdUnion) {15071 } else if (container_type->id == TypeTableEntryIdUnion) {
13958 result = container_type->data.unionation.src_field_count;15072 result = container_type->data.unionation.src_field_count;
15073 } else if (container_type->id == TypeTableEntryIdErrorSet) {
15074 if (!resolve_inferred_error_set(ira, container_type, instruction->base.source_node)) {
15075 return ira->codegen->builtin_types.entry_invalid;
15076 }
15077 if (type_is_global_error_set(container_type)) {
15078 ir_add_error(ira, &instruction->base, buf_sprintf("global error set member count not available at comptime"));
15079 return ira->codegen->builtin_types.entry_invalid;
15080 }
15081 result = container_type->data.error_set.err_count;
13959 } else {15082 } else {
13960 ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));15083 ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));
13961 return ira->codegen->builtin_types.entry_invalid;15084 return ira->codegen->builtin_types.entry_invalid;
...@@ -14120,7 +15243,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc...@@ -14120,7 +15243,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc
14120 case TypeTableEntryIdStruct:15243 case TypeTableEntryIdStruct:
14121 case TypeTableEntryIdMaybe:15244 case TypeTableEntryIdMaybe:
14122 case TypeTableEntryIdErrorUnion:15245 case TypeTableEntryIdErrorUnion:
14123 case TypeTableEntryIdPureError:15246 case TypeTableEntryIdErrorSet:
14124 case TypeTableEntryIdEnum:15247 case TypeTableEntryIdEnum:
14125 case TypeTableEntryIdUnion:15248 case TypeTableEntryIdUnion:
14126 case TypeTableEntryIdFn:15249 case TypeTableEntryIdFn:
...@@ -14251,9 +15374,22 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc...@@ -14251,9 +15374,22 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc
14251 }15374 }
14252 }15375 }
1425315376
15377 TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
15378 if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) {
15379 return ira->codegen->builtin_types.entry_invalid;
15380 }
15381 if (!type_is_global_error_set(err_set_type) &&
15382 err_set_type->data.error_set.err_count == 0)
15383 {
15384 assert(err_set_type->data.error_set.infer_fn == nullptr);
15385 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
15386 out_val->data.x_bool = false;
15387 return ira->codegen->builtin_types.entry_bool;
15388 }
15389
14254 ir_build_test_err_from(&ira->new_irb, &instruction->base, value);15390 ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
14255 return ira->codegen->builtin_types.entry_bool;15391 return ira->codegen->builtin_types.entry_bool;
14256 } else if (type_entry->id == TypeTableEntryIdPureError) {15392 } else if (type_entry->id == TypeTableEntryIdErrorSet) {
14257 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);15393 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
14258 out_val->data.x_bool = true;15394 out_val->data.x_bool = true;
14259 return ira->codegen->builtin_types.entry_bool;15395 return ira->codegen->builtin_types.entry_bool;
...@@ -14289,13 +15425,13 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,...@@ -14289,13 +15425,13 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
14289 assert(err);15425 assert(err);
1429015426
14291 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);15427 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
14292 out_val->data.x_pure_err = err;15428 out_val->data.x_err_set = err;
14293 return ira->codegen->builtin_types.entry_pure_error;15429 return type_entry->data.error_union.err_set_type;
14294 }15430 }
14295 }15431 }
1429615432
14297 ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value);15433 ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value);
14298 return ira->codegen->builtin_types.entry_pure_error;15434 return type_entry->data.error_union.err_set_type;
14299 } else {15435 } else {
14300 ir_add_error(ira, value,15436 ir_add_error(ira, value,
14301 buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));15437 buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));
...@@ -14319,10 +15455,10 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,...@@ -14319,10 +15455,10 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
14319 if (type_is_invalid(type_entry)) {15455 if (type_is_invalid(type_entry)) {
14320 return ira->codegen->builtin_types.entry_invalid;15456 return ira->codegen->builtin_types.entry_invalid;
14321 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {15457 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
14322 TypeTableEntry *child_type = type_entry->data.error.child_type;15458 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
14323 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type,15459 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
14324 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,15460 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
14325 get_abi_alignment(ira->codegen, child_type), 0, 0);15461 get_abi_alignment(ira->codegen, payload_type), 0, 0);
14326 if (instr_is_comptime(value)) {15462 if (instr_is_comptime(value)) {
14327 ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);15463 ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
14328 if (!ptr_val)15464 if (!ptr_val)
...@@ -14332,7 +15468,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,...@@ -14332,7 +15468,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
14332 ErrorTableEntry *err = err_union_val->data.x_err_union.err;15468 ErrorTableEntry *err = err_union_val->data.x_err_union.err;
14333 if (err != nullptr) {15469 if (err != nullptr) {
14334 ir_add_error(ira, &instruction->base,15470 ir_add_error(ira, &instruction->base,
14335 buf_sprintf("unable to unwrap error '%s'", buf_ptr(&err->name)));15471 buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
14336 return ira->codegen->builtin_types.entry_invalid;15472 return ira->codegen->builtin_types.entry_invalid;
14337 }15473 }
1433815474
...@@ -14357,6 +15493,12 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc...@@ -14357,6 +15493,12 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
14357 AstNode *proto_node = instruction->base.source_node;15493 AstNode *proto_node = instruction->base.source_node;
14358 assert(proto_node->type == NodeTypeFnProto);15494 assert(proto_node->type == NodeTypeFnProto);
1435915495
15496 if (proto_node->data.fn_proto.auto_err_set) {
15497 ir_add_error(ira, &instruction->base,
15498 buf_sprintf("inferring error set of return type valid only for function definitions"));
15499 return ira->codegen->builtin_types.entry_invalid;
15500 }
15501
14360 FnTypeId fn_type_id = {0};15502 FnTypeId fn_type_id = {0};
14361 init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);15503 init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
1436215504
...@@ -14482,6 +15624,63 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira...@@ -14482,6 +15624,63 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
14482 }15624 }
14483 }15625 }
14484 }15626 }
15627 } else if (switch_type->id == TypeTableEntryIdErrorSet) {
15628 if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) {
15629 return ira->codegen->builtin_types.entry_invalid;
15630 }
15631
15632 AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length);
15633
15634 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
15635 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
15636
15637 IrInstruction *start_value = range->start->other;
15638 if (type_is_invalid(start_value->value.type))
15639 return ira->codegen->builtin_types.entry_invalid;
15640
15641 IrInstruction *end_value = range->end->other;
15642 if (type_is_invalid(end_value->value.type))
15643 return ira->codegen->builtin_types.entry_invalid;
15644
15645 assert(start_value->value.type->id == TypeTableEntryIdErrorSet);
15646 uint32_t start_index = start_value->value.data.x_err_set->value;
15647
15648 assert(end_value->value.type->id == TypeTableEntryIdErrorSet);
15649 uint32_t end_index = end_value->value.data.x_err_set->value;
15650
15651 if (start_index != end_index) {
15652 ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors"));
15653 return ira->codegen->builtin_types.entry_invalid;
15654 }
15655
15656 AstNode *prev_node = field_prev_uses[start_index];
15657 if (prev_node != nullptr) {
15658 Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name;
15659 ErrorMsg *msg = ir_add_error(ira, start_value,
15660 buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name)));
15661 add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here"));
15662 }
15663 field_prev_uses[start_index] = start_value->source_node;
15664 }
15665 if (!instruction->have_else_prong) {
15666 if (type_is_global_error_set(switch_type)) {
15667 ir_add_error(ira, &instruction->base,
15668 buf_sprintf("else prong required when switching on type 'error'"));
15669 return ira->codegen->builtin_types.entry_invalid;
15670 } else {
15671 for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
15672 ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
15673
15674 AstNode *prev_node = field_prev_uses[err_entry->value];
15675 if (prev_node == nullptr) {
15676 ir_add_error(ira, &instruction->base,
15677 buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
15678 }
15679 }
15680 }
15681 }
15682
15683 free(field_prev_uses);
14485 } else if (switch_type->id == TypeTableEntryIdInt) {15684 } else if (switch_type->id == TypeTableEntryIdInt) {
14486 RangeSet rs = {0};15685 RangeSet rs = {0};
14487 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {15686 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
...@@ -14774,7 +15973,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue...@@ -14774,7 +15973,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
14774 zig_panic("TODO buf_write_value_bytes maybe type");15973 zig_panic("TODO buf_write_value_bytes maybe type");
14775 case TypeTableEntryIdErrorUnion:15974 case TypeTableEntryIdErrorUnion:
14776 zig_panic("TODO buf_write_value_bytes error union");15975 zig_panic("TODO buf_write_value_bytes error union");
14777 case TypeTableEntryIdPureError:15976 case TypeTableEntryIdErrorSet:
14778 zig_panic("TODO buf_write_value_bytes pure error type");15977 zig_panic("TODO buf_write_value_bytes pure error type");
14779 case TypeTableEntryIdEnum:15978 case TypeTableEntryIdEnum:
14780 zig_panic("TODO buf_write_value_bytes enum type");15979 zig_panic("TODO buf_write_value_bytes enum type");
...@@ -14832,7 +16031,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue...@@ -14832,7 +16031,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
14832 zig_panic("TODO buf_read_value_bytes maybe type");16031 zig_panic("TODO buf_read_value_bytes maybe type");
14833 case TypeTableEntryIdErrorUnion:16032 case TypeTableEntryIdErrorUnion:
14834 zig_panic("TODO buf_read_value_bytes error union");16033 zig_panic("TODO buf_read_value_bytes error union");
14835 case TypeTableEntryIdPureError:16034 case TypeTableEntryIdErrorSet:
14836 zig_panic("TODO buf_read_value_bytes pure error type");16035 zig_panic("TODO buf_read_value_bytes pure error type");
14837 case TypeTableEntryIdEnum:16036 case TypeTableEntryIdEnum:
14838 zig_panic("TODO buf_read_value_bytes enum type");16037 zig_panic("TODO buf_read_value_bytes enum type");
...@@ -15010,7 +16209,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,...@@ -15010,7 +16209,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
15010 return ira->codegen->builtin_types.entry_invalid;16209 return ira->codegen->builtin_types.entry_invalid;
1501116210
15012 if (tld_var->extern_lib_name != nullptr) {16211 if (tld_var->extern_lib_name != nullptr) {
15013 add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name);16212 add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);
15014 }16213 }
1501516214
15016 if (lval.is_ptr) {16215 if (lval.is_ptr) {
...@@ -15029,7 +16228,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,...@@ -15029,7 +16228,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
15029 assert(fn_entry->type_entry);16228 assert(fn_entry->type_entry);
1503016229
15031 if (tld_fn->extern_lib_name != nullptr) {16230 if (tld_fn->extern_lib_name != nullptr) {
15032 add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name);16231 add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node);
15033 }16232 }
1503416233
15035 IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,16234 IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,
...@@ -15443,6 +16642,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -15443,6 +16642,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
15443 return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);16642 return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
15444 case IrInstructionIdErrorReturnTrace:16643 case IrInstructionIdErrorReturnTrace:
15445 return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);16644 return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
16645 case IrInstructionIdErrorUnion:
16646 return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction);
15446 }16647 }
15447 zig_unreachable();16648 zig_unreachable();
15448}16649}
...@@ -15628,6 +16829,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -15628,6 +16829,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
15628 case IrInstructionIdArgType:16829 case IrInstructionIdArgType:
15629 case IrInstructionIdTagType:16830 case IrInstructionIdTagType:
15630 case IrInstructionIdErrorReturnTrace:16831 case IrInstructionIdErrorReturnTrace:
16832 case IrInstructionIdErrorUnion:
15631 return false;16833 return false;
15632 case IrInstructionIdAsm:16834 case IrInstructionIdAsm:
15633 {16835 {
src/ir_print.cpp+10-2
...@@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {...@@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
130 return "++";130 return "++";
131 case IrBinOpArrayMult:131 case IrBinOpArrayMult:
132 return "**";132 return "**";
133 case IrBinOpMergeErrorSets:
134 return "||";
133 }135 }
134 zig_unreachable();136 zig_unreachable();
135}137}
...@@ -148,8 +150,6 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {...@@ -148,8 +150,6 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
148 return "*";150 return "*";
149 case IrUnOpMaybe:151 case IrUnOpMaybe:
150 return "?";152 return "?";
151 case IrUnOpError:
152 return "%";
153 }153 }
154 zig_unreachable();154 zig_unreachable();
155}155}
...@@ -1004,6 +1004,11 @@ static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTr...@@ -1004,6 +1004,11 @@ static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTr
1004 fprintf(irp->f, "@errorReturnTrace()");1004 fprintf(irp->f, "@errorReturnTrace()");
1005}1005}
10061006
1007static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) {
1008 ir_print_other_instruction(irp, instruction->err_set);
1009 fprintf(irp->f, "!");
1010 ir_print_other_instruction(irp, instruction->payload);
1011}
10071012
1008static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1013static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1009 ir_print_prefix(irp, instruction);1014 ir_print_prefix(irp, instruction);
...@@ -1322,6 +1327,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1322,6 +1327,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1322 case IrInstructionIdErrorReturnTrace:1327 case IrInstructionIdErrorReturnTrace:
1323 ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);1328 ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
1324 break;1329 break;
1330 case IrInstructionIdErrorUnion:
1331 ir_print_error_union(irp, (IrInstructionErrorUnion *)instruction);
1332 break;
1325 }1333 }
1326 fprintf(irp->f, "\n");1334 fprintf(irp->f, "\n");
1327}1335}
src/main.cpp+31-10
...@@ -66,6 +66,7 @@ static int usage(const char *arg0) {...@@ -66,6 +66,7 @@ static int usage(const char *arg0) {
66 " --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n"66 " --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n"
67 " --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n"67 " --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n"
68 " --library [lib] link against lib\n"68 " --library [lib] link against lib\n"
69 " --forbid-library [lib] make it an error to link against lib\n"
69 " --library-path [dir] add a directory to the library search path\n"70 " --library-path [dir] add a directory to the library search path\n"
70 " --linker-script [path] use a custom linker script\n"71 " --linker-script [path] use a custom linker script\n"
71 " --object [obj] add object file to build\n"72 " --object [obj] add object file to build\n"
...@@ -309,6 +310,7 @@ int main(int argc, char **argv) {...@@ -309,6 +310,7 @@ int main(int argc, char **argv) {
309 ZigList<const char *> llvm_argv = {0};310 ZigList<const char *> llvm_argv = {0};
310 ZigList<const char *> lib_dirs = {0};311 ZigList<const char *> lib_dirs = {0};
311 ZigList<const char *> link_libs = {0};312 ZigList<const char *> link_libs = {0};
313 ZigList<const char *> forbidden_link_libs = {0};
312 ZigList<const char *> frameworks = {0};314 ZigList<const char *> frameworks = {0};
313 int err;315 int err;
314 const char *target_arch = nullptr;316 const char *target_arch = nullptr;
...@@ -339,6 +341,7 @@ int main(int argc, char **argv) {...@@ -339,6 +341,7 @@ int main(int argc, char **argv) {
339 const char *zig_exe_path = arg0;341 const char *zig_exe_path = arg0;
340 const char *build_file = "build.zig";342 const char *build_file = "build.zig";
341 bool asked_for_help = false;343 bool asked_for_help = false;
344 bool asked_to_init = false;
342345
343 init_all_targets();346 init_all_targets();
344347
...@@ -350,6 +353,9 @@ int main(int argc, char **argv) {...@@ -350,6 +353,9 @@ int main(int argc, char **argv) {
350 if (strcmp(argv[i], "--help") == 0) {353 if (strcmp(argv[i], "--help") == 0) {
351 asked_for_help = true;354 asked_for_help = true;
352 args.append(argv[i]);355 args.append(argv[i]);
356 } else if (strcmp(argv[i], "--init") == 0) {
357 asked_to_init = true;
358 args.append(argv[i]);
353 } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) {359 } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) {
354 build_file = argv[i + 1];360 build_file = argv[i + 1];
355 i += 1;361 i += 1;
...@@ -414,6 +420,7 @@ int main(int argc, char **argv) {...@@ -414,6 +420,7 @@ int main(int argc, char **argv) {
414 "\n"420 "\n"
415 "General Options:\n"421 "General Options:\n"
416 " --help Print this help and exit\n"422 " --help Print this help and exit\n"
423 " --init Generate a build.zig template\n"
417 " --build-file [file] Override path to build.zig\n"424 " --build-file [file] Override path to build.zig\n"
418 " --cache-dir [path] Override path to cache directory\n"425 " --cache-dir [path] Override path to cache directory\n"
419 " --verbose Print commands before executing them\n"426 " --verbose Print commands before executing them\n"
...@@ -426,7 +433,6 @@ int main(int argc, char **argv) {...@@ -426,7 +433,6 @@ int main(int argc, char **argv) {
426 " --prefix [path] Override default install prefix\n"433 " --prefix [path] Override default install prefix\n"
427 "\n"434 "\n"
428 "Project-specific options become available when the build file is found.\n"435 "Project-specific options become available when the build file is found.\n"
429 "Run this command with no options to generate a build.zig template.\n"
430 "\n"436 "\n"
431 "Advanced Options:\n"437 "Advanced Options:\n"
432 " --build-file [file] Override path to build.zig\n"438 " --build-file [file] Override path to build.zig\n"
...@@ -439,17 +445,26 @@ int main(int argc, char **argv) {...@@ -439,17 +445,26 @@ int main(int argc, char **argv) {
439 " --verbose-cimport Enable compiler debug output for C imports\n"445 " --verbose-cimport Enable compiler debug output for C imports\n"
440 "\n"446 "\n"
441 , zig_exe_path);447 , zig_exe_path);
442 return 0;448 return EXIT_SUCCESS;
443 }449 } else if (asked_to_init) {
444 Buf *build_template_path = buf_alloc();450 Buf *build_template_path = buf_alloc();
445 os_path_join(special_dir, buf_create_from_str("build_file_template.zig"), build_template_path);451 os_path_join(special_dir, buf_create_from_str("build_file_template.zig"), build_template_path);
446452
447 if ((err = os_copy_file(build_template_path, &build_file_abs))) {453 if ((err = os_copy_file(build_template_path, &build_file_abs))) {
448 fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));454 fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
449 } else {455 } else {
450 fprintf(stderr, "Wrote build.zig template\n");456 fprintf(stderr, "Wrote build.zig template\n");
457 }
458 return EXIT_SUCCESS;
451 }459 }
452 return 1;460
461 fprintf(stderr,
462 "No 'build.zig' file found.\n"
463 "Initialize a 'build.zig' template file with `zig build --init`,\n"
464 "or build an executable directly with `zig build-exe $FILENAME.zig`.\n"
465 "See: `zig build --help` or `zig help` for more options.\n"
466 );
467 return EXIT_FAILURE;
453 }468 }
454469
455 PackageTableEntry *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname),470 PackageTableEntry *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname),
...@@ -592,6 +607,8 @@ int main(int argc, char **argv) {...@@ -592,6 +607,8 @@ int main(int argc, char **argv) {
592 lib_dirs.append(argv[i]);607 lib_dirs.append(argv[i]);
593 } else if (strcmp(arg, "--library") == 0) {608 } else if (strcmp(arg, "--library") == 0) {
594 link_libs.append(argv[i]);609 link_libs.append(argv[i]);
610 } else if (strcmp(arg, "--forbid-library") == 0) {
611 forbidden_link_libs.append(argv[i]);
595 } else if (strcmp(arg, "--object") == 0) {612 } else if (strcmp(arg, "--object") == 0) {
596 objects.append(argv[i]);613 objects.append(argv[i]);
597 } else if (strcmp(arg, "--assembly") == 0) {614 } else if (strcmp(arg, "--assembly") == 0) {
...@@ -804,6 +821,10 @@ int main(int argc, char **argv) {...@@ -804,6 +821,10 @@ int main(int argc, char **argv) {
804 LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));821 LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));
805 link_lib->provided_explicitly = true;822 link_lib->provided_explicitly = true;
806 }823 }
824 for (size_t i = 0; i < forbidden_link_libs.length; i += 1) {
825 Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i));
826 codegen_add_forbidden_lib(g, forbidden_link_lib);
827 }
807 for (size_t i = 0; i < frameworks.length; i += 1) {828 for (size_t i = 0; i < frameworks.length; i += 1) {
808 codegen_add_framework(g, frameworks.at(i));829 codegen_add_framework(g, frameworks.at(i));
809 }830 }
src/parser.cpp+74-42
...@@ -221,6 +221,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bo...@@ -221,6 +221,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bo
221static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory);221static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory);
222static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory);222static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory);
223static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index);223static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index);
224static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index);
224225
225static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {226static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
226 if (token->id == token_id) {227 if (token->id == token_id) {
...@@ -240,7 +241,28 @@ static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token...@@ -240,7 +241,28 @@ static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token
240}241}
241242
242/*243/*
243TypeExpr = PrefixOpExpression | "var"244ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
245*/
246static AstNode *ast_parse_error_set_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
247 AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory);
248 if (!prefix_op_expr) {
249 return nullptr;
250 }
251 Token *token = &pc->tokens->at(*token_index);
252 if (token->id == TokenIdBang) {
253 *token_index += 1;
254 AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
255 node->data.bin_op_expr.op1 = prefix_op_expr;
256 node->data.bin_op_expr.bin_op = BinOpTypeErrorUnion;
257 node->data.bin_op_expr.op2 = ast_parse_prefix_op_expr(pc, token_index, true);
258 return node;
259 } else {
260 return prefix_op_expr;
261 }
262}
263
264/*
265TypeExpr = ErrorSetExpr | "var"
244*/266*/
245static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) {267static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
246 Token *token = &pc->tokens->at(*token_index);268 Token *token = &pc->tokens->at(*token_index);
...@@ -249,7 +271,7 @@ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool...@@ -249,7 +271,7 @@ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool
249 *token_index += 1;271 *token_index += 1;
250 return node;272 return node;
251 } else {273 } else {
252 return ast_parse_prefix_op_expr(pc, token_index, mandatory);274 return ast_parse_error_set_expr(pc, token_index, mandatory);
253 }275 }
254}276}
255277
...@@ -651,8 +673,9 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b...@@ -651,8 +673,9 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
651}673}
652674
653/*675/*
654PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))676PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
655KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"677KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
678ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
656*/679*/
657static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {680static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
658 Token *token = &pc->tokens->at(*token_index);681 Token *token = &pc->tokens->at(*token_index);
...@@ -716,9 +739,31 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo...@@ -716,9 +739,31 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
716 *token_index += 1;739 *token_index += 1;
717 return node;740 return node;
718 } else if (token->id == TokenIdKeywordError) {741 } else if (token->id == TokenIdKeywordError) {
719 AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);742 Token *next_token = &pc->tokens->at(*token_index + 1);
720 *token_index += 1;743 if (next_token->id == TokenIdLBrace) {
721 return node;744 AstNode *node = ast_create_node(pc, NodeTypeErrorSetDecl, token);
745 *token_index += 2;
746 for (;;) {
747 Token *item_tok = &pc->tokens->at(*token_index);
748 if (item_tok->id == TokenIdRBrace) {
749 *token_index += 1;
750 return node;
751 } else if (item_tok->id == TokenIdSymbol) {
752 AstNode *symbol_node = ast_parse_symbol(pc, token_index);
753 node->data.err_set_decl.decls.append(symbol_node);
754 Token *opt_comma_tok = &pc->tokens->at(*token_index);
755 if (opt_comma_tok->id == TokenIdComma) {
756 *token_index += 1;
757 }
758 } else {
759 ast_invalid_token_error(pc, item_tok);
760 }
761 }
762 } else {
763 AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);
764 *token_index += 1;
765 return node;
766 }
722 } else if (token->id == TokenIdAtSign) {767 } else if (token->id == TokenIdAtSign) {
723 *token_index += 1;768 *token_index += 1;
724 Token *name_tok = &pc->tokens->at(*token_index);769 Token *name_tok = &pc->tokens->at(*token_index);
...@@ -950,7 +995,6 @@ static PrefixOp tok_to_prefix_op(Token *token) {...@@ -950,7 +995,6 @@ static PrefixOp tok_to_prefix_op(Token *token) {
950 case TokenIdTilde: return PrefixOpBinNot;995 case TokenIdTilde: return PrefixOpBinNot;
951 case TokenIdStar: return PrefixOpDereference;996 case TokenIdStar: return PrefixOpDereference;
952 case TokenIdMaybe: return PrefixOpMaybe;997 case TokenIdMaybe: return PrefixOpMaybe;
953 case TokenIdPercent: return PrefixOpError;
954 case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;998 case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
955 case TokenIdStarStar: return PrefixOpDereference;999 case TokenIdStarStar: return PrefixOpDereference;
956 default: return PrefixOpInvalid;1000 default: return PrefixOpInvalid;
...@@ -997,8 +1041,8 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {...@@ -997,8 +1041,8 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {
997}1041}
9981042
999/*1043/*
1000PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression1044PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
1001PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" | "try"1045PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
1002*/1046*/
1003static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {1047static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
1004 Token *token = &pc->tokens->at(*token_index);1048 Token *token = &pc->tokens->at(*token_index);
...@@ -1028,7 +1072,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index,...@@ -1028,7 +1072,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index,
1028 node->column += 1;1072 node->column += 1;
1029 }1073 }
10301074
1031 AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, true);1075 AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true);
1032 node->data.prefix_op_expr.primary_expr = prefix_op_expr;1076 node->data.prefix_op_expr.primary_expr = prefix_op_expr;
1033 node->data.prefix_op_expr.prefix_op = prefix_op;1077 node->data.prefix_op_expr.prefix_op = prefix_op;
10341078
...@@ -1043,12 +1087,14 @@ static BinOpType tok_to_mult_op(Token *token) {...@@ -1043,12 +1087,14 @@ static BinOpType tok_to_mult_op(Token *token) {
1043 case TokenIdStarStar: return BinOpTypeArrayMult;1087 case TokenIdStarStar: return BinOpTypeArrayMult;
1044 case TokenIdSlash: return BinOpTypeDiv;1088 case TokenIdSlash: return BinOpTypeDiv;
1045 case TokenIdPercent: return BinOpTypeMod;1089 case TokenIdPercent: return BinOpTypeMod;
1090 case TokenIdBang: return BinOpTypeErrorUnion;
1091 case TokenIdBarBar: return BinOpTypeMergeErrorSets;
1046 default: return BinOpTypeInvalid;1092 default: return BinOpTypeInvalid;
1047 }1093 }
1048}1094}
10491095
1050/*1096/*
1051MultiplyOperator = "*" | "/" | "%" | "**" | "*%"1097MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
1052*/1098*/
1053static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) {1099static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) {
1054 Token *token = &pc->tokens->at(*token_index);1100 Token *token = &pc->tokens->at(*token_index);
...@@ -2240,7 +2286,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand...@@ -2240,7 +2286,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
2240}2286}
22412287
2242/*2288/*
2243FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") TypeExpr2289FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr
2244*/2290*/
2245static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {2291static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
2246 Token *first_token = &pc->tokens->at(*token_index);2292 Token *first_token = &pc->tokens->at(*token_index);
...@@ -2315,6 +2361,18 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m...@@ -2315,6 +2361,18 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
2315 ast_eat_token(pc, token_index, TokenIdRParen);2361 ast_eat_token(pc, token_index, TokenIdRParen);
2316 next_token = &pc->tokens->at(*token_index);2362 next_token = &pc->tokens->at(*token_index);
2317 }2363 }
2364 if (next_token->id == TokenIdKeywordError) {
2365 Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
2366 if (maybe_lbrace_tok->id == TokenIdLBrace) {
2367 *token_index += 1;
2368 node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
2369 return node;
2370 }
2371 } else if (next_token->id == TokenIdBang) {
2372 *token_index += 1;
2373 node->data.fn_proto.auto_err_set = true;
2374 next_token = &pc->tokens->at(*token_index);
2375 }
2318 node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);2376 node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
23192377
2320 return node;2378 return node;
...@@ -2531,7 +2589,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,...@@ -2531,7 +2589,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
2531 Token *colon_token = &pc->tokens->at(*token_index);2589 Token *colon_token = &pc->tokens->at(*token_index);
2532 if (colon_token->id == TokenIdColon) {2590 if (colon_token->id == TokenIdColon) {
2533 *token_index += 1;2591 *token_index += 1;
2534 field_node->data.struct_field.type = ast_parse_prefix_op_expr(pc, token_index, true);2592 field_node->data.struct_field.type = ast_parse_type_expr(pc, token_index, true);
2535 }2593 }
2536 Token *eq_token = &pc->tokens->at(*token_index);2594 Token *eq_token = &pc->tokens->at(*token_index);
2537 if (eq_token->id == TokenIdEq) {2595 if (eq_token->id == TokenIdEq) {
...@@ -2559,26 +2617,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,...@@ -2559,26 +2617,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
2559 return node;2617 return node;
2560}2618}
25612619
2562/*
2563ErrorValueDecl : "error" "Symbol" ";"
2564*/
2565static AstNode *ast_parse_error_value_decl(ParseContext *pc, size_t *token_index) {
2566 Token *first_token = &pc->tokens->at(*token_index);
2567
2568 if (first_token->id != TokenIdKeywordError) {
2569 return nullptr;
2570 }
2571 *token_index += 1;
2572
2573 Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
2574 ast_eat_token(pc, token_index, TokenIdSemicolon);
2575
2576 AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token);
2577 node->data.error_value_decl.name = token_buf(name_tok);
2578
2579 return node;
2580}
2581
2582/*2620/*
2583TestDecl = "test" String Block2621TestDecl = "test" String Block
2584*/2622*/
...@@ -2611,12 +2649,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig...@@ -2611,12 +2649,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
2611 continue;2649 continue;
2612 }2650 }
26132651
2614 AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index);
2615 if (error_value_node) {
2616 top_level_decls->append(error_value_node);
2617 continue;
2618 }
2619
2620 AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index);2652 AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index);
2621 if (test_decl_node) {2653 if (test_decl_node) {
2622 top_level_decls->append(test_decl_node);2654 top_level_decls->append(test_decl_node);
...@@ -2744,9 +2776,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -2744,9 +2776,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
2744 visit_field(&node->data.variable_declaration.align_expr, visit, context);2776 visit_field(&node->data.variable_declaration.align_expr, visit, context);
2745 visit_field(&node->data.variable_declaration.section_expr, visit, context);2777 visit_field(&node->data.variable_declaration.section_expr, visit, context);
2746 break;2778 break;
2747 case NodeTypeErrorValueDecl:
2748 // none
2749 break;
2750 case NodeTypeTestDecl:2779 case NodeTypeTestDecl:
2751 visit_field(&node->data.test_decl.body, visit, context);2780 visit_field(&node->data.test_decl.body, visit, context);
2752 break;2781 break;
...@@ -2899,5 +2928,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -2899,5 +2928,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
2899 visit_field(&node->data.addr_of_expr.align_expr, visit, context);2928 visit_field(&node->data.addr_of_expr.align_expr, visit, context);
2900 visit_field(&node->data.addr_of_expr.op_expr, visit, context);2929 visit_field(&node->data.addr_of_expr.op_expr, visit, context);
2901 break;2930 break;
2931 case NodeTypeErrorSetDecl:
2932 visit_node_list(&node->data.err_set_decl.decls, visit, context);
2933 break;
2902 }2934 }
2903}2935}
src/tokenizer.cpp+25-4
...@@ -195,7 +195,8 @@ enum TokenizeState {...@@ -195,7 +195,8 @@ enum TokenizeState {
195 TokenizeStateSawMinusPercent,195 TokenizeStateSawMinusPercent,
196 TokenizeStateSawAmpersand,196 TokenizeStateSawAmpersand,
197 TokenizeStateSawCaret,197 TokenizeStateSawCaret,
198 TokenizeStateSawPipe,198 TokenizeStateSawBar,
199 TokenizeStateSawBarBar,
199 TokenizeStateLineComment,200 TokenizeStateLineComment,
200 TokenizeStateLineString,201 TokenizeStateLineString,
201 TokenizeStateLineStringEnd,202 TokenizeStateLineStringEnd,
...@@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) {
594 break;595 break;
595 case '|':596 case '|':
596 begin_token(&t, TokenIdBinOr);597 begin_token(&t, TokenIdBinOr);
597 t.state = TokenizeStateSawPipe;598 t.state = TokenizeStateSawBar;
598 break;599 break;
599 case '=':600 case '=':
600 begin_token(&t, TokenIdEq);601 begin_token(&t, TokenIdEq);
...@@ -888,13 +889,17 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -888,13 +889,17 @@ void tokenize(Buf *buf, Tokenization *out) {
888 continue;889 continue;
889 }890 }
890 break;891 break;
891 case TokenizeStateSawPipe:892 case TokenizeStateSawBar:
892 switch (c) {893 switch (c) {
893 case '=':894 case '=':
894 set_token_id(&t, t.cur_tok, TokenIdBitOrEq);895 set_token_id(&t, t.cur_tok, TokenIdBitOrEq);
895 end_token(&t);896 end_token(&t);
896 t.state = TokenizeStateStart;897 t.state = TokenizeStateStart;
897 break;898 break;
899 case '|':
900 set_token_id(&t, t.cur_tok, TokenIdBarBar);
901 t.state = TokenizeStateSawBarBar;
902 break;
898 default:903 default:
899 t.pos -= 1;904 t.pos -= 1;
900 end_token(&t);905 end_token(&t);
...@@ -902,6 +907,19 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -902,6 +907,19 @@ void tokenize(Buf *buf, Tokenization *out) {
902 continue;907 continue;
903 }908 }
904 break;909 break;
910 case TokenizeStateSawBarBar:
911 switch (c) {
912 case '=':
913 set_token_id(&t, t.cur_tok, TokenIdBarBarEq);
914 end_token(&t);
915 t.state = TokenizeStateStart;
916 break;
917 default:
918 t.pos -= 1;
919 end_token(&t);
920 t.state = TokenizeStateStart;
921 continue;
922 }
905 case TokenizeStateSawSlash:923 case TokenizeStateSawSlash:
906 switch (c) {924 switch (c) {
907 case '/':925 case '/':
...@@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) {
1428 case TokenizeStateSawDash:1446 case TokenizeStateSawDash:
1429 case TokenizeStateSawAmpersand:1447 case TokenizeStateSawAmpersand:
1430 case TokenizeStateSawCaret:1448 case TokenizeStateSawCaret:
1431 case TokenizeStateSawPipe:1449 case TokenizeStateSawBar:
1432 case TokenizeStateSawEq:1450 case TokenizeStateSawEq:
1433 case TokenizeStateSawBang:1451 case TokenizeStateSawBang:
1434 case TokenizeStateSawLessThan:1452 case TokenizeStateSawLessThan:
...@@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) {
1443 case TokenizeStateSawMinusPercent:1461 case TokenizeStateSawMinusPercent:
1444 case TokenizeStateLineString:1462 case TokenizeStateLineString:
1445 case TokenizeStateLineStringEnd:1463 case TokenizeStateLineStringEnd:
1464 case TokenizeStateSawBarBar:
1446 end_token(&t);1465 end_token(&t);
1447 break;1466 break;
1448 case TokenizeStateSawDotDot:1467 case TokenizeStateSawDotDot:
...@@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) {...@@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) {
1475 case TokenIdArrow: return "->";1494 case TokenIdArrow: return "->";
1476 case TokenIdAtSign: return "@";1495 case TokenIdAtSign: return "@";
1477 case TokenIdBang: return "!";1496 case TokenIdBang: return "!";
1497 case TokenIdBarBar: return "||";
1478 case TokenIdBinOr: return "|";1498 case TokenIdBinOr: return "|";
1479 case TokenIdBinXor: return "^";1499 case TokenIdBinXor: return "^";
1480 case TokenIdBitAndEq: return "&=";1500 case TokenIdBitAndEq: return "&=";
...@@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) {...@@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) {
1577 case TokenIdTimesEq: return "*=";1597 case TokenIdTimesEq: return "*=";
1578 case TokenIdTimesPercent: return "*%";1598 case TokenIdTimesPercent: return "*%";
1579 case TokenIdTimesPercentEq: return "*%=";1599 case TokenIdTimesPercentEq: return "*%=";
1600 case TokenIdBarBarEq: return "||=";
1580 }1601 }
1581 return "(invalid token)";1602 return "(invalid token)";
1582}1603}
src/tokenizer.hpp+2
...@@ -17,6 +17,8 @@ enum TokenId {...@@ -17,6 +17,8 @@ enum TokenId {
17 TokenIdArrow,17 TokenIdArrow,
18 TokenIdAtSign,18 TokenIdAtSign,
19 TokenIdBang,19 TokenIdBang,
20 TokenIdBarBar,
21 TokenIdBarBarEq,
20 TokenIdBinOr,22 TokenIdBinOr,
21 TokenIdBinXor,23 TokenIdBinXor,
22 TokenIdBitAndEq,24 TokenIdBitAndEq,
src/util.hpp+11-8
...@@ -92,19 +92,22 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {...@@ -92,19 +92,22 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {
92}92}
9393
94template<typename T>94template<typename T>
95static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {95static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
96#ifdef NDEBUG
97 T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));96 T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
98 if (!ptr)97 if (!ptr)
99 zig_panic("allocation failed");98 zig_panic("allocation failed");
99 if (new_count > old_count) {
100 memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
101 }
100 return ptr;102 return ptr;
101#else103}
102 // manually assign every element to trigger compile error for non-copyable structs104
103 T *ptr = allocate_nonzero<T>(new_count);105template<typename T>
104 safe_memcpy(ptr, old, old_count);106static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
105 free(old);107 T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
108 if (!ptr)
109 zig_panic("allocation failed");
106 return ptr;110 return ptr;
107#endif
108}111}
109112
110template <typename T, size_t n>113template <typename T, size_t n>
src/zig_llvm.cpp+4
...@@ -437,6 +437,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {...@@ -437,6 +437,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {
437 return dwarf::DW_TAG_structure_type;437 return dwarf::DW_TAG_structure_type;
438}438}
439439
440unsigned ZigLLVMTag_DW_enumeration_type(void) {
441 return dwarf::DW_TAG_enumeration_type;
442}
443
440unsigned ZigLLVMTag_DW_union_type(void) {444unsigned ZigLLVMTag_DW_union_type(void) {
441 return dwarf::DW_TAG_union_type;445 return dwarf::DW_TAG_union_type;
442}446}
src/zig_llvm.h+1
...@@ -133,6 +133,7 @@ ZIG_EXTERN_C unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);...@@ -133,6 +133,7 @@ ZIG_EXTERN_C unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);
133ZIG_EXTERN_C unsigned ZigLLVMLang_DW_LANG_C99(void);133ZIG_EXTERN_C unsigned ZigLLVMLang_DW_LANG_C99(void);
134ZIG_EXTERN_C unsigned ZigLLVMTag_DW_variable(void);134ZIG_EXTERN_C unsigned ZigLLVMTag_DW_variable(void);
135ZIG_EXTERN_C unsigned ZigLLVMTag_DW_structure_type(void);135ZIG_EXTERN_C unsigned ZigLLVMTag_DW_structure_type(void);
136ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
136ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);137ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
137138
138ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);139ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
std/array_list.zig+44-5
...@@ -40,6 +40,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -40,6 +40,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
40 return l.items[0..l.len];40 return l.items[0..l.len];
41 }41 }
4242
43 pub fn at(l: &const Self, n: usize) T {
44 return l.toSliceConst()[n];
45 }
46
43 /// ArrayList takes ownership of the passed in slice. The slice must have been47 /// ArrayList takes ownership of the passed in slice. The slice must have been
44 /// allocated with `allocator`.48 /// allocated with `allocator`.
45 /// Deinitialize with `deinit` or use `toOwnedSlice`.49 /// Deinitialize with `deinit` or use `toOwnedSlice`.
...@@ -59,18 +63,34 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -59,18 +63,34 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
59 return result;63 return result;
60 }64 }
6165
62 pub fn append(l: &Self, item: &const T) %void {66 pub fn insert(l: &Self, n: usize, item: &const T) !void {
67 try l.ensureCapacity(l.len + 1);
68 l.len += 1;
69
70 mem.copy(T, l.items[n+1..l.len], l.items[n..l.len-1]);
71 l.items[n] = *item;
72 }
73
74 pub fn insertSlice(l: &Self, n: usize, items: []align(A) const T) !void {
75 try l.ensureCapacity(l.len + items.len);
76 l.len += items.len;
77
78 mem.copy(T, l.items[n+items.len..l.len], l.items[n..l.len-items.len]);
79 mem.copy(T, l.items[n..n+items.len], items);
80 }
81
82 pub fn append(l: &Self, item: &const T) !void {
63 const new_item_ptr = try l.addOne();83 const new_item_ptr = try l.addOne();
64 *new_item_ptr = *item;84 *new_item_ptr = *item;
65 }85 }
6686
67 pub fn appendSlice(l: &Self, items: []align(A) const T) %void {87 pub fn appendSlice(l: &Self, items: []align(A) const T) !void {
68 try l.ensureCapacity(l.len + items.len);88 try l.ensureCapacity(l.len + items.len);
69 mem.copy(T, l.items[l.len..], items);89 mem.copy(T, l.items[l.len..], items);
70 l.len += items.len;90 l.len += items.len;
71 }91 }
7292
73 pub fn resize(l: &Self, new_len: usize) %void {93 pub fn resize(l: &Self, new_len: usize) !void {
74 try l.ensureCapacity(new_len);94 try l.ensureCapacity(new_len);
75 l.len = new_len;95 l.len = new_len;
76 }96 }
...@@ -80,7 +100,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -80,7 +100,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
80 l.len = new_len;100 l.len = new_len;
81 }101 }
82102
83 pub fn ensureCapacity(l: &Self, new_capacity: usize) %void {103 pub fn ensureCapacity(l: &Self, new_capacity: usize) !void {
84 var better_capacity = l.items.len;104 var better_capacity = l.items.len;
85 if (better_capacity >= new_capacity) return;105 if (better_capacity >= new_capacity) return;
86 while (true) {106 while (true) {
...@@ -90,7 +110,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -90,7 +110,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
90 l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity);110 l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity);
91 }111 }
92112
93 pub fn addOne(l: &Self) %&T {113 pub fn addOne(l: &Self) !&T {
94 const new_length = l.len + 1;114 const new_length = l.len + 1;
95 try l.ensureCapacity(new_length);115 try l.ensureCapacity(new_length);
96 const result = &l.items[l.len];116 const result = &l.items[l.len];
...@@ -136,3 +156,22 @@ test "basic ArrayList test" {...@@ -136,3 +156,22 @@ test "basic ArrayList test" {
136 list.appendSlice([]const i32 {}) catch unreachable;156 list.appendSlice([]const i32 {}) catch unreachable;
137 assert(list.len == 9);157 assert(list.len == 9);
138}158}
159
160test "insert ArrayList test" {
161 var list = ArrayList(i32).init(debug.global_allocator);
162 defer list.deinit();
163
164 try list.append(1);
165 try list.insert(0, 5);
166 assert(list.items[0] == 5);
167 assert(list.items[1] == 1);
168
169 try list.insertSlice(1, []const i32 { 9, 8 });
170 assert(list.items[0] == 5);
171 assert(list.items[1] == 9);
172 assert(list.items[2] == 8);
173
174 const items = []const i32 { 1 };
175 try list.insertSlice(0, items[0..0]);
176 assert(list.items[0] == 5);
177}
std/base64.zig+11-16
...@@ -79,8 +79,6 @@ pub const Base64Encoder = struct {...@@ -79,8 +79,6 @@ pub const Base64Encoder = struct {
79};79};
8080
81pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);81pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
82error InvalidPadding;
83error InvalidCharacter;
8482
85pub const Base64Decoder = struct {83pub const Base64Decoder = struct {
86 /// e.g. 'A' => 0.84 /// e.g. 'A' => 0.
...@@ -111,7 +109,7 @@ pub const Base64Decoder = struct {...@@ -111,7 +109,7 @@ pub const Base64Decoder = struct {
111 }109 }
112110
113 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.111 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
114 pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) %usize {112 pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) !usize {
115 if (source.len % 4 != 0) return error.InvalidPadding;113 if (source.len % 4 != 0) return error.InvalidPadding;
116 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);114 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
117 }115 }
...@@ -119,7 +117,7 @@ pub const Base64Decoder = struct {...@@ -119,7 +117,7 @@ pub const Base64Decoder = struct {
119 /// dest.len must be what you get from ::calcSize.117 /// dest.len must be what you get from ::calcSize.
120 /// invalid characters result in error.InvalidCharacter.118 /// invalid characters result in error.InvalidCharacter.
121 /// invalid padding results in error.InvalidPadding.119 /// invalid padding results in error.InvalidPadding.
122 pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) %void {120 pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) !void {
123 assert(dest.len == (decoder.calcSize(source) catch unreachable));121 assert(dest.len == (decoder.calcSize(source) catch unreachable));
124 assert(source.len % 4 == 0);122 assert(source.len % 4 == 0);
125123
...@@ -163,8 +161,6 @@ pub const Base64Decoder = struct {...@@ -163,8 +161,6 @@ pub const Base64Decoder = struct {
163 }161 }
164};162};
165163
166error OutputTooSmall;
167
168pub const Base64DecoderWithIgnore = struct {164pub const Base64DecoderWithIgnore = struct {
169 decoder: Base64Decoder,165 decoder: Base64Decoder,
170 char_is_ignored: [256]bool,166 char_is_ignored: [256]bool,
...@@ -185,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct {...@@ -185,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct {
185 }181 }
186182
187 /// If no characters end up being ignored or padding, this will be the exact decoded size.183 /// If no characters end up being ignored or padding, this will be the exact decoded size.
188 pub fn calcSizeUpperBound(encoded_len: usize) %usize {184 pub fn calcSizeUpperBound(encoded_len: usize) usize {
189 return @divTrunc(encoded_len, 4) * 3;185 return @divTrunc(encoded_len, 4) * 3;
190 }186 }
191187
...@@ -193,7 +189,7 @@ pub const Base64DecoderWithIgnore = struct {...@@ -193,7 +189,7 @@ pub const Base64DecoderWithIgnore = struct {
193 /// Invalid padding results in error.InvalidPadding.189 /// Invalid padding results in error.InvalidPadding.
194 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.190 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
195 /// Returns the number of bytes writen to dest.191 /// Returns the number of bytes writen to dest.
196 pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) %usize {192 pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize {
197 const decoder = &decoder_with_ignore.decoder;193 const decoder = &decoder_with_ignore.decoder;
198194
199 var src_cursor: usize = 0;195 var src_cursor: usize = 0;
...@@ -378,7 +374,7 @@ test "base64" {...@@ -378,7 +374,7 @@ test "base64" {
378 comptime (testBase64() catch unreachable);374 comptime (testBase64() catch unreachable);
379}375}
380376
381fn testBase64() %void {377fn testBase64() !void {
382 try testAllApis("", "");378 try testAllApis("", "");
383 try testAllApis("f", "Zg==");379 try testAllApis("f", "Zg==");
384 try testAllApis("fo", "Zm8=");380 try testAllApis("fo", "Zm8=");
...@@ -412,7 +408,7 @@ fn testBase64() %void {...@@ -412,7 +408,7 @@ fn testBase64() %void {
412 try testOutputTooSmallError("AAAAAA==");408 try testOutputTooSmallError("AAAAAA==");
413}409}
414410
415fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void {411fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void {
416 // Base64Encoder412 // Base64Encoder
417 {413 {
418 var buffer: [0x100]u8 = undefined;414 var buffer: [0x100]u8 = undefined;
...@@ -434,7 +430,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void...@@ -434,7 +430,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void
434 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(430 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
435 standard_alphabet_chars, standard_pad_char, "");431 standard_alphabet_chars, standard_pad_char, "");
436 var buffer: [0x100]u8 = undefined;432 var buffer: [0x100]u8 = undefined;
437 var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];433 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
438 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);434 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
439 assert(written <= decoded.len);435 assert(written <= decoded.len);
440 assert(mem.eql(u8, decoded[0..written], expected_decoded));436 assert(mem.eql(u8, decoded[0..written], expected_decoded));
...@@ -449,17 +445,16 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void...@@ -449,17 +445,16 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void
449 }445 }
450}446}
451447
452fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) %void {448fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {
453 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(449 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
454 standard_alphabet_chars, standard_pad_char, " ");450 standard_alphabet_chars, standard_pad_char, " ");
455 var buffer: [0x100]u8 = undefined;451 var buffer: [0x100]u8 = undefined;
456 var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];452 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
457 var written = try standard_decoder_ignore_space.decode(decoded, encoded);453 var written = try standard_decoder_ignore_space.decode(decoded, encoded);
458 assert(mem.eql(u8, decoded[0..written], expected_decoded));454 assert(mem.eql(u8, decoded[0..written], expected_decoded));
459}455}
460456
461error ExpectedError;457fn testError(encoded: []const u8, expected_err: error) !void {
462fn testError(encoded: []const u8, expected_err: error) %void {
463 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(458 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
464 standard_alphabet_chars, standard_pad_char, " ");459 standard_alphabet_chars, standard_pad_char, " ");
465 var buffer: [0x100]u8 = undefined;460 var buffer: [0x100]u8 = undefined;
...@@ -475,7 +470,7 @@ fn testError(encoded: []const u8, expected_err: error) %void {...@@ -475,7 +470,7 @@ fn testError(encoded: []const u8, expected_err: error) %void {
475 } else |err| if (err != expected_err) return err;470 } else |err| if (err != expected_err) return err;
476}471}
477472
478fn testOutputTooSmallError(encoded: []const u8) %void {473fn testOutputTooSmallError(encoded: []const u8) !void {
479 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(474 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
480 standard_alphabet_chars, standard_pad_char, " ");475 standard_alphabet_chars, standard_pad_char, " ");
481 var buffer: [0x100]u8 = undefined;476 var buffer: [0x100]u8 = undefined;
std/buf_map.zig+2-2
...@@ -27,7 +27,7 @@ pub const BufMap = struct {...@@ -27,7 +27,7 @@ pub const BufMap = struct {
27 self.hash_map.deinit();27 self.hash_map.deinit();
28 }28 }
2929
30 pub fn set(self: &BufMap, key: []const u8, value: []const u8) %void {30 pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
31 if (self.hash_map.get(key)) |entry| {31 if (self.hash_map.get(key)) |entry| {
32 const value_copy = try self.copy(value);32 const value_copy = try self.copy(value);
33 errdefer self.free(value_copy);33 errdefer self.free(value_copy);
...@@ -67,7 +67,7 @@ pub const BufMap = struct {...@@ -67,7 +67,7 @@ pub const BufMap = struct {
67 self.hash_map.allocator.free(mut_value);67 self.hash_map.allocator.free(mut_value);
68 }68 }
6969
70 fn copy(self: &BufMap, value: []const u8) %[]const u8 {70 fn copy(self: &BufMap, value: []const u8) ![]const u8 {
71 const result = try self.hash_map.allocator.alloc(u8, value.len);71 const result = try self.hash_map.allocator.alloc(u8, value.len);
72 mem.copy(u8, result, value);72 mem.copy(u8, result, value);
73 return result;73 return result;
std/buf_set.zig+2-2
...@@ -24,7 +24,7 @@ pub const BufSet = struct {...@@ -24,7 +24,7 @@ pub const BufSet = struct {
24 self.hash_map.deinit();24 self.hash_map.deinit();
25 }25 }
2626
27 pub fn put(self: &BufSet, key: []const u8) %void {27 pub fn put(self: &BufSet, key: []const u8) !void {
28 if (self.hash_map.get(key) == null) {28 if (self.hash_map.get(key) == null) {
29 const key_copy = try self.copy(key);29 const key_copy = try self.copy(key);
30 errdefer self.free(key_copy);30 errdefer self.free(key_copy);
...@@ -55,7 +55,7 @@ pub const BufSet = struct {...@@ -55,7 +55,7 @@ pub const BufSet = struct {
55 self.hash_map.allocator.free(mut_value);55 self.hash_map.allocator.free(mut_value);
56 }56 }
5757
58 fn copy(self: &BufSet, value: []const u8) %[]const u8 {58 fn copy(self: &BufSet, value: []const u8) ![]const u8 {
59 const result = try self.hash_map.allocator.alloc(u8, value.len);59 const result = try self.hash_map.allocator.alloc(u8, value.len);
60 mem.copy(u8, result, value);60 mem.copy(u8, result, value);
61 return result;61 return result;
std/buffer.zig+9-9
...@@ -12,14 +12,14 @@ pub const Buffer = struct {...@@ -12,14 +12,14 @@ pub const Buffer = struct {
12 list: ArrayList(u8),12 list: ArrayList(u8),
1313
14 /// Must deinitialize with deinit.14 /// Must deinitialize with deinit.
15 pub fn init(allocator: &Allocator, m: []const u8) %Buffer {15 pub fn init(allocator: &Allocator, m: []const u8) !Buffer {
16 var self = try initSize(allocator, m.len);16 var self = try initSize(allocator, m.len);
17 mem.copy(u8, self.list.items, m);17 mem.copy(u8, self.list.items, m);
18 return self;18 return self;
19 }19 }
2020
21 /// Must deinitialize with deinit.21 /// Must deinitialize with deinit.
22 pub fn initSize(allocator: &Allocator, size: usize) %Buffer {22 pub fn initSize(allocator: &Allocator, size: usize) !Buffer {
23 var self = initNull(allocator);23 var self = initNull(allocator);
24 try self.resize(size);24 try self.resize(size);
25 return self;25 return self;
...@@ -37,7 +37,7 @@ pub const Buffer = struct {...@@ -37,7 +37,7 @@ pub const Buffer = struct {
37 }37 }
3838
39 /// Must deinitialize with deinit.39 /// Must deinitialize with deinit.
40 pub fn initFromBuffer(buffer: &const Buffer) %Buffer {40 pub fn initFromBuffer(buffer: &const Buffer) !Buffer {
41 return Buffer.init(buffer.list.allocator, buffer.toSliceConst());41 return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
42 }42 }
4343
...@@ -80,7 +80,7 @@ pub const Buffer = struct {...@@ -80,7 +80,7 @@ pub const Buffer = struct {
80 self.list.items[self.len()] = 0;80 self.list.items[self.len()] = 0;
81 }81 }
8282
83 pub fn resize(self: &Buffer, new_len: usize) %void {83 pub fn resize(self: &Buffer, new_len: usize) !void {
84 try self.list.resize(new_len + 1);84 try self.list.resize(new_len + 1);
85 self.list.items[self.len()] = 0;85 self.list.items[self.len()] = 0;
86 }86 }
...@@ -93,24 +93,24 @@ pub const Buffer = struct {...@@ -93,24 +93,24 @@ pub const Buffer = struct {
93 return self.list.len - 1;93 return self.list.len - 1;
94 }94 }
9595
96 pub fn append(self: &Buffer, m: []const u8) %void {96 pub fn append(self: &Buffer, m: []const u8) !void {
97 const old_len = self.len();97 const old_len = self.len();
98 try self.resize(old_len + m.len);98 try self.resize(old_len + m.len);
99 mem.copy(u8, self.list.toSlice()[old_len..], m);99 mem.copy(u8, self.list.toSlice()[old_len..], m);
100 }100 }
101101
102 // TODO: remove, use OutStream for this102 // TODO: remove, use OutStream for this
103 pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) %void {103 pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
104 return fmt.format(self, append, format, args);104 return fmt.format(self, append, format, args);
105 }105 }
106106
107 // TODO: remove, use OutStream for this107 // TODO: remove, use OutStream for this
108 pub fn appendByte(self: &Buffer, byte: u8) %void {108 pub fn appendByte(self: &Buffer, byte: u8) !void {
109 return self.appendByteNTimes(byte, 1);109 return self.appendByteNTimes(byte, 1);
110 }110 }
111111
112 // TODO: remove, use OutStream for this112 // TODO: remove, use OutStream for this
113 pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) %void {113 pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
114 var prev_size: usize = self.len();114 var prev_size: usize = self.len();
115 const new_size = prev_size + count;115 const new_size = prev_size + count;
116 try self.resize(new_size);116 try self.resize(new_size);
...@@ -137,7 +137,7 @@ pub const Buffer = struct {...@@ -137,7 +137,7 @@ pub const Buffer = struct {
137 return mem.eql(u8, self.list.items[start..l], m);137 return mem.eql(u8, self.list.items[start..l], m);
138 }138 }
139139
140 pub fn replaceContents(self: &const Buffer, m: []const u8) %void {140 pub fn replaceContents(self: &const Buffer, m: []const u8) !void {
141 try self.resize(m.len);141 try self.resize(m.len);
142 mem.copy(u8, self.list.toSlice(), m);142 mem.copy(u8, self.list.toSlice(), m);
143 }143 }
std/build.zig+35-39
...@@ -15,13 +15,6 @@ const BufSet = std.BufSet;...@@ -15,13 +15,6 @@ const BufSet = std.BufSet;
15const BufMap = std.BufMap;15const BufMap = std.BufMap;
16const fmt_lib = std.fmt;16const fmt_lib = std.fmt;
1717
18error ExtraArg;
19error UncleanExit;
20error InvalidStepName;
21error DependencyLoopDetected;
22error NoCompilerFound;
23error NeedAnObject;
24
25pub const Builder = struct {18pub const Builder = struct {
26 uninstall_tls: TopLevelStep,19 uninstall_tls: TopLevelStep,
27 install_tls: TopLevelStep,20 install_tls: TopLevelStep,
...@@ -242,7 +235,7 @@ pub const Builder = struct {...@@ -242,7 +235,7 @@ pub const Builder = struct {
242 self.lib_paths.append(path) catch unreachable;235 self.lib_paths.append(path) catch unreachable;
243 }236 }
244237
245 pub fn make(self: &Builder, step_names: []const []const u8) %void {238 pub fn make(self: &Builder, step_names: []const []const u8) !void {
246 var wanted_steps = ArrayList(&Step).init(self.allocator);239 var wanted_steps = ArrayList(&Step).init(self.allocator);
247 defer wanted_steps.deinit();240 defer wanted_steps.deinit();
248241
...@@ -278,7 +271,7 @@ pub const Builder = struct {...@@ -278,7 +271,7 @@ pub const Builder = struct {
278 return &self.uninstall_tls.step;271 return &self.uninstall_tls.step;
279 }272 }
280273
281 fn makeUninstall(uninstall_step: &Step) %void {274 fn makeUninstall(uninstall_step: &Step) error!void {
282 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);275 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
283 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);276 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
284277
...@@ -292,7 +285,7 @@ pub const Builder = struct {...@@ -292,7 +285,7 @@ pub const Builder = struct {
292 // TODO remove empty directories285 // TODO remove empty directories
293 }286 }
294287
295 fn makeOneStep(self: &Builder, s: &Step) %void {288 fn makeOneStep(self: &Builder, s: &Step) error!void {
296 if (s.loop_flag) {289 if (s.loop_flag) {
297 warn("Dependency loop detected:\n {}\n", s.name);290 warn("Dependency loop detected:\n {}\n", s.name);
298 return error.DependencyLoopDetected;291 return error.DependencyLoopDetected;
...@@ -313,7 +306,7 @@ pub const Builder = struct {...@@ -313,7 +306,7 @@ pub const Builder = struct {
313 try s.make();306 try s.make();
314 }307 }
315308
316 fn getTopLevelStepByName(self: &Builder, name: []const u8) %&Step {309 fn getTopLevelStepByName(self: &Builder, name: []const u8) !&Step {
317 for (self.top_level_steps.toSliceConst()) |top_level_step| {310 for (self.top_level_steps.toSliceConst()) |top_level_step| {
318 if (mem.eql(u8, top_level_step.step.name, name)) {311 if (mem.eql(u8, top_level_step.step.name, name)) {
319 return &top_level_step.step;312 return &top_level_step.step;
...@@ -548,7 +541,7 @@ pub const Builder = struct {...@@ -548,7 +541,7 @@ pub const Builder = struct {
548 return self.invalid_user_input;541 return self.invalid_user_input;
549 }542 }
550543
551 fn spawnChild(self: &Builder, argv: []const []const u8) %void {544 fn spawnChild(self: &Builder, argv: []const []const u8) !void {
552 return self.spawnChildEnvMap(null, &self.env_map, argv);545 return self.spawnChildEnvMap(null, &self.env_map, argv);
553 }546 }
554547
...@@ -561,7 +554,7 @@ pub const Builder = struct {...@@ -561,7 +554,7 @@ pub const Builder = struct {
561 }554 }
562555
563 fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,556 fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
564 argv: []const []const u8) %void557 argv: []const []const u8) !void
565 {558 {
566 if (self.verbose) {559 if (self.verbose) {
567 printCmd(cwd, argv);560 printCmd(cwd, argv);
...@@ -595,7 +588,7 @@ pub const Builder = struct {...@@ -595,7 +588,7 @@ pub const Builder = struct {
595 }588 }
596 }589 }
597590
598 pub fn makePath(self: &Builder, path: []const u8) %void {591 pub fn makePath(self: &Builder, path: []const u8) !void {
599 os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| {592 os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| {
600 warn("Unable to create path {}: {}\n", path, @errorName(err));593 warn("Unable to create path {}: {}\n", path, @errorName(err));
601 return err;594 return err;
...@@ -630,11 +623,11 @@ pub const Builder = struct {...@@ -630,11 +623,11 @@ pub const Builder = struct {
630 self.installed_files.append(full_path) catch unreachable;623 self.installed_files.append(full_path) catch unreachable;
631 }624 }
632625
633 fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) %void {626 fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void {
634 return self.copyFileMode(source_path, dest_path, 0o666);627 return self.copyFileMode(source_path, dest_path, os.default_file_mode);
635 }628 }
636629
637 fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) %void {630 fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void {
638 if (self.verbose) {631 if (self.verbose) {
639 warn("cp {} {}\n", source_path, dest_path);632 warn("cp {} {}\n", source_path, dest_path);
640 }633 }
...@@ -672,7 +665,7 @@ pub const Builder = struct {...@@ -672,7 +665,7 @@ pub const Builder = struct {
672 }665 }
673 }666 }
674667
675 pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) %[]const u8 {668 pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
676 // TODO report error for ambiguous situations669 // TODO report error for ambiguous situations
677 const exe_extension = (Target { .Native = {}}).exeFileExt();670 const exe_extension = (Target { .Native = {}}).exeFileExt();
678 for (self.search_prefixes.toSliceConst()) |search_prefix| {671 for (self.search_prefixes.toSliceConst()) |search_prefix| {
...@@ -721,7 +714,7 @@ pub const Builder = struct {...@@ -721,7 +714,7 @@ pub const Builder = struct {
721 return error.FileNotFound;714 return error.FileNotFound;
722 }715 }
723716
724 pub fn exec(self: &Builder, argv: []const []const u8) %[]u8 {717 pub fn exec(self: &Builder, argv: []const []const u8) ![]u8 {
725 const max_output_size = 100 * 1024;718 const max_output_size = 100 * 1024;
726 const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);719 const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
727 switch (result.term) {720 switch (result.term) {
...@@ -1180,12 +1173,12 @@ pub const LibExeObjStep = struct {...@@ -1180,12 +1173,12 @@ pub const LibExeObjStep = struct {
1180 self.disable_libc = disable;1173 self.disable_libc = disable;
1181 }1174 }
11821175
1183 fn make(step: &Step) %void {1176 fn make(step: &Step) !void {
1184 const self = @fieldParentPtr(LibExeObjStep, "step", step);1177 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1185 return if (self.is_zig) self.makeZig() else self.makeC();1178 return if (self.is_zig) self.makeZig() else self.makeC();
1186 }1179 }
11871180
1188 fn makeZig(self: &LibExeObjStep) %void {1181 fn makeZig(self: &LibExeObjStep) !void {
1189 const builder = self.builder;1182 const builder = self.builder;
11901183
1191 assert(self.is_zig);1184 assert(self.is_zig);
...@@ -1396,7 +1389,7 @@ pub const LibExeObjStep = struct {...@@ -1396,7 +1389,7 @@ pub const LibExeObjStep = struct {
1396 }1389 }
1397 }1390 }
13981391
1399 fn makeC(self: &LibExeObjStep) %void {1392 fn makeC(self: &LibExeObjStep) !void {
1400 const builder = self.builder;1393 const builder = self.builder;
14011394
1402 const cc = builder.getCCExe();1395 const cc = builder.getCCExe();
...@@ -1687,7 +1680,7 @@ pub const TestStep = struct {...@@ -1687,7 +1680,7 @@ pub const TestStep = struct {
1687 self.exec_cmd_args = args;1680 self.exec_cmd_args = args;
1688 }1681 }
16891682
1690 fn make(step: &Step) %void {1683 fn make(step: &Step) !void {
1691 const self = @fieldParentPtr(TestStep, "step", step);1684 const self = @fieldParentPtr(TestStep, "step", step);
1692 const builder = self.builder;1685 const builder = self.builder;
16931686
...@@ -1796,7 +1789,7 @@ pub const CommandStep = struct {...@@ -1796,7 +1789,7 @@ pub const CommandStep = struct {
1796 return self;1789 return self;
1797 }1790 }
17981791
1799 fn make(step: &Step) %void {1792 fn make(step: &Step) !void {
1800 const self = @fieldParentPtr(CommandStep, "step", step);1793 const self = @fieldParentPtr(CommandStep, "step", step);
18011794
1802 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;1795 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
...@@ -1836,14 +1829,17 @@ const InstallArtifactStep = struct {...@@ -1836,14 +1829,17 @@ const InstallArtifactStep = struct {
1836 return self;1829 return self;
1837 }1830 }
18381831
1839 fn make(step: &Step) %void {1832 fn make(step: &Step) !void {
1840 const self = @fieldParentPtr(Self, "step", step);1833 const self = @fieldParentPtr(Self, "step", step);
1841 const builder = self.builder;1834 const builder = self.builder;
18421835
1843 const mode = switch (self.artifact.kind) {1836 const mode = switch (builtin.os) {
1844 LibExeObjStep.Kind.Obj => unreachable,1837 builtin.Os.windows => {},
1845 LibExeObjStep.Kind.Exe => usize(0o755),1838 else => switch (self.artifact.kind) {
1846 LibExeObjStep.Kind.Lib => if (self.artifact.static) usize(0o666) else usize(0o755),1839 LibExeObjStep.Kind.Obj => unreachable,
1840 LibExeObjStep.Kind.Exe => u32(0o755),
1841 LibExeObjStep.Kind.Lib => if (self.artifact.static) u32(0o666) else u32(0o755),
1842 },
1847 };1843 };
1848 try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode);1844 try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode);
1849 if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {1845 if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
...@@ -1868,7 +1864,7 @@ pub const InstallFileStep = struct {...@@ -1868,7 +1864,7 @@ pub const InstallFileStep = struct {
1868 };1864 };
1869 }1865 }
18701866
1871 fn make(step: &Step) %void {1867 fn make(step: &Step) !void {
1872 const self = @fieldParentPtr(InstallFileStep, "step", step);1868 const self = @fieldParentPtr(InstallFileStep, "step", step);
1873 try self.builder.copyFile(self.src_path, self.dest_path);1869 try self.builder.copyFile(self.src_path, self.dest_path);
1874 }1870 }
...@@ -1889,7 +1885,7 @@ pub const WriteFileStep = struct {...@@ -1889,7 +1885,7 @@ pub const WriteFileStep = struct {
1889 };1885 };
1890 }1886 }
18911887
1892 fn make(step: &Step) %void {1888 fn make(step: &Step) !void {
1893 const self = @fieldParentPtr(WriteFileStep, "step", step);1889 const self = @fieldParentPtr(WriteFileStep, "step", step);
1894 const full_path = self.builder.pathFromRoot(self.file_path);1890 const full_path = self.builder.pathFromRoot(self.file_path);
1895 const full_path_dir = os.path.dirname(full_path);1891 const full_path_dir = os.path.dirname(full_path);
...@@ -1897,7 +1893,7 @@ pub const WriteFileStep = struct {...@@ -1897,7 +1893,7 @@ pub const WriteFileStep = struct {
1897 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));1893 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
1898 return err;1894 return err;
1899 };1895 };
1900 io.writeFile(full_path, self.data, self.builder.allocator) catch |err| {1896 io.writeFile(self.builder.allocator, full_path, self.data) catch |err| {
1901 warn("unable to write {}: {}\n", full_path, @errorName(err));1897 warn("unable to write {}: {}\n", full_path, @errorName(err));
1902 return err;1898 return err;
1903 };1899 };
...@@ -1917,7 +1913,7 @@ pub const LogStep = struct {...@@ -1917,7 +1913,7 @@ pub const LogStep = struct {
1917 };1913 };
1918 }1914 }
19191915
1920 fn make(step: &Step) %void {1916 fn make(step: &Step) error!void {
1921 const self = @fieldParentPtr(LogStep, "step", step);1917 const self = @fieldParentPtr(LogStep, "step", step);
1922 warn("{}", self.data);1918 warn("{}", self.data);
1923 }1919 }
...@@ -1936,7 +1932,7 @@ pub const RemoveDirStep = struct {...@@ -1936,7 +1932,7 @@ pub const RemoveDirStep = struct {
1936 };1932 };
1937 }1933 }
19381934
1939 fn make(step: &Step) %void {1935 fn make(step: &Step) !void {
1940 const self = @fieldParentPtr(RemoveDirStep, "step", step);1936 const self = @fieldParentPtr(RemoveDirStep, "step", step);
19411937
1942 const full_path = self.builder.pathFromRoot(self.dir_path);1938 const full_path = self.builder.pathFromRoot(self.dir_path);
...@@ -1949,12 +1945,12 @@ pub const RemoveDirStep = struct {...@@ -1949,12 +1945,12 @@ pub const RemoveDirStep = struct {
19491945
1950pub const Step = struct {1946pub const Step = struct {
1951 name: []const u8,1947 name: []const u8,
1952 makeFn: fn(self: &Step) %void,1948 makeFn: fn(self: &Step) error!void,
1953 dependencies: ArrayList(&Step),1949 dependencies: ArrayList(&Step),
1954 loop_flag: bool,1950 loop_flag: bool,
1955 done_flag: bool,1951 done_flag: bool,
19561952
1957 pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)%void) Step {1953 pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)error!void) Step {
1958 return Step {1954 return Step {
1959 .name = name,1955 .name = name,
1960 .makeFn = makeFn,1956 .makeFn = makeFn,
...@@ -1967,7 +1963,7 @@ pub const Step = struct {...@@ -1967,7 +1963,7 @@ pub const Step = struct {
1967 return init(name, allocator, makeNoOp);1963 return init(name, allocator, makeNoOp);
1968 }1964 }
19691965
1970 pub fn make(self: &Step) %void {1966 pub fn make(self: &Step) !void {
1971 if (self.done_flag)1967 if (self.done_flag)
1972 return;1968 return;
19731969
...@@ -1979,11 +1975,11 @@ pub const Step = struct {...@@ -1979,11 +1975,11 @@ pub const Step = struct {
1979 self.dependencies.append(other) catch unreachable;1975 self.dependencies.append(other) catch unreachable;
1980 }1976 }
19811977
1982 fn makeNoOp(self: &Step) %void {}1978 fn makeNoOp(self: &Step) error!void {}
1983};1979};
19841980
1985fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8,1981fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8,
1986 filename_name_only: []const u8) %void1982 filename_name_only: []const u8) !void
1987{1983{
1988 const out_dir = os.path.dirname(output_path);1984 const out_dir = os.path.dirname(output_path);
1989 const out_basename = os.path.basename(output_path);1985 const out_basename = os.path.basename(output_path);
std/c/index.zig+1-1
...@@ -20,7 +20,7 @@ pub extern "c" fn open(path: &const u8, oflag: c_int, ...) c_int;...@@ -20,7 +20,7 @@ pub extern "c" fn open(path: &const u8, oflag: c_int, ...) c_int;
20pub extern "c" fn raise(sig: c_int) c_int;20pub extern "c" fn raise(sig: c_int) c_int;
21pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize;21pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize;
22pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int;22pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int;
23pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) c_int;23pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) isize;
24pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int,24pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int,
25 fd: c_int, offset: isize) ?&c_void;25 fd: c_int, offset: isize) ?&c_void;
26pub extern "c" fn munmap(addr: &c_void, len: usize) c_int;26pub extern "c" fn munmap(addr: &c_void, len: usize) c_int;
std/crypto/throughput_test.zig+1-1
...@@ -18,7 +18,7 @@ const c = @cImport({...@@ -18,7 +18,7 @@ const c = @cImport({
1818
19const Mb = 1024 * 1024;19const Mb = 1024 * 1024;
2020
21pub fn main() %void {21pub fn main() !void {
22 var stdout_file = try std.io.getStdOut();22 var stdout_file = try std.io.getStdOut();
23 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);23 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
24 const stdout = &stdout_out_stream.stream;24 const stdout = &stdout_out_stream.stream;
std/cstr.zig+10-4
...@@ -1,8 +1,15 @@...@@ -1,8 +1,15 @@
1const std = @import("index.zig");1const std = @import("index.zig");
2const builtin = @import("builtin");
2const debug = std.debug;3const debug = std.debug;
3const mem = std.mem;4const mem = std.mem;
4const assert = debug.assert;5const assert = debug.assert;
56
7pub const line_sep = switch (builtin.os) {
8 builtin.Os.windows => "\r\n",
9 else => "\n",
10};
11
12
6pub fn len(ptr: &const u8) usize {13pub fn len(ptr: &const u8) usize {
7 var count: usize = 0;14 var count: usize = 0;
8 while (ptr[count] != 0) : (count += 1) {}15 while (ptr[count] != 0) : (count += 1) {}
...@@ -39,10 +46,9 @@ fn testCStrFnsImpl() void {...@@ -39,10 +46,9 @@ fn testCStrFnsImpl() void {
39 assert(len(c"123456789") == 9);46 assert(len(c"123456789") == 9);
40}47}
4148
42/// Returns a mutable slice with exactly the same size which is guaranteed to49/// Returns a mutable slice with 1 more byte of length which is a null byte.
43/// have a null byte after it.
44/// Caller owns the returned memory.50/// Caller owns the returned memory.
45pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) %[]u8 {51pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 {
46 const result = try allocator.alloc(u8, slice.len + 1);52 const result = try allocator.alloc(u8, slice.len + 1);
47 mem.copy(u8, result, slice);53 mem.copy(u8, result, slice);
48 result[slice.len] = 0;54 result[slice.len] = 0;
...@@ -56,7 +62,7 @@ pub const NullTerminated2DArray = struct {...@@ -56,7 +62,7 @@ pub const NullTerminated2DArray = struct {
5662
57 /// Takes N lists of strings, concatenates the lists together, and adds a null terminator63 /// Takes N lists of strings, concatenates the lists together, and adds a null terminator
58 /// Caller must deinit result64 /// Caller must deinit result
59 pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) %NullTerminated2DArray {65 pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray {
60 var new_len: usize = 1; // 1 for the list null66 var new_len: usize = 1; // 1 for the list null
61 var byte_count: usize = 0;67 var byte_count: usize = 0;
62 for (slices) |slice| {68 for (slices) |slice| {
std/debug/failing_allocator.zig+2-2
...@@ -28,7 +28,7 @@ pub const FailingAllocator = struct {...@@ -28,7 +28,7 @@ pub const FailingAllocator = struct {
28 };28 };
29 }29 }
3030
31 fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) %[]u8 {31 fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 {
32 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);32 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
33 if (self.index == self.fail_index) {33 if (self.index == self.fail_index) {
34 return error.OutOfMemory;34 return error.OutOfMemory;
...@@ -39,7 +39,7 @@ pub const FailingAllocator = struct {...@@ -39,7 +39,7 @@ pub const FailingAllocator = struct {
39 return result;39 return result;
40 }40 }
4141
42 fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {42 fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
43 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);43 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
44 if (new_size <= old_mem.len) {44 if (new_size <= old_mem.len) {
45 self.freed_bytes += old_mem.len - new_size;45 self.freed_bytes += old_mem.len - new_size;
std/debug/index.zig+55-55
...@@ -10,26 +10,17 @@ const builtin = @import("builtin");...@@ -10,26 +10,17 @@ const builtin = @import("builtin");
1010
11pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;11pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
1212
13error MissingDebugInfo;
14error InvalidDebugInfo;
15error UnsupportedDebugInfo;
16error UnknownObjectFormat;
17error TodoSupportCoffDebugInfo;
18error TodoSupportMachoDebugInfo;
19error TodoSupportCOFFDebugInfo;
20
21
22/// Tries to write to stderr, unbuffered, and ignores any error returned.13/// Tries to write to stderr, unbuffered, and ignores any error returned.
23/// Does not append a newline.14/// Does not append a newline.
24/// TODO atomic/multithread support15/// TODO atomic/multithread support
25var stderr_file: io.File = undefined;16var stderr_file: os.File = undefined;
26var stderr_file_out_stream: io.FileOutStream = undefined;17var stderr_file_out_stream: io.FileOutStream = undefined;
27var stderr_stream: ?&io.OutStream = null;18var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null;
28pub fn warn(comptime fmt: []const u8, args: ...) void {19pub fn warn(comptime fmt: []const u8, args: ...) void {
29 const stderr = getStderrStream() catch return;20 const stderr = getStderrStream() catch return;
30 stderr.print(fmt, args) catch return;21 stderr.print(fmt, args) catch return;
31}22}
32fn getStderrStream() %&io.OutStream {23fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) {
33 if (stderr_stream) |st| {24 if (stderr_stream) |st| {
34 return st;25 return st;
35 } else {26 } else {
...@@ -42,7 +33,7 @@ fn getStderrStream() %&io.OutStream {...@@ -42,7 +33,7 @@ fn getStderrStream() %&io.OutStream {
42}33}
4334
44var self_debug_info: ?&ElfStackTrace = null;35var self_debug_info: ?&ElfStackTrace = null;
45pub fn getSelfDebugInfo() %&ElfStackTrace {36pub fn getSelfDebugInfo() !&ElfStackTrace {
46 if (self_debug_info) |info| {37 if (self_debug_info) |info| {
47 return info;38 return info;
48 } else {39 } else {
...@@ -149,11 +140,8 @@ const WHITE = "\x1b[37;1m";...@@ -149,11 +140,8 @@ const WHITE = "\x1b[37;1m";
149const DIM = "\x1b[2m";140const DIM = "\x1b[2m";
150const RESET = "\x1b[0m";141const RESET = "\x1b[0m";
151142
152error PathNotFound;143pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator,
153error InvalidDebugInfo;144 debug_info: &ElfStackTrace, tty_color: bool) !void
154
155pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
156 debug_info: &ElfStackTrace, tty_color: bool) %void
157{145{
158 var frame_index: usize = undefined;146 var frame_index: usize = undefined;
159 var frames_left: usize = undefined;147 var frames_left: usize = undefined;
...@@ -174,8 +162,8 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.O...@@ -174,8 +162,8 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.O
174 }162 }
175}163}
176164
177pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,165pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
178 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) %void166 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) !void
179{167{
180 var ignored_count: usize = 0;168 var ignored_count: usize = 0;
181169
...@@ -191,7 +179,7 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat...@@ -191,7 +179,7 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
191 }179 }
192}180}
193181
194fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) %void {182fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
195 if (builtin.os == builtin.Os.windows) {183 if (builtin.os == builtin.Os.windows) {
196 return error.UnsupportedDebugInfo;184 return error.UnsupportedDebugInfo;
197 }185 }
...@@ -221,7 +209,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a...@@ -221,7 +209,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
221 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");209 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
222 }210 }
223 } else |err| switch (err) {211 } else |err| switch (err) {
224 error.EndOfFile, error.PathNotFound => {},212 error.EndOfFile => {},
225 else => return err,213 else => return err,
226 }214 }
227 } else |err| switch (err) {215 } else |err| switch (err) {
...@@ -232,7 +220,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a...@@ -232,7 +220,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
232 }220 }
233}221}
234222
235pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {223pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
236 switch (builtin.object_format) {224 switch (builtin.object_format) {
237 builtin.ObjectFormat.elf => {225 builtin.ObjectFormat.elf => {
238 const st = try allocator.create(ElfStackTrace);226 const st = try allocator.create(ElfStackTrace);
...@@ -276,8 +264,8 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {...@@ -276,8 +264,8 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {
276 }264 }
277}265}
278266
279fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) %void {267fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void {
280 var f = try io.File.openRead(line_info.file_name, allocator);268 var f = try os.File.openRead(allocator, line_info.file_name);
281 defer f.close();269 defer f.close();
282 // TODO fstat and make sure that the file has the correct size270 // TODO fstat and make sure that the file has the correct size
283271
...@@ -310,7 +298,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_...@@ -310,7 +298,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
310}298}
311299
312pub const ElfStackTrace = struct {300pub const ElfStackTrace = struct {
313 self_exe_file: io.File,301 self_exe_file: os.File,
314 elf: elf.Elf,302 elf: elf.Elf,
315 debug_info: &elf.SectionHeader,303 debug_info: &elf.SectionHeader,
316 debug_abbrev: &elf.SectionHeader,304 debug_abbrev: &elf.SectionHeader,
...@@ -324,7 +312,7 @@ pub const ElfStackTrace = struct {...@@ -324,7 +312,7 @@ pub const ElfStackTrace = struct {
324 return self.abbrev_table_list.allocator;312 return self.abbrev_table_list.allocator;
325 }313 }
326314
327 pub fn readString(self: &ElfStackTrace) %[]u8 {315 pub fn readString(self: &ElfStackTrace) ![]u8 {
328 var in_file_stream = io.FileInStream.init(&self.self_exe_file);316 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
329 const in_stream = &in_file_stream.stream;317 const in_stream = &in_file_stream.stream;
330 return readStringRaw(self.allocator(), in_stream);318 return readStringRaw(self.allocator(), in_stream);
...@@ -387,7 +375,7 @@ const Constant = struct {...@@ -387,7 +375,7 @@ const Constant = struct {
387 payload: []u8,375 payload: []u8,
388 signed: bool,376 signed: bool,
389377
390 fn asUnsignedLe(self: &const Constant) %u64 {378 fn asUnsignedLe(self: &const Constant) !u64 {
391 if (self.payload.len > @sizeOf(u64))379 if (self.payload.len > @sizeOf(u64))
392 return error.InvalidDebugInfo;380 return error.InvalidDebugInfo;
393 if (self.signed)381 if (self.signed)
...@@ -414,7 +402,7 @@ const Die = struct {...@@ -414,7 +402,7 @@ const Die = struct {
414 return null;402 return null;
415 }403 }
416404
417 fn getAttrAddr(self: &const Die, id: u64) %u64 {405 fn getAttrAddr(self: &const Die, id: u64) !u64 {
418 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;406 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
419 return switch (*form_value) {407 return switch (*form_value) {
420 FormValue.Address => |value| value,408 FormValue.Address => |value| value,
...@@ -422,7 +410,7 @@ const Die = struct {...@@ -422,7 +410,7 @@ const Die = struct {
422 };410 };
423 }411 }
424412
425 fn getAttrSecOffset(self: &const Die, id: u64) %u64 {413 fn getAttrSecOffset(self: &const Die, id: u64) !u64 {
426 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;414 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
427 return switch (*form_value) {415 return switch (*form_value) {
428 FormValue.Const => |value| value.asUnsignedLe(),416 FormValue.Const => |value| value.asUnsignedLe(),
...@@ -431,7 +419,7 @@ const Die = struct {...@@ -431,7 +419,7 @@ const Die = struct {
431 };419 };
432 }420 }
433421
434 fn getAttrUnsignedLe(self: &const Die, id: u64) %u64 {422 fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 {
435 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;423 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
436 return switch (*form_value) {424 return switch (*form_value) {
437 FormValue.Const => |value| value.asUnsignedLe(),425 FormValue.Const => |value| value.asUnsignedLe(),
...@@ -439,7 +427,7 @@ const Die = struct {...@@ -439,7 +427,7 @@ const Die = struct {
439 };427 };
440 }428 }
441429
442 fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) %[]u8 {430 fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 {
443 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;431 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
444 return switch (*form_value) {432 return switch (*form_value) {
445 FormValue.String => |value| value,433 FormValue.String => |value| value,
...@@ -512,7 +500,7 @@ const LineNumberProgram = struct {...@@ -512,7 +500,7 @@ const LineNumberProgram = struct {
512 };500 };
513 }501 }
514502
515 pub fn checkLineMatch(self: &LineNumberProgram) %?LineInfo {503 pub fn checkLineMatch(self: &LineNumberProgram) !?LineInfo {
516 if (self.target_address >= self.prev_address and self.target_address < self.address) {504 if (self.target_address >= self.prev_address and self.target_address < self.address) {
517 const file_entry = if (self.prev_file == 0) {505 const file_entry = if (self.prev_file == 0) {
518 return error.MissingDebugInfo;506 return error.MissingDebugInfo;
...@@ -544,7 +532,7 @@ const LineNumberProgram = struct {...@@ -544,7 +532,7 @@ const LineNumberProgram = struct {
544 }532 }
545};533};
546534
547fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {535fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 {
548 var buf = ArrayList(u8).init(allocator);536 var buf = ArrayList(u8).init(allocator);
549 while (true) {537 while (true) {
550 const byte = try in_stream.readByte();538 const byte = try in_stream.readByte();
...@@ -555,58 +543,70 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {...@@ -555,58 +543,70 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {
555 return buf.toSlice();543 return buf.toSlice();
556}544}
557545
558fn getString(st: &ElfStackTrace, offset: u64) %[]u8 {546fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
559 const pos = st.debug_str.offset + offset;547 const pos = st.debug_str.offset + offset;
560 try st.self_exe_file.seekTo(pos);548 try st.self_exe_file.seekTo(pos);
561 return st.readString();549 return st.readString();
562}550}
563551
564fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %[]u8 {552fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
565 const buf = try global_allocator.alloc(u8, size);553 const buf = try global_allocator.alloc(u8, size);
566 errdefer global_allocator.free(buf);554 errdefer global_allocator.free(buf);
567 if ((try in_stream.read(buf)) < size) return error.EndOfFile;555 if ((try in_stream.read(buf)) < size) return error.EndOfFile;
568 return buf;556 return buf;
569}557}
570558
571fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {559fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
572 const buf = try readAllocBytes(allocator, in_stream, size);560 const buf = try readAllocBytes(allocator, in_stream, size);
573 return FormValue { .Block = buf };561 return FormValue { .Block = buf };
574}562}
575563
576fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {564fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
577 const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size);565 const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size);
578 return parseFormValueBlockLen(allocator, in_stream, block_len);566 return parseFormValueBlockLen(allocator, in_stream, block_len);
579}567}
580568
581fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) %FormValue {569fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
582 return FormValue { .Const = Constant {570 return FormValue { .Const = Constant {
583 .signed = signed,571 .signed = signed,
584 .payload = try readAllocBytes(allocator, in_stream, size),572 .payload = try readAllocBytes(allocator, in_stream, size),
585 }};573 }};
586}574}
587575
588fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) %u64 {576fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
589 return if (is_64) try in_stream.readIntLe(u64)577 return if (is_64) try in_stream.readIntLe(u64)
590 else u64(try in_stream.readIntLe(u32)) ;578 else u64(try in_stream.readIntLe(u32)) ;
591}579}
592580
593fn parseFormValueTargetAddrSize(in_stream: &io.InStream) %u64 {581fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
594 return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32))582 return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32))
595 else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64)583 else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64)
596 else unreachable;584 else unreachable;
597}585}
598586
599fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {587fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
600 const buf = try readAllocBytes(allocator, in_stream, size);588 const buf = try readAllocBytes(allocator, in_stream, size);
601 return FormValue { .Ref = buf };589 return FormValue { .Ref = buf };
602}590}
603591
604fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) %FormValue {592fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue {
605 const block_len = try in_stream.readIntLe(T);593 const block_len = try in_stream.readIntLe(T);
606 return parseFormValueRefLen(allocator, in_stream, block_len);594 return parseFormValueRefLen(allocator, in_stream, block_len);
607}595}
608596
609fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) %FormValue {597const ParseFormValueError = error {
598 EndOfStream,
599 Io,
600 BadFd,
601 Unexpected,
602 InvalidDebugInfo,
603 EndOfFile,
604 OutOfMemory,
605};
606
607fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool)
608 ParseFormValueError!FormValue
609{
610 return switch (form_id) {610 return switch (form_id) {
611 DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) },611 DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) },
612 DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),612 DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
...@@ -656,7 +656,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u...@@ -656,7 +656,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
656 };656 };
657}657}
658658
659fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {659fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable {
660 const in_file = &st.self_exe_file;660 const in_file = &st.self_exe_file;
661 var in_file_stream = io.FileInStream.init(in_file);661 var in_file_stream = io.FileInStream.init(in_file);
662 const in_stream = &in_file_stream.stream;662 const in_stream = &in_file_stream.stream;
...@@ -688,7 +688,7 @@ fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {...@@ -688,7 +688,7 @@ fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {
688688
689/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,689/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
690/// seeks in the stream and parses it.690/// seeks in the stream and parses it.
691fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) %&const AbbrevTable {691fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable {
692 for (st.abbrev_table_list.toSlice()) |*header| {692 for (st.abbrev_table_list.toSlice()) |*header| {
693 if (header.offset == abbrev_offset) {693 if (header.offset == abbrev_offset) {
694 return &header.table;694 return &header.table;
...@@ -710,7 +710,7 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&con...@@ -710,7 +710,7 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&con
710 return null;710 return null;
711}711}
712712
713fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) %Die {713fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) !Die {
714 const in_file = &st.self_exe_file;714 const in_file = &st.self_exe_file;
715 var in_file_stream = io.FileInStream.init(in_file);715 var in_file_stream = io.FileInStream.init(in_file);
716 const in_stream = &in_file_stream.stream;716 const in_stream = &in_file_stream.stream;
...@@ -732,7 +732,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) %...@@ -732,7 +732,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) %
732 return result;732 return result;
733}733}
734734
735fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) %LineInfo {735fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) !LineInfo {
736 const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);736 const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);
737737
738 const in_file = &st.self_exe_file;738 const in_file = &st.self_exe_file;
...@@ -747,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe...@@ -747,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
747 try in_file.seekTo(this_offset);747 try in_file.seekTo(this_offset);
748748
749 var is_64: bool = undefined;749 var is_64: bool = undefined;
750 const unit_length = try readInitialLength(in_stream, &is_64);750 const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
751 if (unit_length == 0)751 if (unit_length == 0)
752 return error.MissingDebugInfo;752 return error.MissingDebugInfo;
753 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));753 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
...@@ -910,7 +910,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe...@@ -910,7 +910,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
910 return error.MissingDebugInfo;910 return error.MissingDebugInfo;
911}911}
912912
913fn scanAllCompileUnits(st: &ElfStackTrace) %void {913fn scanAllCompileUnits(st: &ElfStackTrace) !void {
914 const debug_info_end = st.debug_info.offset + st.debug_info.size;914 const debug_info_end = st.debug_info.offset + st.debug_info.size;
915 var this_unit_offset = st.debug_info.offset;915 var this_unit_offset = st.debug_info.offset;
916 var cu_index: usize = 0;916 var cu_index: usize = 0;
...@@ -922,7 +922,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {...@@ -922,7 +922,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {
922 try st.self_exe_file.seekTo(this_unit_offset);922 try st.self_exe_file.seekTo(this_unit_offset);
923923
924 var is_64: bool = undefined;924 var is_64: bool = undefined;
925 const unit_length = try readInitialLength(in_stream, &is_64);925 const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
926 if (unit_length == 0)926 if (unit_length == 0)
927 return;927 return;
928 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));928 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
...@@ -986,7 +986,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {...@@ -986,7 +986,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {
986 }986 }
987}987}
988988
989fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit {989fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit {
990 var in_file_stream = io.FileInStream.init(&st.self_exe_file);990 var in_file_stream = io.FileInStream.init(&st.self_exe_file);
991 const in_stream = &in_file_stream.stream;991 const in_stream = &in_file_stream.stream;
992 for (st.compile_unit_list.toSlice()) |*compile_unit| {992 for (st.compile_unit_list.toSlice()) |*compile_unit| {
...@@ -1022,7 +1022,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit...@@ -1022,7 +1022,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit
1022 return error.MissingDebugInfo;1022 return error.MissingDebugInfo;
1023}1023}
10241024
1025fn readInitialLength(in_stream: &io.InStream, is_64: &bool) %u64 {1025fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 {
1026 const first_32_bits = try in_stream.readIntLe(u32);1026 const first_32_bits = try in_stream.readIntLe(u32);
1027 *is_64 = (first_32_bits == 0xffffffff);1027 *is_64 = (first_32_bits == 0xffffffff);
1028 if (*is_64) {1028 if (*is_64) {
...@@ -1033,7 +1033,7 @@ fn readInitialLength(in_stream: &io.InStream, is_64: &bool) %u64 {...@@ -1033,7 +1033,7 @@ fn readInitialLength(in_stream: &io.InStream, is_64: &bool) %u64 {
1033 }1033 }
1034}1034}
10351035
1036fn readULeb128(in_stream: &io.InStream) %u64 {1036fn readULeb128(in_stream: var) !u64 {
1037 var result: u64 = 0;1037 var result: u64 = 0;
1038 var shift: usize = 0;1038 var shift: usize = 0;
10391039
...@@ -1054,7 +1054,7 @@ fn readULeb128(in_stream: &io.InStream) %u64 {...@@ -1054,7 +1054,7 @@ fn readULeb128(in_stream: &io.InStream) %u64 {
1054 }1054 }
1055}1055}
10561056
1057fn readILeb128(in_stream: &io.InStream) %i64 {1057fn readILeb128(in_stream: var) !i64 {
1058 var result: i64 = 0;1058 var result: i64 = 0;
1059 var shift: usize = 0;1059 var shift: usize = 0;
10601060
std/elf.zig+7-8
...@@ -1,13 +1,12 @@...@@ -1,13 +1,12 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("index.zig");2const std = @import("index.zig");
3const io = std.io;3const io = std.io;
4const os = std.os;
4const math = std.math;5const math = std.math;
5const mem = std.mem;6const mem = std.mem;
6const debug = std.debug;7const debug = std.debug;
7const InStream = std.stream.InStream;8const InStream = std.stream.InStream;
89
9error InvalidFormat;
10
11pub const SHT_NULL = 0;10pub const SHT_NULL = 0;
12pub const SHT_PROGBITS = 1;11pub const SHT_PROGBITS = 1;
13pub const SHT_SYMTAB = 2;12pub const SHT_SYMTAB = 2;
...@@ -65,7 +64,7 @@ pub const SectionHeader = struct {...@@ -65,7 +64,7 @@ pub const SectionHeader = struct {
65};64};
6665
67pub const Elf = struct {66pub const Elf = struct {
68 in_file: &io.File,67 in_file: &os.File,
69 auto_close_stream: bool,68 auto_close_stream: bool,
70 is_64: bool,69 is_64: bool,
71 endian: builtin.Endian,70 endian: builtin.Endian,
...@@ -78,17 +77,17 @@ pub const Elf = struct {...@@ -78,17 +77,17 @@ pub const Elf = struct {
78 string_section: &SectionHeader,77 string_section: &SectionHeader,
79 section_headers: []SectionHeader,78 section_headers: []SectionHeader,
80 allocator: &mem.Allocator,79 allocator: &mem.Allocator,
81 prealloc_file: io.File,80 prealloc_file: os.File,
8281
83 /// Call close when done.82 /// Call close when done.
84 pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) %void {83 pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void {
85 try elf.prealloc_file.open(path);84 try elf.prealloc_file.open(path);
86 try elf.openFile(allocator, &elf.prealloc_file);85 try elf.openFile(allocator, &elf.prealloc_file);
87 elf.auto_close_stream = true;86 elf.auto_close_stream = true;
88 }87 }
8988
90 /// Call close when done.89 /// Call close when done.
91 pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) %void {90 pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &os.File) !void {
92 elf.allocator = allocator;91 elf.allocator = allocator;
93 elf.in_file = file;92 elf.in_file = file;
94 elf.auto_close_stream = false;93 elf.auto_close_stream = false;
...@@ -239,7 +238,7 @@ pub const Elf = struct {...@@ -239,7 +238,7 @@ pub const Elf = struct {
239 elf.in_file.close();238 elf.in_file.close();
240 }239 }
241240
242 pub fn findSection(elf: &Elf, name: []const u8) %?&SectionHeader {241 pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader {
243 var file_stream = io.FileInStream.init(elf.in_file);242 var file_stream = io.FileInStream.init(elf.in_file);
244 const in = &file_stream.stream;243 const in = &file_stream.stream;
245244
...@@ -263,7 +262,7 @@ pub const Elf = struct {...@@ -263,7 +262,7 @@ pub const Elf = struct {
263 return null;262 return null;
264 }263 }
265264
266 pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) %void {265 pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void {
267 try elf.in_file.seekTo(elf_section.offset);266 try elf.in_file.seekTo(elf_section.offset);
268 }267 }
269};268};
std/fmt/index.zig+56-52
...@@ -24,8 +24,8 @@ const State = enum { // TODO put inside format function and make sure the name a...@@ -24,8 +24,8 @@ const State = enum { // TODO put inside format function and make sure the name a
24/// Renders fmt string with args, calling output with slices of bytes.24/// Renders fmt string with args, calling output with slices of bytes.
25/// If `output` returns an error, the error is returned from `format` and25/// If `output` returns an error, the error is returned from `format` and
26/// `output` is not called again.26/// `output` is not called again.
27pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,27pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
28 comptime fmt: []const u8, args: ...) %void28 comptime fmt: []const u8, args: ...) Errors!void
29{29{
30 comptime var start_index = 0;30 comptime var start_index = 0;
31 comptime var state = State.Start;31 comptime var state = State.Start;
...@@ -58,7 +58,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -58,7 +58,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
58 start_index = i;58 start_index = i;
59 },59 },
60 '}' => {60 '}' => {
61 try formatValue(args[next_arg], context, output);61 try formatValue(args[next_arg], context, Errors, output);
62 next_arg += 1;62 next_arg += 1;
63 state = State.Start;63 state = State.Start;
64 start_index = i + 1;64 start_index = i + 1;
...@@ -110,7 +110,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -110,7 +110,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
110 },110 },
111 State.Integer => switch (c) {111 State.Integer => switch (c) {
112 '}' => {112 '}' => {
113 try formatInt(args[next_arg], radix, uppercase, width, context, output);113 try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
114 next_arg += 1;114 next_arg += 1;
115 state = State.Start;115 state = State.Start;
116 start_index = i + 1;116 start_index = i + 1;
...@@ -124,7 +124,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -124,7 +124,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
124 State.IntegerWidth => switch (c) {124 State.IntegerWidth => switch (c) {
125 '}' => {125 '}' => {
126 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);126 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
127 try formatInt(args[next_arg], radix, uppercase, width, context, output);127 try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
128 next_arg += 1;128 next_arg += 1;
129 state = State.Start;129 state = State.Start;
130 start_index = i + 1;130 start_index = i + 1;
...@@ -134,7 +134,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -134,7 +134,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
134 },134 },
135 State.Float => switch (c) {135 State.Float => switch (c) {
136 '}' => {136 '}' => {
137 try formatFloatDecimal(args[next_arg], 0, context, output);137 try formatFloatDecimal(args[next_arg], 0, context, Errors, output);
138 next_arg += 1;138 next_arg += 1;
139 state = State.Start;139 state = State.Start;
140 start_index = i + 1;140 start_index = i + 1;
...@@ -148,7 +148,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -148,7 +148,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
148 State.FloatWidth => switch (c) {148 State.FloatWidth => switch (c) {
149 '}' => {149 '}' => {
150 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);150 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
151 try formatFloatDecimal(args[next_arg], width, context, output);151 try formatFloatDecimal(args[next_arg], width, context, Errors, output);
152 next_arg += 1;152 next_arg += 1;
153 state = State.Start;153 state = State.Start;
154 start_index = i + 1;154 start_index = i + 1;
...@@ -159,7 +159,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -159,7 +159,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
159 State.BufWidth => switch (c) {159 State.BufWidth => switch (c) {
160 '}' => {160 '}' => {
161 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);161 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
162 try formatBuf(args[next_arg], width, context, output);162 try formatBuf(args[next_arg], width, context, Errors, output);
163 next_arg += 1;163 next_arg += 1;
164 state = State.Start;164 state = State.Start;
165 start_index = i + 1;165 start_index = i + 1;
...@@ -169,7 +169,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -169,7 +169,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
169 },169 },
170 State.Character => switch (c) {170 State.Character => switch (c) {
171 '}' => {171 '}' => {
172 try formatAsciiChar(args[next_arg], context, output);172 try formatAsciiChar(args[next_arg], context, Errors, output);
173 next_arg += 1;173 next_arg += 1;
174 state = State.Start;174 state = State.Start;
175 start_index = i + 1;175 start_index = i + 1;
...@@ -191,14 +191,14 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,...@@ -191,14 +191,14 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
191 }191 }
192}192}
193193
194pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)%void) %void {194pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
195 const T = @typeOf(value);195 const T = @typeOf(value);
196 switch (@typeId(T)) {196 switch (@typeId(T)) {
197 builtin.TypeId.Int => {197 builtin.TypeId.Int => {
198 return formatInt(value, 10, false, 0, context, output);198 return formatInt(value, 10, false, 0, context, Errors, output);
199 },199 },
200 builtin.TypeId.Float => {200 builtin.TypeId.Float => {
201 return formatFloat(value, context, output);201 return formatFloat(value, context, Errors, output);
202 },202 },
203 builtin.TypeId.Void => {203 builtin.TypeId.Void => {
204 return output(context, "void");204 return output(context, "void");
...@@ -208,19 +208,19 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons...@@ -208,19 +208,19 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
208 },208 },
209 builtin.TypeId.Nullable => {209 builtin.TypeId.Nullable => {
210 if (value) |payload| {210 if (value) |payload| {
211 return formatValue(payload, context, output);211 return formatValue(payload, context, Errors, output);
212 } else {212 } else {
213 return output(context, "null");213 return output(context, "null");
214 }214 }
215 },215 },
216 builtin.TypeId.ErrorUnion => {216 builtin.TypeId.ErrorUnion => {
217 if (value) |payload| {217 if (value) |payload| {
218 return formatValue(payload, context, output);218 return formatValue(payload, context, Errors, output);
219 } else |err| {219 } else |err| {
220 return formatValue(err, context, output);220 return formatValue(err, context, Errors, output);
221 }221 }
222 },222 },
223 builtin.TypeId.Error => {223 builtin.TypeId.ErrorSet => {
224 try output(context, "error.");224 try output(context, "error.");
225 return output(context, @errorName(value));225 return output(context, @errorName(value));
226 },226 },
...@@ -228,7 +228,7 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons...@@ -228,7 +228,7 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
228 if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) {228 if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) {
229 return output(context, (*value)[0..]);229 return output(context, (*value)[0..]);
230 } else {230 } else {
231 return format(context, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));231 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
232 }232 }
233 },233 },
234 else => if (@canImplicitCast([]const u8, value)) {234 else => if (@canImplicitCast([]const u8, value)) {
...@@ -240,12 +240,12 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons...@@ -240,12 +240,12 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
240 }240 }
241}241}
242242
243pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)%void) %void {243pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
244 return output(context, (&c)[0..1]);244 return output(context, (&c)[0..1]);
245}245}
246246
247pub fn formatBuf(buf: []const u8, width: usize,247pub fn formatBuf(buf: []const u8, width: usize,
248 context: var, output: fn(@typeOf(context), []const u8)%void) %void248 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
249{249{
250 try output(context, buf);250 try output(context, buf);
251251
...@@ -256,7 +256,7 @@ pub fn formatBuf(buf: []const u8, width: usize,...@@ -256,7 +256,7 @@ pub fn formatBuf(buf: []const u8, width: usize,
256 }256 }
257}257}
258258
259pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)%void) %void {259pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
260 var x = f64(value);260 var x = f64(value);
261261
262 // Errol doesn't handle these special cases.262 // Errol doesn't handle these special cases.
...@@ -290,11 +290,11 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons...@@ -290,11 +290,11 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
290290
291 if (float_decimal.exp != 1) {291 if (float_decimal.exp != 1) {
292 try output(context, "e");292 try output(context, "e");
293 try formatInt(float_decimal.exp - 1, 10, false, 0, context, output);293 try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);
294 }294 }
295}295}
296296
297pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn(@typeOf(context), []const u8)%void) %void {297pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
298 var x = f64(value);298 var x = f64(value);
299299
300 // Errol doesn't handle these special cases.300 // Errol doesn't handle these special cases.
...@@ -336,17 +336,17 @@ pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn...@@ -336,17 +336,17 @@ pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn
336336
337337
338pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,338pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
339 context: var, output: fn(@typeOf(context), []const u8)%void) %void339 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
340{340{
341 if (@typeOf(value).is_signed) {341 if (@typeOf(value).is_signed) {
342 return formatIntSigned(value, base, uppercase, width, context, output);342 return formatIntSigned(value, base, uppercase, width, context, Errors, output);
343 } else {343 } else {
344 return formatIntUnsigned(value, base, uppercase, width, context, output);344 return formatIntUnsigned(value, base, uppercase, width, context, Errors, output);
345 }345 }
346}346}
347347
348fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,348fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
349 context: var, output: fn(@typeOf(context), []const u8)%void) %void349 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
350{350{
351 const uint = @IntType(false, @typeOf(value).bit_count);351 const uint = @IntType(false, @typeOf(value).bit_count);
352 if (value < 0) {352 if (value < 0) {
...@@ -354,20 +354,20 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,...@@ -354,20 +354,20 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
354 try output(context, (&minus_sign)[0..1]);354 try output(context, (&minus_sign)[0..1]);
355 const new_value = uint(-(value + 1)) + 1;355 const new_value = uint(-(value + 1)) + 1;
356 const new_width = if (width == 0) 0 else (width - 1);356 const new_width = if (width == 0) 0 else (width - 1);
357 return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);357 return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
358 } else if (width == 0) {358 } else if (width == 0) {
359 return formatIntUnsigned(uint(value), base, uppercase, width, context, output);359 return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output);
360 } else {360 } else {
361 const plus_sign: u8 = '+';361 const plus_sign: u8 = '+';
362 try output(context, (&plus_sign)[0..1]);362 try output(context, (&plus_sign)[0..1]);
363 const new_value = uint(value);363 const new_value = uint(value);
364 const new_width = if (width == 0) 0 else (width - 1);364 const new_width = if (width == 0) 0 else (width - 1);
365 return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);365 return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
366 }366 }
367}367}
368368
369fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,369fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
370 context: var, output: fn(@typeOf(context), []const u8)%void) %void370 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
371{371{
372 // max_int_digits accounts for the minus sign. when printing an unsigned372 // max_int_digits accounts for the minus sign. when printing an unsigned
373 // number we don't need to do that.373 // number we don't need to do that.
...@@ -410,19 +410,19 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width:...@@ -410,19 +410,19 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width:
410 .out_buf = out_buf,410 .out_buf = out_buf,
411 .index = 0,411 .index = 0,
412 };412 };
413 formatInt(value, base, uppercase, width, &context, formatIntCallback) catch unreachable;413 formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable;
414 return context.index;414 return context.index;
415}415}
416const FormatIntBuf = struct {416const FormatIntBuf = struct {
417 out_buf: []u8,417 out_buf: []u8,
418 index: usize,418 index: usize,
419};419};
420fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) %void {420fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) {
421 mem.copy(u8, context.out_buf[context.index..], bytes);421 mem.copy(u8, context.out_buf[context.index..], bytes);
422 context.index += bytes.len;422 context.index += bytes.len;
423}423}
424424
425pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T {425pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {
426 if (!T.is_signed)426 if (!T.is_signed)
427 return parseUnsigned(T, buf, radix);427 return parseUnsigned(T, buf, radix);
428 if (buf.len == 0)428 if (buf.len == 0)
...@@ -439,14 +439,21 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T {...@@ -439,14 +439,21 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T {
439test "fmt.parseInt" {439test "fmt.parseInt" {
440 assert((parseInt(i32, "-10", 10) catch unreachable) == -10);440 assert((parseInt(i32, "-10", 10) catch unreachable) == -10);
441 assert((parseInt(i32, "+10", 10) catch unreachable) == 10);441 assert((parseInt(i32, "+10", 10) catch unreachable) == 10);
442 assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidChar);442 assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
443 assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidChar);443 assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter);
444 assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidChar);444 assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter);
445 assert((parseInt(u8, "255", 10) catch unreachable) == 255);445 assert((parseInt(u8, "255", 10) catch unreachable) == 255);
446 assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);446 assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
447}447}
448448
449pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T {449const ParseUnsignedError = error {
450 /// The result cannot fit in the type specified
451 Overflow,
452 /// The input had a byte that was not a digit
453 InvalidCharacter,
454};
455
456pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T {
450 var x: T = 0;457 var x: T = 0;
451458
452 for (buf) |c| {459 for (buf) |c| {
...@@ -458,17 +465,16 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T {...@@ -458,17 +465,16 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T {
458 return x;465 return x;
459}466}
460467
461error InvalidChar;468fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
462fn charToDigit(c: u8, radix: u8) %u8 {
463 const value = switch (c) {469 const value = switch (c) {
464 '0' ... '9' => c - '0',470 '0' ... '9' => c - '0',
465 'A' ... 'Z' => c - 'A' + 10,471 'A' ... 'Z' => c - 'A' + 10,
466 'a' ... 'z' => c - 'a' + 10,472 'a' ... 'z' => c - 'a' + 10,
467 else => return error.InvalidChar,473 else => return error.InvalidCharacter,
468 };474 };
469475
470 if (value >= radix)476 if (value >= radix)
471 return error.InvalidChar;477 return error.InvalidCharacter;
472478
473 return value;479 return value;
474}480}
...@@ -485,28 +491,26 @@ const BufPrintContext = struct {...@@ -485,28 +491,26 @@ const BufPrintContext = struct {
485 remaining: []u8,491 remaining: []u8,
486};492};
487493
488error BufferTooSmall;494fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void {
489fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) %void {
490 if (context.remaining.len < bytes.len) return error.BufferTooSmall;495 if (context.remaining.len < bytes.len) return error.BufferTooSmall;
491 mem.copy(u8, context.remaining, bytes);496 mem.copy(u8, context.remaining, bytes);
492 context.remaining = context.remaining[bytes.len..];497 context.remaining = context.remaining[bytes.len..];
493}498}
494499
495pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) %[]u8 {500pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
496 var context = BufPrintContext { .remaining = buf, };501 var context = BufPrintContext { .remaining = buf, };
497 try format(&context, bufPrintWrite, fmt, args);502 try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args);
498 return buf[0..buf.len - context.remaining.len];503 return buf[0..buf.len - context.remaining.len];
499}504}
500505
501pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) %[]u8 {506pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {
502 var size: usize = 0;507 var size: usize = 0;
503 // Cannot fail because `countSize` cannot fail.508 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
504 format(&size, countSize, fmt, args) catch unreachable;
505 const buf = try allocator.alloc(u8, size);509 const buf = try allocator.alloc(u8, size);
506 return bufPrint(buf, fmt, args);510 return bufPrint(buf, fmt, args);
507}511}
508512
509fn countSize(size: &usize, bytes: []const u8) %void {513fn countSize(size: &usize, bytes: []const u8) (error{}!void) {
510 *size += bytes.len;514 *size += bytes.len;
511}515}
512516
...@@ -534,7 +538,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u...@@ -534,7 +538,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u
534538
535test "parse u64 digit too big" {539test "parse u64 digit too big" {
536 _ = parseUnsigned(u64, "123a", 10) catch |err| {540 _ = parseUnsigned(u64, "123a", 10) catch |err| {
537 if (err == error.InvalidChar) return;541 if (err == error.InvalidCharacter) return;
538 unreachable;542 unreachable;
539 };543 };
540 unreachable;544 unreachable;
...@@ -567,13 +571,13 @@ test "fmt.format" {...@@ -567,13 +571,13 @@ test "fmt.format" {
567 }571 }
568 {572 {
569 var buf1: [32]u8 = undefined;573 var buf1: [32]u8 = undefined;
570 const value: %i32 = 1234;574 const value: error!i32 = 1234;
571 const result = try bufPrint(buf1[0..], "error union: {}\n", value);575 const result = try bufPrint(buf1[0..], "error union: {}\n", value);
572 assert(mem.eql(u8, result, "error union: 1234\n"));576 assert(mem.eql(u8, result, "error union: 1234\n"));
573 }577 }
574 {578 {
575 var buf1: [32]u8 = undefined;579 var buf1: [32]u8 = undefined;
576 const value: %i32 = error.InvalidChar;580 const value: error!i32 = error.InvalidChar;
577 const result = try bufPrint(buf1[0..], "error union: {}\n", value);581 const result = try bufPrint(buf1[0..], "error union: {}\n", value);
578 assert(mem.eql(u8, result, "error union: error.InvalidChar\n"));582 assert(mem.eql(u8, result, "error union: error.InvalidChar\n"));
579 }583 }
std/hash_map.zig+2-2
...@@ -80,7 +80,7 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -80,7 +80,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
80 }80 }
8181
82 /// Returns the value that was already there.82 /// Returns the value that was already there.
83 pub fn put(hm: &Self, key: K, value: &const V) %?V {83 pub fn put(hm: &Self, key: K, value: &const V) !?V {
84 if (hm.entries.len == 0) {84 if (hm.entries.len == 0) {
85 try hm.initCapacity(16);85 try hm.initCapacity(16);
86 }86 }
...@@ -151,7 +151,7 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -151,7 +151,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
151 };151 };
152 }152 }
153153
154 fn initCapacity(hm: &Self, capacity: usize) %void {154 fn initCapacity(hm: &Self, capacity: usize) !void {
155 hm.entries = try hm.allocator.alloc(Entry, capacity);155 hm.entries = try hm.allocator.alloc(Entry, capacity);
156 hm.size = 0;156 hm.size = 0;
157 hm.max_distance_from_start_index = 0;157 hm.max_distance_from_start_index = 0;
std/heap.zig+5-7
...@@ -9,8 +9,6 @@ const c = std.c;...@@ -9,8 +9,6 @@ const c = std.c;
99
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
1111
12error OutOfMemory;
13
14pub const c_allocator = &c_allocator_state;12pub const c_allocator = &c_allocator_state;
15var c_allocator_state = Allocator {13var c_allocator_state = Allocator {
16 .allocFn = cAlloc,14 .allocFn = cAlloc,
...@@ -18,14 +16,14 @@ var c_allocator_state = Allocator {...@@ -18,14 +16,14 @@ var c_allocator_state = Allocator {
18 .freeFn = cFree,16 .freeFn = cFree,
19};17};
2018
21fn cAlloc(self: &Allocator, n: usize, alignment: u29) %[]u8 {19fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 {
22 return if (c.malloc(usize(n))) |buf|20 return if (c.malloc(usize(n))) |buf|
23 @ptrCast(&u8, buf)[0..n]21 @ptrCast(&u8, buf)[0..n]
24 else22 else
25 error.OutOfMemory;23 error.OutOfMemory;
26}24}
2725
28fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {26fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
29 const old_ptr = @ptrCast(&c_void, old_mem.ptr);27 const old_ptr = @ptrCast(&c_void, old_mem.ptr);
30 if (c.realloc(old_ptr, new_size)) |buf| {28 if (c.realloc(old_ptr, new_size)) |buf| {
31 return @ptrCast(&u8, buf)[0..new_size];29 return @ptrCast(&u8, buf)[0..new_size];
...@@ -47,7 +45,7 @@ pub const IncrementingAllocator = struct {...@@ -47,7 +45,7 @@ pub const IncrementingAllocator = struct {
47 end_index: usize,45 end_index: usize,
48 heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,46 heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
4947
50 fn init(capacity: usize) %IncrementingAllocator {48 fn init(capacity: usize) !IncrementingAllocator {
51 switch (builtin.os) {49 switch (builtin.os) {
52 Os.linux, Os.macosx, Os.ios => {50 Os.linux, Os.macosx, Os.ios => {
53 const p = os.posix;51 const p = os.posix;
...@@ -105,7 +103,7 @@ pub const IncrementingAllocator = struct {...@@ -105,7 +103,7 @@ pub const IncrementingAllocator = struct {
105 return self.bytes.len - self.end_index;103 return self.bytes.len - self.end_index;
106 }104 }
107105
108 fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 {106 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
109 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);107 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
110 const addr = @ptrToInt(&self.bytes[self.end_index]);108 const addr = @ptrToInt(&self.bytes[self.end_index]);
111 const rem = @rem(addr, alignment);109 const rem = @rem(addr, alignment);
...@@ -120,7 +118,7 @@ pub const IncrementingAllocator = struct {...@@ -120,7 +118,7 @@ pub const IncrementingAllocator = struct {
120 return result;118 return result;
121 }119 }
122120
123 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {121 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
124 if (new_size <= old_mem.len) {122 if (new_size <= old_mem.len) {
125 return old_mem[0..new_size];123 return old_mem[0..new_size];
126 } else {124 } else {
std/index.zig+2
...@@ -28,6 +28,7 @@ pub const os = @import("os/index.zig");...@@ -28,6 +28,7 @@ pub const os = @import("os/index.zig");
28pub const rand = @import("rand.zig");28pub const rand = @import("rand.zig");
29pub const sort = @import("sort.zig");29pub const sort = @import("sort.zig");
30pub const unicode = @import("unicode.zig");30pub const unicode = @import("unicode.zig");
31pub const zig = @import("zig/index.zig");
3132
32test "std" {33test "std" {
33 // run tests from these34 // run tests from these
...@@ -58,4 +59,5 @@ test "std" {...@@ -58,4 +59,5 @@ test "std" {
58 _ = @import("rand.zig");59 _ = @import("rand.zig");
59 _ = @import("sort.zig");60 _ = @import("sort.zig");
60 _ = @import("unicode.zig");61 _ = @import("unicode.zig");
62 _ = @import("zig/index.zig");
61}63}
std/io.zig+230-456
...@@ -1,12 +1,6 @@...@@ -1,12 +1,6 @@
1const std = @import("index.zig");1const std = @import("index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Os = builtin.Os;3const Os = builtin.Os;
4const system = switch(builtin.os) {
5 Os.linux => @import("os/linux/index.zig"),
6 Os.macosx, Os.ios => @import("os/darwin.zig"),
7 Os.windows => @import("os/windows/index.zig"),
8 else => @compileError("Unsupported OS"),
9};
10const c = std.c;4const c = std.c;
115
12const math = std.math;6const math = std.math;
...@@ -16,65 +10,38 @@ const os = std.os;...@@ -16,65 +10,38 @@ const os = std.os;
16const mem = std.mem;10const mem = std.mem;
17const Buffer = std.Buffer;11const Buffer = std.Buffer;
18const fmt = std.fmt;12const fmt = std.fmt;
13const File = std.os.File;
1914
20const is_posix = builtin.os != builtin.Os.windows;15const is_posix = builtin.os != builtin.Os.windows;
21const is_windows = builtin.os == builtin.Os.windows;16const is_windows = builtin.os == builtin.Os.windows;
2217
23test "import io tests" {18const GetStdIoErrs = os.WindowsGetStdHandleErrs;
24 comptime {
25 _ = @import("io_test.zig");
26 }
27}
2819
29/// The function received invalid input at runtime. An Invalid error means a20pub fn getStdErr() GetStdIoErrs!File {
30/// bug in the program that called the function.
31error Invalid;
32
33error DiskQuota;
34error FileTooBig;
35error Io;
36error NoSpaceLeft;
37error BadPerm;
38error BrokenPipe;
39error BadFd;
40error IsDir;
41error NotDir;
42error SymLinkLoop;
43error ProcessFdQuotaExceeded;
44error SystemFdQuotaExceeded;
45error NameTooLong;
46error NoDevice;
47error PathNotFound;
48error OutOfMemory;
49error Unseekable;
50error EndOfFile;
51error FilePosLargerThanPointerRange;
52
53pub fn getStdErr() %File {
54 const handle = if (is_windows)21 const handle = if (is_windows)
55 try os.windowsGetStdHandle(system.STD_ERROR_HANDLE)22 try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE)
56 else if (is_posix)23 else if (is_posix)
57 system.STDERR_FILENO24 os.posix.STDERR_FILENO
58 else25 else
59 unreachable;26 unreachable;
60 return File.openHandle(handle);27 return File.openHandle(handle);
61}28}
6229
63pub fn getStdOut() %File {30pub fn getStdOut() GetStdIoErrs!File {
64 const handle = if (is_windows)31 const handle = if (is_windows)
65 try os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)32 try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE)
66 else if (is_posix)33 else if (is_posix)
67 system.STDOUT_FILENO34 os.posix.STDOUT_FILENO
68 else35 else
69 unreachable;36 unreachable;
70 return File.openHandle(handle);37 return File.openHandle(handle);
71}38}
7239
73pub fn getStdIn() %File {40pub fn getStdIn() GetStdIoErrs!File {
74 const handle = if (is_windows)41 const handle = if (is_windows)
75 try os.windowsGetStdHandle(system.STD_INPUT_HANDLE)42 try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE)
76 else if (is_posix)43 else if (is_posix)
77 system.STDIN_FILENO44 os.posix.STDIN_FILENO
78 else45 else
79 unreachable;46 unreachable;
80 return File.openHandle(handle);47 return File.openHandle(handle);
...@@ -83,18 +50,21 @@ pub fn getStdIn() %File {...@@ -83,18 +50,21 @@ pub fn getStdIn() %File {
83/// Implementation of InStream trait for File50/// Implementation of InStream trait for File
84pub const FileInStream = struct {51pub const FileInStream = struct {
85 file: &File,52 file: &File,
86 stream: InStream,53 stream: Stream,
54
55 pub const Error = @typeOf(File.read).ReturnType.ErrorSet;
56 pub const Stream = InStream(Error);
8757
88 pub fn init(file: &File) FileInStream {58 pub fn init(file: &File) FileInStream {
89 return FileInStream {59 return FileInStream {
90 .file = file,60 .file = file,
91 .stream = InStream {61 .stream = Stream {
92 .readFn = readFn,62 .readFn = readFn,
93 },63 },
94 };64 };
95 }65 }
9666
97 fn readFn(in_stream: &InStream, buffer: []u8) %usize {67 fn readFn(in_stream: &Stream, buffer: []u8) Error!usize {
98 const self = @fieldParentPtr(FileInStream, "stream", in_stream);68 const self = @fieldParentPtr(FileInStream, "stream", in_stream);
99 return self.file.read(buffer);69 return self.file.read(buffer);
100 }70 }
...@@ -103,453 +73,202 @@ pub const FileInStream = struct {...@@ -103,453 +73,202 @@ pub const FileInStream = struct {
103/// Implementation of OutStream trait for File73/// Implementation of OutStream trait for File
104pub const FileOutStream = struct {74pub const FileOutStream = struct {
105 file: &File,75 file: &File,
106 stream: OutStream,76 stream: Stream,
77
78 pub const Error = File.WriteError;
79 pub const Stream = OutStream(Error);
10780
108 pub fn init(file: &File) FileOutStream {81 pub fn init(file: &File) FileOutStream {
109 return FileOutStream {82 return FileOutStream {
110 .file = file,83 .file = file,
111 .stream = OutStream {84 .stream = Stream {
112 .writeFn = writeFn,85 .writeFn = writeFn,
113 },86 },
114 };87 };
115 }88 }
11689
117 fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {90 fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
118 const self = @fieldParentPtr(FileOutStream, "stream", out_stream);91 const self = @fieldParentPtr(FileOutStream, "stream", out_stream);
119 return self.file.write(bytes);92 return self.file.write(bytes);
120 }93 }
121};94};
12295
123pub const File = struct {96pub fn InStream(comptime ReadError: type) type {
124 /// The OS-specific file descriptor or file handle.97 return struct {
125 handle: os.FileHandle,98 const Self = this;
12699 pub const Error = ReadError;
127 /// `path` may need to be copied in memory to add a null terminating byte. In this case
128 /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
129 /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
130 /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
131 /// Call close to clean up.
132 pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) %File {
133 if (is_posix) {
134 const flags = system.O_LARGEFILE|system.O_RDONLY;
135 const fd = try os.posixOpen(path, flags, 0, allocator);
136 return openHandle(fd);
137 } else if (is_windows) {
138 const handle = try os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
139 system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
140 return openHandle(handle);
141 } else {
142 unreachable;
143 }
144 }
145
146 /// Calls `openWriteMode` with 0o666 for the mode.
147 pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) %File {
148 return openWriteMode(path, 0o666, allocator);
149
150 }
151
152 /// `path` may need to be copied in memory to add a null terminating byte. In this case
153 /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
154 /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
155 /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
156 /// Call close to clean up.
157 pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) %File {
158 if (is_posix) {
159 const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
160 const fd = try os.posixOpen(path, flags, mode, allocator);
161 return openHandle(fd);
162 } else if (is_windows) {
163 const handle = try os.windowsOpen(path, system.GENERIC_WRITE,
164 system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE,
165 system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL, allocator);
166 return openHandle(handle);
167 } else {
168 unreachable;
169 }
170
171 }
172
173 pub fn openHandle(handle: os.FileHandle) File {
174 return File {
175 .handle = handle,
176 };
177 }
178100
101 /// Return the number of bytes read. If the number read is smaller than buf.len, it
102 /// means the stream reached the end. Reaching the end of a stream is not an error
103 /// condition.
104 readFn: fn(self: &Self, buffer: []u8) Error!usize,
179105
180 /// Upon success, the stream is in an uninitialized state. To continue using it,106 /// Replaces `buffer` contents by reading from the stream until it is finished.
181 /// you must use the open() function.107 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
182 pub fn close(self: &File) void {108 /// the contents read from the stream are lost.
183 os.close(self.handle);109 pub fn readAllBuffer(self: &Self, buffer: &Buffer, max_size: usize) !void {
184 self.handle = undefined;110 try buffer.resize(0);
185 }
186111
187 /// Calls `os.isTty` on `self.handle`.112 var actual_buf_len: usize = 0;
188 pub fn isTty(self: &File) bool {113 while (true) {
189 return os.isTty(self.handle);114 const dest_slice = buffer.toSlice()[actual_buf_len..];
190 }115 const bytes_read = try self.readFn(self, dest_slice);
116 actual_buf_len += bytes_read;
191117
192 pub fn seekForward(self: &File, amount: isize) %void {118 if (bytes_read != dest_slice.len) {
193 switch (builtin.os) {119 buffer.shrink(actual_buf_len);
194 Os.linux, Os.macosx, Os.ios => {120 return;
195 const result = system.lseek(self.handle, amount, system.SEEK_CUR);
196 const err = system.getErrno(result);
197 if (err > 0) {
198 return switch (err) {
199 system.EBADF => error.BadFd,
200 system.EINVAL => error.Unseekable,
201 system.EOVERFLOW => error.Unseekable,
202 system.ESPIPE => error.Unseekable,
203 system.ENXIO => error.Unseekable,
204 else => os.unexpectedErrorPosix(err),
205 };
206 }121 }
207 },
208 Os.windows => {
209 if (system.SetFilePointerEx(self.handle, amount, null, system.FILE_CURRENT) == 0) {
210 const err = system.GetLastError();
211 return switch (err) {
212 system.ERROR.INVALID_PARAMETER => error.BadFd,
213 else => os.unexpectedErrorWindows(err),
214 };
215 }
216 },
217 else => @compileError("unsupported OS"),
218 }
219 }
220122
221 pub fn seekTo(self: &File, pos: usize) %void {123 const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
222 switch (builtin.os) {124 if (new_buf_size == actual_buf_len)
223 Os.linux, Os.macosx, Os.ios => {125 return error.StreamTooLong;
224 const ipos = try math.cast(isize, pos);126 try buffer.resize(new_buf_size);
225 const result = system.lseek(self.handle, ipos, system.SEEK_SET);127 }
226 const err = system.getErrno(result);
227 if (err > 0) {
228 return switch (err) {
229 system.EBADF => error.BadFd,
230 system.EINVAL => error.Unseekable,
231 system.EOVERFLOW => error.Unseekable,
232 system.ESPIPE => error.Unseekable,
233 system.ENXIO => error.Unseekable,
234 else => os.unexpectedErrorPosix(err),
235 };
236 }
237 },
238 Os.windows => {
239 const ipos = try math.cast(isize, pos);
240 if (system.SetFilePointerEx(self.handle, ipos, null, system.FILE_BEGIN) == 0) {
241 const err = system.GetLastError();
242 return switch (err) {
243 system.ERROR.INVALID_PARAMETER => error.BadFd,
244 else => os.unexpectedErrorWindows(err),
245 };
246 }
247 },
248 else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
249 }128 }
250 }
251129
252 pub fn getPos(self: &File) %usize {130 /// Allocates enough memory to hold all the contents of the stream. If the allocated
253 switch (builtin.os) {131 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
254 Os.linux, Os.macosx, Os.ios => {132 /// Caller owns returned memory.
255 const result = system.lseek(self.handle, 0, system.SEEK_CUR);133 /// If this function returns an error, the contents from the stream read so far are lost.
256 const err = system.getErrno(result);134 pub fn readAllAlloc(self: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 {
257 if (err > 0) {135 var buf = Buffer.initNull(allocator);
258 return switch (err) {136 defer buf.deinit();
259 system.EBADF => error.BadFd,
260 system.EINVAL => error.Unseekable,
261 system.EOVERFLOW => error.Unseekable,
262 system.ESPIPE => error.Unseekable,
263 system.ENXIO => error.Unseekable,
264 else => os.unexpectedErrorPosix(err),
265 };
266 }
267 return result;
268 },
269 Os.windows => {
270 var pos : system.LARGE_INTEGER = undefined;
271 if (system.SetFilePointerEx(self.handle, 0, &pos, system.FILE_CURRENT) == 0) {
272 const err = system.GetLastError();
273 return switch (err) {
274 system.ERROR.INVALID_PARAMETER => error.BadFd,
275 else => os.unexpectedErrorWindows(err),
276 };
277 }
278
279 assert(pos >= 0);
280 if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) {
281 if (pos > @maxValue(usize)) {
282 return error.FilePosLargerThanPointerRange;
283 }
284 }
285137
286 return usize(pos);138 try self.readAllBuffer(&buf, max_size);
287 },139 return buf.toOwnedSlice();
288 else => @compileError("unsupported OS"),
289 }140 }
290 }
291141
292 pub fn getEndPos(self: &File) %usize {142 /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
293 if (is_posix) {143 /// Does not include the delimiter in the result.
294 var stat: system.Stat = undefined;144 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
295 const err = system.getErrno(system.fstat(self.handle, &stat));145 /// read from the stream so far are lost.
296 if (err > 0) {146 pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
297 return switch (err) {147 try buf.resize(0);
298 system.EBADF => error.BadFd,
299 system.ENOMEM => error.SystemResources,
300 else => os.unexpectedErrorPosix(err),
301 };
302 }
303148
304 return usize(stat.size);149 while (true) {
305 } else if (is_windows) {150 var byte: u8 = try self.readByte();
306 var file_size: system.LARGE_INTEGER = undefined;
307 if (system.GetFileSizeEx(self.handle, &file_size) == 0) {
308 const err = system.GetLastError();
309 return switch (err) {
310 else => os.unexpectedErrorWindows(err),
311 };
312 }
313 if (file_size < 0)
314 return error.Overflow;
315 return math.cast(usize, u64(file_size));
316 } else {
317 unreachable;
318 }
319 }
320151
321 pub fn read(self: &File, buffer: []u8) %usize {152 if (byte == delimiter) {
322 if (is_posix) {153 return;
323 var index: usize = 0;
324 while (index < buffer.len) {
325 const amt_read = system.read(self.handle, &buffer[index], buffer.len - index);
326 const read_err = system.getErrno(amt_read);
327 if (read_err > 0) {
328 switch (read_err) {
329 system.EINTR => continue,
330 system.EINVAL => unreachable,
331 system.EFAULT => unreachable,
332 system.EBADF => return error.BadFd,
333 system.EIO => return error.Io,
334 else => return os.unexpectedErrorPosix(read_err),
335 }
336 }154 }
337 if (amt_read == 0) return index;155
338 index += amt_read;156 if (buf.len() == max_size) {
339 }157 return error.StreamTooLong;
340 return index;
341 } else if (is_windows) {
342 var index: usize = 0;
343 while (index < buffer.len) {
344 const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buffer.len - index));
345 var amt_read: system.DWORD = undefined;
346 if (system.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) {
347 const err = system.GetLastError();
348 return switch (err) {
349 system.ERROR.OPERATION_ABORTED => continue,
350 system.ERROR.BROKEN_PIPE => return index,
351 else => os.unexpectedErrorWindows(err),
352 };
353 }158 }
354 if (amt_read == 0) return index;159
355 index += amt_read;160 try buf.appendByte(byte);
356 }161 }
357 return index;
358 } else {
359 unreachable;
360 }162 }
361 }
362163
363 fn write(self: &File, bytes: []const u8) %void {164 /// Allocates enough memory to read until `delimiter`. If the allocated
364 if (is_posix) {165 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
365 try os.posixWrite(self.handle, bytes);166 /// Caller owns returned memory.
366 } else if (is_windows) {167 /// If this function returns an error, the contents from the stream read so far are lost.
367 try os.windowsWrite(self.handle, bytes);168 pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator,
368 } else {169 delimiter: u8, max_size: usize) ![]u8
369 @compileError("Unsupported OS");170 {
171 var buf = Buffer.initNull(allocator);
172 defer buf.deinit();
173
174 try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
175 return buf.toOwnedSlice();
370 }176 }
371 }
372};
373
374error StreamTooLong;
375error EndOfStream;
376
377pub const InStream = struct {
378 /// Return the number of bytes read. If the number read is smaller than buf.len, it
379 /// means the stream reached the end. Reaching the end of a stream is not an error
380 /// condition.
381 readFn: fn(self: &InStream, buffer: []u8) %usize,
382
383 /// Replaces `buffer` contents by reading from the stream until it is finished.
384 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
385 /// the contents read from the stream are lost.
386 pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) %void {
387 try buffer.resize(0);
388
389 var actual_buf_len: usize = 0;
390 while (true) {
391 const dest_slice = buffer.toSlice()[actual_buf_len..];
392 const bytes_read = try self.readFn(self, dest_slice);
393 actual_buf_len += bytes_read;
394
395 if (bytes_read != dest_slice.len) {
396 buffer.shrink(actual_buf_len);
397 return;
398 }
399177
400 const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);178 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
401 if (new_buf_size == actual_buf_len)179 /// means the stream reached the end. Reaching the end of a stream is not an error
402 return error.StreamTooLong;180 /// condition.
403 try buffer.resize(new_buf_size);181 pub fn read(self: &Self, buffer: []u8) !usize {
182 return self.readFn(self, buffer);
404 }183 }
405 }
406
407 /// Allocates enough memory to hold all the contents of the stream. If the allocated
408 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
409 /// Caller owns returned memory.
410 /// If this function returns an error, the contents from the stream read so far are lost.
411 pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) %[]u8 {
412 var buf = Buffer.initNull(allocator);
413 defer buf.deinit();
414
415 try self.readAllBuffer(&buf, max_size);
416 return buf.toOwnedSlice();
417 }
418
419 /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
420 /// Does not include the delimiter in the result.
421 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
422 /// read from the stream so far are lost.
423 pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) %void {
424 try buf.resize(0);
425
426 while (true) {
427 var byte: u8 = try self.readByte();
428
429 if (byte == delimiter) {
430 return;
431 }
432184
433 if (buf.len() == max_size) {185 /// Same as `read` but end of stream returns `error.EndOfStream`.
434 return error.StreamTooLong;186 pub fn readNoEof(self: &Self, buf: []u8) !void {
435 }187 const amt_read = try self.read(buf);
436188 if (amt_read < buf.len) return error.EndOfStream;
437 try buf.appendByte(byte);
438 }189 }
439 }
440
441 /// Allocates enough memory to read until `delimiter`. If the allocated
442 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
443 /// Caller owns returned memory.
444 /// If this function returns an error, the contents from the stream read so far are lost.
445 pub fn readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator,
446 delimiter: u8, max_size: usize) %[]u8
447 {
448 var buf = Buffer.initNull(allocator);
449 defer buf.deinit();
450
451 try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
452 return buf.toOwnedSlice();
453 }
454
455 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
456 /// means the stream reached the end. Reaching the end of a stream is not an error
457 /// condition.
458 pub fn read(self: &InStream, buffer: []u8) %usize {
459 return self.readFn(self, buffer);
460 }
461
462 /// Same as `read` but end of stream returns `error.EndOfStream`.
463 pub fn readNoEof(self: &InStream, buf: []u8) %void {
464 const amt_read = try self.read(buf);
465 if (amt_read < buf.len) return error.EndOfStream;
466 }
467190
468 /// Reads 1 byte from the stream or returns `error.EndOfStream`.191 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
469 pub fn readByte(self: &InStream) %u8 {192 pub fn readByte(self: &Self) !u8 {
470 var result: [1]u8 = undefined;193 var result: [1]u8 = undefined;
471 try self.readNoEof(result[0..]);194 try self.readNoEof(result[0..]);
472 return result[0];195 return result[0];
473 }196 }
474
475 /// Same as `readByte` except the returned byte is signed.
476 pub fn readByteSigned(self: &InStream) %i8 {
477 return @bitCast(i8, try self.readByte());
478 }
479197
480 pub fn readIntLe(self: &InStream, comptime T: type) %T {198 /// Same as `readByte` except the returned byte is signed.
481 return self.readInt(builtin.Endian.Little, T);199 pub fn readByteSigned(self: &Self) !i8 {
482 }200 return @bitCast(i8, try self.readByte());
201 }
483202
484 pub fn readIntBe(self: &InStream, comptime T: type) %T {203 pub fn readIntLe(self: &Self, comptime T: type) !T {
485 return self.readInt(builtin.Endian.Big, T);204 return self.readInt(builtin.Endian.Little, T);
486 }205 }
487206
488 pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) %T {207 pub fn readIntBe(self: &Self, comptime T: type) !T {
489 var bytes: [@sizeOf(T)]u8 = undefined;208 return self.readInt(builtin.Endian.Big, T);
490 try self.readNoEof(bytes[0..]);209 }
491 return mem.readInt(bytes, T, endian);
492 }
493210
494 pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) %T {211 pub fn readInt(self: &Self, endian: builtin.Endian, comptime T: type) !T {
495 assert(size <= @sizeOf(T));212 var bytes: [@sizeOf(T)]u8 = undefined;
496 assert(size <= 8);213 try self.readNoEof(bytes[0..]);
497 var input_buf: [8]u8 = undefined;214 return mem.readInt(bytes, T, endian);
498 const input_slice = input_buf[0..size];215 }
499 try self.readNoEof(input_slice);
500 return mem.readInt(input_slice, T, endian);
501 }
502216
217 pub fn readVarInt(self: &Self, endian: builtin.Endian, comptime T: type, size: usize) !T {
218 assert(size <= @sizeOf(T));
219 assert(size <= 8);
220 var input_buf: [8]u8 = undefined;
221 const input_slice = input_buf[0..size];
222 try self.readNoEof(input_slice);
223 return mem.readInt(input_slice, T, endian);
224 }
225 };
226}
503227
504};228pub fn OutStream(comptime WriteError: type) type {
229 return struct {
230 const Self = this;
231 pub const Error = WriteError;
505232
506pub const OutStream = struct {233 writeFn: fn(self: &Self, bytes: []const u8) Error!void,
507 writeFn: fn(self: &OutStream, bytes: []const u8) %void,
508234
509 pub fn print(self: &OutStream, comptime format: []const u8, args: ...) %void {235 pub fn print(self: &Self, comptime format: []const u8, args: ...) !void {
510 return std.fmt.format(self, self.writeFn, format, args);236 return std.fmt.format(self, Error, self.writeFn, format, args);
511 }237 }
512238
513 pub fn write(self: &OutStream, bytes: []const u8) %void {239 pub fn write(self: &Self, bytes: []const u8) !void {
514 return self.writeFn(self, bytes);240 return self.writeFn(self, bytes);
515 }241 }
516242
517 pub fn writeByte(self: &OutStream, byte: u8) %void {243 pub fn writeByte(self: &Self, byte: u8) !void {
518 const slice = (&byte)[0..1];244 const slice = (&byte)[0..1];
519 return self.writeFn(self, slice);245 return self.writeFn(self, slice);
520 }246 }
521247
522 pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) %void {248 pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void {
523 const slice = (&byte)[0..1];249 const slice = (&byte)[0..1];
524 var i: usize = 0;250 var i: usize = 0;
525 while (i < n) : (i += 1) {251 while (i < n) : (i += 1) {
526 try self.writeFn(self, slice);252 try self.writeFn(self, slice);
253 }
527 }254 }
528 }255 };
529};256}
530257
531/// `path` may need to be copied in memory to add a null terminating byte. In this case258/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
532/// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed259pub fn writeFile(allocator: &mem.Allocator, path: []const u8, data: []const u8) !void {
533/// size buffer is too small, and the provided allocator is null, `error.NameTooLong` is returned.260 var file = try File.openWrite(allocator, path);
534/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
535pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) %void {
536 var file = try File.openWrite(path, allocator);
537 defer file.close();261 defer file.close();
538 try file.write(data);262 try file.write(data);
539}263}
540264
541/// On success, caller owns returned buffer.265/// On success, caller owns returned buffer.
542pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) %[]u8 {266pub fn readFileAlloc(allocator: &mem.Allocator, path: []const u8) ![]u8 {
543 return readFileAllocExtra(path, allocator, 0);267 var file = try File.openRead(allocator, path);
544}
545/// On success, caller owns returned buffer.
546/// Allocates extra_len extra bytes at the end of the file buffer, which are uninitialized.
547pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len: usize) %[]u8 {
548 var file = try File.openRead(path, allocator);
549 defer file.close();268 defer file.close();
550269
551 const size = try file.getEndPos();270 const size = try file.getEndPos();
552 const buf = try allocator.alloc(u8, size + extra_len);271 const buf = try allocator.alloc(u8, size);
553 errdefer allocator.free(buf);272 errdefer allocator.free(buf);
554273
555 var adapter = FileInStream.init(&file);274 var adapter = FileInStream.init(&file);
...@@ -557,21 +276,24 @@ pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len...@@ -557,21 +276,24 @@ pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len
557 return buf;276 return buf;
558}277}
559278
560pub const BufferedInStream = BufferedInStreamCustom(os.page_size);279pub fn BufferedInStream(comptime Error: type) type {
280 return BufferedInStreamCustom(os.page_size, Error);
281}
561282
562pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {283pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type {
563 return struct {284 return struct {
564 const Self = this;285 const Self = this;
286 const Stream = InStream(Error);
565287
566 pub stream: InStream,288 pub stream: Stream,
567289
568 unbuffered_in_stream: &InStream,290 unbuffered_in_stream: &Stream,
569291
570 buffer: [buffer_size]u8,292 buffer: [buffer_size]u8,
571 start_index: usize,293 start_index: usize,
572 end_index: usize,294 end_index: usize,
573295
574 pub fn init(unbuffered_in_stream: &InStream) Self {296 pub fn init(unbuffered_in_stream: &Stream) Self {
575 return Self {297 return Self {
576 .unbuffered_in_stream = unbuffered_in_stream,298 .unbuffered_in_stream = unbuffered_in_stream,
577 .buffer = undefined,299 .buffer = undefined,
...@@ -583,13 +305,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {...@@ -583,13 +305,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
583 .start_index = buffer_size,305 .start_index = buffer_size,
584 .end_index = buffer_size,306 .end_index = buffer_size,
585307
586 .stream = InStream {308 .stream = Stream {
587 .readFn = readFn,309 .readFn = readFn,
588 },310 },
589 };311 };
590 }312 }
591313
592 fn readFn(in_stream: &InStream, dest: []u8) %usize {314 fn readFn(in_stream: &Stream, dest: []u8) !usize {
593 const self = @fieldParentPtr(Self, "stream", in_stream);315 const self = @fieldParentPtr(Self, "stream", in_stream);
594316
595 var dest_index: usize = 0;317 var dest_index: usize = 0;
...@@ -628,39 +350,40 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {...@@ -628,39 +350,40 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
628 };350 };
629}351}
630352
631pub const BufferedOutStream = BufferedOutStreamCustom(os.page_size);353pub fn BufferedOutStream(comptime Error: type) type {
354 return BufferedOutStreamCustom(os.page_size, Error);
355}
632356
633pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type {357pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type {
634 return struct {358 return struct {
635 const Self = this;359 const Self = this;
360 pub const Stream = OutStream(Error);
361 pub const Error = OutStreamError;
636362
637 pub stream: OutStream,363 pub stream: Stream,
638364
639 unbuffered_out_stream: &OutStream,365 unbuffered_out_stream: &Stream,
640366
641 buffer: [buffer_size]u8,367 buffer: [buffer_size]u8,
642 index: usize,368 index: usize,
643369
644 pub fn init(unbuffered_out_stream: &OutStream) Self {370 pub fn init(unbuffered_out_stream: &Stream) Self {
645 return Self {371 return Self {
646 .unbuffered_out_stream = unbuffered_out_stream,372 .unbuffered_out_stream = unbuffered_out_stream,
647 .buffer = undefined,373 .buffer = undefined,
648 .index = 0,374 .index = 0,
649 .stream = OutStream {375 .stream = Stream {
650 .writeFn = writeFn,376 .writeFn = writeFn,
651 },377 },
652 };378 };
653 }379 }
654380
655 pub fn flush(self: &Self) %void {381 pub fn flush(self: &Self) !void {
656 if (self.index == 0)
657 return;
658
659 try self.unbuffered_out_stream.write(self.buffer[0..self.index]);382 try self.unbuffered_out_stream.write(self.buffer[0..self.index]);
660 self.index = 0;383 self.index = 0;
661 }384 }
662385
663 fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {386 fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
664 const self = @fieldParentPtr(Self, "stream", out_stream);387 const self = @fieldParentPtr(Self, "stream", out_stream);
665388
666 if (bytes.len >= self.buffer.len) {389 if (bytes.len >= self.buffer.len) {
...@@ -687,20 +410,71 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type {...@@ -687,20 +410,71 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type {
687/// Implementation of OutStream trait for Buffer410/// Implementation of OutStream trait for Buffer
688pub const BufferOutStream = struct {411pub const BufferOutStream = struct {
689 buffer: &Buffer,412 buffer: &Buffer,
690 stream: OutStream,413 stream: Stream,
414
415 pub const Error = error{OutOfMemory};
416 pub const Stream = OutStream(Error);
691417
692 pub fn init(buffer: &Buffer) BufferOutStream {418 pub fn init(buffer: &Buffer) BufferOutStream {
693 return BufferOutStream {419 return BufferOutStream {
694 .buffer = buffer,420 .buffer = buffer,
695 .stream = OutStream {421 .stream = Stream {
696 .writeFn = writeFn,422 .writeFn = writeFn,
697 },423 },
698 };424 };
699 }425 }
700426
701 fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {427 fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
702 const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);428 const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
703 return self.buffer.append(bytes);429 return self.buffer.append(bytes);
704 }430 }
705};431};
706432
433
434pub const BufferedAtomicFile = struct {
435 atomic_file: os.AtomicFile,
436 file_stream: FileOutStream,
437 buffered_stream: BufferedOutStream(FileOutStream.Error),
438
439 pub fn create(allocator: &mem.Allocator, dest_path: []const u8) !&BufferedAtomicFile {
440 // TODO with well defined copy elision we don't need this allocation
441 var self = try allocator.create(BufferedAtomicFile);
442 errdefer allocator.destroy(self);
443
444 *self = BufferedAtomicFile {
445 .atomic_file = undefined,
446 .file_stream = undefined,
447 .buffered_stream = undefined,
448 };
449
450 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode);
451 errdefer self.atomic_file.deinit();
452
453 self.file_stream = FileOutStream.init(&self.atomic_file.file);
454 self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream);
455 return self;
456 }
457
458 /// always call destroy, even after successful finish()
459 pub fn destroy(self: &BufferedAtomicFile) void {
460 const allocator = self.atomic_file.allocator;
461 self.atomic_file.deinit();
462 allocator.destroy(self);
463 }
464
465 pub fn finish(self: &BufferedAtomicFile) !void {
466 try self.buffered_stream.flush();
467 try self.atomic_file.finish();
468 }
469
470 pub fn stream(self: &BufferedAtomicFile) &OutStream(FileOutStream.Error) {
471 return &self.buffered_stream.stream;
472 }
473};
474
475test "import io tests" {
476 comptime {
477 _ = @import("io_test.zig");
478 }
479}
480
std/io_test.zig+4-4
...@@ -13,11 +13,11 @@ test "write a file, read it, then delete it" {...@@ -13,11 +13,11 @@ test "write a file, read it, then delete it" {
13 rng.fillBytes(data[0..]);13 rng.fillBytes(data[0..]);
14 const tmp_file_name = "temp_test_file.txt";14 const tmp_file_name = "temp_test_file.txt";
15 {15 {
16 var file = try io.File.openWrite(tmp_file_name, allocator);16 var file = try os.File.openWrite(allocator, tmp_file_name);
17 defer file.close();17 defer file.close();
1818
19 var file_out_stream = io.FileOutStream.init(&file);19 var file_out_stream = io.FileOutStream.init(&file);
20 var buf_stream = io.BufferedOutStream.init(&file_out_stream.stream);20 var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
21 const st = &buf_stream.stream;21 const st = &buf_stream.stream;
22 try st.print("begin");22 try st.print("begin");
23 try st.write(data[0..]);23 try st.write(data[0..]);
...@@ -25,7 +25,7 @@ test "write a file, read it, then delete it" {...@@ -25,7 +25,7 @@ test "write a file, read it, then delete it" {
25 try buf_stream.flush();25 try buf_stream.flush();
26 }26 }
27 {27 {
28 var file = try io.File.openRead(tmp_file_name, allocator);28 var file = try os.File.openRead(allocator, tmp_file_name);
29 defer file.close();29 defer file.close();
3030
31 const file_size = try file.getEndPos();31 const file_size = try file.getEndPos();
...@@ -33,7 +33,7 @@ test "write a file, read it, then delete it" {...@@ -33,7 +33,7 @@ test "write a file, read it, then delete it" {
33 assert(file_size == expected_file_size);33 assert(file_size == expected_file_size);
3434
35 var file_in_stream = io.FileInStream.init(&file);35 var file_in_stream = io.FileInStream.init(&file);
36 var buf_stream = io.BufferedInStream.init(&file_in_stream.stream);36 var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream);
37 const st = &buf_stream.stream;37 const st = &buf_stream.stream;
38 const contents = try st.readAllAlloc(allocator, 2 * 1024);38 const contents = try st.readAllAlloc(allocator, 2 * 1024);
39 defer allocator.free(contents);39 defer allocator.free(contents);
std/linked_list.zig+2-2
...@@ -190,7 +190,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -190,7 +190,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
190 ///190 ///
191 /// Returns:191 /// Returns:
192 /// A pointer to the new node.192 /// A pointer to the new node.
193 pub fn allocateNode(list: &Self, allocator: &Allocator) %&Node {193 pub fn allocateNode(list: &Self, allocator: &Allocator) !&Node {
194 comptime assert(!isIntrusive());194 comptime assert(!isIntrusive());
195 return allocator.create(Node);195 return allocator.create(Node);
196 }196 }
...@@ -213,7 +213,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -213,7 +213,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
213 ///213 ///
214 /// Returns:214 /// Returns:
215 /// A pointer to the new node.215 /// A pointer to the new node.
216 pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) %&Node {216 pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) !&Node {
217 comptime assert(!isIntrusive());217 comptime assert(!isIntrusive());
218 var node = try list.allocateNode(allocator);218 var node = try list.allocateNode(allocator);
219 *node = Node.init(data);219 *node = Node.init(data);
std/math/index.zig+13-31
...@@ -191,30 +191,26 @@ test "math.max" {...@@ -191,30 +191,26 @@ test "math.max" {
191 assert(max(i32(-1), i32(2)) == 2);191 assert(max(i32(-1), i32(2)) == 2);
192}192}
193193
194error Overflow;194pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
195pub fn mul(comptime T: type, a: T, b: T) %T {
196 var answer: T = undefined;195 var answer: T = undefined;
197 return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;196 return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;
198}197}
199198
200error Overflow;199pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
201pub fn add(comptime T: type, a: T, b: T) %T {
202 var answer: T = undefined;200 var answer: T = undefined;
203 return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;201 return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
204}202}
205203
206error Overflow;204pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
207pub fn sub(comptime T: type, a: T, b: T) %T {
208 var answer: T = undefined;205 var answer: T = undefined;
209 return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;206 return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
210}207}
211208
212pub fn negate(x: var) %@typeOf(x) {209pub fn negate(x: var) !@typeOf(x) {
213 return sub(@typeOf(x), 0, x);210 return sub(@typeOf(x), 0, x);
214}211}
215212
216error Overflow;213pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
217pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) %T {
218 var answer: T = undefined;214 var answer: T = undefined;
219 return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;215 return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;
220}216}
...@@ -323,8 +319,7 @@ fn testOverflow() void {...@@ -323,8 +319,7 @@ fn testOverflow() void {
323}319}
324320
325321
326error Overflow;322pub fn absInt(x: var) !@typeOf(x) {
327pub fn absInt(x: var) %@typeOf(x) {
328 const T = @typeOf(x);323 const T = @typeOf(x);
329 comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt324 comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
330 comptime assert(T.is_signed); // must pass a signed integer to absInt325 comptime assert(T.is_signed); // must pass a signed integer to absInt
...@@ -347,9 +342,7 @@ fn testAbsInt() void {...@@ -347,9 +342,7 @@ fn testAbsInt() void {
347342
348pub const absFloat = @import("fabs.zig").fabs;343pub const absFloat = @import("fabs.zig").fabs;
349344
350error DivisionByZero;345pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
351error Overflow;
352pub fn divTrunc(comptime T: type, numerator: T, denominator: T) %T {
353 @setRuntimeSafety(false);346 @setRuntimeSafety(false);
354 if (denominator == 0)347 if (denominator == 0)
355 return error.DivisionByZero;348 return error.DivisionByZero;
...@@ -372,9 +365,7 @@ fn testDivTrunc() void {...@@ -372,9 +365,7 @@ fn testDivTrunc() void {
372 assert((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0);365 assert((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0);
373}366}
374367
375error DivisionByZero;368pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
376error Overflow;
377pub fn divFloor(comptime T: type, numerator: T, denominator: T) %T {
378 @setRuntimeSafety(false);369 @setRuntimeSafety(false);
379 if (denominator == 0)370 if (denominator == 0)
380 return error.DivisionByZero;371 return error.DivisionByZero;
...@@ -397,10 +388,7 @@ fn testDivFloor() void {...@@ -397,10 +388,7 @@ fn testDivFloor() void {
397 assert((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0);388 assert((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0);
398}389}
399390
400error DivisionByZero;391pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
401error Overflow;
402error UnexpectedRemainder;
403pub fn divExact(comptime T: type, numerator: T, denominator: T) %T {
404 @setRuntimeSafety(false);392 @setRuntimeSafety(false);
405 if (denominator == 0)393 if (denominator == 0)
406 return error.DivisionByZero;394 return error.DivisionByZero;
...@@ -428,9 +416,7 @@ fn testDivExact() void {...@@ -428,9 +416,7 @@ fn testDivExact() void {
428 if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder);416 if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder);
429}417}
430418
431error DivisionByZero;419pub fn mod(comptime T: type, numerator: T, denominator: T) !T {
432error NegativeDenominator;
433pub fn mod(comptime T: type, numerator: T, denominator: T) %T {
434 @setRuntimeSafety(false);420 @setRuntimeSafety(false);
435 if (denominator == 0)421 if (denominator == 0)
436 return error.DivisionByZero;422 return error.DivisionByZero;
...@@ -455,9 +441,7 @@ fn testMod() void {...@@ -455,9 +441,7 @@ fn testMod() void {
455 if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);441 if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
456}442}
457443
458error DivisionByZero;444pub fn rem(comptime T: type, numerator: T, denominator: T) !T {
459error NegativeDenominator;
460pub fn rem(comptime T: type, numerator: T, denominator: T) %T {
461 @setRuntimeSafety(false);445 @setRuntimeSafety(false);
462 if (denominator == 0)446 if (denominator == 0)
463 return error.DivisionByZero;447 return error.DivisionByZero;
...@@ -505,8 +489,7 @@ test "math.absCast" {...@@ -505,8 +489,7 @@ test "math.absCast" {
505489
506/// Returns the negation of the integer parameter.490/// Returns the negation of the integer parameter.
507/// Result is a signed integer.491/// Result is a signed integer.
508error Overflow;492pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) {
509pub fn negateCast(x: var) %@IntType(true, @typeOf(x).bit_count) {
510 if (@typeOf(x).is_signed)493 if (@typeOf(x).is_signed)
511 return negate(x);494 return negate(x);
512495
...@@ -532,8 +515,7 @@ test "math.negateCast" {...@@ -532,8 +515,7 @@ test "math.negateCast" {
532515
533/// Cast an integer to a different integer type. If the value doesn't fit, 516/// Cast an integer to a different integer type. If the value doesn't fit,
534/// return an error.517/// return an error.
535error Overflow;518pub fn cast(comptime T: type, x: var) !T {
536pub fn cast(comptime T: type, x: var) %T {
537 comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer519 comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer
538 if (x > @maxValue(T)) {520 if (x > @maxValue(T)) {
539 return error.Overflow;521 return error.Overflow;
std/mem.zig+15-15
...@@ -4,13 +4,13 @@ const assert = debug.assert;...@@ -4,13 +4,13 @@ const assert = debug.assert;
4const math = std.math;4const math = std.math;
5const builtin = @import("builtin");5const builtin = @import("builtin");
66
7error OutOfMemory;
8
9pub const Allocator = struct {7pub const Allocator = struct {
8 const Error = error {OutOfMemory};
9
10 /// Allocate byte_count bytes and return them in a slice, with the10 /// Allocate byte_count bytes and return them in a slice, with the
11 /// slice's pointer aligned at least to alignment bytes.11 /// slice's pointer aligned at least to alignment bytes.
12 /// The returned newly allocated memory is undefined.12 /// The returned newly allocated memory is undefined.
13 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) %[]u8,13 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
1414
15 /// If `new_byte_count > old_mem.len`:15 /// If `new_byte_count > old_mem.len`:
16 /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.16 /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
...@@ -21,12 +21,12 @@ pub const Allocator = struct {...@@ -21,12 +21,12 @@ pub const Allocator = struct {
21 /// * alignment <= alignment of old_mem.ptr21 /// * alignment <= alignment of old_mem.ptr
22 ///22 ///
23 /// The returned newly allocated memory is undefined.23 /// The returned newly allocated memory is undefined.
24 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) %[]u8,24 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
2525
26 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`26 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
27 freeFn: fn (self: &Allocator, old_mem: []u8) void,27 freeFn: fn (self: &Allocator, old_mem: []u8) void,
2828
29 fn create(self: &Allocator, comptime T: type) %&T {29 fn create(self: &Allocator, comptime T: type) !&T {
30 const slice = try self.alloc(T, 1);30 const slice = try self.alloc(T, 1);
31 return &slice[0];31 return &slice[0];
32 }32 }
...@@ -35,14 +35,14 @@ pub const Allocator = struct {...@@ -35,14 +35,14 @@ pub const Allocator = struct {
35 self.free(ptr[0..1]);35 self.free(ptr[0..1]);
36 }36 }
3737
38 fn alloc(self: &Allocator, comptime T: type, n: usize) %[]T {38 fn alloc(self: &Allocator, comptime T: type, n: usize) ![]T {
39 return self.alignedAlloc(T, @alignOf(T), n);39 return self.alignedAlloc(T, @alignOf(T), n);
40 }40 }
4141
42 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,42 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
43 n: usize) %[]align(alignment) T43 n: usize) ![]align(alignment) T
44 {44 {
45 const byte_count = try math.mul(usize, @sizeOf(T), n);45 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
46 const byte_slice = try self.allocFn(self, byte_count, alignment);46 const byte_slice = try self.allocFn(self, byte_count, alignment);
47 // This loop should get optimized out in ReleaseFast mode47 // This loop should get optimized out in ReleaseFast mode
48 for (byte_slice) |*byte| {48 for (byte_slice) |*byte| {
...@@ -51,19 +51,19 @@ pub const Allocator = struct {...@@ -51,19 +51,19 @@ pub const Allocator = struct {
51 return ([]align(alignment) T)(@alignCast(alignment, byte_slice));51 return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
52 }52 }
5353
54 fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) %[]T {54 fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) ![]T {
55 return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);55 return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
56 }56 }
5757
58 fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,58 fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,
59 old_mem: []align(alignment) T, n: usize) %[]align(alignment) T59 old_mem: []align(alignment) T, n: usize) ![]align(alignment) T
60 {60 {
61 if (old_mem.len == 0) {61 if (old_mem.len == 0) {
62 return self.alloc(T, n);62 return self.alloc(T, n);
63 }63 }
6464
65 const old_byte_slice = ([]u8)(old_mem);65 const old_byte_slice = ([]u8)(old_mem);
66 const byte_count = try math.mul(usize, @sizeOf(T), n);66 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
67 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);67 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
68 // This loop should get optimized out in ReleaseFast mode68 // This loop should get optimized out in ReleaseFast mode
69 for (byte_slice[old_byte_slice.len..]) |*byte| {69 for (byte_slice[old_byte_slice.len..]) |*byte| {
...@@ -123,7 +123,7 @@ pub const FixedBufferAllocator = struct {...@@ -123,7 +123,7 @@ pub const FixedBufferAllocator = struct {
123 };123 };
124 }124 }
125125
126 fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 {126 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
127 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);127 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
128 const addr = @ptrToInt(&self.buffer[self.end_index]);128 const addr = @ptrToInt(&self.buffer[self.end_index]);
129 const rem = @rem(addr, alignment);129 const rem = @rem(addr, alignment);
...@@ -138,7 +138,7 @@ pub const FixedBufferAllocator = struct {...@@ -138,7 +138,7 @@ pub const FixedBufferAllocator = struct {
138 return result;138 return result;
139 }139 }
140140
141 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {141 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
142 if (new_size <= old_mem.len) {142 if (new_size <= old_mem.len) {
143 return old_mem[0..new_size];143 return old_mem[0..new_size];
144 } else {144 } else {
...@@ -197,7 +197,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {...@@ -197,7 +197,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
197}197}
198198
199/// Copies ::m to newly allocated memory. Caller is responsible to free it.199/// Copies ::m to newly allocated memory. Caller is responsible to free it.
200pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) %[]T {200pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
201 const new_buf = try allocator.alloc(T, m.len);201 const new_buf = try allocator.alloc(T, m.len);
202 copy(T, new_buf, m);202 copy(T, new_buf, m);
203 return new_buf;203 return new_buf;
...@@ -428,7 +428,7 @@ const SplitIterator = struct {...@@ -428,7 +428,7 @@ const SplitIterator = struct {
428428
429/// Naively combines a series of strings with a separator.429/// Naively combines a series of strings with a separator.
430/// Allocates memory for the result, which must be freed by the caller.430/// Allocates memory for the result, which must be freed by the caller.
431pub fn join(allocator: &Allocator, sep: u8, strings: ...) %[]u8 {431pub fn join(allocator: &Allocator, sep: u8, strings: ...) ![]u8 {
432 comptime assert(strings.len >= 1);432 comptime assert(strings.len >= 1);
433 var total_strings_len: usize = strings.len; // 1 sep per string433 var total_strings_len: usize = strings.len; // 1 sep per string
434 {434 {
std/net.zig+9-25
...@@ -5,19 +5,10 @@ const endian = std.endian;...@@ -5,19 +5,10 @@ const endian = std.endian;
55
6// TODO don't trust this file, it bit rotted. start over6// TODO don't trust this file, it bit rotted. start over
77
8error SigInterrupt;
9error Io;
10error TimedOut;
11error ConnectionReset;
12error ConnectionRefused;
13error OutOfMemory;
14error NotSocket;
15error BadFd;
16
17const Connection = struct {8const Connection = struct {
18 socket_fd: i32,9 socket_fd: i32,
1910
20 pub fn send(c: Connection, buf: []const u8) %usize {11 pub fn send(c: Connection, buf: []const u8) !usize {
21 const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);12 const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
22 const send_err = linux.getErrno(send_ret);13 const send_err = linux.getErrno(send_ret);
23 switch (send_err) {14 switch (send_err) {
...@@ -31,7 +22,7 @@ const Connection = struct {...@@ -31,7 +22,7 @@ const Connection = struct {
31 }22 }
32 }23 }
3324
34 pub fn recv(c: Connection, buf: []u8) %[]u8 {25 pub fn recv(c: Connection, buf: []u8) ![]u8 {
35 const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);26 const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
36 const recv_err = linux.getErrno(recv_ret);27 const recv_err = linux.getErrno(recv_ret);
37 switch (recv_err) {28 switch (recv_err) {
...@@ -48,7 +39,7 @@ const Connection = struct {...@@ -48,7 +39,7 @@ const Connection = struct {
48 }39 }
49 }40 }
5041
51 pub fn close(c: Connection) %void {42 pub fn close(c: Connection) !void {
52 switch (linux.getErrno(linux.close(c.socket_fd))) {43 switch (linux.getErrno(linux.close(c.socket_fd))) {
53 0 => return,44 0 => return,
54 linux.EBADF => unreachable,45 linux.EBADF => unreachable,
...@@ -66,7 +57,7 @@ const Address = struct {...@@ -66,7 +57,7 @@ const Address = struct {
66 sort_key: i32,57 sort_key: i32,
67};58};
6859
69pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {60pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
70 if (hostname.len == 0) {61 if (hostname.len == 0) {
7162
72 unreachable; // TODO63 unreachable; // TODO
...@@ -75,7 +66,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {...@@ -75,7 +66,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {
75 unreachable; // TODO66 unreachable; // TODO
76}67}
7768
78pub fn connectAddr(addr: &Address, port: u16) %Connection {69pub fn connectAddr(addr: &Address, port: u16) !Connection {
79 const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);70 const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
80 const socket_err = linux.getErrno(socket_ret);71 const socket_err = linux.getErrno(socket_ret);
81 if (socket_err > 0) {72 if (socket_err > 0) {
...@@ -118,7 +109,7 @@ pub fn connectAddr(addr: &Address, port: u16) %Connection {...@@ -118,7 +109,7 @@ pub fn connectAddr(addr: &Address, port: u16) %Connection {
118 };109 };
119}110}
120111
121pub fn connect(hostname: []const u8, port: u16) %Connection {112pub fn connect(hostname: []const u8, port: u16) !Connection {
122 var addrs_buf: [1]Address = undefined;113 var addrs_buf: [1]Address = undefined;
123 const addrs_slice = try lookup(hostname, addrs_buf[0..]);114 const addrs_slice = try lookup(hostname, addrs_buf[0..]);
124 const main_addr = &addrs_slice[0];115 const main_addr = &addrs_slice[0];
...@@ -126,9 +117,7 @@ pub fn connect(hostname: []const u8, port: u16) %Connection {...@@ -126,9 +117,7 @@ pub fn connect(hostname: []const u8, port: u16) %Connection {
126 return connectAddr(main_addr, port);117 return connectAddr(main_addr, port);
127}118}
128119
129error InvalidIpLiteral;120pub fn parseIpLiteral(buf: []const u8) !Address {
130
131pub fn parseIpLiteral(buf: []const u8) %Address {
132121
133 return error.InvalidIpLiteral;122 return error.InvalidIpLiteral;
134}123}
...@@ -146,12 +135,7 @@ fn hexDigit(c: u8) u8 {...@@ -146,12 +135,7 @@ fn hexDigit(c: u8) u8 {
146 }135 }
147}136}
148137
149error InvalidChar;138fn parseIp6(buf: []const u8) !Address {
150error Overflow;
151error JunkAtEnd;
152error Incomplete;
153
154fn parseIp6(buf: []const u8) %Address {
155 var result: Address = undefined;139 var result: Address = undefined;
156 result.family = linux.AF_INET6;140 result.family = linux.AF_INET6;
157 result.scope_id = 0;141 result.scope_id = 0;
...@@ -232,7 +216,7 @@ fn parseIp6(buf: []const u8) %Address {...@@ -232,7 +216,7 @@ fn parseIp6(buf: []const u8) %Address {
232 return error.Incomplete;216 return error.Incomplete;
233}217}
234218
235fn parseIp4(buf: []const u8) %u32 {219fn parseIp4(buf: []const u8) !u32 {
236 var result: u32 = undefined;220 var result: u32 = undefined;
237 const out_ptr = ([]u8)((&result)[0..1]);221 const out_ptr = ([]u8)((&result)[0..1]);
238222
std/os/child_process.zig+75-54
...@@ -13,10 +13,6 @@ const builtin = @import("builtin");...@@ -13,10 +13,6 @@ const builtin = @import("builtin");
13const Os = builtin.Os;13const Os = builtin.Os;
14const LinkedList = std.LinkedList;14const LinkedList = std.LinkedList;
1515
16error PermissionDenied;
17error ProcessNotFound;
18error InvalidName;
19
20var children_nodes = LinkedList(&ChildProcess).init();16var children_nodes = LinkedList(&ChildProcess).init();
2117
22const is_windows = builtin.os == Os.windows;18const is_windows = builtin.os == Os.windows;
...@@ -28,11 +24,11 @@ pub const ChildProcess = struct {...@@ -28,11 +24,11 @@ pub const ChildProcess = struct {
2824
29 pub allocator: &mem.Allocator,25 pub allocator: &mem.Allocator,
3026
31 pub stdin: ?io.File,27 pub stdin: ?os.File,
32 pub stdout: ?io.File,28 pub stdout: ?os.File,
33 pub stderr: ?io.File,29 pub stderr: ?os.File,
3430
35 pub term: ?%Term,31 pub term: ?(SpawnError!Term),
3632
37 pub argv: []const []const u8,33 pub argv: []const []const u8,
3834
...@@ -58,6 +54,25 @@ pub const ChildProcess = struct {...@@ -58,6 +54,25 @@ pub const ChildProcess = struct {
58 err_pipe: if (is_windows) void else [2]i32,54 err_pipe: if (is_windows) void else [2]i32,
59 llnode: if (is_windows) void else LinkedList(&ChildProcess).Node,55 llnode: if (is_windows) void else LinkedList(&ChildProcess).Node,
6056
57 pub const SpawnError = error {
58 ProcessFdQuotaExceeded,
59 Unexpected,
60 NotDir,
61 SystemResources,
62 FileNotFound,
63 NameTooLong,
64 SymLinkLoop,
65 FileSystem,
66 OutOfMemory,
67 AccessDenied,
68 PermissionDenied,
69 InvalidUserId,
70 ResourceLimitReached,
71 InvalidExe,
72 IsDir,
73 FileBusy,
74 };
75
61 pub const Term = union(enum) {76 pub const Term = union(enum) {
62 Exited: i32,77 Exited: i32,
63 Signal: i32,78 Signal: i32,
...@@ -74,7 +89,7 @@ pub const ChildProcess = struct {...@@ -74,7 +89,7 @@ pub const ChildProcess = struct {
7489
75 /// First argument in argv is the executable.90 /// First argument in argv is the executable.
76 /// On success must call deinit.91 /// On success must call deinit.
77 pub fn init(argv: []const []const u8, allocator: &mem.Allocator) %&ChildProcess {92 pub fn init(argv: []const []const u8, allocator: &mem.Allocator) !&ChildProcess {
78 const child = try allocator.create(ChildProcess);93 const child = try allocator.create(ChildProcess);
79 errdefer allocator.destroy(child);94 errdefer allocator.destroy(child);
8095
...@@ -103,7 +118,7 @@ pub const ChildProcess = struct {...@@ -103,7 +118,7 @@ pub const ChildProcess = struct {
103 return child;118 return child;
104 }119 }
105120
106 pub fn setUserName(self: &ChildProcess, name: []const u8) %void {121 pub fn setUserName(self: &ChildProcess, name: []const u8) !void {
107 const user_info = try os.getUserInfo(name);122 const user_info = try os.getUserInfo(name);
108 self.uid = user_info.uid;123 self.uid = user_info.uid;
109 self.gid = user_info.gid;124 self.gid = user_info.gid;
...@@ -111,7 +126,7 @@ pub const ChildProcess = struct {...@@ -111,7 +126,7 @@ pub const ChildProcess = struct {
111126
112 /// onTerm can be called before `spawn` returns.127 /// onTerm can be called before `spawn` returns.
113 /// On success must call `kill` or `wait`.128 /// On success must call `kill` or `wait`.
114 pub fn spawn(self: &ChildProcess) %void {129 pub fn spawn(self: &ChildProcess) !void {
115 if (is_windows) {130 if (is_windows) {
116 return self.spawnWindows();131 return self.spawnWindows();
117 } else {132 } else {
...@@ -119,13 +134,13 @@ pub const ChildProcess = struct {...@@ -119,13 +134,13 @@ pub const ChildProcess = struct {
119 }134 }
120 }135 }
121136
122 pub fn spawnAndWait(self: &ChildProcess) %Term {137 pub fn spawnAndWait(self: &ChildProcess) !Term {
123 try self.spawn();138 try self.spawn();
124 return self.wait();139 return self.wait();
125 }140 }
126141
127 /// Forcibly terminates child process and then cleans up all resources.142 /// Forcibly terminates child process and then cleans up all resources.
128 pub fn kill(self: &ChildProcess) %Term {143 pub fn kill(self: &ChildProcess) !Term {
129 if (is_windows) {144 if (is_windows) {
130 return self.killWindows(1);145 return self.killWindows(1);
131 } else {146 } else {
...@@ -133,7 +148,7 @@ pub const ChildProcess = struct {...@@ -133,7 +148,7 @@ pub const ChildProcess = struct {
133 }148 }
134 }149 }
135150
136 pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) %Term {151 pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) !Term {
137 if (self.term) |term| {152 if (self.term) |term| {
138 self.cleanupStreams();153 self.cleanupStreams();
139 return term;154 return term;
...@@ -145,11 +160,11 @@ pub const ChildProcess = struct {...@@ -145,11 +160,11 @@ pub const ChildProcess = struct {
145 else => os.unexpectedErrorWindows(err),160 else => os.unexpectedErrorWindows(err),
146 };161 };
147 }162 }
148 self.waitUnwrappedWindows();163 try self.waitUnwrappedWindows();
149 return ??self.term;164 return ??self.term;
150 }165 }
151166
152 pub fn killPosix(self: &ChildProcess) %Term {167 pub fn killPosix(self: &ChildProcess) !Term {
153 block_SIGCHLD();168 block_SIGCHLD();
154 defer restore_SIGCHLD();169 defer restore_SIGCHLD();
155170
...@@ -172,7 +187,7 @@ pub const ChildProcess = struct {...@@ -172,7 +187,7 @@ pub const ChildProcess = struct {
172 }187 }
173188
174 /// Blocks until child process terminates and then cleans up all resources.189 /// Blocks until child process terminates and then cleans up all resources.
175 pub fn wait(self: &ChildProcess) %Term {190 pub fn wait(self: &ChildProcess) !Term {
176 if (is_windows) {191 if (is_windows) {
177 return self.waitWindows();192 return self.waitWindows();
178 } else {193 } else {
...@@ -189,7 +204,7 @@ pub const ChildProcess = struct {...@@ -189,7 +204,7 @@ pub const ChildProcess = struct {
189 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.204 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
190 /// If it succeeds, the caller owns result.stdout and result.stderr memory.205 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
191 pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8,206 pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8,
192 env_map: ?&const BufMap, max_output_size: usize) %ExecResult207 env_map: ?&const BufMap, max_output_size: usize) !ExecResult
193 {208 {
194 const child = try ChildProcess.init(argv, allocator);209 const child = try ChildProcess.init(argv, allocator);
195 defer child.deinit();210 defer child.deinit();
...@@ -220,7 +235,7 @@ pub const ChildProcess = struct {...@@ -220,7 +235,7 @@ pub const ChildProcess = struct {
220 };235 };
221 }236 }
222237
223 fn waitWindows(self: &ChildProcess) %Term {238 fn waitWindows(self: &ChildProcess) !Term {
224 if (self.term) |term| {239 if (self.term) |term| {
225 self.cleanupStreams();240 self.cleanupStreams();
226 return term;241 return term;
...@@ -230,7 +245,7 @@ pub const ChildProcess = struct {...@@ -230,7 +245,7 @@ pub const ChildProcess = struct {
230 return ??self.term;245 return ??self.term;
231 }246 }
232247
233 fn waitPosix(self: &ChildProcess) %Term {248 fn waitPosix(self: &ChildProcess) !Term {
234 block_SIGCHLD();249 block_SIGCHLD();
235 defer restore_SIGCHLD();250 defer restore_SIGCHLD();
236251
...@@ -247,10 +262,10 @@ pub const ChildProcess = struct {...@@ -247,10 +262,10 @@ pub const ChildProcess = struct {
247 self.allocator.destroy(self);262 self.allocator.destroy(self);
248 }263 }
249264
250 fn waitUnwrappedWindows(self: &ChildProcess) %void {265 fn waitUnwrappedWindows(self: &ChildProcess) !void {
251 const result = os.windowsWaitSingle(self.handle, windows.INFINITE);266 const result = os.windowsWaitSingle(self.handle, windows.INFINITE);
252267
253 self.term = (%Term)(x: {268 self.term = (SpawnError!Term)(x: {
254 var exit_code: windows.DWORD = undefined;269 var exit_code: windows.DWORD = undefined;
255 if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {270 if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {
256 break :x Term { .Unknown = 0 };271 break :x Term { .Unknown = 0 };
...@@ -295,7 +310,7 @@ pub const ChildProcess = struct {...@@ -295,7 +310,7 @@ pub const ChildProcess = struct {
295 if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; }310 if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; }
296 }311 }
297312
298 fn cleanupAfterWait(self: &ChildProcess, status: i32) %Term {313 fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term {
299 children_nodes.remove(&self.llnode);314 children_nodes.remove(&self.llnode);
300315
301 defer {316 defer {
...@@ -313,7 +328,7 @@ pub const ChildProcess = struct {...@@ -313,7 +328,7 @@ pub const ChildProcess = struct {
313 // Here we potentially return the fork child's error328 // Here we potentially return the fork child's error
314 // from the parent pid.329 // from the parent pid.
315 if (err_int != @maxValue(ErrInt)) {330 if (err_int != @maxValue(ErrInt)) {
316 return error(err_int);331 return SpawnError(err_int);
317 }332 }
318333
319 return statusToTerm(status);334 return statusToTerm(status);
...@@ -331,7 +346,7 @@ pub const ChildProcess = struct {...@@ -331,7 +346,7 @@ pub const ChildProcess = struct {
331 ;346 ;
332 }347 }
333348
334 fn spawnPosix(self: &ChildProcess) %void {349 fn spawnPosix(self: &ChildProcess) !void {
335 // TODO atomically set a flag saying that we already did this350 // TODO atomically set a flag saying that we already did this
336 install_SIGCHLD_handler();351 install_SIGCHLD_handler();
337352
...@@ -345,11 +360,14 @@ pub const ChildProcess = struct {...@@ -345,11 +360,14 @@ pub const ChildProcess = struct {
345 errdefer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };360 errdefer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
346361
347 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);362 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
348 const dev_null_fd = if (any_ignore)363 const dev_null_fd = if (any_ignore) blk: {
349 try os.posixOpen("/dev/null", posix.O_RDWR, 0, null)364 const dev_null_path = "/dev/null";
350 else365 var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined;
351 undefined366 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
352 ;367 break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0);
368 } else blk: {
369 break :blk undefined;
370 };
353 defer { if (any_ignore) os.close(dev_null_fd); }371 defer { if (any_ignore) os.close(dev_null_fd); }
354372
355 var env_map_owned: BufMap = undefined;373 var env_map_owned: BufMap = undefined;
...@@ -410,17 +428,17 @@ pub const ChildProcess = struct {...@@ -410,17 +428,17 @@ pub const ChildProcess = struct {
410 // we are the parent428 // we are the parent
411 const pid = i32(pid_result);429 const pid = i32(pid_result);
412 if (self.stdin_behavior == StdIo.Pipe) {430 if (self.stdin_behavior == StdIo.Pipe) {
413 self.stdin = io.File.openHandle(stdin_pipe[1]);431 self.stdin = os.File.openHandle(stdin_pipe[1]);
414 } else {432 } else {
415 self.stdin = null;433 self.stdin = null;
416 }434 }
417 if (self.stdout_behavior == StdIo.Pipe) {435 if (self.stdout_behavior == StdIo.Pipe) {
418 self.stdout = io.File.openHandle(stdout_pipe[0]);436 self.stdout = os.File.openHandle(stdout_pipe[0]);
419 } else {437 } else {
420 self.stdout = null;438 self.stdout = null;
421 }439 }
422 if (self.stderr_behavior == StdIo.Pipe) {440 if (self.stderr_behavior == StdIo.Pipe) {
423 self.stderr = io.File.openHandle(stderr_pipe[0]);441 self.stderr = os.File.openHandle(stderr_pipe[0]);
424 } else {442 } else {
425 self.stderr = null;443 self.stderr = null;
426 }444 }
...@@ -440,7 +458,7 @@ pub const ChildProcess = struct {...@@ -440,7 +458,7 @@ pub const ChildProcess = struct {
440 if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }458 if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
441 }459 }
442460
443 fn spawnWindows(self: &ChildProcess) %void {461 fn spawnWindows(self: &ChildProcess) !void {
444 const saAttr = windows.SECURITY_ATTRIBUTES {462 const saAttr = windows.SECURITY_ATTRIBUTES {
445 .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),463 .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
446 .bInheritHandle = windows.TRUE,464 .bInheritHandle = windows.TRUE,
...@@ -451,12 +469,15 @@ pub const ChildProcess = struct {...@@ -451,12 +469,15 @@ pub const ChildProcess = struct {
451 self.stdout_behavior == StdIo.Ignore or469 self.stdout_behavior == StdIo.Ignore or
452 self.stderr_behavior == StdIo.Ignore);470 self.stderr_behavior == StdIo.Ignore);
453471
454 const nul_handle = if (any_ignore)472 const nul_handle = if (any_ignore) blk: {
455 try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,473 const nul_file_path = "NUL";
456 windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null)474 var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined;
457 else475 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
458 undefined476 break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
459 ;477 windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
478 } else blk: {
479 break :blk undefined;
480 };
460 defer { if (any_ignore) os.close(nul_handle); }481 defer { if (any_ignore) os.close(nul_handle); }
461 if (any_ignore) {482 if (any_ignore) {
462 try windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);483 try windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);
...@@ -599,17 +620,17 @@ pub const ChildProcess = struct {...@@ -599,17 +620,17 @@ pub const ChildProcess = struct {
599 };620 };
600621
601 if (g_hChildStd_IN_Wr) |h| {622 if (g_hChildStd_IN_Wr) |h| {
602 self.stdin = io.File.openHandle(h);623 self.stdin = os.File.openHandle(h);
603 } else {624 } else {
604 self.stdin = null;625 self.stdin = null;
605 }626 }
606 if (g_hChildStd_OUT_Rd) |h| {627 if (g_hChildStd_OUT_Rd) |h| {
607 self.stdout = io.File.openHandle(h);628 self.stdout = os.File.openHandle(h);
608 } else {629 } else {
609 self.stdout = null;630 self.stdout = null;
610 }631 }
611 if (g_hChildStd_ERR_Rd) |h| {632 if (g_hChildStd_ERR_Rd) |h| {
612 self.stderr = io.File.openHandle(h);633 self.stderr = os.File.openHandle(h);
613 } else {634 } else {
614 self.stderr = null;635 self.stderr = null;
615 }636 }
...@@ -623,7 +644,7 @@ pub const ChildProcess = struct {...@@ -623,7 +644,7 @@ pub const ChildProcess = struct {
623 if (self.stdout_behavior == StdIo.Pipe) { os.close(??g_hChildStd_OUT_Wr); }644 if (self.stdout_behavior == StdIo.Pipe) { os.close(??g_hChildStd_OUT_Wr); }
624 }645 }
625646
626 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) %void {647 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
627 switch (stdio) {648 switch (stdio) {
628 StdIo.Pipe => try os.posixDup2(pipe_fd, std_fileno),649 StdIo.Pipe => try os.posixDup2(pipe_fd, std_fileno),
629 StdIo.Close => os.close(std_fileno),650 StdIo.Close => os.close(std_fileno),
...@@ -635,7 +656,7 @@ pub const ChildProcess = struct {...@@ -635,7 +656,7 @@ pub const ChildProcess = struct {
635};656};
636657
637fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8,658fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8,
638 lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) %void659 lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void
639{660{
640 if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0,661 if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0,
641 @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0)662 @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0)
...@@ -655,7 +676,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?...@@ -655,7 +676,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?
655676
656/// Caller must dealloc.677/// Caller must dealloc.
657/// Guarantees a null byte at result[result.len].678/// Guarantees a null byte at result[result.len].
658fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) %[]u8 {679fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) ![]u8 {
659 var buf = try Buffer.initSize(allocator, 0);680 var buf = try Buffer.initSize(allocator, 0);
660 defer buf.deinit();681 defer buf.deinit();
661682
...@@ -700,7 +721,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {...@@ -700,7 +721,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
700// a namespace field lookup721// a namespace field lookup
701const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;722const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
702723
703fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void {724fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void {
704 if (windows.CreatePipe(rd, wr, sattr, 0) == 0) {725 if (windows.CreatePipe(rd, wr, sattr, 0) == 0) {
705 const err = windows.GetLastError();726 const err = windows.GetLastError();
706 return switch (err) {727 return switch (err) {
...@@ -709,7 +730,7 @@ fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECUR...@@ -709,7 +730,7 @@ fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECUR
709 }730 }
710}731}
711732
712fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) %void {733fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) !void {
713 if (windows.SetHandleInformation(h, mask, flags) == 0) {734 if (windows.SetHandleInformation(h, mask, flags) == 0) {
714 const err = windows.GetLastError();735 const err = windows.GetLastError();
715 return switch (err) {736 return switch (err) {
...@@ -718,7 +739,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D...@@ -718,7 +739,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D
718 }739 }
719}740}
720741
721fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void {742fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void {
722 var rd_h: windows.HANDLE = undefined;743 var rd_h: windows.HANDLE = undefined;
723 var wr_h: windows.HANDLE = undefined;744 var wr_h: windows.HANDLE = undefined;
724 try windowsMakePipe(&rd_h, &wr_h, sattr);745 try windowsMakePipe(&rd_h, &wr_h, sattr);
...@@ -728,7 +749,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S...@@ -728,7 +749,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S
728 *wr = wr_h;749 *wr = wr_h;
729}750}
730751
731fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void {752fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void {
732 var rd_h: windows.HANDLE = undefined;753 var rd_h: windows.HANDLE = undefined;
733 var wr_h: windows.HANDLE = undefined;754 var wr_h: windows.HANDLE = undefined;
734 try windowsMakePipe(&rd_h, &wr_h, sattr);755 try windowsMakePipe(&rd_h, &wr_h, sattr);
...@@ -738,7 +759,7 @@ fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const...@@ -738,7 +759,7 @@ fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const
738 *wr = wr_h;759 *wr = wr_h;
739}760}
740761
741fn makePipe() %[2]i32 {762fn makePipe() ![2]i32 {
742 var fds: [2]i32 = undefined;763 var fds: [2]i32 = undefined;
743 const err = posix.getErrno(posix.pipe(&fds));764 const err = posix.getErrno(posix.pipe(&fds));
744 if (err > 0) {765 if (err > 0) {
...@@ -757,20 +778,20 @@ fn destroyPipe(pipe: &const [2]i32) void {...@@ -757,20 +778,20 @@ fn destroyPipe(pipe: &const [2]i32) void {
757778
758// Child of fork calls this to report an error to the fork parent.779// Child of fork calls this to report an error to the fork parent.
759// Then the child exits.780// Then the child exits.
760fn forkChildErrReport(fd: i32, err: error) noreturn {781fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
761 _ = writeIntFd(fd, ErrInt(err));782 _ = writeIntFd(fd, ErrInt(err));
762 posix.exit(1);783 posix.exit(1);
763}784}
764785
765const ErrInt = @IntType(false, @sizeOf(error) * 8);786const ErrInt = @IntType(false, @sizeOf(error) * 8);
766787
767fn writeIntFd(fd: i32, value: ErrInt) %void {788fn writeIntFd(fd: i32, value: ErrInt) !void {
768 var bytes: [@sizeOf(ErrInt)]u8 = undefined;789 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
769 mem.writeInt(bytes[0..], value, builtin.endian);790 mem.writeInt(bytes[0..], value, builtin.endian);
770 os.posixWrite(fd, bytes[0..]) catch return error.SystemResources;791 os.posixWrite(fd, bytes[0..]) catch return error.SystemResources;
771}792}
772793
773fn readIntFd(fd: i32) %ErrInt {794fn readIntFd(fd: i32) !ErrInt {
774 var bytes: [@sizeOf(ErrInt)]u8 = undefined;795 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
775 os.posixRead(fd, bytes[0..]) catch return error.SystemResources;796 os.posixRead(fd, bytes[0..]) catch return error.SystemResources;
776 return mem.readInt(bytes[0..], ErrInt, builtin.endian);797 return mem.readInt(bytes[0..], ErrInt, builtin.endian);
std/os/file.zig created+311
...@@ -0,0 +1,311 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const os = std.os;
4const mem = std.mem;
5const math = std.math;
6const assert = std.debug.assert;
7const posix = os.posix;
8const windows = os.windows;
9const Os = builtin.Os;
10
11const is_posix = builtin.os != builtin.Os.windows;
12const is_windows = builtin.os == builtin.Os.windows;
13
14pub const File = struct {
15 /// The OS-specific file descriptor or file handle.
16 handle: os.FileHandle,
17
18 const OpenError = os.WindowsOpenError || os.PosixOpenError;
19
20 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
21 /// Call close to clean up.
22 pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File {
23 if (is_posix) {
24 const flags = posix.O_LARGEFILE|posix.O_RDONLY;
25 const fd = try os.posixOpen(allocator, path, flags, 0);
26 return openHandle(fd);
27 } else if (is_windows) {
28 const handle = try os.windowsOpen(allocator, path, windows.GENERIC_READ, windows.FILE_SHARE_READ,
29 windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
30 return openHandle(handle);
31 } else {
32 @compileError("TODO implement openRead for this OS");
33 }
34 }
35
36 /// Calls `openWriteMode` with os.default_file_mode for the mode.
37 pub fn openWrite(allocator: &mem.Allocator, path: []const u8) OpenError!File {
38 return openWriteMode(allocator, path, os.default_file_mode);
39
40 }
41
42 /// If the path does not exist it will be created.
43 /// If a file already exists in the destination it will be truncated.
44 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
45 /// Call close to clean up.
46 pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
47 if (is_posix) {
48 const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_TRUNC;
49 const fd = try os.posixOpen(allocator, path, flags, file_mode);
50 return openHandle(fd);
51 } else if (is_windows) {
52 const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE,
53 windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE,
54 windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL);
55 return openHandle(handle);
56 } else {
57 @compileError("TODO implement openWriteMode for this OS");
58 }
59
60 }
61
62 /// If the path does not exist it will be created.
63 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
64 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
65 /// Call close to clean up.
66 pub fn openWriteNoClobber(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
67 if (is_posix) {
68 const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_EXCL;
69 const fd = try os.posixOpen(allocator, path, flags, file_mode);
70 return openHandle(fd);
71 } else if (is_windows) {
72 const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE,
73 windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE,
74 windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL);
75 return openHandle(handle);
76 } else {
77 @compileError("TODO implement openWriteMode for this OS");
78 }
79
80 }
81
82 pub fn openHandle(handle: os.FileHandle) File {
83 return File {
84 .handle = handle,
85 };
86 }
87
88
89 /// Upon success, the stream is in an uninitialized state. To continue using it,
90 /// you must use the open() function.
91 pub fn close(self: &File) void {
92 os.close(self.handle);
93 self.handle = undefined;
94 }
95
96 /// Calls `os.isTty` on `self.handle`.
97 pub fn isTty(self: &File) bool {
98 return os.isTty(self.handle);
99 }
100
101 pub fn seekForward(self: &File, amount: isize) !void {
102 switch (builtin.os) {
103 Os.linux, Os.macosx, Os.ios => {
104 const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
105 const err = posix.getErrno(result);
106 if (err > 0) {
107 return switch (err) {
108 posix.EBADF => error.BadFd,
109 posix.EINVAL => error.Unseekable,
110 posix.EOVERFLOW => error.Unseekable,
111 posix.ESPIPE => error.Unseekable,
112 posix.ENXIO => error.Unseekable,
113 else => os.unexpectedErrorPosix(err),
114 };
115 }
116 },
117 Os.windows => {
118 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
119 const err = windows.GetLastError();
120 return switch (err) {
121 windows.ERROR.INVALID_PARAMETER => error.BadFd,
122 else => os.unexpectedErrorWindows(err),
123 };
124 }
125 },
126 else => @compileError("unsupported OS"),
127 }
128 }
129
130 pub fn seekTo(self: &File, pos: usize) !void {
131 switch (builtin.os) {
132 Os.linux, Os.macosx, Os.ios => {
133 const ipos = try math.cast(isize, pos);
134 const result = posix.lseek(self.handle, ipos, posix.SEEK_SET);
135 const err = posix.getErrno(result);
136 if (err > 0) {
137 return switch (err) {
138 posix.EBADF => error.BadFd,
139 posix.EINVAL => error.Unseekable,
140 posix.EOVERFLOW => error.Unseekable,
141 posix.ESPIPE => error.Unseekable,
142 posix.ENXIO => error.Unseekable,
143 else => os.unexpectedErrorPosix(err),
144 };
145 }
146 },
147 Os.windows => {
148 const ipos = try math.cast(isize, pos);
149 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
150 const err = windows.GetLastError();
151 return switch (err) {
152 windows.ERROR.INVALID_PARAMETER => error.BadFd,
153 else => os.unexpectedErrorWindows(err),
154 };
155 }
156 },
157 else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
158 }
159 }
160
161 pub fn getPos(self: &File) !usize {
162 switch (builtin.os) {
163 Os.linux, Os.macosx, Os.ios => {
164 const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
165 const err = posix.getErrno(result);
166 if (err > 0) {
167 return switch (err) {
168 posix.EBADF => error.BadFd,
169 posix.EINVAL => error.Unseekable,
170 posix.EOVERFLOW => error.Unseekable,
171 posix.ESPIPE => error.Unseekable,
172 posix.ENXIO => error.Unseekable,
173 else => os.unexpectedErrorPosix(err),
174 };
175 }
176 return result;
177 },
178 Os.windows => {
179 var pos : windows.LARGE_INTEGER = undefined;
180 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
181 const err = windows.GetLastError();
182 return switch (err) {
183 windows.ERROR.INVALID_PARAMETER => error.BadFd,
184 else => os.unexpectedErrorWindows(err),
185 };
186 }
187
188 assert(pos >= 0);
189 if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) {
190 if (pos > @maxValue(usize)) {
191 return error.FilePosLargerThanPointerRange;
192 }
193 }
194
195 return usize(pos);
196 },
197 else => @compileError("unsupported OS"),
198 }
199 }
200
201 pub fn getEndPos(self: &File) !usize {
202 if (is_posix) {
203 var stat: posix.Stat = undefined;
204 const err = posix.getErrno(posix.fstat(self.handle, &stat));
205 if (err > 0) {
206 return switch (err) {
207 posix.EBADF => error.BadFd,
208 posix.ENOMEM => error.SystemResources,
209 else => os.unexpectedErrorPosix(err),
210 };
211 }
212
213 return usize(stat.size);
214 } else if (is_windows) {
215 var file_size: windows.LARGE_INTEGER = undefined;
216 if (windows.GetFileSizeEx(self.handle, &file_size) == 0) {
217 const err = windows.GetLastError();
218 return switch (err) {
219 else => os.unexpectedErrorWindows(err),
220 };
221 }
222 if (file_size < 0)
223 return error.Overflow;
224 return math.cast(usize, u64(file_size));
225 } else {
226 @compileError("TODO support getEndPos on this OS");
227 }
228 }
229
230 pub const ModeError = error {
231 BadFd,
232 SystemResources,
233 Unexpected,
234 };
235
236 fn mode(self: &File) ModeError!FileMode {
237 if (is_posix) {
238 var stat: posix.Stat = undefined;
239 const err = posix.getErrno(posix.fstat(self.handle, &stat));
240 if (err > 0) {
241 return switch (err) {
242 posix.EBADF => error.BadFd,
243 posix.ENOMEM => error.SystemResources,
244 else => os.unexpectedErrorPosix(err),
245 };
246 }
247
248 return stat.mode;
249 } else if (is_windows) {
250 return {};
251 } else {
252 @compileError("TODO support file mode on this OS");
253 }
254 }
255
256 pub const ReadError = error {};
257
258 pub fn read(self: &File, buffer: []u8) !usize {
259 if (is_posix) {
260 var index: usize = 0;
261 while (index < buffer.len) {
262 const amt_read = posix.read(self.handle, &buffer[index], buffer.len - index);
263 const read_err = posix.getErrno(amt_read);
264 if (read_err > 0) {
265 switch (read_err) {
266 posix.EINTR => continue,
267 posix.EINVAL => unreachable,
268 posix.EFAULT => unreachable,
269 posix.EBADF => return error.BadFd,
270 posix.EIO => return error.Io,
271 else => return os.unexpectedErrorPosix(read_err),
272 }
273 }
274 if (amt_read == 0) return index;
275 index += amt_read;
276 }
277 return index;
278 } else if (is_windows) {
279 var index: usize = 0;
280 while (index < buffer.len) {
281 const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
282 var amt_read: windows.DWORD = undefined;
283 if (windows.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) {
284 const err = windows.GetLastError();
285 return switch (err) {
286 windows.ERROR.OPERATION_ABORTED => continue,
287 windows.ERROR.BROKEN_PIPE => return index,
288 else => os.unexpectedErrorWindows(err),
289 };
290 }
291 if (amt_read == 0) return index;
292 index += amt_read;
293 }
294 return index;
295 } else {
296 unreachable;
297 }
298 }
299
300 pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
301
302 fn write(self: &File, bytes: []const u8) WriteError!void {
303 if (is_posix) {
304 try os.posixWrite(self.handle, bytes);
305 } else if (is_windows) {
306 try os.windowsWrite(self.handle, bytes);
307 } else {
308 @compileError("Unsupported OS");
309 }
310 }
311};
std/os/get_user_id.zig+2-5
...@@ -9,7 +9,7 @@ pub const UserInfo = struct {...@@ -9,7 +9,7 @@ pub const UserInfo = struct {
9};9};
1010
11/// POSIX function which gets a uid from username.11/// POSIX function which gets a uid from username.
12pub fn getUserInfo(name: []const u8) %UserInfo {12pub fn getUserInfo(name: []const u8) !UserInfo {
13 return switch (builtin.os) {13 return switch (builtin.os) {
14 Os.linux, Os.macosx, Os.ios => posixGetUserInfo(name),14 Os.linux, Os.macosx, Os.ios => posixGetUserInfo(name),
15 else => @compileError("Unsupported OS"),15 else => @compileError("Unsupported OS"),
...@@ -24,13 +24,10 @@ const State = enum {...@@ -24,13 +24,10 @@ const State = enum {
24 ReadGroupId,24 ReadGroupId,
25};25};
2626
27error UserNotFound;
28error CorruptPasswordFile;
29
30// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else27// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
31// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.28// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
3229
33pub fn posixGetUserInfo(name: []const u8) %UserInfo {30pub fn posixGetUserInfo(name: []const u8) !UserInfo {
34 var in_stream = try io.InStream.open("/etc/passwd", null);31 var in_stream = try io.InStream.open("/etc/passwd", null);
35 defer in_stream.close();32 defer in_stream.close();
3633
std/os/index.zig+341-181
...@@ -15,13 +15,18 @@ pub const posix = switch(builtin.os) {...@@ -15,13 +15,18 @@ pub const posix = switch(builtin.os) {
15 else => @compileError("Unsupported OS"),15 else => @compileError("Unsupported OS"),
16};16};
1717
18pub const max_noalloc_path_len = 1024;
19pub const ChildProcess = @import("child_process.zig").ChildProcess;18pub const ChildProcess = @import("child_process.zig").ChildProcess;
20pub const path = @import("path.zig");19pub const path = @import("path.zig");
20pub const File = @import("file.zig").File;
2121
22pub const line_sep = switch (builtin.os) {22pub const FileMode = switch (builtin.os) {
23 Os.windows => "\r\n",23 Os.windows => void,
24 else => "\n",24 else => u32,
25};
26
27pub const default_file_mode = switch (builtin.os) {
28 Os.windows => {},
29 else => 0o666,
25};30};
2631
27pub const page_size = 4 * 1024;32pub const page_size = 4 * 1024;
...@@ -38,6 +43,10 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;...@@ -38,6 +43,10 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;
38pub const windowsUnloadDll = windows_util.windowsUnloadDll; 43pub const windowsUnloadDll = windows_util.windowsUnloadDll;
39pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;44pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
4045
46pub const WindowsWaitError = windows_util.WaitError;
47pub const WindowsOpenError = windows_util.OpenError;
48pub const WindowsWriteError = windows_util.WriteError;
49
41pub const FileHandle = if (is_windows) windows.HANDLE else i32;50pub const FileHandle = if (is_windows) windows.HANDLE else i32;
4251
43const debug = std.debug;52const debug = std.debug;
...@@ -57,25 +66,10 @@ const ArrayList = std.ArrayList;...@@ -57,25 +66,10 @@ const ArrayList = std.ArrayList;
57const Buffer = std.Buffer;66const Buffer = std.Buffer;
58const math = std.math;67const math = std.math;
5968
60error SystemResources;
61error AccessDenied;
62error InvalidExe;
63error FileSystem;
64error IsDir;
65error FileNotFound;
66error FileBusy;
67error PathAlreadyExists;
68error SymLinkLoop;
69error ReadOnlyFileSystem;
70error LinkQuotaExceeded;
71error RenameAcrossMountPoints;
72error DirNotEmpty;
73error WouldBlock;
74
75/// Fills `buf` with random bytes. If linking against libc, this calls the69/// Fills `buf` with random bytes. If linking against libc, this calls the
76/// appropriate OS-specific library call. Otherwise it uses the zig standard70/// appropriate OS-specific library call. Otherwise it uses the zig standard
77/// library implementation.71/// library implementation.
78pub fn getRandomBytes(buf: []u8) %void {72pub fn getRandomBytes(buf: []u8) !void {
79 switch (builtin.os) {73 switch (builtin.os) {
80 Os.linux => while (true) {74 Os.linux => while (true) {
81 // TODO check libc version and potentially call c.getrandom.75 // TODO check libc version and potentially call c.getrandom.
...@@ -188,7 +182,7 @@ pub fn close(handle: FileHandle) void {...@@ -188,7 +182,7 @@ pub fn close(handle: FileHandle) void {
188}182}
189183
190/// Calls POSIX read, and keeps trying if it gets interrupted.184/// Calls POSIX read, and keeps trying if it gets interrupted.
191pub fn posixRead(fd: i32, buf: []u8) %void {185pub fn posixRead(fd: i32, buf: []u8) !void {
192 // Linux can return EINVAL when read amount is > 0x7ffff000186 // Linux can return EINVAL when read amount is > 0x7ffff000
193 // See https://github.com/zig-lang/zig/pull/743#issuecomment-363158274187 // See https://github.com/zig-lang/zig/pull/743#issuecomment-363158274
194 const max_buf_len = 0x7ffff000;188 const max_buf_len = 0x7ffff000;
...@@ -214,17 +208,21 @@ pub fn posixRead(fd: i32, buf: []u8) %void {...@@ -214,17 +208,21 @@ pub fn posixRead(fd: i32, buf: []u8) %void {
214 }208 }
215}209}
216210
217error WouldBlock;211pub const PosixWriteError = error {
218error FileClosed;212 WouldBlock,
219error DestinationAddressRequired;213 FileClosed,
220error DiskQuota;214 DestinationAddressRequired,
221error FileTooBig;215 DiskQuota,
222error InputOutput;216 FileTooBig,
223error NoSpaceLeft;217 InputOutput,
224error BrokenPipe;218 NoSpaceLeft,
219 AccessDenied,
220 BrokenPipe,
221 Unexpected,
222};
225223
226/// Calls POSIX write, and keeps trying if it gets interrupted.224/// Calls POSIX write, and keeps trying if it gets interrupted.
227pub fn posixWrite(fd: i32, bytes: []const u8) %void {225pub fn posixWrite(fd: i32, bytes: []const u8) !void {
228 // Linux can return EINVAL when write amount is > 0x7ffff000226 // Linux can return EINVAL when write amount is > 0x7ffff000
229 // See https://github.com/zig-lang/zig/pull/743#issuecomment-363165856227 // See https://github.com/zig-lang/zig/pull/743#issuecomment-363165856
230 const max_bytes_len = 0x7ffff000;228 const max_bytes_len = 0x7ffff000;
...@@ -238,15 +236,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {...@@ -238,15 +236,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {
238 return switch (write_err) {236 return switch (write_err) {
239 posix.EINTR => continue,237 posix.EINTR => continue,
240 posix.EINVAL, posix.EFAULT => unreachable,238 posix.EINVAL, posix.EFAULT => unreachable,
241 posix.EAGAIN => error.WouldBlock,239 posix.EAGAIN => PosixWriteError.WouldBlock,
242 posix.EBADF => error.FileClosed,240 posix.EBADF => PosixWriteError.FileClosed,
243 posix.EDESTADDRREQ => error.DestinationAddressRequired,241 posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
244 posix.EDQUOT => error.DiskQuota,242 posix.EDQUOT => PosixWriteError.DiskQuota,
245 posix.EFBIG => error.FileTooBig,243 posix.EFBIG => PosixWriteError.FileTooBig,
246 posix.EIO => error.InputOutput,244 posix.EIO => PosixWriteError.InputOutput,
247 posix.ENOSPC => error.NoSpaceLeft,245 posix.ENOSPC => PosixWriteError.NoSpaceLeft,
248 posix.EPERM => error.AccessDenied,246 posix.EPERM => PosixWriteError.AccessDenied,
249 posix.EPIPE => error.BrokenPipe,247 posix.EPIPE => PosixWriteError.BrokenPipe,
250 else => unexpectedErrorPosix(write_err),248 else => unexpectedErrorPosix(write_err),
251 };249 };
252 }250 }
...@@ -254,66 +252,66 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {...@@ -254,66 +252,66 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {
254 }252 }
255}253}
256254
257/// ::file_path may need to be copied in memory to add a null terminating byte. In this case255pub const PosixOpenError = error {
258/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed256 OutOfMemory,
259/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.257 AccessDenied,
260/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.258 FileTooBig,
259 IsDir,
260 SymLinkLoop,
261 ProcessFdQuotaExceeded,
262 NameTooLong,
263 SystemFdQuotaExceeded,
264 NoDevice,
265 PathNotFound,
266 SystemResources,
267 NoSpaceLeft,
268 NotDir,
269 PathAlreadyExists,
270 Unexpected,
271};
272
273/// ::file_path needs to be copied in memory to add a null terminating byte.
261/// Calls POSIX open, keeps trying if it gets interrupted, and translates274/// Calls POSIX open, keeps trying if it gets interrupted, and translates
262/// the return value into zig errors.275/// the return value into zig errors.
263pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) %i32 {276pub fn posixOpen(allocator: &Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
264 var stack_buf: [max_noalloc_path_len]u8 = undefined;277 const path_with_null = try cstr.addNullByte(allocator, file_path);
265 var path0: []u8 = undefined;278 defer allocator.free(path_with_null);
266 var need_free = false;
267
268 if (file_path.len < stack_buf.len) {
269 path0 = stack_buf[0..file_path.len + 1];
270 } else if (allocator) |a| {
271 path0 = try a.alloc(u8, file_path.len + 1);
272 need_free = true;
273 } else {
274 return error.NameTooLong;
275 }
276 defer if (need_free) {
277 (??allocator).free(path0);
278 };
279 mem.copy(u8, path0, file_path);
280 path0[file_path.len] = 0;
281279
282 return posixOpenC(path0.ptr, flags, perm);280 return posixOpenC(path_with_null.ptr, flags, perm);
283}281}
284282
285pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) %i32 {283pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
286 while (true) {284 while (true) {
287 const result = posix.open(file_path, flags, perm);285 const result = posix.open(file_path, flags, perm);
288 const err = posix.getErrno(result);286 const err = posix.getErrno(result);
289 if (err > 0) {287 if (err > 0) {
290 return switch (err) {288 switch (err) {
291 posix.EINTR => continue,289 posix.EINTR => continue,
292290
293 posix.EFAULT => unreachable,291 posix.EFAULT => unreachable,
294 posix.EINVAL => unreachable,292 posix.EINVAL => unreachable,
295 posix.EACCES => error.AccessDenied,293 posix.EACCES => return PosixOpenError.AccessDenied,
296 posix.EFBIG, posix.EOVERFLOW => error.FileTooBig,294 posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,
297 posix.EISDIR => error.IsDir,295 posix.EISDIR => return PosixOpenError.IsDir,
298 posix.ELOOP => error.SymLinkLoop,296 posix.ELOOP => return PosixOpenError.SymLinkLoop,
299 posix.EMFILE => error.ProcessFdQuotaExceeded,297 posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
300 posix.ENAMETOOLONG => error.NameTooLong,298 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
301 posix.ENFILE => error.SystemFdQuotaExceeded,299 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
302 posix.ENODEV => error.NoDevice,300 posix.ENODEV => return PosixOpenError.NoDevice,
303 posix.ENOENT => error.PathNotFound,301 posix.ENOENT => return PosixOpenError.PathNotFound,
304 posix.ENOMEM => error.SystemResources,302 posix.ENOMEM => return PosixOpenError.SystemResources,
305 posix.ENOSPC => error.NoSpaceLeft,303 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
306 posix.ENOTDIR => error.NotDir,304 posix.ENOTDIR => return PosixOpenError.NotDir,
307 posix.EPERM => error.AccessDenied,305 posix.EPERM => return PosixOpenError.AccessDenied,
308 posix.EEXIST => error.PathAlreadyExists,306 posix.EEXIST => return PosixOpenError.PathAlreadyExists,
309 else => unexpectedErrorPosix(err),307 else => return unexpectedErrorPosix(err),
310 };308 }
311 }309 }
312 return i32(result);310 return i32(result);
313 }311 }
314}312}
315313
316pub fn posixDup2(old_fd: i32, new_fd: i32) %void {314pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
317 while (true) {315 while (true) {
318 const err = posix.getErrno(posix.dup2(old_fd, new_fd));316 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
319 if (err > 0) {317 if (err > 0) {
...@@ -328,7 +326,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) %void {...@@ -328,7 +326,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) %void {
328 }326 }
329}327}
330328
331pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) %[]?&u8 {329pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) ![]?&u8 {
332 const envp_count = env_map.count();330 const envp_count = env_map.count();
333 const envp_buf = try allocator.alloc(?&u8, envp_count + 1);331 const envp_buf = try allocator.alloc(?&u8, envp_count + 1);
334 mem.set(?&u8, envp_buf, null);332 mem.set(?&u8, envp_buf, null);
...@@ -365,7 +363,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {...@@ -365,7 +363,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
365/// `argv[0]` is the executable path.363/// `argv[0]` is the executable path.
366/// This function also uses the PATH environment variable to get the full path to the executable.364/// This function also uses the PATH environment variable to get the full path to the executable.
367pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,365pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
368 allocator: &Allocator) %void366 allocator: &Allocator) !void
369{367{
370 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);368 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
371 mem.set(?&u8, argv_buf, null);369 mem.set(?&u8, argv_buf, null);
...@@ -421,7 +419,19 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,...@@ -421,7 +419,19 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
421 return posixExecveErrnoToErr(err);419 return posixExecveErrnoToErr(err);
422}420}
423421
424fn posixExecveErrnoToErr(err: usize) error {422pub const PosixExecveError = error {
423 SystemResources,
424 AccessDenied,
425 InvalidExe,
426 FileSystem,
427 IsDir,
428 FileNotFound,
429 NotDir,
430 FileBusy,
431 Unexpected,
432};
433
434fn posixExecveErrnoToErr(err: usize) PosixExecveError {
425 assert(err > 0);435 assert(err > 0);
426 return switch (err) {436 return switch (err) {
427 posix.EFAULT => unreachable,437 posix.EFAULT => unreachable,
...@@ -440,7 +450,7 @@ fn posixExecveErrnoToErr(err: usize) error {...@@ -440,7 +450,7 @@ fn posixExecveErrnoToErr(err: usize) error {
440pub var posix_environ_raw: []&u8 = undefined;450pub var posix_environ_raw: []&u8 = undefined;
441451
442/// Caller must free result when done.452/// Caller must free result when done.
443pub fn getEnvMap(allocator: &Allocator) %BufMap {453pub fn getEnvMap(allocator: &Allocator) !BufMap {
444 var result = BufMap.init(allocator);454 var result = BufMap.init(allocator);
445 errdefer result.deinit();455 errdefer result.deinit();
446456
...@@ -501,10 +511,8 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {...@@ -501,10 +511,8 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
501 return null;511 return null;
502}512}
503513
504error EnvironmentVariableNotFound;
505
506/// Caller must free returned memory.514/// Caller must free returned memory.
507pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) %[]u8 {515pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) ![]u8 {
508 if (is_windows) {516 if (is_windows) {
509 const key_with_null = try cstr.addNullByte(allocator, key);517 const key_with_null = try cstr.addNullByte(allocator, key);
510 defer allocator.free(key_with_null);518 defer allocator.free(key_with_null);
...@@ -538,7 +546,7 @@ pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) %[]u8 {...@@ -538,7 +546,7 @@ pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) %[]u8 {
538}546}
539547
540/// Caller must free the returned memory.548/// Caller must free the returned memory.
541pub fn getCwd(allocator: &Allocator) %[]u8 {549pub fn getCwd(allocator: &Allocator) ![]u8 {
542 switch (builtin.os) {550 switch (builtin.os) {
543 Os.windows => {551 Os.windows => {
544 var buf = try allocator.alloc(u8, 256);552 var buf = try allocator.alloc(u8, 256);
...@@ -585,7 +593,9 @@ test "os.getCwd" {...@@ -585,7 +593,9 @@ test "os.getCwd" {
585 _ = getCwd(debug.global_allocator);593 _ = getCwd(debug.global_allocator);
586}594}
587595
588pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {596pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
597
598pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
589 if (is_windows) {599 if (is_windows) {
590 return symLinkWindows(allocator, existing_path, new_path);600 return symLinkWindows(allocator, existing_path, new_path);
591 } else {601 } else {
...@@ -593,7 +603,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con...@@ -593,7 +603,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
593 }603 }
594}604}
595605
596pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {606pub const WindowsSymLinkError = error {
607 OutOfMemory,
608 Unexpected,
609};
610
611pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
597 const existing_with_null = try cstr.addNullByte(allocator, existing_path);612 const existing_with_null = try cstr.addNullByte(allocator, existing_path);
598 defer allocator.free(existing_with_null);613 defer allocator.free(existing_with_null);
599 const new_with_null = try cstr.addNullByte(allocator, new_path);614 const new_with_null = try cstr.addNullByte(allocator, new_path);
...@@ -607,7 +622,23 @@ pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path...@@ -607,7 +622,23 @@ pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path
607 }622 }
608}623}
609624
610pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {625pub const PosixSymLinkError = error {
626 OutOfMemory,
627 AccessDenied,
628 DiskQuota,
629 PathAlreadyExists,
630 FileSystem,
631 SymLinkLoop,
632 NameTooLong,
633 FileNotFound,
634 SystemResources,
635 NoSpaceLeft,
636 ReadOnlyFileSystem,
637 NotDir,
638 Unexpected,
639};
640
641pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
611 const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);642 const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
612 defer allocator.free(full_buf);643 defer allocator.free(full_buf);
613644
...@@ -644,36 +675,36 @@ const b64_fs_encoder = base64.Base64Encoder.init(...@@ -644,36 +675,36 @@ const b64_fs_encoder = base64.Base64Encoder.init(
644 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",675 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
645 base64.standard_pad_char);676 base64.standard_pad_char);
646677
647pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {678pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
648 if (symLink(allocator, existing_path, new_path)) {679 if (symLink(allocator, existing_path, new_path)) {
649 return;680 return;
650 } else |err| {681 } else |err| switch (err) {
651 if (err != error.PathAlreadyExists) {682 error.PathAlreadyExists => {},
652 return err;683 else => return err, // TODO zig should know this set does not include PathAlreadyExists
653 }
654 }684 }
655685
686 const dirname = os.path.dirname(new_path);
687
656 var rand_buf: [12]u8 = undefined;688 var rand_buf: [12]u8 = undefined;
657 const tmp_path = try allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len));689 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
658 defer allocator.free(tmp_path);690 defer allocator.free(tmp_path);
659 mem.copy(u8, tmp_path[0..], new_path);691 mem.copy(u8, tmp_path[0..], dirname);
692 tmp_path[dirname.len] = os.path.sep;
660 while (true) {693 while (true) {
661 try getRandomBytes(rand_buf[0..]);694 try getRandomBytes(rand_buf[0..]);
662 b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf);695 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
696
663 if (symLink(allocator, existing_path, tmp_path)) {697 if (symLink(allocator, existing_path, tmp_path)) {
664 return rename(allocator, tmp_path, new_path);698 return rename(allocator, tmp_path, new_path);
665 } else |err| {699 } else |err| switch (err) {
666 if (err == error.PathAlreadyExists) {700 error.PathAlreadyExists => continue,
667 continue;701 else => return err, // TODO zig should know this set does not include PathAlreadyExists
668 } else {
669 return err;
670 }
671 }702 }
672 }703 }
673704
674}705}
675706
676pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {707pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
677 if (builtin.os == Os.windows) {708 if (builtin.os == Os.windows) {
678 return deleteFileWindows(allocator, file_path);709 return deleteFileWindows(allocator, file_path);
679 } else {710 } else {
...@@ -681,10 +712,7 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {...@@ -681,10 +712,7 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {
681 }712 }
682}713}
683714
684error FileNotFound;715pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
685error AccessDenied;
686
687pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {
688 const buf = try allocator.alloc(u8, file_path.len + 1);716 const buf = try allocator.alloc(u8, file_path.len + 1);
689 defer allocator.free(buf);717 defer allocator.free(buf);
690718
...@@ -702,7 +730,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {...@@ -702,7 +730,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {
702 }730 }
703}731}
704732
705pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {733pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
706 const buf = try allocator.alloc(u8, file_path.len + 1);734 const buf = try allocator.alloc(u8, file_path.len + 1);
707 defer allocator.free(buf);735 defer allocator.free(buf);
708736
...@@ -728,38 +756,109 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {...@@ -728,38 +756,109 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {
728 }756 }
729}757}
730758
731/// Calls ::copyFileMode with 0o666 for the mode.759/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
732pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) %void {760/// merged and readily available,
733 return copyFileMode(allocator, source_path, dest_path, 0o666);761/// there is a possibility of power loss or application termination leaving temporary files present
734}762/// in the same directory as dest_path.
763/// Destination file will have the same mode as the source file.
764pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) !void {
765 var in_file = try os.File.openRead(allocator, source_path);
766 defer in_file.close();
735767
736// TODO instead of accepting a mode argument, use the mode from fstat'ing the source path once open768 const mode = try in_file.mode();
737/// Guaranteed to be atomic.
738pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) %void {
739 var rand_buf: [12]u8 = undefined;
740 const tmp_path = try allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
741 defer allocator.free(tmp_path);
742 mem.copy(u8, tmp_path[0..], dest_path);
743 try getRandomBytes(rand_buf[0..]);
744 b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf);
745769
746 var out_file = try io.File.openWriteMode(tmp_path, mode, allocator);770 var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
747 defer out_file.close();771 defer atomic_file.deinit();
748 errdefer _ = deleteFile(allocator, tmp_path);
749772
750 var in_file = try io.File.openRead(source_path, allocator);773 var buf: [page_size]u8 = undefined;
774 while (true) {
775 const amt = try in_file.read(buf[0..]);
776 try atomic_file.file.write(buf[0..amt]);
777 if (amt != buf.len) {
778 return atomic_file.finish();
779 }
780 }
781}
782
783/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
784/// merged and readily available,
785/// there is a possibility of power loss or application termination leaving temporary files present
786pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void {
787 var in_file = try os.File.openRead(allocator, source_path);
751 defer in_file.close();788 defer in_file.close();
752789
790 var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
791 defer atomic_file.deinit();
792
753 var buf: [page_size]u8 = undefined;793 var buf: [page_size]u8 = undefined;
754 while (true) {794 while (true) {
755 const amt = try in_file.read(buf[0..]);795 const amt = try in_file.read(buf[0..]);
756 try out_file.write(buf[0..amt]);796 try atomic_file.file.write(buf[0..amt]);
757 if (amt != buf.len)797 if (amt != buf.len) {
758 return rename(allocator, tmp_path, dest_path);798 return atomic_file.finish();
799 }
759 }800 }
760}801}
761802
762pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) %void {803pub const AtomicFile = struct {
804 allocator: &Allocator,
805 file: os.File,
806 tmp_path: []u8,
807 dest_path: []const u8,
808 finished: bool,
809
810 /// dest_path must remain valid for the lifetime of AtomicFile
811 /// call finish to atomically replace dest_path with contents
812 pub fn init(allocator: &Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile {
813 const dirname = os.path.dirname(dest_path);
814
815 var rand_buf: [12]u8 = undefined;
816 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
817 errdefer allocator.free(tmp_path);
818 mem.copy(u8, tmp_path[0..], dirname);
819 tmp_path[dirname.len] = os.path.sep;
820
821 while (true) {
822 try getRandomBytes(rand_buf[0..]);
823 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
824
825 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
826 error.PathAlreadyExists => continue,
827 // TODO zig should figure out that this error set does not include PathAlreadyExists since
828 // it is handled in the above switch
829 else => return err,
830 };
831
832 return AtomicFile {
833 .allocator = allocator,
834 .file = file,
835 .tmp_path = tmp_path,
836 .dest_path = dest_path,
837 .finished = false,
838 };
839 }
840 }
841
842 /// always call deinit, even after successful finish()
843 pub fn deinit(self: &AtomicFile) void {
844 if (!self.finished) {
845 self.file.close();
846 deleteFile(self.allocator, self.tmp_path) catch {};
847 self.allocator.free(self.tmp_path);
848 self.finished = true;
849 }
850 }
851
852 pub fn finish(self: &AtomicFile) !void {
853 assert(!self.finished);
854 self.file.close();
855 try rename(self.allocator, self.tmp_path, self.dest_path);
856 self.allocator.free(self.tmp_path);
857 self.finished = true;
858 }
859};
860
861pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) !void {
763 const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);862 const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);
764 defer allocator.free(full_buf);863 defer allocator.free(full_buf);
765864
...@@ -804,7 +903,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -804,7 +903,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
804 }903 }
805}904}
806905
807pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {906pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void {
808 if (is_windows) {907 if (is_windows) {
809 return makeDirWindows(allocator, dir_path);908 return makeDirWindows(allocator, dir_path);
810 } else {909 } else {
...@@ -812,7 +911,7 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {...@@ -812,7 +911,7 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {
812 }911 }
813}912}
814913
815pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {914pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void {
816 const path_buf = try cstr.addNullByte(allocator, dir_path);915 const path_buf = try cstr.addNullByte(allocator, dir_path);
817 defer allocator.free(path_buf);916 defer allocator.free(path_buf);
818917
...@@ -826,7 +925,7 @@ pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {...@@ -826,7 +925,7 @@ pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {
826 }925 }
827}926}
828927
829pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {928pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
830 const path_buf = try cstr.addNullByte(allocator, dir_path);929 const path_buf = try cstr.addNullByte(allocator, dir_path);
831 defer allocator.free(path_buf);930 defer allocator.free(path_buf);
832931
...@@ -852,7 +951,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {...@@ -852,7 +951,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {
852951
853/// Calls makeDir recursively to make an entire path. Returns success if the path952/// Calls makeDir recursively to make an entire path. Returns success if the path
854/// already exists and is a directory.953/// already exists and is a directory.
855pub fn makePath(allocator: &Allocator, full_path: []const u8) %void {954pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
856 const resolved_path = try path.resolve(allocator, full_path);955 const resolved_path = try path.resolve(allocator, full_path);
857 defer allocator.free(resolved_path);956 defer allocator.free(resolved_path);
858957
...@@ -890,7 +989,7 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) %void {...@@ -890,7 +989,7 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) %void {
890989
891/// Returns ::error.DirNotEmpty if the directory is not empty.990/// Returns ::error.DirNotEmpty if the directory is not empty.
892/// To delete a directory recursively, see ::deleteTree991/// To delete a directory recursively, see ::deleteTree
893pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) %void {992pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
894 const path_buf = try allocator.alloc(u8, dir_path.len + 1);993 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
895 defer allocator.free(path_buf);994 defer allocator.free(path_buf);
896995
...@@ -919,24 +1018,68 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) %void {...@@ -919,24 +1018,68 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) %void {
919/// removes it. If it cannot be removed because it is a non-empty directory,1018/// removes it. If it cannot be removed because it is a non-empty directory,
920/// this function recursively removes its entries and then tries again.1019/// this function recursively removes its entries and then tries again.
921// TODO non-recursive implementation1020// TODO non-recursive implementation
922pub fn deleteTree(allocator: &Allocator, full_path: []const u8) %void {1021const DeleteTreeError = error {
1022 OutOfMemory,
1023 AccessDenied,
1024 FileTooBig,
1025 IsDir,
1026 SymLinkLoop,
1027 ProcessFdQuotaExceeded,
1028 NameTooLong,
1029 SystemFdQuotaExceeded,
1030 NoDevice,
1031 PathNotFound,
1032 SystemResources,
1033 NoSpaceLeft,
1034 PathAlreadyExists,
1035 ReadOnlyFileSystem,
1036 NotDir,
1037 FileNotFound,
1038 FileSystem,
1039 FileBusy,
1040 DirNotEmpty,
1041 Unexpected,
1042};
1043pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {
923 start_over: while (true) {1044 start_over: while (true) {
924 // First, try deleting the item as a file. This way we don't follow sym links.1045 // First, try deleting the item as a file. This way we don't follow sym links.
925 if (deleteFile(allocator, full_path)) {1046 if (deleteFile(allocator, full_path)) {
926 return;1047 return;
927 } else |err| {1048 } else |err| switch (err) {
928 if (err == error.FileNotFound)1049 error.FileNotFound => return,
929 return;1050 error.IsDir => {},
930 if (err != error.IsDir)1051
931 return err;1052 error.OutOfMemory,
1053 error.AccessDenied,
1054 error.SymLinkLoop,
1055 error.NameTooLong,
1056 error.SystemResources,
1057 error.ReadOnlyFileSystem,
1058 error.NotDir,
1059 error.FileSystem,
1060 error.FileBusy,
1061 error.Unexpected
1062 => return err,
932 }1063 }
933 {1064 {
934 var dir = Dir.open(allocator, full_path) catch |err| {1065 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
935 if (err == error.FileNotFound)1066 error.NotDir => continue :start_over,
936 return;1067
937 if (err == error.NotDir)1068 error.OutOfMemory,
938 continue :start_over;1069 error.AccessDenied,
939 return err;1070 error.FileTooBig,
1071 error.IsDir,
1072 error.SymLinkLoop,
1073 error.ProcessFdQuotaExceeded,
1074 error.NameTooLong,
1075 error.SystemFdQuotaExceeded,
1076 error.NoDevice,
1077 error.PathNotFound,
1078 error.SystemResources,
1079 error.NoSpaceLeft,
1080 error.PathAlreadyExists,
1081 error.Unexpected
1082 => return err,
940 };1083 };
941 defer dir.close();1084 defer dir.close();
9421085
...@@ -988,8 +1131,8 @@ pub const Dir = struct {...@@ -988,8 +1131,8 @@ pub const Dir = struct {
988 };1131 };
989 };1132 };
9901133
991 pub fn open(allocator: &Allocator, dir_path: []const u8) %Dir {1134 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
992 const fd = try posixOpen(dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0, allocator);1135 const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0);
993 return Dir {1136 return Dir {
994 .allocator = allocator,1137 .allocator = allocator,
995 .fd = fd,1138 .fd = fd,
...@@ -1006,7 +1149,7 @@ pub const Dir = struct {...@@ -1006,7 +1149,7 @@ pub const Dir = struct {
10061149
1007 /// Memory such as file names referenced in this returned entry becomes invalid1150 /// Memory such as file names referenced in this returned entry becomes invalid
1008 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.1151 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
1009 pub fn next(self: &Dir) %?Entry {1152 pub fn next(self: &Dir) !?Entry {
1010 start_over: while (true) {1153 start_over: while (true) {
1011 if (self.index >= self.end_index) {1154 if (self.index >= self.end_index) {
1012 if (self.buf.len == 0) {1155 if (self.buf.len == 0) {
...@@ -1063,7 +1206,7 @@ pub const Dir = struct {...@@ -1063,7 +1206,7 @@ pub const Dir = struct {
1063 }1206 }
1064};1207};
10651208
1066pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {1209pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void {
1067 const path_buf = try allocator.alloc(u8, dir_path.len + 1);1210 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
1068 defer allocator.free(path_buf);1211 defer allocator.free(path_buf);
10691212
...@@ -1087,7 +1230,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {...@@ -1087,7 +1230,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {
1087}1230}
10881231
1089/// Read value of a symbolic link.1232/// Read value of a symbolic link.
1090pub fn readLink(allocator: &Allocator, pathname: []const u8) %[]u8 {1233pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
1091 const path_buf = try allocator.alloc(u8, pathname.len + 1);1234 const path_buf = try allocator.alloc(u8, pathname.len + 1);
1092 defer allocator.free(path_buf);1235 defer allocator.free(path_buf);
10931236
...@@ -1164,11 +1307,7 @@ test "os.sleep" {...@@ -1164,11 +1307,7 @@ test "os.sleep" {
1164 sleep(0, 1);1307 sleep(0, 1);
1165}1308}
11661309
1167error ResourceLimitReached;1310pub fn posix_setuid(uid: u32) !void {
1168error InvalidUserId;
1169error PermissionDenied;
1170
1171pub fn posix_setuid(uid: u32) %void {
1172 const err = posix.getErrno(posix.setuid(uid));1311 const err = posix.getErrno(posix.setuid(uid));
1173 if (err == 0) return;1312 if (err == 0) return;
1174 return switch (err) {1313 return switch (err) {
...@@ -1179,7 +1318,7 @@ pub fn posix_setuid(uid: u32) %void {...@@ -1179,7 +1318,7 @@ pub fn posix_setuid(uid: u32) %void {
1179 };1318 };
1180}1319}
11811320
1182pub fn posix_setreuid(ruid: u32, euid: u32) %void {1321pub fn posix_setreuid(ruid: u32, euid: u32) !void {
1183 const err = posix.getErrno(posix.setreuid(ruid, euid));1322 const err = posix.getErrno(posix.setreuid(ruid, euid));
1184 if (err == 0) return;1323 if (err == 0) return;
1185 return switch (err) {1324 return switch (err) {
...@@ -1190,7 +1329,7 @@ pub fn posix_setreuid(ruid: u32, euid: u32) %void {...@@ -1190,7 +1329,7 @@ pub fn posix_setreuid(ruid: u32, euid: u32) %void {
1190 };1329 };
1191}1330}
11921331
1193pub fn posix_setgid(gid: u32) %void {1332pub fn posix_setgid(gid: u32) !void {
1194 const err = posix.getErrno(posix.setgid(gid));1333 const err = posix.getErrno(posix.setgid(gid));
1195 if (err == 0) return;1334 if (err == 0) return;
1196 return switch (err) {1335 return switch (err) {
...@@ -1201,7 +1340,7 @@ pub fn posix_setgid(gid: u32) %void {...@@ -1201,7 +1340,7 @@ pub fn posix_setgid(gid: u32) %void {
1201 };1340 };
1202}1341}
12031342
1204pub fn posix_setregid(rgid: u32, egid: u32) %void {1343pub fn posix_setregid(rgid: u32, egid: u32) !void {
1205 const err = posix.getErrno(posix.setregid(rgid, egid));1344 const err = posix.getErrno(posix.setregid(rgid, egid));
1206 if (err == 0) return;1345 if (err == 0) return;
1207 return switch (err) {1346 return switch (err) {
...@@ -1212,8 +1351,12 @@ pub fn posix_setregid(rgid: u32, egid: u32) %void {...@@ -1212,8 +1351,12 @@ pub fn posix_setregid(rgid: u32, egid: u32) %void {
1212 };1351 };
1213}1352}
12141353
1215error NoStdHandles;1354pub const WindowsGetStdHandleErrs = error {
1216pub fn windowsGetStdHandle(handle_id: windows.DWORD) %windows.HANDLE {1355 NoStdHandles,
1356 Unexpected,
1357};
1358
1359pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE {
1217 if (windows.GetStdHandle(handle_id)) |handle| {1360 if (windows.GetStdHandle(handle_id)) |handle| {
1218 if (handle == windows.INVALID_HANDLE_VALUE) {1361 if (handle == windows.INVALID_HANDLE_VALUE) {
1219 const err = windows.GetLastError();1362 const err = windows.GetLastError();
...@@ -1267,6 +1410,8 @@ pub const ArgIteratorWindows = struct {...@@ -1267,6 +1410,8 @@ pub const ArgIteratorWindows = struct {
1267 quote_count: usize,1410 quote_count: usize,
1268 seen_quote_count: usize,1411 seen_quote_count: usize,
12691412
1413 pub const NextError = error{OutOfMemory};
1414
1270 pub fn init() ArgIteratorWindows {1415 pub fn init() ArgIteratorWindows {
1271 return initWithCmdLine(windows.GetCommandLineA());1416 return initWithCmdLine(windows.GetCommandLineA());
1272 }1417 }
...@@ -1282,7 +1427,7 @@ pub const ArgIteratorWindows = struct {...@@ -1282,7 +1427,7 @@ pub const ArgIteratorWindows = struct {
1282 }1427 }
12831428
1284 /// You must free the returned memory when done.1429 /// You must free the returned memory when done.
1285 pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?%[]u8 {1430 pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(NextError![]u8) {
1286 // march forward over whitespace1431 // march forward over whitespace
1287 while (true) : (self.index += 1) {1432 while (true) : (self.index += 1) {
1288 const byte = self.cmd_line[self.index];1433 const byte = self.cmd_line[self.index];
...@@ -1335,7 +1480,7 @@ pub const ArgIteratorWindows = struct {...@@ -1335,7 +1480,7 @@ pub const ArgIteratorWindows = struct {
1335 }1480 }
1336 }1481 }
13371482
1338 fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) %[]u8 {1483 fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) NextError![]u8 {
1339 var buf = try Buffer.initSize(allocator, 0);1484 var buf = try Buffer.initSize(allocator, 0);
1340 defer buf.deinit();1485 defer buf.deinit();
13411486
...@@ -1379,7 +1524,7 @@ pub const ArgIteratorWindows = struct {...@@ -1379,7 +1524,7 @@ pub const ArgIteratorWindows = struct {
1379 }1524 }
1380 }1525 }
13811526
1382 fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) %void {1527 fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) !void {
1383 var i: usize = 0;1528 var i: usize = 0;
1384 while (i < emit_count) : (i += 1) {1529 while (i < emit_count) : (i += 1) {
1385 try buf.appendByte('\\');1530 try buf.appendByte('\\');
...@@ -1409,16 +1554,20 @@ pub const ArgIteratorWindows = struct {...@@ -1409,16 +1554,20 @@ pub const ArgIteratorWindows = struct {
1409};1554};
14101555
1411pub const ArgIterator = struct {1556pub const ArgIterator = struct {
1412 inner: if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix,1557 const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix;
1558
1559 inner: InnerType,
14131560
1414 pub fn init() ArgIterator {1561 pub fn init() ArgIterator {
1415 return ArgIterator {1562 return ArgIterator {
1416 .inner = if (builtin.os == Os.windows) ArgIteratorWindows.init() else ArgIteratorPosix.init(),1563 .inner = InnerType.init(),
1417 };1564 };
1418 }1565 }
1566
1567 pub const NextError = ArgIteratorWindows.NextError;
1419 1568
1420 /// You must free the returned memory when done.1569 /// You must free the returned memory when done.
1421 pub fn next(self: &ArgIterator, allocator: &Allocator) ?%[]u8 {1570 pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
1422 if (builtin.os == Os.windows) {1571 if (builtin.os == Os.windows) {
1423 return self.inner.next(allocator);1572 return self.inner.next(allocator);
1424 } else {1573 } else {
...@@ -1443,7 +1592,7 @@ pub fn args() ArgIterator {...@@ -1443,7 +1592,7 @@ pub fn args() ArgIterator {
1443}1592}
14441593
1445/// Caller must call freeArgs on result.1594/// Caller must call freeArgs on result.
1446pub fn argsAlloc(allocator: &mem.Allocator) %[]const []u8 {1595pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 {
1447 // TODO refactor to only make 1 allocation.1596 // TODO refactor to only make 1 allocation.
1448 var it = args();1597 var it = args();
1449 var contents = try Buffer.initSize(allocator, 0);1598 var contents = try Buffer.initSize(allocator, 0);
...@@ -1525,14 +1674,12 @@ test "std.os" {...@@ -1525,14 +1674,12 @@ test "std.os" {
1525}1674}
15261675
15271676
1528error Unexpected;
1529
1530// TODO make this a build variable that you can set1677// TODO make this a build variable that you can set
1531const unexpected_error_tracing = false;1678const unexpected_error_tracing = false;
15321679
1533/// Call this when you made a syscall or something that sets errno1680/// Call this when you made a syscall or something that sets errno
1534/// and you get an unexpected error.1681/// and you get an unexpected error.
1535pub fn unexpectedErrorPosix(errno: usize) error {1682pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
1536 if (unexpected_error_tracing) {1683 if (unexpected_error_tracing) {
1537 debug.warn("unexpected errno: {}\n", errno);1684 debug.warn("unexpected errno: {}\n", errno);
1538 debug.dumpStackTrace();1685 debug.dumpStackTrace();
...@@ -1542,7 +1689,7 @@ pub fn unexpectedErrorPosix(errno: usize) error {...@@ -1542,7 +1689,7 @@ pub fn unexpectedErrorPosix(errno: usize) error {
15421689
1543/// Call this when you made a windows DLL call or something that does SetLastError1690/// Call this when you made a windows DLL call or something that does SetLastError
1544/// and you get an unexpected error.1691/// and you get an unexpected error.
1545pub fn unexpectedErrorWindows(err: windows.DWORD) error {1692pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
1546 if (unexpected_error_tracing) {1693 if (unexpected_error_tracing) {
1547 debug.warn("unexpected GetLastError(): {}\n", err);1694 debug.warn("unexpected GetLastError(): {}\n", err);
1548 debug.dumpStackTrace();1695 debug.dumpStackTrace();
...@@ -1550,25 +1697,38 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) error {...@@ -1550,25 +1697,38 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) error {
1550 return error.Unexpected;1697 return error.Unexpected;
1551}1698}
15521699
1553pub fn openSelfExe() %io.File {1700pub fn openSelfExe() !os.File {
1554 switch (builtin.os) {1701 switch (builtin.os) {
1555 Os.linux => {1702 Os.linux => {
1556 return io.File.openRead("/proc/self/exe", null);1703 const proc_file_path = "/proc/self/exe";
1704 var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
1705 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1706 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
1557 },1707 },
1558 Os.macosx, Os.ios => {1708 Os.macosx, Os.ios => {
1559 @panic("TODO: openSelfExe on Darwin");1709 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
1710 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1711 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
1712 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
1560 },1713 },
1561 else => @compileError("Unsupported OS"),1714 else => @compileError("Unsupported OS"),
1562 }1715 }
1563}1716}
15641717
1718test "openSelfExe" {
1719 switch (builtin.os) {
1720 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
1721 else => return, // Unsupported OS.
1722 }
1723}
1724
1565/// Get the path to the current executable.1725/// Get the path to the current executable.
1566/// If you only need the directory, use selfExeDirPath.1726/// If you only need the directory, use selfExeDirPath.
1567/// If you only want an open file handle, use openSelfExe.1727/// If you only want an open file handle, use openSelfExe.
1568/// This function may return an error if the current executable1728/// This function may return an error if the current executable
1569/// was deleted after spawning.1729/// was deleted after spawning.
1570/// Caller owns returned memory.1730/// Caller owns returned memory.
1571pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {1731pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
1572 switch (builtin.os) {1732 switch (builtin.os) {
1573 Os.linux => {1733 Os.linux => {
1574 // If the currently executing binary has been deleted,1734 // If the currently executing binary has been deleted,
...@@ -1611,7 +1771,7 @@ pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {...@@ -1611,7 +1771,7 @@ pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {
16111771
1612/// Get the directory path that contains the current executable.1772/// Get the directory path that contains the current executable.
1613/// Caller owns returned memory.1773/// Caller owns returned memory.
1614pub fn selfExeDirPath(allocator: &mem.Allocator) %[]u8 {1774pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
1615 switch (builtin.os) {1775 switch (builtin.os) {
1616 Os.linux => {1776 Os.linux => {
1617 // If the currently executing binary has been deleted,1777 // If the currently executing binary has been deleted,
std/os/linux/index.zig+1-1
...@@ -720,7 +720,7 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:...@@ -720,7 +720,7 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
720// error SystemResources;720// error SystemResources;
721// error Io;721// error Io;
722// 722//
723// pub fn if_nametoindex(name: []u8) %u32 {723// pub fn if_nametoindex(name: []u8) !u32 {
724// var ifr: ifreq = undefined;724// var ifr: ifreq = undefined;
725// 725//
726// if (name.len >= ifr.ifr_name.len) {726// if (name.len >= ifr.ifr_name.len) {
std/os/path.zig+12-19
...@@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool {...@@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool {
3232
33/// Naively combines a series of paths with the native path seperator.33/// Naively combines a series of paths with the native path seperator.
34/// Allocates memory for the result, which must be freed by the caller.34/// Allocates memory for the result, which must be freed by the caller.
35pub fn join(allocator: &Allocator, paths: ...) %[]u8 {35pub fn join(allocator: &Allocator, paths: ...) ![]u8 {
36 if (is_windows) {36 if (is_windows) {
37 return joinWindows(allocator, paths);37 return joinWindows(allocator, paths);
38 } else {38 } else {
...@@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) %[]u8 {...@@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) %[]u8 {
40 }40 }
41}41}
4242
43pub fn joinWindows(allocator: &Allocator, paths: ...) %[]u8 {43pub fn joinWindows(allocator: &Allocator, paths: ...) ![]u8 {
44 return mem.join(allocator, sep_windows, paths);44 return mem.join(allocator, sep_windows, paths);
45}45}
4646
47pub fn joinPosix(allocator: &Allocator, paths: ...) %[]u8 {47pub fn joinPosix(allocator: &Allocator, paths: ...) ![]u8 {
48 return mem.join(allocator, sep_posix, paths);48 return mem.join(allocator, sep_posix, paths);
49}49}
5050
...@@ -313,7 +313,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {...@@ -313,7 +313,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
313}313}
314314
315/// Converts the command line arguments into a slice and calls `resolveSlice`.315/// Converts the command line arguments into a slice and calls `resolveSlice`.
316pub fn resolve(allocator: &Allocator, args: ...) %[]u8 {316pub fn resolve(allocator: &Allocator, args: ...) ![]u8 {
317 var paths: [args.len][]const u8 = undefined;317 var paths: [args.len][]const u8 = undefined;
318 comptime var arg_i = 0;318 comptime var arg_i = 0;
319 inline while (arg_i < args.len) : (arg_i += 1) {319 inline while (arg_i < args.len) : (arg_i += 1) {
...@@ -323,7 +323,7 @@ pub fn resolve(allocator: &Allocator, args: ...) %[]u8 {...@@ -323,7 +323,7 @@ pub fn resolve(allocator: &Allocator, args: ...) %[]u8 {
323}323}
324324
325/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.325/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
326pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 {326pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 {
327 if (is_windows) {327 if (is_windows) {
328 return resolveWindows(allocator, paths);328 return resolveWindows(allocator, paths);
329 } else {329 } else {
...@@ -337,7 +337,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 {...@@ -337,7 +337,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 {
337/// If all paths are relative it uses the current working directory as a starting point.337/// If all paths are relative it uses the current working directory as a starting point.
338/// Each drive has its own current working directory.338/// Each drive has its own current working directory.
339/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.339/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
340pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 {340pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 {
341 if (paths.len == 0) {341 if (paths.len == 0) {
342 assert(is_windows); // resolveWindows called on non windows can't use getCwd342 assert(is_windows); // resolveWindows called on non windows can't use getCwd
343 return os.getCwd(allocator);343 return os.getCwd(allocator);
...@@ -520,7 +520,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 {...@@ -520,7 +520,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 {
520/// It resolves "." and "..".520/// It resolves "." and "..".
521/// The result does not have a trailing path separator.521/// The result does not have a trailing path separator.
522/// If all paths are relative it uses the current working directory as a starting point.522/// If all paths are relative it uses the current working directory as a starting point.
523pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) %[]u8 {523pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) ![]u8 {
524 if (paths.len == 0) {524 if (paths.len == 0) {
525 assert(!is_windows); // resolvePosix called on windows can't use getCwd525 assert(!is_windows); // resolvePosix called on windows can't use getCwd
526 return os.getCwd(allocator);526 return os.getCwd(allocator);
...@@ -890,7 +890,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void {...@@ -890,7 +890,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void {
890/// resolve to the same path (after calling `resolve` on each), a zero-length890/// resolve to the same path (after calling `resolve` on each), a zero-length
891/// string is returned.891/// string is returned.
892/// On Windows this canonicalizes the drive to a capital letter and paths to `\\`.892/// On Windows this canonicalizes the drive to a capital letter and paths to `\\`.
893pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {893pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 {
894 if (is_windows) {894 if (is_windows) {
895 return relativeWindows(allocator, from, to);895 return relativeWindows(allocator, from, to);
896 } else {896 } else {
...@@ -898,7 +898,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {...@@ -898,7 +898,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {
898 }898 }
899}899}
900900
901pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {901pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 {
902 const resolved_from = try resolveWindows(allocator, [][]const u8{from});902 const resolved_from = try resolveWindows(allocator, [][]const u8{from});
903 defer allocator.free(resolved_from);903 defer allocator.free(resolved_from);
904904
...@@ -971,7 +971,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8)...@@ -971,7 +971,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8)
971 return []u8{};971 return []u8{};
972}972}
973973
974pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {974pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 {
975 const resolved_from = try resolvePosix(allocator, [][]const u8{from});975 const resolved_from = try resolvePosix(allocator, [][]const u8{from});
976 defer allocator.free(resolved_from);976 defer allocator.free(resolved_from);
977977
...@@ -1066,18 +1066,11 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons...@@ -1066,18 +1066,11 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
1066 assert(mem.eql(u8, result, expected_output));1066 assert(mem.eql(u8, result, expected_output));
1067}1067}
10681068
1069error AccessDenied;
1070error FileNotFound;
1071error NotSupported;
1072error NotDir;
1073error NameTooLong;
1074error SymLinkLoop;
1075error InputOutput;
1076/// Return the canonicalized absolute pathname.1069/// Return the canonicalized absolute pathname.
1077/// Expands all symbolic links and resolves references to `.`, `..`, and1070/// Expands all symbolic links and resolves references to `.`, `..`, and
1078/// extra `/` characters in ::pathname.1071/// extra `/` characters in ::pathname.
1079/// Caller must deallocate result.1072/// Caller must deallocate result.
1080pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {1073pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 {
1081 switch (builtin.os) {1074 switch (builtin.os) {
1082 Os.windows => {1075 Os.windows => {
1083 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);1076 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
...@@ -1168,7 +1161,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {...@@ -1168,7 +1161,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {
1168 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));1161 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
1169 },1162 },
1170 Os.linux => {1163 Os.linux => {
1171 const fd = try os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator);1164 const fd = try os.posixOpen(allocator, pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0);
1172 defer os.close(fd);1165 defer os.close(fd);
11731166
1174 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;1167 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
std/os/windows/util.zig+47-48
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("../../index.zig");1const std = @import("../../index.zig");
2const builtin = @import("builtin");
2const os = std.os;3const os = std.os;
3const windows = std.os.windows;4const windows = std.os.windows;
4const assert = std.debug.assert;5const assert = std.debug.assert;
...@@ -6,11 +7,13 @@ const mem = std.mem;...@@ -6,11 +7,13 @@ const mem = std.mem;
6const BufMap = std.BufMap;7const BufMap = std.BufMap;
7const cstr = std.cstr;8const cstr = std.cstr;
89
9error WaitAbandoned;10pub const WaitError = error {
10error WaitTimeOut;11 WaitAbandoned,
11error Unexpected;12 WaitTimeOut,
13 Unexpected,
14};
1215
13pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) %void {16pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void {
14 const result = windows.WaitForSingleObject(handle, milliseconds);17 const result = windows.WaitForSingleObject(handle, milliseconds);
15 return switch (result) {18 return switch (result) {
16 windows.WAIT_ABANDONED => error.WaitAbandoned,19 windows.WAIT_ABANDONED => error.WaitAbandoned,
...@@ -30,21 +33,24 @@ pub fn windowsClose(handle: windows.HANDLE) void {...@@ -30,21 +33,24 @@ pub fn windowsClose(handle: windows.HANDLE) void {
30 assert(windows.CloseHandle(handle) != 0);33 assert(windows.CloseHandle(handle) != 0);
31}34}
3235
33error SystemResources;36pub const WriteError = error {
34error OperationAborted;37 SystemResources,
35error IoPending;38 OperationAborted,
36error BrokenPipe;39 IoPending,
40 BrokenPipe,
41 Unexpected,
42};
3743
38pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) %void {44pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
39 if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {45 if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {
40 const err = windows.GetLastError();46 const err = windows.GetLastError();
41 return switch (err) {47 return switch (err) {
42 windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
43 windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,49 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
44 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,50 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
45 windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,51 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
46 windows.ERROR.IO_PENDING => error.IoPending,52 windows.ERROR.IO_PENDING => WriteError.IoPending,
47 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,53 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
48 else => os.unexpectedErrorWindows(err),54 else => os.unexpectedErrorWindows(err),
49 };55 };
50 }56 }
...@@ -75,43 +81,35 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {...@@ -75,43 +81,35 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {
75 mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null;81 mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null;
76}82}
7783
78error SharingViolation;84pub const OpenError = error {
79error PipeBusy;85 SharingViolation,
8086 PathAlreadyExists,
81/// `file_path` may need to be copied in memory to add a null terminating byte. In this case87 FileNotFound,
82/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed88 AccessDenied,
83/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.89 PipeBusy,
84/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.90 Unexpected,
85pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD,91 OutOfMemory,
86 creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) %windows.HANDLE92};
93
94/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
95pub fn windowsOpen(allocator: &mem.Allocator, file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD,
96 creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD)
97 OpenError!windows.HANDLE
87{98{
88 var stack_buf: [os.max_noalloc_path_len]u8 = undefined;99 const path_with_null = try cstr.addNullByte(allocator, file_path);
89 var path0: []u8 = undefined;100 defer allocator.free(path_with_null);
90 var need_free = false;
91 defer if (need_free) (??allocator).free(path0);
92
93 if (file_path.len < stack_buf.len) {
94 path0 = stack_buf[0..file_path.len + 1];
95 } else if (allocator) |a| {
96 path0 = try a.alloc(u8, file_path.len + 1);
97 need_free = true;
98 } else {
99 return error.NameTooLong;
100 }
101 mem.copy(u8, path0, file_path);
102 path0[file_path.len] = 0;
103101
104 const result = windows.CreateFileA(path0.ptr, desired_access, share_mode, null, creation_disposition,102 const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition,
105 flags_and_attrs, null);103 flags_and_attrs, null);
106104
107 if (result == windows.INVALID_HANDLE_VALUE) {105 if (result == windows.INVALID_HANDLE_VALUE) {
108 const err = windows.GetLastError();106 const err = windows.GetLastError();
109 return switch (err) {107 return switch (err) {
110 windows.ERROR.SHARING_VIOLATION => error.SharingViolation,108 windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
111 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists,109 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
112 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,110 windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
113 windows.ERROR.ACCESS_DENIED => error.AccessDenied,111 windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
114 windows.ERROR.PIPE_BUSY => error.PipeBusy,112 windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
115 else => os.unexpectedErrorWindows(err),113 else => os.unexpectedErrorWindows(err),
116 };114 };
117 }115 }
...@@ -120,7 +118,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m...@@ -120,7 +118,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
120}118}
121119
122/// Caller must free result.120/// Caller must free result.
123pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) %[]u8 {121pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) ![]u8 {
124 // count bytes needed122 // count bytes needed
125 const bytes_needed = x: {123 const bytes_needed = x: {
126 var bytes_needed: usize = 1; // 1 for the final null byte124 var bytes_needed: usize = 1; // 1 for the final null byte
...@@ -151,8 +149,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)...@@ -151,8 +149,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
151 return result;149 return result;
152}150}
153151
154error DllNotFound;152pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows.HMODULE {
155pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) %windows.HMODULE {
156 const padded_buff = try cstr.addNullByte(allocator, dll_path);153 const padded_buff = try cstr.addNullByte(allocator, dll_path);
157 defer allocator.free(padded_buff);154 defer allocator.free(padded_buff);
158 return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;155 return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
...@@ -164,6 +161,8 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {...@@ -164,6 +161,8 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
164161
165162
166test "InvalidDll" {163test "InvalidDll" {
164 if (builtin.os != builtin.Os.windows) return;
165
167 const DllName = "asdf.dll";166 const DllName = "asdf.dll";
168 const allocator = std.debug.global_allocator;167 const allocator = std.debug.global_allocator;
169 const handle = os.windowsLoadDll(allocator, DllName) catch |err| {168 const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
std/special/bootstrap.zig+2-2
...@@ -77,7 +77,7 @@ fn callMain() u8 {...@@ -77,7 +77,7 @@ fn callMain() u8 {
77 },77 },
78 builtin.TypeId.Int => {78 builtin.TypeId.Int => {
79 if (@typeOf(root.main).ReturnType.bit_count != 8) {79 if (@typeOf(root.main).ReturnType.bit_count != 8) {
80 @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");80 @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
81 }81 }
82 return root.main();82 return root.main();
83 },83 },
...@@ -91,6 +91,6 @@ fn callMain() u8 {...@@ -91,6 +91,6 @@ fn callMain() u8 {
91 };91 };
92 return 0;92 return 0;
93 },93 },
94 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),94 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
95 }95 }
96}96}
std/special/build_file_template.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) !void {
4 const mode = b.standardReleaseOptions();4 const mode = b.standardReleaseOptions();
5 const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");5 const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
6 exe.setBuildMode(mode);6 exe.setBuildMode(mode);
std/special/build_runner.zig+20-10
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const root = @import("@build");1const root = @import("@build");
2const std = @import("std");2const std = @import("std");
3const builtin = @import("builtin");
3const io = std.io;4const io = std.io;
4const fmt = std.fmt;5const fmt = std.fmt;
5const os = std.os;6const os = std.os;
...@@ -8,9 +9,7 @@ const mem = std.mem;...@@ -8,9 +9,7 @@ const mem = std.mem;
8const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
9const warn = std.debug.warn;10const warn = std.debug.warn;
1011
11error InvalidArgs;12pub fn main() !void {
12
13pub fn main() %void {
14 var arg_it = os.args();13 var arg_it = os.args();
1514
16 // TODO use a more general purpose allocator here15 // TODO use a more general purpose allocator here
...@@ -45,14 +44,14 @@ pub fn main() %void {...@@ -45,14 +44,14 @@ pub fn main() %void {
4544
46 var stderr_file = io.getStdErr();45 var stderr_file = io.getStdErr();
47 var stderr_file_stream: io.FileOutStream = undefined;46 var stderr_file_stream: io.FileOutStream = undefined;
48 var stderr_stream: %&io.OutStream = if (stderr_file) |*f| x: {47 var stderr_stream = if (stderr_file) |*f| x: {
49 stderr_file_stream = io.FileOutStream.init(f);48 stderr_file_stream = io.FileOutStream.init(f);
50 break :x &stderr_file_stream.stream;49 break :x &stderr_file_stream.stream;
51 } else |err| err;50 } else |err| err;
5251
53 var stdout_file = io.getStdOut();52 var stdout_file = io.getStdOut();
54 var stdout_file_stream: io.FileOutStream = undefined;53 var stdout_file_stream: io.FileOutStream = undefined;
55 var stdout_stream: %&io.OutStream = if (stdout_file) |*f| x: {54 var stdout_stream = if (stdout_file) |*f| x: {
56 stdout_file_stream = io.FileOutStream.init(f);55 stdout_file_stream = io.FileOutStream.init(f);
57 break :x &stdout_file_stream.stream;56 break :x &stdout_file_stream.stream;
58 } else |err| err;57 } else |err| err;
...@@ -112,7 +111,7 @@ pub fn main() %void {...@@ -112,7 +111,7 @@ pub fn main() %void {
112 }111 }
113112
114 builder.setInstallPrefix(prefix);113 builder.setInstallPrefix(prefix);
115 try root.build(&builder);114 try runBuild(&builder);
116115
117 if (builder.validateUserInputDidItFail())116 if (builder.validateUserInputDidItFail())
118 return usageAndErr(&builder, true, try stderr_stream);117 return usageAndErr(&builder, true, try stderr_stream);
...@@ -125,11 +124,19 @@ pub fn main() %void {...@@ -125,11 +124,19 @@ pub fn main() %void {
125 };124 };
126}125}
127126
128fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) %void {127fn runBuild(builder: &Builder) error!void {
128 switch (@typeId(@typeOf(root.build).ReturnType)) {
129 builtin.TypeId.Void => root.build(builder),
130 builtin.TypeId.ErrorUnion => try root.build(builder),
131 else => @compileError("expected return type of build to be 'void' or '!void'"),
132 }
133}
134
135fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void {
129 // run the build script to collect the options136 // run the build script to collect the options
130 if (!already_ran_build) {137 if (!already_ran_build) {
131 builder.setInstallPrefix(null);138 builder.setInstallPrefix(null);
132 try root.build(builder);139 try runBuild(builder);
133 }140 }
134141
135 // This usage text has to be synchronized with src/main.cpp142 // This usage text has to be synchronized with src/main.cpp
...@@ -149,6 +156,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)...@@ -149,6 +156,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
149 \\156 \\
150 \\General Options:157 \\General Options:
151 \\ --help Print this help and exit158 \\ --help Print this help and exit
159 \\ --init Generate a build.zig template
152 \\ --verbose Print commands before executing them160 \\ --verbose Print commands before executing them
153 \\ --prefix [path] Override default install prefix161 \\ --prefix [path] Override default install prefix
154 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers162 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
...@@ -183,12 +191,14 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)...@@ -183,12 +191,14 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
183 );191 );
184}192}
185193
186fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) error {194fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: var) error {
187 usage(builder, already_ran_build, out_stream) catch {};195 usage(builder, already_ran_build, out_stream) catch {};
188 return error.InvalidArgs;196 return error.InvalidArgs;
189}197}
190198
191fn unwrapArg(arg: %[]u8) %[]u8 {199const UnwrapArgError = error {OutOfMemory};
200
201fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
192 return arg catch |err| {202 return arg catch |err| {
193 warn("Unable to parse command line: {}\n", err);203 warn("Unable to parse command line: {}\n", err);
194 return err;204 return err;
std/special/test_runner.zig+1-1
...@@ -4,7 +4,7 @@ const builtin = @import("builtin");...@@ -4,7 +4,7 @@ const builtin = @import("builtin");
4const test_fn_list = builtin.__zig_test_fn_slice;4const test_fn_list = builtin.__zig_test_fn_slice;
5const warn = std.debug.warn;5const warn = std.debug.warn;
66
7pub fn main() %void {7pub fn main() !void {
8 for (test_fn_list) |test_fn, i| {8 for (test_fn_list) |test_fn, i| {
9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1010
std/unicode.zig+6-14
...@@ -1,11 +1,9 @@...@@ -1,11 +1,9 @@
1const std = @import("./index.zig");1const std = @import("./index.zig");
22
3error Utf8InvalidStartByte;
4
5/// Given the first byte of a UTF-8 codepoint,3/// Given the first byte of a UTF-8 codepoint,
6/// returns a number 1-4 indicating the total length of the codepoint in bytes.4/// returns a number 1-4 indicating the total length of the codepoint in bytes.
7/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.5/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.
8pub fn utf8ByteSequenceLength(first_byte: u8) %u3 {6pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
9 if (first_byte < 0b10000000) return u3(1);7 if (first_byte < 0b10000000) return u3(1);
10 if (first_byte & 0b11100000 == 0b11000000) return u3(2);8 if (first_byte & 0b11100000 == 0b11000000) return u3(2);
11 if (first_byte & 0b11110000 == 0b11100000) return u3(3);9 if (first_byte & 0b11110000 == 0b11100000) return u3(3);
...@@ -13,16 +11,11 @@ pub fn utf8ByteSequenceLength(first_byte: u8) %u3 {...@@ -13,16 +11,11 @@ pub fn utf8ByteSequenceLength(first_byte: u8) %u3 {
13 return error.Utf8InvalidStartByte;11 return error.Utf8InvalidStartByte;
14}12}
1513
16error Utf8OverlongEncoding;
17error Utf8ExpectedContinuation;
18error Utf8EncodesSurrogateHalf;
19error Utf8CodepointTooLarge;
20
21/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.14/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
22/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.15/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
23/// If you already know the length at comptime, you can call one of16/// If you already know the length at comptime, you can call one of
24/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.17/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
25pub fn utf8Decode(bytes: []const u8) %u32 {18pub fn utf8Decode(bytes: []const u8) !u32 {
26 return switch (bytes.len) {19 return switch (bytes.len) {
27 1 => u32(bytes[0]),20 1 => u32(bytes[0]),
28 2 => utf8Decode2(bytes),21 2 => utf8Decode2(bytes),
...@@ -31,7 +24,7 @@ pub fn utf8Decode(bytes: []const u8) %u32 {...@@ -31,7 +24,7 @@ pub fn utf8Decode(bytes: []const u8) %u32 {
31 else => unreachable,24 else => unreachable,
32 };25 };
33}26}
34pub fn utf8Decode2(bytes: []const u8) %u32 {27pub fn utf8Decode2(bytes: []const u8) !u32 {
35 std.debug.assert(bytes.len == 2);28 std.debug.assert(bytes.len == 2);
36 std.debug.assert(bytes[0] & 0b11100000 == 0b11000000);29 std.debug.assert(bytes[0] & 0b11100000 == 0b11000000);
37 var value: u32 = bytes[0] & 0b00011111;30 var value: u32 = bytes[0] & 0b00011111;
...@@ -44,7 +37,7 @@ pub fn utf8Decode2(bytes: []const u8) %u32 {...@@ -44,7 +37,7 @@ pub fn utf8Decode2(bytes: []const u8) %u32 {
4437
45 return value;38 return value;
46}39}
47pub fn utf8Decode3(bytes: []const u8) %u32 {40pub fn utf8Decode3(bytes: []const u8) !u32 {
48 std.debug.assert(bytes.len == 3);41 std.debug.assert(bytes.len == 3);
49 std.debug.assert(bytes[0] & 0b11110000 == 0b11100000);42 std.debug.assert(bytes[0] & 0b11110000 == 0b11100000);
50 var value: u32 = bytes[0] & 0b00001111;43 var value: u32 = bytes[0] & 0b00001111;
...@@ -62,7 +55,7 @@ pub fn utf8Decode3(bytes: []const u8) %u32 {...@@ -62,7 +55,7 @@ pub fn utf8Decode3(bytes: []const u8) %u32 {
6255
63 return value;56 return value;
64}57}
65pub fn utf8Decode4(bytes: []const u8) %u32 {58pub fn utf8Decode4(bytes: []const u8) !u32 {
66 std.debug.assert(bytes.len == 4);59 std.debug.assert(bytes.len == 4);
67 std.debug.assert(bytes[0] & 0b11111000 == 0b11110000);60 std.debug.assert(bytes[0] & 0b11111000 == 0b11110000);
68 var value: u32 = bytes[0] & 0b00000111;61 var value: u32 = bytes[0] & 0b00000111;
...@@ -85,7 +78,6 @@ pub fn utf8Decode4(bytes: []const u8) %u32 {...@@ -85,7 +78,6 @@ pub fn utf8Decode4(bytes: []const u8) %u32 {
85 return value;78 return value;
86}79}
8780
88error UnexpectedEof;
89test "valid utf8" {81test "valid utf8" {
90 testValid("\x00", 0x0);82 testValid("\x00", 0x0);
91 testValid("\x20", 0x20);83 testValid("\x20", 0x20);
...@@ -161,7 +153,7 @@ fn testValid(bytes: []const u8, expected_codepoint: u32) void {...@@ -161,7 +153,7 @@ fn testValid(bytes: []const u8, expected_codepoint: u32) void {
161 std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);153 std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
162}154}
163155
164fn testDecode(bytes: []const u8) %u32 {156fn testDecode(bytes: []const u8) !u32 {
165 const length = try utf8ByteSequenceLength(bytes[0]);157 const length = try utf8ByteSequenceLength(bytes[0]);
166 if (bytes.len < length) return error.UnexpectedEof;158 if (bytes.len < length) return error.UnexpectedEof;
167 std.debug.assert(bytes.len == length);159 std.debug.assert(bytes.len == length);
std/zig/ast.zig created+271
...@@ -0,0 +1,271 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const Token = std.zig.Token;
5const mem = std.mem;
6
7pub const Node = struct {
8 id: Id,
9
10 pub const Id = enum {
11 Root,
12 VarDecl,
13 Identifier,
14 FnProto,
15 ParamDecl,
16 Block,
17 InfixOp,
18 PrefixOp,
19 IntegerLiteral,
20 FloatLiteral,
21 };
22
23 pub fn iterate(base: &Node, index: usize) ?&Node {
24 return switch (base.id) {
25 Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
26 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
27 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
28 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
29 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
30 Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
31 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
32 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
33 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
34 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
35 };
36 }
37
38 pub fn destroy(base: &Node, allocator: &mem.Allocator) void {
39 return switch (base.id) {
40 Id.Root => allocator.destroy(@fieldParentPtr(NodeRoot, "base", base)),
41 Id.VarDecl => allocator.destroy(@fieldParentPtr(NodeVarDecl, "base", base)),
42 Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
43 Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
44 Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
45 Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
46 Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
47 Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
48 Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
49 Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
50 };
51 }
52};
53
54pub const NodeRoot = struct {
55 base: Node,
56 decls: ArrayList(&Node),
57
58 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
59 if (index < self.decls.len) {
60 return self.decls.items[self.decls.len - index - 1];
61 }
62 return null;
63 }
64};
65
66pub const NodeVarDecl = struct {
67 base: Node,
68 visib_token: ?Token,
69 name_token: Token,
70 eq_token: Token,
71 mut_token: Token,
72 comptime_token: ?Token,
73 extern_token: ?Token,
74 lib_name: ?&Node,
75 type_node: ?&Node,
76 align_node: ?&Node,
77 init_node: ?&Node,
78
79 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
80 var i = index;
81
82 if (self.type_node) |type_node| {
83 if (i < 1) return type_node;
84 i -= 1;
85 }
86
87 if (self.align_node) |align_node| {
88 if (i < 1) return align_node;
89 i -= 1;
90 }
91
92 if (self.init_node) |init_node| {
93 if (i < 1) return init_node;
94 i -= 1;
95 }
96
97 return null;
98 }
99};
100
101pub const NodeIdentifier = struct {
102 base: Node,
103 name_token: Token,
104
105 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
106 return null;
107 }
108};
109
110pub const NodeFnProto = struct {
111 base: Node,
112 visib_token: ?Token,
113 fn_token: Token,
114 name_token: ?Token,
115 params: ArrayList(&Node),
116 return_type: &Node,
117 var_args_token: ?Token,
118 extern_token: ?Token,
119 inline_token: ?Token,
120 cc_token: ?Token,
121 body_node: ?&Node,
122 lib_name: ?&Node, // populated if this is an extern declaration
123 align_expr: ?&Node, // populated if align(A) is present
124
125 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
126 var i = index;
127
128 if (self.body_node) |body_node| {
129 if (i < 1) return body_node;
130 i -= 1;
131 }
132
133 if (i < 1) return self.return_type;
134 i -= 1;
135
136 if (self.align_expr) |align_expr| {
137 if (i < 1) return align_expr;
138 i -= 1;
139 }
140
141 if (i < self.params.len) return self.params.items[self.params.len - i - 1];
142 i -= self.params.len;
143
144 if (self.lib_name) |lib_name| {
145 if (i < 1) return lib_name;
146 i -= 1;
147 }
148
149 return null;
150 }
151};
152
153pub const NodeParamDecl = struct {
154 base: Node,
155 comptime_token: ?Token,
156 noalias_token: ?Token,
157 name_token: ?Token,
158 type_node: &Node,
159 var_args_token: ?Token,
160
161 pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {
162 var i = index;
163
164 if (i < 1) return self.type_node;
165 i -= 1;
166
167 return null;
168 }
169};
170
171pub const NodeBlock = struct {
172 base: Node,
173 begin_token: Token,
174 end_token: Token,
175 statements: ArrayList(&Node),
176
177 pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
178 var i = index;
179
180 if (i < self.statements.len) return self.statements.items[i];
181 i -= self.statements.len;
182
183 return null;
184 }
185};
186
187pub const NodeInfixOp = struct {
188 base: Node,
189 op_token: Token,
190 lhs: &Node,
191 op: InfixOp,
192 rhs: &Node,
193
194 const InfixOp = enum {
195 EqualEqual,
196 BangEqual,
197 };
198
199 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
200 var i = index;
201
202 if (i < 1) return self.lhs;
203 i -= 1;
204
205 switch (self.op) {
206 InfixOp.EqualEqual => {},
207 InfixOp.BangEqual => {},
208 }
209
210 if (i < 1) return self.rhs;
211 i -= 1;
212
213 return null;
214 }
215};
216
217pub const NodePrefixOp = struct {
218 base: Node,
219 op_token: Token,
220 op: PrefixOp,
221 rhs: &Node,
222
223 const PrefixOp = union(enum) {
224 Return,
225 AddrOf: AddrOfInfo,
226 };
227 const AddrOfInfo = struct {
228 align_expr: ?&Node,
229 bit_offset_start_token: ?Token,
230 bit_offset_end_token: ?Token,
231 const_token: ?Token,
232 volatile_token: ?Token,
233 };
234
235 pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {
236 var i = index;
237
238 switch (self.op) {
239 PrefixOp.Return => {},
240 PrefixOp.AddrOf => |addr_of_info| {
241 if (addr_of_info.align_expr) |align_expr| {
242 if (i < 1) return align_expr;
243 i -= 1;
244 }
245 },
246 }
247
248 if (i < 1) return self.rhs;
249 i -= 1;
250
251 return null;
252 }
253};
254
255pub const NodeIntegerLiteral = struct {
256 base: Node,
257 token: Token,
258
259 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
260 return null;
261 }
262};
263
264pub const NodeFloatLiteral = struct {
265 base: Node,
266 token: Token,
267
268 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
269 return null;
270 }
271};
std/zig/index.zig created+11
...@@ -0,0 +1,11 @@
1const tokenizer = @import("tokenizer.zig");
2pub const Token = tokenizer.Token;
3pub const Tokenizer = tokenizer.Tokenizer;
4pub const Parser = @import("parser.zig").Parser;
5pub const ast = @import("ast.zig");
6
7test "std.zig tests" {
8 _ = @import("tokenizer.zig");
9 _ = @import("parser.zig");
10 _ = @import("ast.zig");
11}
std/zig/parser.zig created+1158
...@@ -0,0 +1,1158 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const mem = std.mem;
5const ast = std.zig.ast;
6const Tokenizer = std.zig.Tokenizer;
7const Token = std.zig.Token;
8const builtin = @import("builtin");
9const io = std.io;
10
11// TODO when we make parse errors into error types instead of printing directly,
12// get rid of this
13const warn = std.debug.warn;
14
15pub const Parser = struct {
16 allocator: &mem.Allocator,
17 tokenizer: &Tokenizer,
18 put_back_tokens: [2]Token,
19 put_back_count: usize,
20 source_file_name: []const u8,
21
22 pub const Tree = struct {
23 root_node: &ast.NodeRoot,
24
25 pub fn deinit(self: &const Tree) void {
26 // TODO free the whole arena
27 }
28 };
29
30 // This memory contents are used only during a function call. It's used to repurpose memory;
31 // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and
32 // source rendering.
33 const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
34 utility_bytes: []align(utility_bytes_align) u8,
35
36 /// `allocator` should be an arena allocator. Parser never calls free on anything. After you're
37 /// done with a Parser, free the arena. After the arena is freed, no member functions of Parser
38 /// may be called.
39 pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
40 return Parser {
41 .allocator = allocator,
42 .tokenizer = tokenizer,
43 .put_back_tokens = undefined,
44 .put_back_count = 0,
45 .source_file_name = source_file_name,
46 .utility_bytes = []align(utility_bytes_align) u8{},
47 };
48 }
49
50 pub fn deinit(self: &Parser) void {
51 self.allocator.free(self.utility_bytes);
52 }
53
54 const TopLevelDeclCtx = struct {
55 visib_token: ?Token,
56 extern_token: ?Token,
57 };
58
59 const DestPtr = union(enum) {
60 Field: &&ast.Node,
61 NullableField: &?&ast.Node,
62 List: &ArrayList(&ast.Node),
63
64 pub fn store(self: &const DestPtr, value: &ast.Node) !void {
65 switch (*self) {
66 DestPtr.Field => |ptr| *ptr = value,
67 DestPtr.NullableField => |ptr| *ptr = value,
68 DestPtr.List => |list| try list.append(value),
69 }
70 }
71 };
72
73 const State = union(enum) {
74 TopLevel,
75 TopLevelExtern: ?Token,
76 TopLevelDecl: TopLevelDeclCtx,
77 Expression: DestPtr,
78 ExpectOperand,
79 Operand: &ast.Node,
80 AfterOperand,
81 InfixOp: &ast.NodeInfixOp,
82 PrefixOp: &ast.NodePrefixOp,
83 AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
84 TypeExpr: DestPtr,
85 VarDecl: &ast.NodeVarDecl,
86 VarDeclAlign: &ast.NodeVarDecl,
87 VarDeclEq: &ast.NodeVarDecl,
88 ExpectToken: @TagType(Token.Id),
89 FnProto: &ast.NodeFnProto,
90 FnProtoAlign: &ast.NodeFnProto,
91 ParamDecl: &ast.NodeFnProto,
92 ParamDeclComma,
93 FnDef: &ast.NodeFnProto,
94 Block: &ast.NodeBlock,
95 Statement: &ast.NodeBlock,
96 };
97
98 /// Returns an AST tree, allocated with the parser's allocator.
99 /// Result should be freed with `freeAst` when done.
100 pub fn parse(self: &Parser) !Tree {
101 var stack = self.initUtilityArrayList(State);
102 defer self.deinitUtilityArrayList(stack);
103
104 const root_node = try self.createRoot();
105 // TODO errdefer arena free root node
106
107 try stack.append(State.TopLevel);
108
109 while (true) {
110 //{
111 // const token = self.getNextToken();
112 // warn("{} ", @tagName(token.id));
113 // self.putBackToken(token);
114 // var i: usize = stack.len;
115 // while (i != 0) {
116 // i -= 1;
117 // warn("{} ", @tagName(stack.items[i]));
118 // }
119 // warn("\n");
120 //}
121
122 // This gives us 1 free append that can't fail
123 const state = stack.pop();
124
125 switch (state) {
126 State.TopLevel => {
127 const token = self.getNextToken();
128 switch (token.id) {
129 Token.Id.Keyword_pub, Token.Id.Keyword_export => {
130 stack.append(State { .TopLevelExtern = token }) catch unreachable;
131 continue;
132 },
133 Token.Id.Eof => return Tree {.root_node = root_node},
134 else => {
135 self.putBackToken(token);
136 stack.append(State { .TopLevelExtern = null }) catch unreachable;
137 continue;
138 },
139 }
140 },
141 State.TopLevelExtern => |visib_token| {
142 const token = self.getNextToken();
143 if (token.id == Token.Id.Keyword_extern) {
144 stack.append(State {
145 .TopLevelDecl = TopLevelDeclCtx {
146 .visib_token = visib_token,
147 .extern_token = token,
148 },
149 }) catch unreachable;
150 continue;
151 }
152 self.putBackToken(token);
153 stack.append(State {
154 .TopLevelDecl = TopLevelDeclCtx {
155 .visib_token = visib_token,
156 .extern_token = null,
157 },
158 }) catch unreachable;
159 continue;
160 },
161 State.TopLevelDecl => |ctx| {
162 const token = self.getNextToken();
163 switch (token.id) {
164 Token.Id.Keyword_var, Token.Id.Keyword_const => {
165 stack.append(State.TopLevel) catch unreachable;
166 // TODO shouldn't need these casts
167 const var_decl_node = try self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
168 token, (?Token)(null), ctx.extern_token);
169 try stack.append(State { .VarDecl = var_decl_node });
170 continue;
171 },
172 Token.Id.Keyword_fn => {
173 stack.append(State.TopLevel) catch unreachable;
174 // TODO shouldn't need these casts
175 const fn_proto = try self.createAttachFnProto(&root_node.decls, token,
176 ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
177 try stack.append(State { .FnDef = fn_proto });
178 try stack.append(State { .FnProto = fn_proto });
179 continue;
180 },
181 Token.Id.StringLiteral => {
182 @panic("TODO extern with string literal");
183 },
184 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
185 stack.append(State.TopLevel) catch unreachable;
186 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
187 // TODO shouldn't need this cast
188 const fn_proto = try self.createAttachFnProto(&root_node.decls, fn_token,
189 ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
190 try stack.append(State { .FnDef = fn_proto });
191 try stack.append(State { .FnProto = fn_proto });
192 continue;
193 },
194 else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
195 }
196 },
197 State.VarDecl => |var_decl| {
198 var_decl.name_token = try self.eatToken(Token.Id.Identifier);
199 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
200
201 const next_token = self.getNextToken();
202 if (next_token.id == Token.Id.Colon) {
203 try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
204 continue;
205 }
206
207 self.putBackToken(next_token);
208 continue;
209 },
210 State.VarDeclAlign => |var_decl| {
211 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
212
213 const next_token = self.getNextToken();
214 if (next_token.id == Token.Id.Keyword_align) {
215 _ = try self.eatToken(Token.Id.LParen);
216 try stack.append(State { .ExpectToken = Token.Id.RParen });
217 try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
218 continue;
219 }
220
221 self.putBackToken(next_token);
222 continue;
223 },
224 State.VarDeclEq => |var_decl| {
225 const token = self.getNextToken();
226 if (token.id == Token.Id.Equal) {
227 var_decl.eq_token = token;
228 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
229 try stack.append(State {
230 .Expression = DestPtr {.NullableField = &var_decl.init_node},
231 });
232 continue;
233 }
234 if (token.id == Token.Id.Semicolon) {
235 continue;
236 }
237 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
238 },
239 State.ExpectToken => |token_id| {
240 _ = try self.eatToken(token_id);
241 continue;
242 },
243
244 State.Expression => |dest_ptr| {
245 // save the dest_ptr for later
246 stack.append(state) catch unreachable;
247 try stack.append(State.ExpectOperand);
248 continue;
249 },
250 State.ExpectOperand => {
251 // we'll either get an operand (like 1 or x),
252 // or a prefix operator (like ~ or return).
253 const token = self.getNextToken();
254 switch (token.id) {
255 Token.Id.Keyword_return => {
256 try stack.append(State { .PrefixOp = try self.createPrefixOp(token,
257 ast.NodePrefixOp.PrefixOp.Return) });
258 try stack.append(State.ExpectOperand);
259 continue;
260 },
261 Token.Id.Ampersand => {
262 const prefix_op = try self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
263 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
264 .align_expr = null,
265 .bit_offset_start_token = null,
266 .bit_offset_end_token = null,
267 .const_token = null,
268 .volatile_token = null,
269 }
270 });
271 try stack.append(State { .PrefixOp = prefix_op });
272 try stack.append(State.ExpectOperand);
273 try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
274 continue;
275 },
276 Token.Id.Identifier => {
277 try stack.append(State {
278 .Operand = &(try self.createIdentifier(token)).base
279 });
280 try stack.append(State.AfterOperand);
281 continue;
282 },
283 Token.Id.IntegerLiteral => {
284 try stack.append(State {
285 .Operand = &(try self.createIntegerLiteral(token)).base
286 });
287 try stack.append(State.AfterOperand);
288 continue;
289 },
290 Token.Id.FloatLiteral => {
291 try stack.append(State {
292 .Operand = &(try self.createFloatLiteral(token)).base
293 });
294 try stack.append(State.AfterOperand);
295 continue;
296 },
297 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
298 }
299 },
300
301 State.AfterOperand => {
302 // we'll either get an infix operator (like != or ^),
303 // or a postfix operator (like () or {}),
304 // otherwise this expression is done (like on a ; or else).
305 var token = self.getNextToken();
306 switch (token.id) {
307 Token.Id.EqualEqual => {
308 try stack.append(State {
309 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
310 });
311 try stack.append(State.ExpectOperand);
312 continue;
313 },
314 Token.Id.BangEqual => {
315 try stack.append(State {
316 .InfixOp = try self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
317 });
318 try stack.append(State.ExpectOperand);
319 continue;
320 },
321 else => {
322 // no postfix/infix operator after this operand.
323 self.putBackToken(token);
324 // reduce the stack
325 var expression: &ast.Node = stack.pop().Operand;
326 while (true) {
327 switch (stack.pop()) {
328 State.Expression => |dest_ptr| {
329 // we're done
330 try dest_ptr.store(expression);
331 break;
332 },
333 State.InfixOp => |infix_op| {
334 infix_op.rhs = expression;
335 infix_op.lhs = stack.pop().Operand;
336 expression = &infix_op.base;
337 continue;
338 },
339 State.PrefixOp => |prefix_op| {
340 prefix_op.rhs = expression;
341 expression = &prefix_op.base;
342 continue;
343 },
344 else => unreachable,
345 }
346 }
347 continue;
348 },
349 }
350 },
351
352 State.AddrOfModifiers => |addr_of_info| {
353 var token = self.getNextToken();
354 switch (token.id) {
355 Token.Id.Keyword_align => {
356 stack.append(state) catch unreachable;
357 if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
358 _ = try self.eatToken(Token.Id.LParen);
359 try stack.append(State { .ExpectToken = Token.Id.RParen });
360 try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
361 continue;
362 },
363 Token.Id.Keyword_const => {
364 stack.append(state) catch unreachable;
365 if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
366 addr_of_info.const_token = token;
367 continue;
368 },
369 Token.Id.Keyword_volatile => {
370 stack.append(state) catch unreachable;
371 if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
372 addr_of_info.volatile_token = token;
373 continue;
374 },
375 else => {
376 self.putBackToken(token);
377 continue;
378 },
379 }
380 },
381
382 State.TypeExpr => |dest_ptr| {
383 const token = self.getNextToken();
384 if (token.id == Token.Id.Keyword_var) {
385 @panic("TODO param with type var");
386 }
387 self.putBackToken(token);
388
389 stack.append(State { .Expression = dest_ptr }) catch unreachable;
390 continue;
391 },
392
393 State.FnProto => |fn_proto| {
394 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
395 try stack.append(State { .ParamDecl = fn_proto });
396 try stack.append(State { .ExpectToken = Token.Id.LParen });
397
398 const next_token = self.getNextToken();
399 if (next_token.id == Token.Id.Identifier) {
400 fn_proto.name_token = next_token;
401 continue;
402 }
403 self.putBackToken(next_token);
404 continue;
405 },
406
407 State.FnProtoAlign => |fn_proto| {
408 const token = self.getNextToken();
409 if (token.id == Token.Id.Keyword_align) {
410 @panic("TODO fn proto align");
411 }
412 self.putBackToken(token);
413 stack.append(State {
414 .TypeExpr = DestPtr {.Field = &fn_proto.return_type},
415 }) catch unreachable;
416 continue;
417 },
418
419 State.ParamDecl => |fn_proto| {
420 var token = self.getNextToken();
421 if (token.id == Token.Id.RParen) {
422 continue;
423 }
424 const param_decl = try self.createAttachParamDecl(&fn_proto.params);
425 if (token.id == Token.Id.Keyword_comptime) {
426 param_decl.comptime_token = token;
427 token = self.getNextToken();
428 } else if (token.id == Token.Id.Keyword_noalias) {
429 param_decl.noalias_token = token;
430 token = self.getNextToken();
431 }
432 if (token.id == Token.Id.Identifier) {
433 const next_token = self.getNextToken();
434 if (next_token.id == Token.Id.Colon) {
435 param_decl.name_token = token;
436 token = self.getNextToken();
437 } else {
438 self.putBackToken(next_token);
439 }
440 }
441 if (token.id == Token.Id.Ellipsis3) {
442 param_decl.var_args_token = token;
443 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
444 continue;
445 } else {
446 self.putBackToken(token);
447 }
448
449 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
450 try stack.append(State.ParamDeclComma);
451 try stack.append(State {
452 .TypeExpr = DestPtr {.Field = &param_decl.type_node}
453 });
454 continue;
455 },
456
457 State.ParamDeclComma => {
458 const token = self.getNextToken();
459 switch (token.id) {
460 Token.Id.RParen => {
461 _ = stack.pop(); // pop off the ParamDecl
462 continue;
463 },
464 Token.Id.Comma => continue,
465 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
466 }
467 },
468
469 State.FnDef => |fn_proto| {
470 const token = self.getNextToken();
471 switch(token.id) {
472 Token.Id.LBrace => {
473 const block = try self.createBlock(token);
474 fn_proto.body_node = &block.base;
475 stack.append(State { .Block = block }) catch unreachable;
476 continue;
477 },
478 Token.Id.Semicolon => continue,
479 else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
480 }
481 },
482
483 State.Block => |block| {
484 const token = self.getNextToken();
485 switch (token.id) {
486 Token.Id.RBrace => {
487 block.end_token = token;
488 continue;
489 },
490 else => {
491 self.putBackToken(token);
492 stack.append(State { .Block = block }) catch unreachable;
493 try stack.append(State { .Statement = block });
494 continue;
495 },
496 }
497 },
498
499 State.Statement => |block| {
500 {
501 // Look for comptime var, comptime const
502 const comptime_token = self.getNextToken();
503 if (comptime_token.id == Token.Id.Keyword_comptime) {
504 const mut_token = self.getNextToken();
505 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
506 // TODO shouldn't need these casts
507 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
508 mut_token, (?Token)(comptime_token), (?Token)(null));
509 try stack.append(State { .VarDecl = var_decl });
510 continue;
511 }
512 self.putBackToken(mut_token);
513 }
514 self.putBackToken(comptime_token);
515 }
516 {
517 // Look for const, var
518 const mut_token = self.getNextToken();
519 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
520 // TODO shouldn't need these casts
521 const var_decl = try self.createAttachVarDecl(&block.statements, (?Token)(null),
522 mut_token, (?Token)(null), (?Token)(null));
523 try stack.append(State { .VarDecl = var_decl });
524 continue;
525 }
526 self.putBackToken(mut_token);
527 }
528
529 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
530 try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
531 continue;
532 },
533
534 // These are data, not control flow.
535 State.InfixOp => unreachable,
536 State.PrefixOp => unreachable,
537 State.Operand => unreachable,
538 }
539 @import("std").debug.panic("{}", @tagName(state));
540 //unreachable;
541 }
542 }
543
544 fn createRoot(self: &Parser) !&ast.NodeRoot {
545 const node = try self.allocator.create(ast.NodeRoot);
546
547 *node = ast.NodeRoot {
548 .base = ast.Node {.id = ast.Node.Id.Root},
549 .decls = ArrayList(&ast.Node).init(self.allocator),
550 };
551 return node;
552 }
553
554 fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
555 extern_token: &const ?Token) !&ast.NodeVarDecl
556 {
557 const node = try self.allocator.create(ast.NodeVarDecl);
558
559 *node = ast.NodeVarDecl {
560 .base = ast.Node {.id = ast.Node.Id.VarDecl},
561 .visib_token = *visib_token,
562 .mut_token = *mut_token,
563 .comptime_token = *comptime_token,
564 .extern_token = *extern_token,
565 .type_node = null,
566 .align_node = null,
567 .init_node = null,
568 .lib_name = null,
569 // initialized later
570 .name_token = undefined,
571 .eq_token = undefined,
572 };
573 return node;
574 }
575
576 fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
577 cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
578 {
579 const node = try self.allocator.create(ast.NodeFnProto);
580
581 *node = ast.NodeFnProto {
582 .base = ast.Node {.id = ast.Node.Id.FnProto},
583 .visib_token = *visib_token,
584 .name_token = null,
585 .fn_token = *fn_token,
586 .params = ArrayList(&ast.Node).init(self.allocator),
587 .return_type = undefined,
588 .var_args_token = null,
589 .extern_token = *extern_token,
590 .inline_token = *inline_token,
591 .cc_token = *cc_token,
592 .body_node = null,
593 .lib_name = null,
594 .align_expr = null,
595 };
596 return node;
597 }
598
599 fn createParamDecl(self: &Parser) !&ast.NodeParamDecl {
600 const node = try self.allocator.create(ast.NodeParamDecl);
601
602 *node = ast.NodeParamDecl {
603 .base = ast.Node {.id = ast.Node.Id.ParamDecl},
604 .comptime_token = null,
605 .noalias_token = null,
606 .name_token = null,
607 .type_node = undefined,
608 .var_args_token = null,
609 };
610 return node;
611 }
612
613 fn createBlock(self: &Parser, begin_token: &const Token) !&ast.NodeBlock {
614 const node = try self.allocator.create(ast.NodeBlock);
615
616 *node = ast.NodeBlock {
617 .base = ast.Node {.id = ast.Node.Id.Block},
618 .begin_token = *begin_token,
619 .end_token = undefined,
620 .statements = ArrayList(&ast.Node).init(self.allocator),
621 };
622 return node;
623 }
624
625 fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
626 const node = try self.allocator.create(ast.NodeInfixOp);
627
628 *node = ast.NodeInfixOp {
629 .base = ast.Node {.id = ast.Node.Id.InfixOp},
630 .op_token = *op_token,
631 .lhs = undefined,
632 .op = *op,
633 .rhs = undefined,
634 };
635 return node;
636 }
637
638 fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
639 const node = try self.allocator.create(ast.NodePrefixOp);
640
641 *node = ast.NodePrefixOp {
642 .base = ast.Node {.id = ast.Node.Id.PrefixOp},
643 .op_token = *op_token,
644 .op = *op,
645 .rhs = undefined,
646 };
647 return node;
648 }
649
650 fn createIdentifier(self: &Parser, name_token: &const Token) !&ast.NodeIdentifier {
651 const node = try self.allocator.create(ast.NodeIdentifier);
652
653 *node = ast.NodeIdentifier {
654 .base = ast.Node {.id = ast.Node.Id.Identifier},
655 .name_token = *name_token,
656 };
657 return node;
658 }
659
660 fn createIntegerLiteral(self: &Parser, token: &const Token) !&ast.NodeIntegerLiteral {
661 const node = try self.allocator.create(ast.NodeIntegerLiteral);
662
663 *node = ast.NodeIntegerLiteral {
664 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
665 .token = *token,
666 };
667 return node;
668 }
669
670 fn createFloatLiteral(self: &Parser, token: &const Token) !&ast.NodeFloatLiteral {
671 const node = try self.allocator.create(ast.NodeFloatLiteral);
672
673 *node = ast.NodeFloatLiteral {
674 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
675 .token = *token,
676 };
677 return node;
678 }
679
680 fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
681 const node = try self.createIdentifier(name_token);
682 try dest_ptr.store(&node.base);
683 return node;
684 }
685
686 fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
687 const node = try self.createParamDecl();
688 try list.append(&node.base);
689 return node;
690 }
691
692 fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
693 extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
694 inline_token: &const ?Token) !&ast.NodeFnProto
695 {
696 const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
697 try list.append(&node.base);
698 return node;
699 }
700
701 fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
702 mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
703 {
704 const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
705 try list.append(&node.base);
706 return node;
707 }
708
709 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
710 const loc = self.tokenizer.getTokenLocation(token);
711 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
712 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
713 {
714 var i: usize = 0;
715 while (i < loc.column) : (i += 1) {
716 warn(" ");
717 }
718 }
719 {
720 const caret_count = token.end - token.start;
721 var i: usize = 0;
722 while (i < caret_count) : (i += 1) {
723 warn("~");
724 }
725 }
726 warn("\n");
727 return error.ParseError;
728 }
729
730 fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
731 if (token.id != id) {
732 return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
733 }
734 }
735
736 fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
737 const token = self.getNextToken();
738 try self.expectToken(token, id);
739 return token;
740 }
741
742 fn putBackToken(self: &Parser, token: &const Token) void {
743 self.put_back_tokens[self.put_back_count] = *token;
744 self.put_back_count += 1;
745 }
746
747 fn getNextToken(self: &Parser) Token {
748 if (self.put_back_count != 0) {
749 const put_back_index = self.put_back_count - 1;
750 const put_back_token = self.put_back_tokens[put_back_index];
751 self.put_back_count = put_back_index;
752 return put_back_token;
753 } else {
754 return self.tokenizer.next();
755 }
756 }
757
758 const RenderAstFrame = struct {
759 node: &ast.Node,
760 indent: usize,
761 };
762
763 pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
764 var stack = self.initUtilityArrayList(RenderAstFrame);
765 defer self.deinitUtilityArrayList(stack);
766
767 try stack.append(RenderAstFrame {
768 .node = &root_node.base,
769 .indent = 0,
770 });
771
772 while (stack.popOrNull()) |frame| {
773 {
774 var i: usize = 0;
775 while (i < frame.indent) : (i += 1) {
776 try stream.print(" ");
777 }
778 }
779 try stream.print("{}\n", @tagName(frame.node.id));
780 var child_i: usize = 0;
781 while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
782 try stack.append(RenderAstFrame {
783 .node = child,
784 .indent = frame.indent + 2,
785 });
786 }
787 }
788 }
789
790 const RenderState = union(enum) {
791 TopLevelDecl: &ast.Node,
792 FnProtoRParen: &ast.NodeFnProto,
793 ParamDecl: &ast.Node,
794 Text: []const u8,
795 Expression: &ast.Node,
796 VarDecl: &ast.NodeVarDecl,
797 Statement: &ast.Node,
798 PrintIndent,
799 Indent: usize,
800 };
801
802 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
803 var stack = self.initUtilityArrayList(RenderState);
804 defer self.deinitUtilityArrayList(stack);
805
806 {
807 var i = root_node.decls.len;
808 while (i != 0) {
809 i -= 1;
810 const decl = root_node.decls.items[i];
811 try stack.append(RenderState {.TopLevelDecl = decl});
812 }
813 }
814
815 const indent_delta = 4;
816 var indent: usize = 0;
817 while (stack.popOrNull()) |state| {
818 switch (state) {
819 RenderState.TopLevelDecl => |decl| {
820 switch (decl.id) {
821 ast.Node.Id.FnProto => {
822 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
823 if (fn_proto.visib_token) |visib_token| {
824 switch (visib_token.id) {
825 Token.Id.Keyword_pub => try stream.print("pub "),
826 Token.Id.Keyword_export => try stream.print("export "),
827 else => unreachable,
828 }
829 }
830 if (fn_proto.extern_token) |extern_token| {
831 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
832 }
833 try stream.print("fn");
834
835 if (fn_proto.name_token) |name_token| {
836 try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
837 }
838
839 try stream.print("(");
840
841 try stack.append(RenderState { .Text = "\n" });
842 if (fn_proto.body_node == null) {
843 try stack.append(RenderState { .Text = ";" });
844 }
845
846 try stack.append(RenderState { .FnProtoRParen = fn_proto});
847 var i = fn_proto.params.len;
848 while (i != 0) {
849 i -= 1;
850 const param_decl_node = fn_proto.params.items[i];
851 try stack.append(RenderState { .ParamDecl = param_decl_node});
852 if (i != 0) {
853 try stack.append(RenderState { .Text = ", " });
854 }
855 }
856 },
857 ast.Node.Id.VarDecl => {
858 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
859 try stack.append(RenderState { .Text = "\n"});
860 try stack.append(RenderState { .VarDecl = var_decl});
861
862 },
863 else => unreachable,
864 }
865 },
866
867 RenderState.VarDecl => |var_decl| {
868 if (var_decl.visib_token) |visib_token| {
869 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
870 }
871 if (var_decl.extern_token) |extern_token| {
872 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
873 if (var_decl.lib_name != null) {
874 @panic("TODO");
875 }
876 }
877 if (var_decl.comptime_token) |comptime_token| {
878 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
879 }
880 try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
881 try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
882
883 try stack.append(RenderState { .Text = ";" });
884 if (var_decl.init_node) |init_node| {
885 try stack.append(RenderState { .Expression = init_node });
886 try stack.append(RenderState { .Text = " = " });
887 }
888 if (var_decl.align_node) |align_node| {
889 try stack.append(RenderState { .Text = ")" });
890 try stack.append(RenderState { .Expression = align_node });
891 try stack.append(RenderState { .Text = " align(" });
892 }
893 if (var_decl.type_node) |type_node| {
894 try stream.print(": ");
895 try stack.append(RenderState { .Expression = type_node });
896 }
897 },
898
899 RenderState.ParamDecl => |base| {
900 const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
901 if (param_decl.comptime_token) |comptime_token| {
902 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
903 }
904 if (param_decl.noalias_token) |noalias_token| {
905 try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
906 }
907 if (param_decl.name_token) |name_token| {
908 try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
909 }
910 if (param_decl.var_args_token) |var_args_token| {
911 try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
912 } else {
913 try stack.append(RenderState { .Expression = param_decl.type_node});
914 }
915 },
916 RenderState.Text => |bytes| {
917 try stream.write(bytes);
918 },
919 RenderState.Expression => |base| switch (base.id) {
920 ast.Node.Id.Identifier => {
921 const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
922 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
923 },
924 ast.Node.Id.Block => {
925 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
926 try stream.write("{");
927 try stack.append(RenderState { .Text = "}"});
928 try stack.append(RenderState.PrintIndent);
929 try stack.append(RenderState { .Indent = indent});
930 try stack.append(RenderState { .Text = "\n"});
931 var i = block.statements.len;
932 while (i != 0) {
933 i -= 1;
934 const statement_node = block.statements.items[i];
935 try stack.append(RenderState { .Statement = statement_node});
936 try stack.append(RenderState.PrintIndent);
937 try stack.append(RenderState { .Indent = indent + indent_delta});
938 try stack.append(RenderState { .Text = "\n" });
939 }
940 },
941 ast.Node.Id.InfixOp => {
942 const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
943 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
944 switch (prefix_op_node.op) {
945 ast.NodeInfixOp.InfixOp.EqualEqual => {
946 try stack.append(RenderState { .Text = " == "});
947 },
948 ast.NodeInfixOp.InfixOp.BangEqual => {
949 try stack.append(RenderState { .Text = " != "});
950 },
951 else => unreachable,
952 }
953 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
954 },
955 ast.Node.Id.PrefixOp => {
956 const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
957 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
958 switch (prefix_op_node.op) {
959 ast.NodePrefixOp.PrefixOp.Return => {
960 try stream.write("return ");
961 },
962 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
963 try stream.write("&");
964 if (addr_of_info.volatile_token != null) {
965 try stack.append(RenderState { .Text = "volatile "});
966 }
967 if (addr_of_info.const_token != null) {
968 try stack.append(RenderState { .Text = "const "});
969 }
970 if (addr_of_info.align_expr) |align_expr| {
971 try stream.print("align(");
972 try stack.append(RenderState { .Text = ") "});
973 try stack.append(RenderState { .Expression = align_expr});
974 }
975 },
976 else => unreachable,
977 }
978 },
979 ast.Node.Id.IntegerLiteral => {
980 const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
981 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
982 },
983 ast.Node.Id.FloatLiteral => {
984 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
985 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
986 },
987 else => unreachable,
988 },
989 RenderState.FnProtoRParen => |fn_proto| {
990 try stream.print(")");
991 if (fn_proto.align_expr != null) {
992 @panic("TODO");
993 }
994 try stream.print(" ");
995 if (fn_proto.body_node) |body_node| {
996 try stack.append(RenderState { .Expression = body_node});
997 try stack.append(RenderState { .Text = " "});
998 }
999 try stack.append(RenderState { .Expression = fn_proto.return_type});
1000 },
1001 RenderState.Statement => |base| {
1002 switch (base.id) {
1003 ast.Node.Id.VarDecl => {
1004 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
1005 try stack.append(RenderState { .VarDecl = var_decl});
1006 },
1007 else => {
1008 try stack.append(RenderState { .Text = ";"});
1009 try stack.append(RenderState { .Expression = base});
1010 },
1011 }
1012 },
1013 RenderState.Indent => |new_indent| indent = new_indent,
1014 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
1015 }
1016 }
1017 }
1018
1019 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
1020 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
1021 self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
1022 const typed_slice = ([]T)(self.utility_bytes);
1023 return ArrayList(T) {
1024 .allocator = self.allocator,
1025 .items = typed_slice,
1026 .len = 0,
1027 };
1028 }
1029
1030 fn deinitUtilityArrayList(self: &Parser, list: var) void {
1031 self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
1032 }
1033
1034};
1035
1036var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1037
1038fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1039 var padded_source: [0x100]u8 = undefined;
1040 std.mem.copy(u8, padded_source[0..source.len], source);
1041
1042 var tokenizer = Tokenizer.init(padded_source[0..source.len]);
1043 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1044 defer parser.deinit();
1045
1046 const tree = try parser.parse();
1047 defer tree.deinit();
1048
1049 var buffer = try std.Buffer.initSize(allocator, 0);
1050 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1051 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
1052 return buffer.toOwnedSlice();
1053}
1054
1055// TODO test for memory leaks
1056// TODO test for valid frees
1057fn testCanonical(source: []const u8) !void {
1058 const needed_alloc_count = x: {
1059 // Try it once with unlimited memory, make sure it works
1060 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1061 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1062 const result_source = try testParse(source, &failing_allocator.allocator);
1063 if (!mem.eql(u8, result_source, source)) {
1064 warn("\n====== expected this output: =========\n");
1065 warn("{}", source);
1066 warn("\n======== instead found this: =========\n");
1067 warn("{}", result_source);
1068 warn("\n======================================\n");
1069 return error.TestFailed;
1070 }
1071 failing_allocator.allocator.free(result_source);
1072 break :x failing_allocator.index;
1073 };
1074
1075 var fail_index: usize = 0;
1076 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1077 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1078 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1079 if (testParse(source, &failing_allocator.allocator)) |_| {
1080 return error.NondeterministicMemoryUsage;
1081 } else |err| switch (err) {
1082 error.OutOfMemory => {
1083 // TODO make this pass
1084 //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1085 // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1086 // fail_index, needed_alloc_count,
1087 // failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1088 // failing_allocator.index, failing_allocator.deallocations);
1089 // return error.MemoryLeakDetected;
1090 //}
1091 },
1092 error.ParseError => @panic("test failed"),
1093 }
1094 }
1095}
1096
1097test "zig fmt" {
1098 try testCanonical(
1099 \\extern fn puts(s: &const u8) c_int;
1100 \\
1101 );
1102
1103 try testCanonical(
1104 \\const a = b;
1105 \\pub const a = b;
1106 \\var a = b;
1107 \\pub var a = b;
1108 \\const a: i32 = b;
1109 \\pub const a: i32 = b;
1110 \\var a: i32 = b;
1111 \\pub var a: i32 = b;
1112 \\
1113 );
1114
1115 try testCanonical(
1116 \\extern var foo: c_int;
1117 \\
1118 );
1119
1120 try testCanonical(
1121 \\var foo: c_int align(1);
1122 \\
1123 );
1124
1125 try testCanonical(
1126 \\fn main(argc: c_int, argv: &&u8) c_int {
1127 \\ const a = b;
1128 \\}
1129 \\
1130 );
1131
1132 try testCanonical(
1133 \\fn foo(argc: c_int, argv: &&u8) c_int {
1134 \\ return 0;
1135 \\}
1136 \\
1137 );
1138
1139 try testCanonical(
1140 \\extern fn f1(s: &align(&u8) u8) c_int;
1141 \\
1142 );
1143
1144 try testCanonical(
1145 \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
1146 \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
1147 \\extern fn f3(s: &align(1) const volatile u8) c_int;
1148 \\
1149 );
1150
1151 try testCanonical(
1152 \\fn f1(a: bool, b: bool) bool {
1153 \\ a != b;
1154 \\ return a == b;
1155 \\}
1156 \\
1157 );
1158}
std/zig/tokenizer.zig created+655
...@@ -0,0 +1,655 @@
1const std = @import("../index.zig");
2const mem = std.mem;
3
4pub const Token = struct {
5 id: Id,
6 start: usize,
7 end: usize,
8
9 const KeywordId = struct {
10 bytes: []const u8,
11 id: Id,
12 };
13
14 const keywords = []KeywordId {
15 KeywordId{.bytes="align", .id = Id.Keyword_align},
16 KeywordId{.bytes="and", .id = Id.Keyword_and},
17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
18 KeywordId{.bytes="break", .id = Id.Keyword_break},
19 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
20 KeywordId{.bytes="const", .id = Id.Keyword_const},
21 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
22 KeywordId{.bytes="defer", .id = Id.Keyword_defer},
23 KeywordId{.bytes="else", .id = Id.Keyword_else},
24 KeywordId{.bytes="enum", .id = Id.Keyword_enum},
25 KeywordId{.bytes="error", .id = Id.Keyword_error},
26 KeywordId{.bytes="export", .id = Id.Keyword_export},
27 KeywordId{.bytes="extern", .id = Id.Keyword_extern},
28 KeywordId{.bytes="false", .id = Id.Keyword_false},
29 KeywordId{.bytes="fn", .id = Id.Keyword_fn},
30 KeywordId{.bytes="for", .id = Id.Keyword_for},
31 KeywordId{.bytes="goto", .id = Id.Keyword_goto},
32 KeywordId{.bytes="if", .id = Id.Keyword_if},
33 KeywordId{.bytes="inline", .id = Id.Keyword_inline},
34 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
35 KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
36 KeywordId{.bytes="null", .id = Id.Keyword_null},
37 KeywordId{.bytes="or", .id = Id.Keyword_or},
38 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
39 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
40 KeywordId{.bytes="return", .id = Id.Keyword_return},
41 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
42 KeywordId{.bytes="struct", .id = Id.Keyword_struct},
43 KeywordId{.bytes="switch", .id = Id.Keyword_switch},
44 KeywordId{.bytes="test", .id = Id.Keyword_test},
45 KeywordId{.bytes="this", .id = Id.Keyword_this},
46 KeywordId{.bytes="true", .id = Id.Keyword_true},
47 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
48 KeywordId{.bytes="union", .id = Id.Keyword_union},
49 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
50 KeywordId{.bytes="use", .id = Id.Keyword_use},
51 KeywordId{.bytes="var", .id = Id.Keyword_var},
52 KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
53 KeywordId{.bytes="while", .id = Id.Keyword_while},
54 };
55
56 fn getKeyword(bytes: []const u8) ?Id {
57 for (keywords) |kw| {
58 if (mem.eql(u8, kw.bytes, bytes)) {
59 return kw.id;
60 }
61 }
62 return null;
63 }
64
65 const StrLitKind = enum {Normal, C};
66
67 pub const Id = union(enum) {
68 Invalid,
69 Identifier,
70 StringLiteral: StrLitKind,
71 Eof,
72 Builtin,
73 Bang,
74 Equal,
75 EqualEqual,
76 BangEqual,
77 LParen,
78 RParen,
79 Semicolon,
80 Percent,
81 LBrace,
82 RBrace,
83 Period,
84 Ellipsis2,
85 Ellipsis3,
86 Minus,
87 Arrow,
88 Colon,
89 Slash,
90 Comma,
91 Ampersand,
92 AmpersandEqual,
93 IntegerLiteral,
94 FloatLiteral,
95 Keyword_align,
96 Keyword_and,
97 Keyword_asm,
98 Keyword_break,
99 Keyword_comptime,
100 Keyword_const,
101 Keyword_continue,
102 Keyword_defer,
103 Keyword_else,
104 Keyword_enum,
105 Keyword_error,
106 Keyword_export,
107 Keyword_extern,
108 Keyword_false,
109 Keyword_fn,
110 Keyword_for,
111 Keyword_goto,
112 Keyword_if,
113 Keyword_inline,
114 Keyword_nakedcc,
115 Keyword_noalias,
116 Keyword_null,
117 Keyword_or,
118 Keyword_packed,
119 Keyword_pub,
120 Keyword_return,
121 Keyword_stdcallcc,
122 Keyword_struct,
123 Keyword_switch,
124 Keyword_test,
125 Keyword_this,
126 Keyword_true,
127 Keyword_undefined,
128 Keyword_union,
129 Keyword_unreachable,
130 Keyword_use,
131 Keyword_var,
132 Keyword_volatile,
133 Keyword_while,
134 };
135};
136
137pub const Tokenizer = struct {
138 buffer: []const u8,
139 index: usize,
140 pending_invalid_token: ?Token,
141
142 pub const Location = struct {
143 line: usize,
144 column: usize,
145 line_start: usize,
146 line_end: usize,
147 };
148
149 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
150 var loc = Location {
151 .line = 0,
152 .column = 0,
153 .line_start = 0,
154 .line_end = 0,
155 };
156 for (self.buffer) |c, i| {
157 if (i == token.start) {
158 loc.line_end = i;
159 while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
160 return loc;
161 }
162 if (c == '\n') {
163 loc.line += 1;
164 loc.column = 0;
165 loc.line_start = i + 1;
166 } else {
167 loc.column += 1;
168 }
169 }
170 return loc;
171 }
172
173 /// For debugging purposes
174 pub fn dump(self: &Tokenizer, token: &const Token) void {
175 std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
176 }
177
178 pub fn init(buffer: []const u8) Tokenizer {
179 return Tokenizer {
180 .buffer = buffer,
181 .index = 0,
182 .pending_invalid_token = null,
183 };
184 }
185
186 const State = enum {
187 Start,
188 Identifier,
189 Builtin,
190 C,
191 StringLiteral,
192 StringLiteralBackslash,
193 Equal,
194 Bang,
195 Minus,
196 Slash,
197 LineComment,
198 Zero,
199 IntegerLiteral,
200 IntegerLiteralWithRadix,
201 NumberDot,
202 FloatFraction,
203 FloatExponentUnsigned,
204 FloatExponentNumber,
205 Ampersand,
206 Period,
207 Period2,
208 };
209
210 pub fn next(self: &Tokenizer) Token {
211 if (self.pending_invalid_token) |token| {
212 self.pending_invalid_token = null;
213 return token;
214 }
215 var state = State.Start;
216 var result = Token {
217 .id = Token.Id.Eof,
218 .start = self.index,
219 .end = undefined,
220 };
221 while (self.index < self.buffer.len) : (self.index += 1) {
222 const c = self.buffer[self.index];
223 switch (state) {
224 State.Start => switch (c) {
225 ' ', '\n' => {
226 result.start = self.index + 1;
227 },
228 'c' => {
229 state = State.C;
230 result.id = Token.Id.Identifier;
231 },
232 '"' => {
233 state = State.StringLiteral;
234 result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
235 },
236 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
237 state = State.Identifier;
238 result.id = Token.Id.Identifier;
239 },
240 '@' => {
241 state = State.Builtin;
242 result.id = Token.Id.Builtin;
243 },
244 '=' => {
245 state = State.Equal;
246 },
247 '!' => {
248 state = State.Bang;
249 },
250 '(' => {
251 result.id = Token.Id.LParen;
252 self.index += 1;
253 break;
254 },
255 ')' => {
256 result.id = Token.Id.RParen;
257 self.index += 1;
258 break;
259 },
260 ';' => {
261 result.id = Token.Id.Semicolon;
262 self.index += 1;
263 break;
264 },
265 ',' => {
266 result.id = Token.Id.Comma;
267 self.index += 1;
268 break;
269 },
270 ':' => {
271 result.id = Token.Id.Colon;
272 self.index += 1;
273 break;
274 },
275 '%' => {
276 result.id = Token.Id.Percent;
277 self.index += 1;
278 break;
279 },
280 '{' => {
281 result.id = Token.Id.LBrace;
282 self.index += 1;
283 break;
284 },
285 '}' => {
286 result.id = Token.Id.RBrace;
287 self.index += 1;
288 break;
289 },
290 '.' => {
291 state = State.Period;
292 },
293 '-' => {
294 state = State.Minus;
295 },
296 '/' => {
297 state = State.Slash;
298 },
299 '&' => {
300 state = State.Ampersand;
301 },
302 '0' => {
303 state = State.Zero;
304 result.id = Token.Id.IntegerLiteral;
305 },
306 '1'...'9' => {
307 state = State.IntegerLiteral;
308 result.id = Token.Id.IntegerLiteral;
309 },
310 else => {
311 result.id = Token.Id.Invalid;
312 self.index += 1;
313 break;
314 },
315 },
316 State.Ampersand => switch (c) {
317 '=' => {
318 result.id = Token.Id.AmpersandEqual;
319 self.index += 1;
320 break;
321 },
322 else => {
323 result.id = Token.Id.Ampersand;
324 break;
325 },
326 },
327 State.Identifier => switch (c) {
328 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
329 else => {
330 if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
331 result.id = id;
332 }
333 break;
334 },
335 },
336 State.Builtin => switch (c) {
337 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
338 else => break,
339 },
340 State.C => switch (c) {
341 '\\' => @panic("TODO"),
342 '"' => {
343 state = State.StringLiteral;
344 result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
345 },
346 'a'...'z', 'A'...'Z', '_', '0'...'9' => {
347 state = State.Identifier;
348 },
349 else => break,
350 },
351 State.StringLiteral => switch (c) {
352 '\\' => {
353 state = State.StringLiteralBackslash;
354 },
355 '"' => {
356 self.index += 1;
357 break;
358 },
359 '\n' => break, // Look for this error later.
360 else => self.checkLiteralCharacter(),
361 },
362
363 State.StringLiteralBackslash => switch (c) {
364 '\n' => break, // Look for this error later.
365 else => {
366 state = State.StringLiteral;
367 },
368 },
369
370 State.Bang => switch (c) {
371 '=' => {
372 result.id = Token.Id.BangEqual;
373 self.index += 1;
374 break;
375 },
376 else => {
377 result.id = Token.Id.Bang;
378 break;
379 },
380 },
381
382 State.Equal => switch (c) {
383 '=' => {
384 result.id = Token.Id.EqualEqual;
385 self.index += 1;
386 break;
387 },
388 else => {
389 result.id = Token.Id.Equal;
390 break;
391 },
392 },
393
394 State.Minus => switch (c) {
395 '>' => {
396 result.id = Token.Id.Arrow;
397 self.index += 1;
398 break;
399 },
400 else => {
401 result.id = Token.Id.Minus;
402 break;
403 },
404 },
405
406 State.Period => switch (c) {
407 '.' => {
408 state = State.Period2;
409 },
410 else => {
411 result.id = Token.Id.Period;
412 break;
413 },
414 },
415
416 State.Period2 => switch (c) {
417 '.' => {
418 result.id = Token.Id.Ellipsis3;
419 self.index += 1;
420 break;
421 },
422 else => {
423 result.id = Token.Id.Ellipsis2;
424 break;
425 },
426 },
427
428 State.Slash => switch (c) {
429 '/' => {
430 result.id = undefined;
431 state = State.LineComment;
432 },
433 else => {
434 result.id = Token.Id.Slash;
435 break;
436 },
437 },
438 State.LineComment => switch (c) {
439 '\n' => {
440 state = State.Start;
441 result = Token {
442 .id = Token.Id.Eof,
443 .start = self.index + 1,
444 .end = undefined,
445 };
446 },
447 else => self.checkLiteralCharacter(),
448 },
449 State.Zero => switch (c) {
450 'b', 'o', 'x' => {
451 state = State.IntegerLiteralWithRadix;
452 },
453 else => {
454 // reinterpret as a normal number
455 self.index -= 1;
456 state = State.IntegerLiteral;
457 },
458 },
459 State.IntegerLiteral => switch (c) {
460 '.' => {
461 state = State.NumberDot;
462 },
463 'p', 'P', 'e', 'E' => {
464 state = State.FloatExponentUnsigned;
465 },
466 '0'...'9' => {},
467 else => break,
468 },
469 State.IntegerLiteralWithRadix => switch (c) {
470 '.' => {
471 state = State.NumberDot;
472 },
473 'p', 'P' => {
474 state = State.FloatExponentUnsigned;
475 },
476 '0'...'9', 'a'...'f', 'A'...'F' => {},
477 else => break,
478 },
479 State.NumberDot => switch (c) {
480 '.' => {
481 self.index -= 1;
482 state = State.Start;
483 break;
484 },
485 else => {
486 self.index -= 1;
487 result.id = Token.Id.FloatLiteral;
488 state = State.FloatFraction;
489 },
490 },
491 State.FloatFraction => switch (c) {
492 'p', 'P' => {
493 state = State.FloatExponentUnsigned;
494 },
495 '0'...'9', 'a'...'f', 'A'...'F' => {},
496 else => break,
497 },
498 State.FloatExponentUnsigned => switch (c) {
499 '+', '-' => {
500 state = State.FloatExponentNumber;
501 },
502 else => {
503 // reinterpret as a normal exponent number
504 self.index -= 1;
505 state = State.FloatExponentNumber;
506 }
507 },
508 State.FloatExponentNumber => switch (c) {
509 '0'...'9', 'a'...'f', 'A'...'F' => {},
510 else => break,
511 },
512 }
513 }
514 result.end = self.index;
515
516 if (result.id == Token.Id.Eof) {
517 if (self.pending_invalid_token) |token| {
518 self.pending_invalid_token = null;
519 return token;
520 }
521 }
522
523 return result;
524 }
525
526 pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 {
527 return self.buffer[token.start..token.end];
528 }
529
530 fn checkLiteralCharacter(self: &Tokenizer) void {
531 if (self.pending_invalid_token != null) return;
532 const invalid_length = self.getInvalidCharacterLength();
533 if (invalid_length == 0) return;
534 self.pending_invalid_token = Token {
535 .id = Token.Id.Invalid,
536 .start = self.index,
537 .end = self.index + invalid_length,
538 };
539 }
540
541 fn getInvalidCharacterLength(self: &Tokenizer) u3 {
542 const c0 = self.buffer[self.index];
543 if (c0 < 0x80) {
544 if (c0 < 0x20 or c0 == 0x7f) {
545 // ascii control codes are never allowed
546 // (note that \n was checked before we got here)
547 return 1;
548 }
549 // looks fine to me.
550 return 0;
551 } else {
552 // check utf8-encoded character.
553 const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1;
554 if (self.index + length >= self.buffer.len) {
555 return u3(self.buffer.len - self.index);
556 }
557 const bytes = self.buffer[self.index..self.index + length];
558 switch (length) {
559 2 => {
560 const value = std.unicode.utf8Decode2(bytes) catch return length;
561 if (value == 0x85) return length; // U+0085 (NEL)
562 },
563 3 => {
564 const value = std.unicode.utf8Decode3(bytes) catch return length;
565 if (value == 0x2028) return length; // U+2028 (LS)
566 if (value == 0x2029) return length; // U+2029 (PS)
567 },
568 4 => {
569 _ = std.unicode.utf8Decode4(bytes) catch return length;
570 },
571 else => unreachable,
572 }
573 self.index += length - 1;
574 return 0;
575 }
576 }
577};
578
579
580
581test "tokenizer" {
582 testTokenize("test", []Token.Id {
583 Token.Id.Keyword_test,
584 });
585}
586
587test "tokenizer - invalid token characters" {
588 testTokenize("#", []Token.Id{Token.Id.Invalid});
589 testTokenize("`", []Token.Id{Token.Id.Invalid});
590}
591
592test "tokenizer - invalid literal/comment characters" {
593 testTokenize("\"\x00\"", []Token.Id {
594 Token.Id { .StringLiteral = Token.StrLitKind.Normal },
595 Token.Id.Invalid,
596 });
597 testTokenize("//\x00", []Token.Id {
598 Token.Id.Invalid,
599 });
600 testTokenize("//\x1f", []Token.Id {
601 Token.Id.Invalid,
602 });
603 testTokenize("//\x7f", []Token.Id {
604 Token.Id.Invalid,
605 });
606}
607
608test "tokenizer - utf8" {
609 testTokenize("//\xc2\x80", []Token.Id{});
610 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});
611}
612
613test "tokenizer - invalid utf8" {
614 testTokenize("//\x80", []Token.Id{Token.Id.Invalid});
615 testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});
616 testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});
617 testTokenize("//\xff", []Token.Id{Token.Id.Invalid});
618 testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});
619 testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});
620 testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});
621 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});
622}
623
624test "tokenizer - illegal unicode codepoints" {
625 // unicode newline characters.U+0085, U+2028, U+2029
626 testTokenize("//\xc2\x84", []Token.Id{});
627 testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});
628 testTokenize("//\xc2\x86", []Token.Id{});
629 testTokenize("//\xe2\x80\xa7", []Token.Id{});
630 testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});
631 testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});
632 testTokenize("//\xe2\x80\xaa", []Token.Id{});
633}
634
635fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
636 // (test authors, just make this bigger if you need it)
637 var padded_source: [0x100]u8 = undefined;
638 std.mem.copy(u8, padded_source[0..source.len], source);
639 padded_source[source.len + 0] = '\n';
640 padded_source[source.len + 1] = '\n';
641 padded_source[source.len + 2] = '\n';
642
643 var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
644 for (expected_tokens) |expected_token_id| {
645 const token = tokenizer.next();
646 std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
647 switch (expected_token_id) {
648 Token.Id.StringLiteral => |expected_kind| {
649 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
650 },
651 else => {},
652 }
653 }
654 std.debug.assert(tokenizer.next().id == Token.Id.Eof);
655}
test/cases/cast.zig+16-18
...@@ -32,7 +32,6 @@ fn funcWithConstPtrPtr(x: &const &i32) void {...@@ -32,7 +32,6 @@ fn funcWithConstPtrPtr(x: &const &i32) void {
32 **x += 1;32 **x += 1;
33}33}
3434
35error ItBroke;
36test "explicit cast from integer to error type" {35test "explicit cast from integer to error type" {
37 testCastIntToErr(error.ItBroke);36 testCastIntToErr(error.ItBroke);
38 comptime testCastIntToErr(error.ItBroke);37 comptime testCastIntToErr(error.ItBroke);
...@@ -75,7 +74,7 @@ test "string literal to &const []const u8" {...@@ -75,7 +74,7 @@ test "string literal to &const []const u8" {
75 assert(mem.eql(u8, *x, "hello"));74 assert(mem.eql(u8, *x, "hello"));
76}75}
7776
78test "implicitly cast from T to %?T" {77test "implicitly cast from T to error!?T" {
79 castToMaybeTypeError(1);78 castToMaybeTypeError(1);
80 comptime castToMaybeTypeError(1);79 comptime castToMaybeTypeError(1);
81}80}
...@@ -84,37 +83,37 @@ const A = struct {...@@ -84,37 +83,37 @@ const A = struct {
84};83};
85fn castToMaybeTypeError(z: i32) void {84fn castToMaybeTypeError(z: i32) void {
86 const x = i32(1);85 const x = i32(1);
87 const y: %?i32 = x;86 const y: error!?i32 = x;
88 assert(??(try y) == 1);87 assert(??(try y) == 1);
8988
90 const f = z;89 const f = z;
91 const g: %?i32 = f;90 const g: error!?i32 = f;
9291
93 const a = A{ .a = z };92 const a = A{ .a = z };
94 const b: %?A = a;93 const b: error!?A = a;
95 assert((??(b catch unreachable)).a == 1);94 assert((??(b catch unreachable)).a == 1);
96}95}
9796
98test "implicitly cast from int to %?T" {97test "implicitly cast from int to error!?T" {
99 implicitIntLitToMaybe();98 implicitIntLitToMaybe();
100 comptime implicitIntLitToMaybe();99 comptime implicitIntLitToMaybe();
101}100}
102fn implicitIntLitToMaybe() void {101fn implicitIntLitToMaybe() void {
103 const f: ?i32 = 1;102 const f: ?i32 = 1;
104 const g: %?i32 = 1;103 const g: error!?i32 = 1;
105}104}
106105
107106
108test "return null from fn() %?&T" {107test "return null from fn() error!?&T" {
109 const a = returnNullFromMaybeTypeErrorRef();108 const a = returnNullFromMaybeTypeErrorRef();
110 const b = returnNullLitFromMaybeTypeErrorRef();109 const b = returnNullLitFromMaybeTypeErrorRef();
111 assert((try a) == null and (try b) == null);110 assert((try a) == null and (try b) == null);
112}111}
113fn returnNullFromMaybeTypeErrorRef() %?&A {112fn returnNullFromMaybeTypeErrorRef() error!?&A {
114 const a: ?&A = null;113 const a: ?&A = null;
115 return a;114 return a;
116}115}
117fn returnNullLitFromMaybeTypeErrorRef() %?&A {116fn returnNullLitFromMaybeTypeErrorRef() error!?&A {
118 return null;117 return null;
119}118}
120119
...@@ -161,7 +160,7 @@ fn castToMaybeSlice() ?[]const u8 {...@@ -161,7 +160,7 @@ fn castToMaybeSlice() ?[]const u8 {
161}160}
162161
163162
164test "implicitly cast from [0]T to %[]T" {163test "implicitly cast from [0]T to error![]T" {
165 testCastZeroArrayToErrSliceMut();164 testCastZeroArrayToErrSliceMut();
166 comptime testCastZeroArrayToErrSliceMut();165 comptime testCastZeroArrayToErrSliceMut();
167}166}
...@@ -170,11 +169,11 @@ fn testCastZeroArrayToErrSliceMut() void {...@@ -170,11 +169,11 @@ fn testCastZeroArrayToErrSliceMut() void {
170 assert((gimmeErrOrSlice() catch unreachable).len == 0);169 assert((gimmeErrOrSlice() catch unreachable).len == 0);
171}170}
172171
173fn gimmeErrOrSlice() %[]u8 {172fn gimmeErrOrSlice() error![]u8 {
174 return []u8{};173 return []u8{};
175}174}
176175
177test "peer type resolution: [0]u8, []const u8, and %[]u8" {176test "peer type resolution: [0]u8, []const u8, and error![]u8" {
178 {177 {
179 var data = "hi";178 var data = "hi";
180 const slice = data[0..];179 const slice = data[0..];
...@@ -188,7 +187,7 @@ test "peer type resolution: [0]u8, []const u8, and %[]u8" {...@@ -188,7 +187,7 @@ test "peer type resolution: [0]u8, []const u8, and %[]u8" {
188 assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);187 assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
189 }188 }
190}189}
191fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) %[]u8 {190fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) error![]u8 {
192 if (a) {191 if (a) {
193 return []u8{};192 return []u8{};
194 }193 }
...@@ -230,7 +229,7 @@ fn foo(args: ...) void {...@@ -230,7 +229,7 @@ fn foo(args: ...) void {
230229
231230
232test "peer type resolution: error and [N]T" {231test "peer type resolution: error and [N]T" {
233 // TODO: implicit %T to %U where T can implicitly cast to U232 // TODO: implicit error!T to error!U where T can implicitly cast to U
234 //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));233 //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
235 //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));234 //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
236235
...@@ -238,14 +237,13 @@ test "peer type resolution: error and [N]T" {...@@ -238,14 +237,13 @@ test "peer type resolution: error and [N]T" {
238 comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK"));237 comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK"));
239}238}
240239
241error BadValue;240//fn testPeerErrorAndArray(x: u8) error![]const u8 {
242//fn testPeerErrorAndArray(x: u8) %[]const u8 {
243// return switch (x) {241// return switch (x) {
244// 0x00 => "OK",242// 0x00 => "OK",
245// else => error.BadValue,243// else => error.BadValue,
246// };244// };
247//}245//}
248fn testPeerErrorAndArray2(x: u8) %[]const u8 {246fn testPeerErrorAndArray2(x: u8) error![]const u8 {
249 return switch (x) {247 return switch (x) {
250 0x00 => "OK",248 0x00 => "OK",
251 0x01 => "OKK",249 0x01 => "OKK",
test/cases/defer.zig+1-3
...@@ -3,9 +3,7 @@ const assert = @import("std").debug.assert;...@@ -3,9 +3,7 @@ const assert = @import("std").debug.assert;
3var result: [3]u8 = undefined;3var result: [3]u8 = undefined;
4var index: usize = undefined;4var index: usize = undefined;
55
6error FalseNotAllowed;6fn runSomeErrorDefers(x: bool) !bool {
7
8fn runSomeErrorDefers(x: bool) %bool {
9 index = 0;7 index = 0;
10 defer {result[index] = 'a'; index += 1;}8 defer {result[index] = 'a'; index += 1;}
11 errdefer {result[index] = 'b'; index += 1;}9 errdefer {result[index] = 'b'; index += 1;}
test/cases/enum_with_members.zig+1-1
...@@ -6,7 +6,7 @@ const ET = union(enum) {...@@ -6,7 +6,7 @@ const ET = union(enum) {
6 SINT: i32,6 SINT: i32,
7 UINT: u32,7 UINT: u32,
88
9 pub fn print(a: &const ET, buf: []u8) %usize {9 pub fn print(a: &const ET, buf: []u8) error!usize {
10 return switch (*a) {10 return switch (*a) {
11 ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),11 ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
12 ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),12 ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
test/cases/error.zig+110-18
...@@ -1,16 +1,18 @@...@@ -1,16 +1,18 @@
1const assert = @import("std").debug.assert;1const std = @import("std");
2const mem = @import("std").mem;2const assert = std.debug.assert;
3const mem = std.mem;
4const builtin = @import("builtin");
35
4pub fn foo() %i32 {6pub fn foo() error!i32 {
5 const x = try bar();7 const x = try bar();
6 return x + 1;8 return x + 1;
7}9}
810
9pub fn bar() %i32 {11pub fn bar() error!i32 {
10 return 13;12 return 13;
11}13}
1214
13pub fn baz() %i32 {15pub fn baz() error!i32 {
14 const y = foo() catch 1234;16 const y = foo() catch 1234;
15 return y + 1;17 return y + 1;
16}18}
...@@ -19,7 +21,6 @@ test "error wrapping" {...@@ -19,7 +21,6 @@ test "error wrapping" {
19 assert((baz() catch unreachable) == 15);21 assert((baz() catch unreachable) == 15);
20}22}
2123
22error ItBroke;
23fn gimmeItBroke() []const u8 {24fn gimmeItBroke() []const u8 {
24 return @errorName(error.ItBroke);25 return @errorName(error.ItBroke);
25}26}
...@@ -28,8 +29,6 @@ test "@errorName" {...@@ -28,8 +29,6 @@ test "@errorName" {
28 assert(mem.eql(u8, @errorName(error.AnError), "AnError"));29 assert(mem.eql(u8, @errorName(error.AnError), "AnError"));
29 assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));30 assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
30}31}
31error AnError;
32error ALongerErrorName;
3332
3433
35test "error values" {34test "error values" {
...@@ -37,16 +36,11 @@ test "error values" {...@@ -37,16 +36,11 @@ test "error values" {
37 const b = i32(error.err2);36 const b = i32(error.err2);
38 assert(a != b);37 assert(a != b);
39}38}
40error err1;
41error err2;
4239
4340
44test "redefinition of error values allowed" {41test "redefinition of error values allowed" {
45 shouldBeNotEqual(error.AnError, error.SecondError);42 shouldBeNotEqual(error.AnError, error.SecondError);
46}43}
47error AnError;
48error AnError;
49error SecondError;
50fn shouldBeNotEqual(a: error, b: error) void {44fn shouldBeNotEqual(a: error, b: error) void {
51 if (a == b) unreachable;45 if (a == b) unreachable;
52}46}
...@@ -58,8 +52,7 @@ test "error binary operator" {...@@ -58,8 +52,7 @@ test "error binary operator" {
58 assert(a == 3);52 assert(a == 3);
59 assert(b == 10);53 assert(b == 10);
60}54}
61error ItBroke;55fn errBinaryOperatorG(x: bool) error!isize {
62fn errBinaryOperatorG(x: bool) %isize {
63 return if (x) error.ItBroke else isize(10);56 return if (x) error.ItBroke else isize(10);
64}57}
6558
...@@ -68,18 +61,117 @@ test "unwrap simple value from error" {...@@ -68,18 +61,117 @@ test "unwrap simple value from error" {
68 const i = unwrapSimpleValueFromErrorDo() catch unreachable;61 const i = unwrapSimpleValueFromErrorDo() catch unreachable;
69 assert(i == 13);62 assert(i == 13);
70}63}
71fn unwrapSimpleValueFromErrorDo() %isize { return 13; }64fn unwrapSimpleValueFromErrorDo() error!isize { return 13; }
7265
7366
74test "error return in assignment" {67test "error return in assignment" {
75 doErrReturnInAssignment() catch unreachable;68 doErrReturnInAssignment() catch unreachable;
76}69}
7770
78fn doErrReturnInAssignment() %void {71fn doErrReturnInAssignment() error!void {
79 var x : i32 = undefined;72 var x : i32 = undefined;
80 x = try makeANonErr();73 x = try makeANonErr();
81}74}
8275
83fn makeANonErr() %i32 {76fn makeANonErr() error!i32 {
84 return 1;77 return 1;
85}78}
79
80test "error union type " {
81 testErrorUnionType();
82 comptime testErrorUnionType();
83}
84
85fn testErrorUnionType() void {
86 const x: error!i32 = 1234;
87 if (x) |value| assert(value == 1234) else |_| unreachable;
88 assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion);
89 assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet);
90 assert(@typeOf(x).ErrorSet == error);
91}
92
93test "error set type " {
94 testErrorSetType();
95 comptime testErrorSetType();
96}
97
98const MyErrSet = error {OutOfMemory, FileNotFound};
99
100fn testErrorSetType() void {
101 assert(@memberCount(MyErrSet) == 2);
102
103 const a: MyErrSet!i32 = 5678;
104 const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
105
106 if (a) |value| assert(value == 5678) else |err| switch (err) {
107 error.OutOfMemory => unreachable,
108 error.FileNotFound => unreachable,
109 }
110}
111
112
113test "explicit error set cast" {
114 testExplicitErrorSetCast(Set1.A);
115 comptime testExplicitErrorSetCast(Set1.A);
116}
117
118const Set1 = error{A, B};
119const Set2 = error{A, C};
120
121fn testExplicitErrorSetCast(set1: Set1) void {
122 var x = Set2(set1);
123 var y = Set1(x);
124 assert(y == error.A);
125}
126
127test "comptime test error for empty error set" {
128 testComptimeTestErrorEmptySet(1234);
129 comptime testComptimeTestErrorEmptySet(1234);
130}
131
132const EmptyErrorSet = error {};
133
134fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void {
135 if (x) |v| assert(v == 1234) else |err| @compileError("bad");
136}
137
138test "syntax: nullable operator in front of error union operator" {
139 comptime {
140 assert(?error!i32 == ?(error!i32));
141 }
142}
143
144test "comptime err to int of error set with only 1 possible value" {
145 testErrToIntWithOnePossibleValue(error.A, u32(error.A));
146 comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A));
147}
148fn testErrToIntWithOnePossibleValue(x: error{A}, comptime value: u32) void {
149 if (u32(x) != value) {
150 @compileError("bad");
151 }
152}
153
154test "error union peer type resolution" {
155 testErrorUnionPeerTypeResolution(1);
156 comptime testErrorUnionPeerTypeResolution(1);
157}
158
159fn testErrorUnionPeerTypeResolution(x: i32) void {
160 const y = switch (x) {
161 1 => bar_1(),
162 2 => baz_1(),
163 else => quux_1(),
164 };
165}
166
167fn bar_1() error {
168 return error.A;
169}
170
171fn baz_1() !i32 {
172 return error.B;
173}
174
175fn quux_1() !i32 {
176 return error.C;
177}
test/cases/ir_block_deps.zig+2-4
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
22
3fn foo(id: u64) %i32 {3fn foo(id: u64) !i32 {
4 return switch (id) {4 return switch (id) {
5 1 => getErrInt(),5 1 => getErrInt(),
6 2 => {6 2 => {
...@@ -11,9 +11,7 @@ fn foo(id: u64) %i32 {...@@ -11,9 +11,7 @@ fn foo(id: u64) %i32 {
11 };11 };
12}12}
1313
14fn getErrInt() %i32 { return 0; }14fn getErrInt() error!i32 { return 0; }
15
16error ItBroke;
1715
18test "ir block deps" {16test "ir block deps" {
19 assert((foo(1) catch unreachable) == 0);17 assert((foo(1) catch unreachable) == 0);
test/cases/misc.zig+4-4
...@@ -262,7 +262,7 @@ test "generic malloc free" {...@@ -262,7 +262,7 @@ test "generic malloc free" {
262 memFree(u8, a);262 memFree(u8, a);
263}263}
264const some_mem : [100]u8 = undefined;264const some_mem : [100]u8 = undefined;
265fn memAlloc(comptime T: type, n: usize) %[]T {265fn memAlloc(comptime T: type, n: usize) error![]T {
266 return @ptrCast(&T, &some_mem[0])[0..n];266 return @ptrCast(&T, &some_mem[0])[0..n];
267}267}
268fn memFree(comptime T: type, memory: []T) void { }268fn memFree(comptime T: type, memory: []T) void { }
...@@ -419,7 +419,7 @@ test "cast slice to u8 slice" {...@@ -419,7 +419,7 @@ test "cast slice to u8 slice" {
419test "pointer to void return type" {419test "pointer to void return type" {
420 testPointerToVoidReturnType() catch unreachable;420 testPointerToVoidReturnType() catch unreachable;
421}421}
422fn testPointerToVoidReturnType() %void {422fn testPointerToVoidReturnType() error!void {
423 const a = testPointerToVoidReturnType2();423 const a = testPointerToVoidReturnType2();
424 return *a;424 return *a;
425}425}
...@@ -475,8 +475,8 @@ test "@typeId" {...@@ -475,8 +475,8 @@ test "@typeId" {
475 assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral);475 assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral);
476 assert(@typeId(@typeOf(null)) == Tid.NullLiteral);476 assert(@typeId(@typeOf(null)) == Tid.NullLiteral);
477 assert(@typeId(?i32) == Tid.Nullable);477 assert(@typeId(?i32) == Tid.Nullable);
478 assert(@typeId(%i32) == Tid.ErrorUnion);478 assert(@typeId(error!i32) == Tid.ErrorUnion);
479 assert(@typeId(error) == Tid.Error);479 assert(@typeId(error) == Tid.ErrorSet);
480 assert(@typeId(AnEnum) == Tid.Enum);480 assert(@typeId(AnEnum) == Tid.Enum);
481 assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum);481 assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum);
482 assert(@typeId(AUnionEnum) == Tid.Union);482 assert(@typeId(AUnionEnum) == Tid.Union);
test/cases/reflection.zig+1-1
...@@ -5,7 +5,7 @@ test "reflection: array, pointer, nullable, error union type child" {...@@ -5,7 +5,7 @@ test "reflection: array, pointer, nullable, error union type child" {
5 comptime {5 comptime {
6 assert(([10]u8).Child == u8);6 assert(([10]u8).Child == u8);
7 assert((&u8).Child == u8);7 assert((&u8).Child == u8);
8 assert((%u8).Child == u8);8 assert((error!u8).Payload == u8);
9 assert((?u8).Child == u8);9 assert((?u8).Child == u8);
10 }10 }
11}11}
test/cases/switch.zig+1-1
...@@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 {...@@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 {
225 return 10;225 return 10;
226}226}
227227
228fn return_a_number() %i32 {228fn return_a_number() error!i32 {
229 return 1;229 return 1;
230}230}
231231
test/cases/switch_prong_err_enum.zig+2-4
...@@ -2,19 +2,17 @@ const assert = @import("std").debug.assert;...@@ -2,19 +2,17 @@ const assert = @import("std").debug.assert;
22
3var read_count: u64 = 0;3var read_count: u64 = 0;
44
5fn readOnce() %u64 {5fn readOnce() error!u64 {
6 read_count += 1;6 read_count += 1;
7 return read_count;7 return read_count;
8}8}
99
10error InvalidDebugInfo;
11
12const FormValue = union(enum) {10const FormValue = union(enum) {
13 Address: u64,11 Address: u64,
14 Other: bool,12 Other: bool,
15};13};
1614
17fn doThing(form_id: u64) %FormValue {15fn doThing(form_id: u64) error!FormValue {
18 return switch (form_id) {16 return switch (form_id) {
19 17 => FormValue { .Address = try readOnce() },17 17 => FormValue { .Address = try readOnce() },
20 else => error.InvalidDebugInfo,18 else => error.InvalidDebugInfo,
test/cases/switch_prong_implicit_cast.zig+1-3
...@@ -5,9 +5,7 @@ const FormValue = union(enum) {...@@ -5,9 +5,7 @@ const FormValue = union(enum) {
5 Two: bool,5 Two: bool,
6};6};
77
8error Whatever;8fn foo(id: u64) !FormValue {
9
10fn foo(id: u64) %FormValue {
11 return switch (id) {9 return switch (id) {
12 2 => FormValue { .Two = true },10 2 => FormValue { .Two = true },
13 1 => FormValue { .One = {} },11 1 => FormValue { .One = {} },
test/cases/try.zig+2-5
...@@ -17,10 +17,7 @@ fn tryOnErrorUnionImpl() void {...@@ -17,10 +17,7 @@ fn tryOnErrorUnionImpl() void {
17 assert(x == 11);17 assert(x == 11);
18}18}
1919
20error ItBroke;20fn returnsTen() error!i32 {
21error NoMem;
22error CrappedOut;
23fn returnsTen() %i32 {
24 return 10;21 return 10;
25}22}
2623
...@@ -32,7 +29,7 @@ test "try without vars" {...@@ -32,7 +29,7 @@ test "try without vars" {
32 assert(result2 == 1);29 assert(result2 == 1);
33}30}
3431
35fn failIfTrue(ok: bool) %void {32fn failIfTrue(ok: bool) error!void {
36 if (ok) {33 if (ok) {
37 return error.ItBroke;34 return error.ItBroke;
38 } else {35 } else {
test/cases/union.zig+1-1
...@@ -13,7 +13,7 @@ const Agg = struct {...@@ -13,7 +13,7 @@ const Agg = struct {
13const v1 = Value { .Int = 1234 };13const v1 = Value { .Int = 1234 };
14const v2 = Value { .Array = []u8{3} ** 9 };14const v2 = Value { .Array = []u8{3} ** 9 };
1515
16const err = (%Agg)(Agg {16const err = (error!Agg)(Agg {
17 .val1 = v1,17 .val1 = v1,
18 .val2 = v2,18 .val2 = v2,
19});19});
test/cases/while.zig+4-6
...@@ -50,7 +50,7 @@ fn runContinueAndBreakTest() void {...@@ -50,7 +50,7 @@ fn runContinueAndBreakTest() void {
50test "return with implicit cast from while loop" {50test "return with implicit cast from while loop" {
51 returnWithImplicitCastFromWhileLoopTest() catch unreachable;51 returnWithImplicitCastFromWhileLoopTest() catch unreachable;
52}52}
53fn returnWithImplicitCastFromWhileLoopTest() %void {53fn returnWithImplicitCastFromWhileLoopTest() error!void {
54 while (true) {54 while (true) {
55 return;55 return;
56 }56 }
...@@ -116,8 +116,7 @@ test "while with error union condition" {...@@ -116,8 +116,7 @@ test "while with error union condition" {
116}116}
117117
118var numbers_left: i32 = undefined;118var numbers_left: i32 = undefined;
119error OutOfNumbers;119fn getNumberOrErr() error!i32 {
120fn getNumberOrErr() %i32 {
121 return if (numbers_left == 0)120 return if (numbers_left == 0)
122 error.OutOfNumbers121 error.OutOfNumbers
123 else x: {122 else x: {
...@@ -205,8 +204,7 @@ fn testContinueOuter() void {...@@ -205,8 +204,7 @@ fn testContinueOuter() void {
205204
206fn returnNull() ?i32 { return null; }205fn returnNull() ?i32 { return null; }
207fn returnMaybe(x: i32) ?i32 { return x; }206fn returnMaybe(x: i32) ?i32 { return x; }
208error YouWantedAnError;207fn returnError() error!i32 { return error.YouWantedAnError; }
209fn returnError() %i32 { return error.YouWantedAnError; }208fn returnSuccess(x: i32) error!i32 { return x; }
210fn returnSuccess(x: i32) %i32 { return x; }
211fn returnFalse() bool { return false; }209fn returnFalse() bool { return false; }
212fn returnTrue() bool { return true; }210fn returnTrue() bool { return true; }
test/compare_output.zig+21-20
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1const os = @import("std").os;1const builtin = @import("builtin");
2const std = @import("std");
3const os = std.os;
2const tests = @import("tests.zig");4const tests = @import("tests.zig");
35
4pub fn addCases(cases: &tests.CompareOutputContext) void {6pub fn addCases(cases: &tests.CompareOutputContext) void {
...@@ -8,14 +10,14 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -8,14 +10,14 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
8 \\ _ = c.puts(c"Hello, world!");10 \\ _ = c.puts(c"Hello, world!");
9 \\ return 0;11 \\ return 0;
10 \\}12 \\}
11 , "Hello, world!" ++ os.line_sep);13 , "Hello, world!" ++ std.cstr.line_sep);
1214
13 cases.addCase(x: {15 cases.addCase(x: {
14 var tc = cases.create("multiple files with private function",16 var tc = cases.create("multiple files with private function",
15 \\use @import("std").io;17 \\use @import("std").io;
16 \\use @import("foo.zig");18 \\use @import("foo.zig");
17 \\19 \\
18 \\pub fn main() %void {20 \\pub fn main() void {
19 \\ privateFunction();21 \\ privateFunction();
20 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);22 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
21 \\ stdout.print("OK 2\n") catch unreachable;23 \\ stdout.print("OK 2\n") catch unreachable;
...@@ -49,7 +51,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -49,7 +51,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
49 \\use @import("foo.zig");51 \\use @import("foo.zig");
50 \\use @import("bar.zig");52 \\use @import("bar.zig");
51 \\53 \\
52 \\pub fn main() %void {54 \\pub fn main() void {
53 \\ foo_function();55 \\ foo_function();
54 \\ bar_function();56 \\ bar_function();
55 \\}57 \\}
...@@ -89,7 +91,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -89,7 +91,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
89 var tc = cases.create("two files use import each other",91 var tc = cases.create("two files use import each other",
90 \\use @import("a.zig");92 \\use @import("a.zig");
91 \\93 \\
92 \\pub fn main() %void {94 \\pub fn main() void {
93 \\ ok();95 \\ ok();
94 \\}96 \\}
95 , "OK\n");97 , "OK\n");
...@@ -118,7 +120,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -118,7 +120,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
118 cases.add("hello world without libc",120 cases.add("hello world without libc",
119 \\const io = @import("std").io;121 \\const io = @import("std").io;
120 \\122 \\
121 \\pub fn main() %void {123 \\pub fn main() void {
122 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);124 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
123 \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;125 \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
124 \\}126 \\}
...@@ -268,7 +270,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -268,7 +270,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
268 \\const z = io.stdin_fileno;270 \\const z = io.stdin_fileno;
269 \\const x : @typeOf(y) = 1234;271 \\const x : @typeOf(y) = 1234;
270 \\const y : u16 = 5678;272 \\const y : u16 = 5678;
271 \\pub fn main() %void {273 \\pub fn main() void {
272 \\ var x_local : i32 = print_ok(x);274 \\ var x_local : i32 = print_ok(x);
273 \\}275 \\}
274 \\fn print_ok(val: @typeOf(x)) @typeOf(foo) {276 \\fn print_ok(val: @typeOf(x)) @typeOf(foo) {
...@@ -351,7 +353,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -351,7 +353,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
351 \\ fn method(b: &const Bar) bool { return true; }353 \\ fn method(b: &const Bar) bool { return true; }
352 \\};354 \\};
353 \\355 \\
354 \\pub fn main() %void {356 \\pub fn main() void {
355 \\ const bar = Bar {.field2 = 13,};357 \\ const bar = Bar {.field2 = 13,};
356 \\ const foo = Foo {.field1 = bar,};358 \\ const foo = Foo {.field1 = bar,};
357 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);359 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
...@@ -367,7 +369,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -367,7 +369,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
367369
368 cases.add("defer with only fallthrough",370 cases.add("defer with only fallthrough",
369 \\const io = @import("std").io;371 \\const io = @import("std").io;
370 \\pub fn main() %void {372 \\pub fn main() void {
371 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);373 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
372 \\ stdout.print("before\n") catch unreachable;374 \\ stdout.print("before\n") catch unreachable;
373 \\ defer stdout.print("defer1\n") catch unreachable;375 \\ defer stdout.print("defer1\n") catch unreachable;
...@@ -380,7 +382,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -380,7 +382,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
380 cases.add("defer with return",382 cases.add("defer with return",
381 \\const io = @import("std").io;383 \\const io = @import("std").io;
382 \\const os = @import("std").os;384 \\const os = @import("std").os;
383 \\pub fn main() %void {385 \\pub fn main() void {
384 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);386 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
385 \\ stdout.print("before\n") catch unreachable;387 \\ stdout.print("before\n") catch unreachable;
386 \\ defer stdout.print("defer1\n") catch unreachable;388 \\ defer stdout.print("defer1\n") catch unreachable;
...@@ -394,10 +396,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -394,10 +396,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
394396
395 cases.add("errdefer and it fails",397 cases.add("errdefer and it fails",
396 \\const io = @import("std").io;398 \\const io = @import("std").io;
397 \\pub fn main() %void {399 \\pub fn main() void {
398 \\ do_test() catch return;400 \\ do_test() catch return;
399 \\}401 \\}
400 \\fn do_test() %void {402 \\fn do_test() !void {
401 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);403 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
402 \\ stdout.print("before\n") catch unreachable;404 \\ stdout.print("before\n") catch unreachable;
403 \\ defer stdout.print("defer1\n") catch unreachable;405 \\ defer stdout.print("defer1\n") catch unreachable;
...@@ -406,18 +408,17 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -406,18 +408,17 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
406 \\ defer stdout.print("defer3\n") catch unreachable;408 \\ defer stdout.print("defer3\n") catch unreachable;
407 \\ stdout.print("after\n") catch unreachable;409 \\ stdout.print("after\n") catch unreachable;
408 \\}410 \\}
409 \\error IToldYouItWouldFail;411 \\fn its_gonna_fail() !void {
410 \\fn its_gonna_fail() %void {
411 \\ return error.IToldYouItWouldFail;412 \\ return error.IToldYouItWouldFail;
412 \\}413 \\}
413 , "before\ndeferErr\ndefer1\n");414 , "before\ndeferErr\ndefer1\n");
414415
415 cases.add("errdefer and it passes",416 cases.add("errdefer and it passes",
416 \\const io = @import("std").io;417 \\const io = @import("std").io;
417 \\pub fn main() %void {418 \\pub fn main() void {
418 \\ do_test() catch return;419 \\ do_test() catch return;
419 \\}420 \\}
420 \\fn do_test() %void {421 \\fn do_test() !void {
421 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);422 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
422 \\ stdout.print("before\n") catch unreachable;423 \\ stdout.print("before\n") catch unreachable;
423 \\ defer stdout.print("defer1\n") catch unreachable;424 \\ defer stdout.print("defer1\n") catch unreachable;
...@@ -426,7 +427,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -426,7 +427,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
426 \\ defer stdout.print("defer3\n") catch unreachable;427 \\ defer stdout.print("defer3\n") catch unreachable;
427 \\ stdout.print("after\n") catch unreachable;428 \\ stdout.print("after\n") catch unreachable;
428 \\}429 \\}
429 \\fn its_gonna_pass() %void { }430 \\fn its_gonna_pass() error!void { }
430 , "before\nafter\ndefer3\ndefer1\n");431 , "before\nafter\ndefer3\ndefer1\n");
431432
432 cases.addCase(x: {433 cases.addCase(x: {
...@@ -434,7 +435,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -434,7 +435,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
434 \\const foo_txt = @embedFile("foo.txt");435 \\const foo_txt = @embedFile("foo.txt");
435 \\const io = @import("std").io;436 \\const io = @import("std").io;
436 \\437 \\
437 \\pub fn main() %void {438 \\pub fn main() void {
438 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);439 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
439 \\ stdout.print(foo_txt) catch unreachable;440 \\ stdout.print(foo_txt) catch unreachable;
440 \\}441 \\}
...@@ -452,7 +453,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -452,7 +453,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
452 \\const os = std.os;453 \\const os = std.os;
453 \\const allocator = std.debug.global_allocator;454 \\const allocator = std.debug.global_allocator;
454 \\455 \\
455 \\pub fn main() %void {456 \\pub fn main() !void {
456 \\ var args_it = os.args();457 \\ var args_it = os.args();
457 \\ var stdout_file = try io.getStdOut();458 \\ var stdout_file = try io.getStdOut();
458 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);459 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
...@@ -493,7 +494,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -493,7 +494,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
493 \\const os = std.os;494 \\const os = std.os;
494 \\const allocator = std.debug.global_allocator;495 \\const allocator = std.debug.global_allocator;
495 \\496 \\
496 \\pub fn main() %void {497 \\pub fn main() !void {
497 \\ var args_it = os.args();498 \\ var args_it = os.args();
498 \\ var stdout_file = try io.getStdOut();499 \\ var stdout_file = try io.getStdOut();
499 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);500 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
test/compile_errors.zig+231-18
...@@ -1,6 +1,220 @@...@@ -1,6 +1,220 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) void {3pub fn addCases(cases: &tests.CompileErrorContext) void {
4 cases.add("no else prong on switch on global error set",
5 \\export fn entry() void {
6 \\ foo(error.A);
7 \\}
8 \\fn foo(a: error) void {
9 \\ switch (a) {
10 \\ error.A => {},
11 \\ }
12 \\}
13 ,
14 ".tmp_source.zig:5:5: error: else prong required when switching on type 'error'");
15
16 cases.add("inferred error set with no returned error",
17 \\export fn entry() void {
18 \\ foo() catch unreachable;
19 \\}
20 \\fn foo() !void {
21 \\}
22 ,
23 ".tmp_source.zig:4:11: error: function with inferred error set must return at least one possible error");
24
25 cases.add("error not handled in switch",
26 \\export fn entry() void {
27 \\ foo(452) catch |err| switch (err) {
28 \\ error.Foo => {},
29 \\ };
30 \\}
31 \\fn foo(x: i32) !void {
32 \\ switch (x) {
33 \\ 0 ... 10 => return error.Foo,
34 \\ 11 ... 20 => return error.Bar,
35 \\ 21 ... 30 => return error.Baz,
36 \\ else => {},
37 \\ }
38 \\}
39 ,
40 ".tmp_source.zig:2:26: error: error.Baz not handled in switch",
41 ".tmp_source.zig:2:26: error: error.Bar not handled in switch");
42
43 cases.add("duplicate error in switch",
44 \\export fn entry() void {
45 \\ foo(452) catch |err| switch (err) {
46 \\ error.Foo => {},
47 \\ error.Bar => {},
48 \\ error.Foo => {},
49 \\ else => {},
50 \\ };
51 \\}
52 \\fn foo(x: i32) !void {
53 \\ switch (x) {
54 \\ 0 ... 10 => return error.Foo,
55 \\ 11 ... 20 => return error.Bar,
56 \\ else => {},
57 \\ }
58 \\}
59 ,
60 ".tmp_source.zig:5:14: error: duplicate switch value: '@typeOf(foo).ReturnType.ErrorSet.Foo'",
61 ".tmp_source.zig:3:14: note: other value is here");
62
63 cases.add("range operator in switch used on error set",
64 \\export fn entry() void {
65 \\ try foo(452) catch |err| switch (err) {
66 \\ error.A ... error.B => {},
67 \\ else => {},
68 \\ };
69 \\}
70 \\fn foo(x: i32) !void {
71 \\ switch (x) {
72 \\ 0 ... 10 => return error.Foo,
73 \\ 11 ... 20 => return error.Bar,
74 \\ else => {},
75 \\ }
76 \\}
77 ,
78 ".tmp_source.zig:3:17: error: operator not allowed for errors");
79
80 cases.add("inferring error set of function pointer",
81 \\comptime {
82 \\ const z: ?fn()!void = null;
83 \\}
84 ,
85 ".tmp_source.zig:2:15: error: inferring error set of return type valid only for function definitions");
86
87 cases.add("access non-existent member of error set",
88 \\const Foo = error{A};
89 \\comptime {
90 \\ const z = Foo.Bar;
91 \\}
92 ,
93 ".tmp_source.zig:3:18: error: no error named 'Bar' in 'Foo'");
94
95 cases.add("error union operator with non error set LHS",
96 \\comptime {
97 \\ const z = i32!i32;
98 \\}
99 ,
100 ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'");
101
102 cases.add("error equality but sets have no common members",
103 \\const Set1 = error{A, C};
104 \\const Set2 = error{B, D};
105 \\export fn entry() void {
106 \\ foo(Set1.A);
107 \\}
108 \\fn foo(x: Set1) void {
109 \\ if (x == Set2.B) {
110 \\
111 \\ }
112 \\}
113 ,
114 ".tmp_source.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors");
115
116 cases.add("only equality binary operator allowed for error sets",
117 \\comptime {
118 \\ const z = error.A > error.B;
119 \\}
120 ,
121 ".tmp_source.zig:2:23: error: operator not allowed for errors");
122
123 cases.add("explicit error set cast known at comptime violates error sets",
124 \\const Set1 = error {A, B};
125 \\const Set2 = error {A, C};
126 \\comptime {
127 \\ var x = Set1.B;
128 \\ var y = Set2(x);
129 \\}
130 ,
131 ".tmp_source.zig:5:17: error: error.B not a member of error set 'Set2'");
132
133 cases.add("cast error union of global error set to error union of smaller error set",
134 \\const SmallErrorSet = error{A};
135 \\export fn entry() void {
136 \\ var x: SmallErrorSet!i32 = foo();
137 \\}
138 \\fn foo() error!i32 {
139 \\ return error.B;
140 \\}
141 ,
142 ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32', found 'error!i32'",
143 ".tmp_source.zig:3:35: note: unable to cast global error set into smaller set");
144
145 cases.add("cast global error set to error set",
146 \\const SmallErrorSet = error{A};
147 \\export fn entry() void {
148 \\ var x: SmallErrorSet = foo();
149 \\}
150 \\fn foo() error {
151 \\ return error.B;
152 \\}
153 ,
154 ".tmp_source.zig:3:31: error: expected 'SmallErrorSet', found 'error'",
155 ".tmp_source.zig:3:31: note: unable to cast global error set into smaller set");
156
157 cases.add("recursive inferred error set",
158 \\export fn entry() void {
159 \\ foo() catch unreachable;
160 \\}
161 \\fn foo() !void {
162 \\ try foo();
163 \\}
164 ,
165 ".tmp_source.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet");
166
167 cases.add("implicit cast of error set not a subset",
168 \\const Set1 = error{A, B};
169 \\const Set2 = error{A, C};
170 \\export fn entry() void {
171 \\ foo(Set1.B);
172 \\}
173 \\fn foo(set1: Set1) void {
174 \\ var x: Set2 = set1;
175 \\}
176 ,
177 ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'",
178 ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set");
179
180 cases.add("int to err global invalid number",
181 \\const Set1 = error{A, B};
182 \\comptime {
183 \\ var x: usize = 3;
184 \\ var y = error(x);
185 \\}
186 ,
187 ".tmp_source.zig:4:18: error: integer value 3 represents no error");
188
189 cases.add("int to err non global invalid number",
190 \\const Set1 = error{A, B};
191 \\const Set2 = error{A, C};
192 \\comptime {
193 \\ var x = usize(Set1.B);
194 \\ var y = Set2(x);
195 \\}
196 ,
197 ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'");
198
199 cases.add("@memberCount of error",
200 \\comptime {
201 \\ _ = @memberCount(error);
202 \\}
203 ,
204 ".tmp_source.zig:2:9: error: global error set member count not available at comptime");
205
206 cases.add("duplicate error value in error set",
207 \\const Foo = error {
208 \\ Bar,
209 \\ Bar,
210 \\};
211 \\export fn entry() void {
212 \\ const a: Foo = undefined;
213 \\}
214 ,
215 ".tmp_source.zig:3:5: error: duplicate error: 'Bar'",
216 ".tmp_source.zig:2:5: note: other error here");
217
4 cases.add("cast negative integer literal to usize",218 cases.add("cast negative integer literal to usize",
5 \\export fn entry() void {219 \\export fn entry() void {
6 \\ const x = usize(-10);220 \\ const x = usize(-10);
...@@ -112,12 +326,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -112,12 +326,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
112326
113 cases.add("wrong return type for main",327 cases.add("wrong return type for main",
114 \\pub fn main() f32 { }328 \\pub fn main() f32 { }
115 , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");329 , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
116330
117 cases.add("double ?? on main return value",331 cases.add("double ?? on main return value",
118 \\pub fn main() ??void {332 \\pub fn main() ??void {
119 \\}333 \\}
120 , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");334 , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
121335
122 cases.add("bad identifier in function with struct defined inside function which references local const",336 cases.add("bad identifier in function with struct defined inside function which references local const",
123 \\export fn entry() void {337 \\export fn entry() void {
...@@ -1173,7 +1387,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1173,7 +1387,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1173 \\export fn f() void {1387 \\export fn f() void {
1174 \\ try something();1388 \\ try something();
1175 \\}1389 \\}
1176 \\fn something() %void { }1390 \\fn something() error!void { }
1177 ,1391 ,
1178 ".tmp_source.zig:2:5: error: expected type 'void', found 'error'");1392 ".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
11791393
...@@ -1264,7 +1478,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1264,7 +1478,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1264 , ".tmp_source.zig:3:11: error: cannot assign to constant");1478 , ".tmp_source.zig:3:11: error: cannot assign to constant");
12651479
1266 cases.add("main function with bogus args type",1480 cases.add("main function with bogus args type",
1267 \\pub fn main(args: [][]bogus) %void {}1481 \\pub fn main(args: [][]bogus) !void {}
1268 , ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'");1482 , ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'");
12691483
1270 cases.add("for loop missing element param",1484 cases.add("for loop missing element param",
...@@ -1396,7 +1610,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1396,7 +1610,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1396 , ".tmp_source.zig:6:13: error: cannot assign to constant");1610 , ".tmp_source.zig:6:13: error: cannot assign to constant");
13971611
1398 cases.add("return from defer expression",1612 cases.add("return from defer expression",
1399 \\pub fn testTrickyDefer() %void {1613 \\pub fn testTrickyDefer() !void {
1400 \\ defer canFail() catch {};1614 \\ defer canFail() catch {};
1401 \\1615 \\
1402 \\ defer try canFail();1616 \\ defer try canFail();
...@@ -1404,7 +1618,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1404,7 +1618,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1404 \\ const a = maybeInt() ?? return;1618 \\ const a = maybeInt() ?? return;
1405 \\}1619 \\}
1406 \\1620 \\
1407 \\fn canFail() %void { }1621 \\fn canFail() error!void { }
1408 \\1622 \\
1409 \\pub fn maybeInt() ?i32 {1623 \\pub fn maybeInt() ?i32 {
1410 \\ return 0;1624 \\ return 0;
...@@ -1534,7 +1748,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1534,7 +1748,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1534 \\export fn foo() void {1748 \\export fn foo() void {
1535 \\ bar() catch unreachable;1749 \\ bar() catch unreachable;
1536 \\}1750 \\}
1537 \\fn bar() %i32 { return 0; }1751 \\fn bar() error!i32 { return 0; }
1538 , ".tmp_source.zig:2:11: error: expression value is ignored");1752 , ".tmp_source.zig:2:11: error: expression value is ignored");
15391753
1540 cases.add("ignored statement value",1754 cases.add("ignored statement value",
...@@ -1565,7 +1779,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1565,7 +1779,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1565 \\export fn foo() void {1779 \\export fn foo() void {
1566 \\ defer bar();1780 \\ defer bar();
1567 \\}1781 \\}
1568 \\fn bar() %i32 { return 0; }1782 \\fn bar() error!i32 { return 0; }
1569 , ".tmp_source.zig:2:14: error: expression value is ignored");1783 , ".tmp_source.zig:2:14: error: expression value is ignored");
15701784
1571 cases.add("dereference an array",1785 cases.add("dereference an array",
...@@ -1632,13 +1846,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1632,13 +1846,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1632 , ".tmp_source.zig:2:21: error: expected pointer, found 'usize'");1846 , ".tmp_source.zig:2:21: error: expected pointer, found 'usize'");
16331847
1634 cases.add("too many error values to cast to small integer",1848 cases.add("too many error values to cast to small integer",
1635 \\error A; error B; error C; error D; error E; error F; error G; error H;1849 \\const Error = error { A, B, C, D, E, F, G, H };
1636 \\const u2 = @IntType(false, 2);1850 \\fn foo(e: Error) u2 {
1637 \\fn foo(e: error) u2 {
1638 \\ return u2(e);1851 \\ return u2(e);
1639 \\}1852 \\}
1640 \\export fn entry() usize { return @sizeOf(@typeOf(foo)); }1853 \\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
1641 , ".tmp_source.zig:4:14: error: too many error values to fit in 'u2'");1854 , ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'");
16421855
1643 cases.add("asm at compile time",1856 cases.add("asm at compile time",
1644 \\comptime {1857 \\comptime {
...@@ -1821,9 +2034,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1821,9 +2034,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1821 \\export fn foo() void {2034 \\export fn foo() void {
1822 \\ while (bar()) {}2035 \\ while (bar()) {}
1823 \\}2036 \\}
1824 \\fn bar() %i32 { return 1; }2037 \\fn bar() error!i32 { return 1; }
1825 ,2038 ,
1826 ".tmp_source.zig:2:15: error: expected type 'bool', found '%i32'");2039 ".tmp_source.zig:2:15: error: expected type 'bool', found 'error!i32'");
18272040
1828 cases.add("while expected nullable, got bool",2041 cases.add("while expected nullable, got bool",
1829 \\export fn foo() void {2042 \\export fn foo() void {
...@@ -1837,9 +2050,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1837,9 +2050,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1837 \\export fn foo() void {2050 \\export fn foo() void {
1838 \\ while (bar()) |x| {}2051 \\ while (bar()) |x| {}
1839 \\}2052 \\}
1840 \\fn bar() %i32 { return 1; }2053 \\fn bar() error!i32 { return 1; }
1841 ,2054 ,
1842 ".tmp_source.zig:2:15: error: expected nullable type, found '%i32'");2055 ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'");
18432056
1844 cases.add("while expected error union, got bool",2057 cases.add("while expected error union, got bool",
1845 \\export fn foo() void {2058 \\export fn foo() void {
...@@ -1983,7 +2196,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1983,7 +2196,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1983 \\fn foo1(args: ...) void {}2196 \\fn foo1(args: ...) void {}
1984 \\fn foo2(args: ...) void {}2197 \\fn foo2(args: ...) void {}
1985 \\2198 \\
1986 \\pub fn main() %void {2199 \\pub fn main() !void {
1987 \\ foos[0]();2200 \\ foos[0]();
1988 \\}2201 \\}
1989 ,2202 ,
...@@ -1995,7 +2208,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1995,7 +2208,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1995 \\fn foo1(arg: var) void {}2208 \\fn foo1(arg: var) void {}
1996 \\fn foo2(arg: var) void {}2209 \\fn foo2(arg: var) void {}
1997 \\2210 \\
1998 \\pub fn main() %void {2211 \\pub fn main() !void {
1999 \\ foos[0](true);2212 \\ foos[0](true);
2000 \\}2213 \\}
2001 ,2214 ,
test/runtime_safety.zig+36-38
...@@ -5,7 +5,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -5,7 +5,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
5 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {5 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
6 \\ @import("std").os.exit(126);6 \\ @import("std").os.exit(126);
7 \\}7 \\}
8 \\pub fn main() %void {8 \\pub fn main() void {
9 \\ @panic("oh no");9 \\ @panic("oh no");
10 \\}10 \\}
11 );11 );
...@@ -14,7 +14,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -14,7 +14,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
14 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {14 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
15 \\ @import("std").os.exit(126);15 \\ @import("std").os.exit(126);
16 \\}16 \\}
17 \\pub fn main() %void {17 \\pub fn main() void {
18 \\ const a = []i32{1, 2, 3, 4};18 \\ const a = []i32{1, 2, 3, 4};
19 \\ baz(bar(a));19 \\ baz(bar(a));
20 \\}20 \\}
...@@ -28,8 +28,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -28,8 +28,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
28 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {28 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
29 \\ @import("std").os.exit(126);29 \\ @import("std").os.exit(126);
30 \\}30 \\}
31 \\error Whatever;31 \\pub fn main() !void {
32 \\pub fn main() %void {
33 \\ const x = add(65530, 10);32 \\ const x = add(65530, 10);
34 \\ if (x == 0) return error.Whatever;33 \\ if (x == 0) return error.Whatever;
35 \\}34 \\}
...@@ -42,8 +41,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -42,8 +41,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
42 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {41 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
43 \\ @import("std").os.exit(126);42 \\ @import("std").os.exit(126);
44 \\}43 \\}
45 \\error Whatever;44 \\pub fn main() !void {
46 \\pub fn main() %void {
47 \\ const x = sub(10, 20);45 \\ const x = sub(10, 20);
48 \\ if (x == 0) return error.Whatever;46 \\ if (x == 0) return error.Whatever;
49 \\}47 \\}
...@@ -56,8 +54,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -56,8 +54,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
56 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {54 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
57 \\ @import("std").os.exit(126);55 \\ @import("std").os.exit(126);
58 \\}56 \\}
59 \\error Whatever;57 \\pub fn main() !void {
60 \\pub fn main() %void {
61 \\ const x = mul(300, 6000);58 \\ const x = mul(300, 6000);
62 \\ if (x == 0) return error.Whatever;59 \\ if (x == 0) return error.Whatever;
63 \\}60 \\}
...@@ -70,8 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -70,8 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
70 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {67 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
71 \\ @import("std").os.exit(126);68 \\ @import("std").os.exit(126);
72 \\}69 \\}
73 \\error Whatever;70 \\pub fn main() !void {
74 \\pub fn main() %void {
75 \\ const x = neg(-32768);71 \\ const x = neg(-32768);
76 \\ if (x == 32767) return error.Whatever;72 \\ if (x == 32767) return error.Whatever;
77 \\}73 \\}
...@@ -84,8 +80,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -84,8 +80,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
84 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {80 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
85 \\ @import("std").os.exit(126);81 \\ @import("std").os.exit(126);
86 \\}82 \\}
87 \\error Whatever;83 \\pub fn main() !void {
88 \\pub fn main() %void {
89 \\ const x = div(-32768, -1);84 \\ const x = div(-32768, -1);
90 \\ if (x == 32767) return error.Whatever;85 \\ if (x == 32767) return error.Whatever;
91 \\}86 \\}
...@@ -98,8 +93,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -98,8 +93,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
98 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {93 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
99 \\ @import("std").os.exit(126);94 \\ @import("std").os.exit(126);
100 \\}95 \\}
101 \\error Whatever;96 \\pub fn main() !void {
102 \\pub fn main() %void {
103 \\ const x = shl(-16385, 1);97 \\ const x = shl(-16385, 1);
104 \\ if (x == 0) return error.Whatever;98 \\ if (x == 0) return error.Whatever;
105 \\}99 \\}
...@@ -112,8 +106,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -112,8 +106,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
112 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {106 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
113 \\ @import("std").os.exit(126);107 \\ @import("std").os.exit(126);
114 \\}108 \\}
115 \\error Whatever;109 \\pub fn main() !void {
116 \\pub fn main() %void {
117 \\ const x = shl(0b0010111111111111, 3);110 \\ const x = shl(0b0010111111111111, 3);
118 \\ if (x == 0) return error.Whatever;111 \\ if (x == 0) return error.Whatever;
119 \\}112 \\}
...@@ -126,8 +119,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -126,8 +119,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
126 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {119 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
127 \\ @import("std").os.exit(126);120 \\ @import("std").os.exit(126);
128 \\}121 \\}
129 \\error Whatever;122 \\pub fn main() !void {
130 \\pub fn main() %void {
131 \\ const x = shr(-16385, 1);123 \\ const x = shr(-16385, 1);
132 \\ if (x == 0) return error.Whatever;124 \\ if (x == 0) return error.Whatever;
133 \\}125 \\}
...@@ -140,8 +132,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -140,8 +132,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
140 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {132 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
141 \\ @import("std").os.exit(126);133 \\ @import("std").os.exit(126);
142 \\}134 \\}
143 \\error Whatever;135 \\pub fn main() !void {
144 \\pub fn main() %void {
145 \\ const x = shr(0b0010111111111111, 3);136 \\ const x = shr(0b0010111111111111, 3);
146 \\ if (x == 0) return error.Whatever;137 \\ if (x == 0) return error.Whatever;
147 \\}138 \\}
...@@ -154,8 +145,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -154,8 +145,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
154 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {145 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
155 \\ @import("std").os.exit(126);146 \\ @import("std").os.exit(126);
156 \\}147 \\}
157 \\error Whatever;148 \\pub fn main() void {
158 \\pub fn main() %void {
159 \\ const x = div0(999, 0);149 \\ const x = div0(999, 0);
160 \\}150 \\}
161 \\fn div0(a: i32, b: i32) i32 {151 \\fn div0(a: i32, b: i32) i32 {
...@@ -167,8 +157,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -167,8 +157,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
167 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {157 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
168 \\ @import("std").os.exit(126);158 \\ @import("std").os.exit(126);
169 \\}159 \\}
170 \\error Whatever;160 \\pub fn main() !void {
171 \\pub fn main() %void {
172 \\ const x = divExact(10, 3);161 \\ const x = divExact(10, 3);
173 \\ if (x == 0) return error.Whatever;162 \\ if (x == 0) return error.Whatever;
174 \\}163 \\}
...@@ -181,8 +170,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -181,8 +170,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
181 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {170 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
182 \\ @import("std").os.exit(126);171 \\ @import("std").os.exit(126);
183 \\}172 \\}
184 \\error Whatever;173 \\pub fn main() !void {
185 \\pub fn main() %void {
186 \\ const x = widenSlice([]u8{1, 2, 3, 4, 5});174 \\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
187 \\ if (x.len == 0) return error.Whatever;175 \\ if (x.len == 0) return error.Whatever;
188 \\}176 \\}
...@@ -195,8 +183,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -195,8 +183,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
195 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {183 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
196 \\ @import("std").os.exit(126);184 \\ @import("std").os.exit(126);
197 \\}185 \\}
198 \\error Whatever;186 \\pub fn main() !void {
199 \\pub fn main() %void {
200 \\ const x = shorten_cast(200);187 \\ const x = shorten_cast(200);
201 \\ if (x == 0) return error.Whatever;188 \\ if (x == 0) return error.Whatever;
202 \\}189 \\}
...@@ -209,8 +196,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -209,8 +196,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
209 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {196 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
210 \\ @import("std").os.exit(126);197 \\ @import("std").os.exit(126);
211 \\}198 \\}
212 \\error Whatever;199 \\pub fn main() !void {
213 \\pub fn main() %void {
214 \\ const x = unsigned_cast(-10);200 \\ const x = unsigned_cast(-10);
215 \\ if (x == 0) return error.Whatever;201 \\ if (x == 0) return error.Whatever;
216 \\}202 \\}
...@@ -226,20 +212,19 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -226,20 +212,19 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
226 \\ }212 \\ }
227 \\ @import("std").os.exit(0); // test failed213 \\ @import("std").os.exit(0); // test failed
228 \\}214 \\}
229 \\error Whatever;215 \\pub fn main() void {
230 \\pub fn main() %void {
231 \\ bar() catch unreachable;216 \\ bar() catch unreachable;
232 \\}217 \\}
233 \\fn bar() %void {218 \\fn bar() !void {
234 \\ return error.Whatever;219 \\ return error.Whatever;
235 \\}220 \\}
236 );221 );
237222
238 cases.addRuntimeSafety("cast integer to error and no code matches",223 cases.addRuntimeSafety("cast integer to global error and no code matches",
239 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {224 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
240 \\ @import("std").os.exit(126);225 \\ @import("std").os.exit(126);
241 \\}226 \\}
242 \\pub fn main() %void {227 \\pub fn main() void {
243 \\ _ = bar(9999);228 \\ _ = bar(9999);
244 \\}229 \\}
245 \\fn bar(x: u32) error {230 \\fn bar(x: u32) error {
...@@ -247,12 +232,25 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -247,12 +232,25 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
247 \\}232 \\}
248 );233 );
249234
235 cases.addRuntimeSafety("cast integer to non-global error set and no match",
236 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
237 \\ @import("std").os.exit(126);
238 \\}
239 \\const Set1 = error{A, B};
240 \\const Set2 = error{A, C};
241 \\pub fn main() void {
242 \\ _ = foo(Set1.B);
243 \\}
244 \\fn foo(set1: Set1) Set2 {
245 \\ return Set2(set1);
246 \\}
247 );
248
250 cases.addRuntimeSafety("@alignCast misaligned",249 cases.addRuntimeSafety("@alignCast misaligned",
251 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {250 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
252 \\ @import("std").os.exit(126);251 \\ @import("std").os.exit(126);
253 \\}252 \\}
254 \\error Wrong;253 \\pub fn main() !void {
255 \\pub fn main() %void {
256 \\ var array align(4) = []u32{0x11111111, 0x11111111};254 \\ var array align(4) = []u32{0x11111111, 0x11111111};
257 \\ const bytes = ([]u8)(array[0..]);255 \\ const bytes = ([]u8)(array[0..]);
258 \\ if (foo(bytes) != 0x11111111) return error.Wrong;256 \\ if (foo(bytes) != 0x11111111) return error.Wrong;
...@@ -274,7 +272,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {...@@ -274,7 +272,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
274 \\ int: u32,272 \\ int: u32,
275 \\};273 \\};
276 \\274 \\
277 \\pub fn main() %void {275 \\pub fn main() void {
278 \\ var f = Foo { .int = 42 };276 \\ var f = Foo { .int = 42 };
279 \\ bar(&f);277 \\ bar(&f);
280 \\}278 \\}
test/standalone/brace_expansion/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) void {
4 const main = b.addTest("main.zig");4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());5 main.setBuildMode(b.standardReleaseOptions());
66
test/standalone/brace_expansion/main.zig+14-8
...@@ -6,9 +6,6 @@ const assert = debug.assert;...@@ -6,9 +6,6 @@ const assert = debug.assert;
6const Buffer = std.Buffer;6const Buffer = std.Buffer;
7const ArrayList = std.ArrayList;7const ArrayList = std.ArrayList;
88
9error InvalidInput;
10error OutOfMem;
11
12const Token = union(enum) {9const Token = union(enum) {
13 Word: []const u8,10 Word: []const u8,
14 OpenBrace,11 OpenBrace,
...@@ -19,7 +16,7 @@ const Token = union(enum) {...@@ -19,7 +16,7 @@ const Token = union(enum) {
1916
20var global_allocator: &mem.Allocator = undefined;17var global_allocator: &mem.Allocator = undefined;
2118
22fn tokenize(input:[] const u8) %ArrayList(Token) {19fn tokenize(input:[] const u8) !ArrayList(Token) {
23 const State = enum {20 const State = enum {
24 Start,21 Start,
25 Word,22 Word,
...@@ -71,7 +68,12 @@ const Node = union(enum) {...@@ -71,7 +68,12 @@ const Node = union(enum) {
71 Combine: []Node,68 Combine: []Node,
72};69};
7370
74fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node {71const ParseError = error {
72 InvalidInput,
73 OutOfMemory,
74};
75
76fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node {
75 const first_token = tokens.items[*token_index];77 const first_token = tokens.items[*token_index];
76 *token_index += 1;78 *token_index += 1;
7779
...@@ -107,7 +109,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node {...@@ -107,7 +109,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node {
107 }109 }
108}110}
109111
110fn expandString(input: []const u8, output: &Buffer) %void {112fn expandString(input: []const u8, output: &Buffer) !void {
111 const tokens = try tokenize(input);113 const tokens = try tokenize(input);
112 if (tokens.len == 1) {114 if (tokens.len == 1) {
113 return output.resize(0);115 return output.resize(0);
...@@ -135,7 +137,11 @@ fn expandString(input: []const u8, output: &Buffer) %void {...@@ -135,7 +137,11 @@ fn expandString(input: []const u8, output: &Buffer) %void {
135 }137 }
136}138}
137139
138fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void {140const ExpandNodeError = error {
141 OutOfMemory,
142};
143
144fn expandNode(node: &const Node, output: &ArrayList(Buffer)) ExpandNodeError!void {
139 assert(output.len == 0);145 assert(output.len == 0);
140 switch (*node) {146 switch (*node) {
141 Node.Scalar => |scalar| {147 Node.Scalar => |scalar| {
...@@ -172,7 +178,7 @@ fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void {...@@ -172,7 +178,7 @@ fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void {
172 }178 }
173}179}
174180
175pub fn main() %void {181pub fn main() !void {
176 var stdin_file = try io.getStdIn();182 var stdin_file = try io.getStdIn();
177 var stdout_file = try io.getStdOut();183 var stdout_file = try io.getStdOut();
178184
test/standalone/issue_339/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) void {
4 const obj = b.addObject("test", "test.zig");4 const obj = b.addObject("test", "test.zig");
55
6 const test_step = b.step("test", "Test the program");6 const test_step = b.step("test", "Test the program");
test/standalone/issue_339/test.zig+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const StackTrace = @import("builtin").StackTrace;1const StackTrace = @import("builtin").StackTrace;
2pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} }2pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} }
33
4fn bar() %void {}4fn bar() error!void {}
55
6export fn foo() void {6export fn foo() void {
7 bar() catch unreachable;7 bar() catch unreachable;
test/standalone/pkg_import/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) void {
4 const exe = b.addExecutable("test", "test.zig");4 const exe = b.addExecutable("test", "test.zig");
5 exe.addPackagePath("my_pkg", "pkg.zig");5 exe.addPackagePath("my_pkg", "pkg.zig");
66
test/standalone/pkg_import/test.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const my_pkg = @import("my_pkg");1const my_pkg = @import("my_pkg");
2const assert = @import("std").debug.assert;2const assert = @import("std").debug.assert;
33
4pub fn main() %void {4pub fn main() void {
5 assert(my_pkg.add(10, 20) == 30);5 assert(my_pkg.add(10, 20) == 30);
6}6}
test/standalone/use_alias/build.zig+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const Builder = @import("std").build.Builder;1const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {3pub fn build(b: &Builder) void {
4 b.addCIncludePath(".");4 b.addCIncludePath(".");
55
6 const main = b.addTest("main.zig");6 const main = b.addTest("main.zig");
test/tests.zig+6-9
...@@ -45,9 +45,6 @@ const test_targets = []TestTarget {...@@ -45,9 +45,6 @@ const test_targets = []TestTarget {
45 },45 },
46};46};
4747
48error TestFailed;
49error CompilationIncorrectlySucceeded;
50
51const max_stdout_size = 1 * 1024 * 1024; // 1 MB48const max_stdout_size = 1 * 1024 * 1024; // 1 MB
5249
53pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step {50pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step {
...@@ -248,7 +245,7 @@ pub const CompareOutputContext = struct {...@@ -248,7 +245,7 @@ pub const CompareOutputContext = struct {
248 return ptr;245 return ptr;
249 }246 }
250247
251 fn make(step: &build.Step) %void {248 fn make(step: &build.Step) !void {
252 const self = @fieldParentPtr(RunCompareOutputStep, "step", step);249 const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
253 const b = self.context.b;250 const b = self.context.b;
254251
...@@ -337,7 +334,7 @@ pub const CompareOutputContext = struct {...@@ -337,7 +334,7 @@ pub const CompareOutputContext = struct {
337 return ptr;334 return ptr;
338 }335 }
339336
340 fn make(step: &build.Step) %void {337 fn make(step: &build.Step) !void {
341 const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step);338 const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step);
342 const b = self.context.b;339 const b = self.context.b;
343340
...@@ -563,7 +560,7 @@ pub const CompileErrorContext = struct {...@@ -563,7 +560,7 @@ pub const CompileErrorContext = struct {
563 return ptr;560 return ptr;
564 }561 }
565562
566 fn make(step: &build.Step) %void {563 fn make(step: &build.Step) !void {
567 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);564 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
568 const b = self.context.b;565 const b = self.context.b;
569566
...@@ -847,7 +844,7 @@ pub const TranslateCContext = struct {...@@ -847,7 +844,7 @@ pub const TranslateCContext = struct {
847 return ptr;844 return ptr;
848 }845 }
849846
850 fn make(step: &build.Step) %void {847 fn make(step: &build.Step) !void {
851 const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);848 const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
852 const b = self.context.b;849 const b = self.context.b;
853850
...@@ -1045,14 +1042,14 @@ pub const GenHContext = struct {...@@ -1045,14 +1042,14 @@ pub const GenHContext = struct {
1045 return ptr;1042 return ptr;
1046 }1043 }
10471044
1048 fn make(step: &build.Step) %void {1045 fn make(step: &build.Step) !void {
1049 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);1046 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
1050 const b = self.context.b;1047 const b = self.context.b;
10511048
1052 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);1049 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
10531050
1054 const full_h_path = b.pathFromRoot(self.h_path);1051 const full_h_path = b.pathFromRoot(self.h_path);
1055 const actual_h = try io.readFileAlloc(full_h_path, b.allocator);1052 const actual_h = try io.readFileAlloc(b.allocator, full_h_path);
10561053
1057 for (self.case.expected_lines.toSliceConst()) |expected_line| {1054 for (self.case.expected_lines.toSliceConst()) |expected_line| {
1058 if (mem.indexOf(u8, actual_h, expected_line) == null) {1055 if (mem.indexOf(u8, actual_h, expected_line) == null) {