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
480480 "os/child_process.zig"
481481 "os/darwin.zig"
482482 "os/darwin_errno.zig"
483 "os/file.zig"
483484 "os/get_user_id.zig"
484485 "os/index.zig"
485 "os/linux/index.zig"
486486 "os/linux/errno.zig"
487487 "os/linux/i386.zig"
488 "os/linux/index.zig"
488489 "os/linux/x86_64.zig"
489490 "os/path.zig"
490491 "os/windows/error.zig"
......@@ -520,6 +521,10 @@ set(ZIG_STD_FILES
520521 "special/panic.zig"
521522 "special/test_runner.zig"
522523 "unicode.zig"
524 "zig/ast.zig"
525 "zig/index.zig"
526 "zig/parser.zig"
527 "zig/tokenizer.zig"
523528)
524529
525530set(ZIG_C_HEADER_FILES
build.zig+2-7
......@@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
1010const Buffer = std.Buffer;
1111const io = std.io;
1212
13pub fn build(b: &Builder) %void {
13pub fn build(b: &Builder) !void {
1414 const mode = b.standardReleaseOptions();
1515
1616 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
......@@ -78,7 +78,6 @@ pub fn build(b: &Builder) %void {
7878 exe.linkSystemLibrary("c");
7979
8080 b.default_step.dependOn(&exe.step);
81 b.default_step.dependOn(docs_step);
8281
8382 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;
8483 if (!skip_self_hosted) {
......@@ -108,10 +107,6 @@ pub fn build(b: &Builder) %void {
108107 "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",
109108 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
115110 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
116111 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
117112 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
......@@ -149,7 +144,7 @@ const LibraryDep = struct {
149144 includes: ArrayList([]const u8),
150145};
151146
152fn findLLVM(b: &Builder, llvm_config_exe: []const u8) %LibraryDep {
147fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep {
153148 const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
154149 const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
155150 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%
2424cmake.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
2525msbuild /p:Configuration=Release INSTALL.vcxproj || exit /b
2626
27bin\zig.exe build --build-file ..\build.zig test -Dverbose-link || exit /b
27bin\zig.exe build --build-file ..\build.zig test || exit /b
2828
29@echo "MSVC build succeeded, proceeding with MinGW build"
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"
29@echo "MSVC build succeeded"
doc/docgen.zig+13-22
......@@ -12,7 +12,7 @@ const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt();
1212const obj_ext = std.build.Target(std.build.Target.Native).oFileExt();
1313const tmp_dir_name = "docgen_tmp";
1414
15pub fn main() %void {
15pub fn main() !void {
1616 // TODO use a more general purpose allocator here
1717 var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size);
1818 defer inc_allocator.deinit();
......@@ -31,10 +31,10 @@ pub fn main() %void {
3131 const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));
3232 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);
3535 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);
3838 defer out_file.close();
3939
4040 var file_in_stream = io.FileInStream.init(&in_file);
......@@ -42,7 +42,7 @@ pub fn main() %void {
4242 const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size);
4343
4444 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
4747 var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
4848 var toc = try genToc(allocator, &tokenizer);
......@@ -218,8 +218,6 @@ const Tokenizer = struct {
218218 }
219219};
220220
221error ParseError;
222
223221fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error {
224222 const loc = tokenizer.getTokenLocation(token);
225223 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
243241 return error.ParseError;
244242}
245243
246fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) %void {
244fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void {
247245 if (token.id != id) {
248246 return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
249247 }
250248}
251249
252fn eatToken(tokenizer: &Tokenizer, id: Token.Id) %Token {
250fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token {
253251 const token = tokenizer.next();
254252 try assertToken(tokenizer, token, id);
255253 return token;
......@@ -316,7 +314,7 @@ const Action = enum {
316314 Close,
317315};
318316
319fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc {
317fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
320318 var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator);
321319 errdefer urls.deinit();
322320
......@@ -540,7 +538,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc {
540538 };
541539}
542540
543fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 {
541fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 {
544542 var buf = try std.Buffer.initSize(allocator, 0);
545543 defer buf.deinit();
546544
......@@ -560,7 +558,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 {
560558 return buf.toOwnedSlice();
561559}
562560
563fn escapeHtml(allocator: &mem.Allocator, input: []const u8) %[]u8 {
561fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 {
564562 var buf = try std.Buffer.initSize(allocator, 0);
565563 defer buf.deinit();
566564
......@@ -596,15 +594,13 @@ const TermState = enum {
596594 ExpectEnd,
597595};
598596
599error UnsupportedEscape;
600
601597test "term color" {
602598 const input_bytes = "A\x1b[32;1mgreen\x1b[0mB";
603599 const result = try termColor(std.debug.global_allocator, input_bytes);
604600 assert(mem.eql(u8, result, "A<span class=\"t32\">green</span>B"));
605601}
606602
607fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 {
603fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 {
608604 var buf = try std.Buffer.initSize(allocator, 0);
609605 defer buf.deinit();
610606
......@@ -684,9 +680,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 {
684680 return buf.toOwnedSlice();
685681}
686682
687error ExampleFailedToCompile;
688
689fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io.OutStream, zig_exe: []const u8) %void {
683fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var, zig_exe: []const u8) !void {
690684 var code_progress_index: usize = 0;
691685 for (toc.nodes) |node| {
692686 switch (node) {
......@@ -729,7 +723,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
729723 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
730724 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
731725 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);
733727
734728 switch (code.id) {
735729 Code.Id.Exe => |expected_outcome| {
......@@ -974,10 +968,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
974968
975969}
976970
977error ChildCrashed;
978error ChildExitError;
979
980fn exec(allocator: &mem.Allocator, args: []const []const u8) %os.ChildProcess.ExecResult {
971fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {
981972 const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
982973 switch (result.term) {
983974 os.ChildProcess.Term.Exited => |exit_code| {
doc/langref.html.in+62-76
......@@ -108,7 +108,7 @@
108108 {#code_begin|exe|hello#}
109109const std = @import("std");
110110
111pub fn main() %void {
111pub fn main() !void {
112112 // If this program is run without stdout attached, exit with an error.
113113 var stdout_file = try std.io.getStdOut();
114114 // If this program encounters pipe failure when printing to stdout, exit
......@@ -129,8 +129,8 @@ pub fn main() void {
129129}
130130 {#code_end#}
131131 <p>
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.
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 must use the <code class="zig">void</code> return type.
134134 </p>
135135 {#see_also|Values|@import|Errors|Root Source File#}
136136 {#header_close#}
......@@ -141,10 +141,7 @@ const warn = std.debug.warn;
141141const os = std.os;
142142const assert = std.debug.assert;
143143
144// error declaration, makes `error.ArgNotFound` available
145error ArgNotFound;
146
147pub fn main() %void {
144pub fn main() void {
148145 // integers
149146 const one_plus_one: i32 = 1 + 1;
150147 warn("1 + 1 = {}\n", one_plus_one);
......@@ -173,7 +170,7 @@ pub fn main() %void {
173170 @typeName(@typeOf(nullable_value)), nullable_value);
174171
175172 // error union
176 var number_or_error: %i32 = error.ArgNotFound;
173 var number_or_error: error!i32 = error.ArgNotFound;
177174
178175 warn("\nerror union 1\ntype: {}\nvalue: {}\n",
179176 @typeName(@typeOf(number_or_error)), number_or_error);
......@@ -681,7 +678,7 @@ const warn = @import("std").debug.warn;
681678extern fn foo_strict(x: f64) f64;
682679extern fn foo_optimized(x: f64) f64;
683680
684pub fn main() %void {
681pub fn main() void {
685682 const x = 0.001;
686683 warn("optimized = {}\n", foo_optimized(x));
687684 warn("strict = {}\n", foo_strict(x));
......@@ -1036,7 +1033,7 @@ a catch |err| b</code></pre></td>
10361033 <code>err</code> is the <code>error</code> and is in scope of the expression <code>b</code>.
10371034 </td>
10381035 <td>
1039 <pre><code class="zig">const value: %u32 = null;
1036 <pre><code class="zig">const value: error!u32 = error.Broken;
10401037const unwrapped = value catch 1234;
10411038unwrapped == 1234</code></pre>
10421039 </td>
......@@ -1269,9 +1266,10 @@ const ptr = &amp;x;
12691266 {#header_close#}
12701267 {#header_open|Precedence#}
12711268 <pre><code>x() x[] x.y
1272!x -x -%x ~x *x &amp;x ?x %x ??x
1269a!b
1270!x -x -%x ~x *x &amp;x ?x ??x
12731271x{}
1274* / % ** *%
1272! * / % ** *%
12751273+ - ++ +% -%
12761274&lt;&lt; &gt;&gt;
12771275&amp;
......@@ -2268,8 +2266,8 @@ fn eventuallyNullSequence() ?u32 {
22682266 break :blk numbers_left;
22692267 };
22702268}
2271error ReachedZero;
2272fn eventuallyErrorSequence() %u32 {
2269
2270fn eventuallyErrorSequence() error!u32 {
22732271 return if (numbers_left == 0) error.ReachedZero else blk: {
22742272 numbers_left -= 1;
22752273 break :blk numbers_left;
......@@ -2398,7 +2396,7 @@ fn typeNameLength(comptime T: type) usize {
23982396// If expressions have three uses, corresponding to the three types:
23992397// * bool
24002398// * ?T
2401// * %T
2399// * error!T
24022400
24032401const assert = @import("std").debug.assert;
24042402
......@@ -2459,20 +2457,18 @@ test "if nullable" {
24592457 }
24602458}
24612459
2462error BadValue;
2463error LessBadValue;
24642460test "if error union" {
24652461 // If expressions test for errors.
24662462 // Note the |err| capture on the else.
24672463
2468 const a: %u32 = 0;
2464 const a: error!u32 = 0;
24692465 if (a) |value| {
24702466 assert(value == 0);
24712467 } else |err| {
24722468 unreachable;
24732469 }
24742470
2475 const b: %u32 = error.BadValue;
2471 const b: error!u32 = error.BadValue;
24762472 if (b) |value| {
24772473 unreachable;
24782474 } else |err| {
......@@ -2490,7 +2486,7 @@ test "if error union" {
24902486 }
24912487
24922488 // Access the value by reference using a pointer capture.
2493 var c: %u32 = 3;
2489 var c: error!u32 = 3;
24942490 if (c) |*value| {
24952491 *value = 9;
24962492 } else |err| {
......@@ -2558,8 +2554,7 @@ test "defer unwinding" {
25582554//
25592555// This is especially useful in allowing a function to clean up properly
25602556// on error, and replaces goto error handling tactics as seen in c.
2561error DeferError;
2562fn deferErrorExample(is_error: bool) %void {
2557fn deferErrorExample(is_error: bool) !void {
25632558 warn("\nstart of function\n");
25642559
25652560 // This will always be executed on exit
......@@ -2668,7 +2663,7 @@ test "foo" {
26682663 assert(value == 1234);
26692664}
26702665
2671fn bar() %u32 {
2666fn bar() error!u32 {
26722667 return 1234;
26732668}
26742669
......@@ -2791,13 +2786,8 @@ test "fn reflection" {
27912786 One of the distinguishing features of Zig is its exception handling strategy.
27922787 </p>
27932788 <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
27952790 </p>
2796 {#code_begin|syntax#}
2797error FileNotFound;
2798error OutOfMemory;
2799error UnexpectedToken;
2800 {#code_end#}
28012791 <p>
28022792 These error values are assigned an unsigned integer value greater than 0 at
28032793 compile time. You are allowed to declare the same error value more than once,
......@@ -2809,26 +2799,23 @@ error UnexpectedToken;
28092799 </p>
28102800 <p>
28112801 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.
28132803 </p>
28142804 <p>
2815 The pure error 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.
2805 The error set type is one of the error values, and in the same way that pointers
2806 cannot be null, a error set instance is always an error.
28172807 </p>
28182808 {#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}
28192809 <p>
2820 Most of the time you will not find yourself using a pure error type. Instead,
2821 likely you will be using the error union type. This is when you take a normal type,
2822 and prefix it with the <code>%</code> operator.
2810 Most of the time you will not find yourself using an error set type. Instead,
2811 likely you will be using the error union type. This is when you take an error set
2812 and a normal type, and create an error union with the <code>!</code> binary operator.
28232813 </p>
28242814 <p>
28252815 Here is a function to parse a string into a 64-bit integer:
28262816 </p>
28272817 {#code_begin|test#}
2828error InvalidChar;
2829error Overflow;
2830
2831pub fn parseU64(buf: []const u8, radix: u8) %u64 {
2818pub fn parseU64(buf: []const u8, radix: u8) !u64 {
28322819 var x: u64 = 0;
28332820
28342821 for (buf) |c| {
......@@ -2867,13 +2854,14 @@ test "parse u64" {
28672854}
28682855 {#code_end#}
28692856 <p>
2870 Notice the return type is <code>%u64</code>. This means that the function
2871 either returns an unsigned 64 bit integer, or an error.
2857 Notice the return type is <code>!u64</code>. This means that the function
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.
28722860 </p>
28732861 <p>
28742862 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>.
2876 Both types implicitly cast to <code>%u64</code>.
2863 an error, and at the bottom a return statement that returns a <code>u64</code>.
2864 Both types implicitly cast to <code>error!u64</code>.
28772865 </p>
28782866 <p>
28792867 What it looks like to use this function varies depending on what you're
......@@ -2900,7 +2888,7 @@ fn doAThing(str: []u8) void {
29002888 <p>Let's say you wanted to return the error if you got one, otherwise continue with the
29012889 function logic:</p>
29022890 {#code_begin|syntax#}
2903fn doAThing(str: []u8) %void {
2891fn doAThing(str: []u8) !void {
29042892 const number = parseU64(str, 10) catch |err| return err;
29052893 // ...
29062894}
......@@ -2909,7 +2897,7 @@ fn doAThing(str: []u8) %void {
29092897 There is a shortcut for this. The <code>try</code> expression:
29102898 </p>
29112899 {#code_begin|syntax#}
2912fn doAThing(str: []u8) %void {
2900fn doAThing(str: []u8) !void {
29132901 const number = try parseU64(str, 10);
29142902 // ...
29152903}
......@@ -2959,7 +2947,7 @@ fn doAThing(str: []u8) void {
29592947 Example:
29602948 </p>
29612949 {#code_begin|syntax#}
2962fn createFoo(param: i32) %Foo {
2950fn createFoo(param: i32) !Foo {
29632951 const foo = try tryToAllocateFoo();
29642952 // now we have allocated foo. we need to free it if the function fails.
29652953 // but we want to return it if the function succeeds.
......@@ -2999,15 +2987,13 @@ fn createFoo(param: i32) %Foo {
29992987 </ul>
30002988 {#see_also|defer|if|switch#}
30012989 {#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.
30032991 You can use compile-time reflection to access the child type of an error union:</p>
30042992 {#code_begin|test#}
30052993const assert = @import("std").debug.assert;
30062994
3007error SomeError;
3008
30092995test "error union" {
3010 var foo: %i32 = undefined;
2996 var foo: error!i32 = undefined;
30112997
30122998 // Implicitly cast from child type of an error union:
30132999 foo = 1234;
......@@ -3015,8 +3001,11 @@ test "error union" {
30153001 // Implicitly cast from an error set:
30163002 foo = error.SomeError;
30173003
3018 // Use compile-time reflection to access the child type of an error union:
3019 comptime assert(@typeOf(foo).Child == i32);
3004 // Use compile-time reflection to access the payload type of an error union:
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);
30203009}
30213010 {#code_end#}
30223011 {#header_close#}
......@@ -3610,7 +3599,7 @@ pub fn main() void {
36103599
36113600 {#code_begin|syntax#}
36123601/// 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 {
36143603 const State = enum {
36153604 Start,
36163605 OpenBrace,
......@@ -3682,7 +3671,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void {
36823671 and emits a function that actually looks like this:
36833672 </p>
36843673 {#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 {
36863675 try self.write("here is a string: '");
36873676 try self.printValue(arg0);
36883677 try self.write("' here is a number: ");
......@@ -3696,7 +3685,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void {
36963685 on the type:
36973686 </p>
36983687 {#code_begin|syntax#}
3699pub fn printValue(self: &OutStream, value: var) %void {
3688pub fn printValue(self: &OutStream, value: var) !void {
37003689 const T = @typeOf(value);
37013690 if (@isInteger(T)) {
37023691 return self.printInt(T, value);
......@@ -4647,7 +4636,7 @@ pub const TypeId = enum {
46474636 {#code_begin|syntax#}
46484637const Builder = @import("std").build.Builder;
46494638
4650pub fn build(b: &Builder) %void {
4639pub fn build(b: &Builder) void {
46514640 const exe = b.addExecutable("example", "example.zig");
46524641 exe.setBuildMode(b.standardReleaseOptions());
46534642 b.default_step.dependOn(&exe.step);
......@@ -4789,7 +4778,7 @@ comptime {
47894778 {#code_begin|exe_err#}
47904779const math = @import("std").math;
47914780const warn = @import("std").debug.warn;
4792pub fn main() %void {
4781pub fn main() !void {
47934782 var byte: u8 = 255;
47944783
47954784 byte = if (math.add(u8, byte, 1)) |result| result else |err| {
......@@ -4817,7 +4806,7 @@ pub fn main() %void {
48174806 </p>
48184807 {#code_begin|exe#}
48194808const warn = @import("std").debug.warn;
4820pub fn main() %void {
4809pub fn main() void {
48214810 var byte: u8 = 255;
48224811
48234812 var result: u8 = undefined;
......@@ -4926,14 +4915,12 @@ pub fn main() void {
49264915 {#header_close#}
49274916 {#header_open|Attempt to Unwrap Error#}
49284917 <p>At compile-time:</p>
4929 {#code_begin|test_err|unable to unwrap error 'UnableToReturnNumber'#}
4918 {#code_begin|test_err|caught unexpected error 'UnableToReturnNumber'#}
49304919comptime {
49314920 const number = getNumberOrFail() catch unreachable;
49324921}
49334922
4934error UnableToReturnNumber;
4935
4936fn getNumberOrFail() %i32 {
4923fn getNumberOrFail() !i32 {
49374924 return error.UnableToReturnNumber;
49384925}
49394926 {#code_end#}
......@@ -4953,9 +4940,7 @@ pub fn main() void {
49534940 }
49544941}
49554942
4956error UnableToReturnNumber;
4957
4958fn getNumberOrFail() %i32 {
4943fn getNumberOrFail() !i32 {
49594944 return error.UnableToReturnNumber;
49604945}
49614946 {#code_end#}
......@@ -4963,7 +4948,6 @@ fn getNumberOrFail() %i32 {
49634948 {#header_open|Invalid Error Code#}
49644949 <p>At compile-time:</p>
49654950 {#code_begin|test_err|integer value 11 represents no error#}
4966error AnError;
49674951comptime {
49684952 const err = error.AnError;
49694953 const number = u32(err) + 10;
......@@ -5363,7 +5347,7 @@ int main(int argc, char **argv) {
53635347 {#code_begin|syntax#}
53645348const Builder = @import("std").build.Builder;
53655349
5366pub fn build(b: &Builder) %void {
5350pub fn build(b: &Builder) void {
53675351 const obj = b.addObject("base64", "base64.zig");
53685352
53695353 const exe = b.addCExecutable("test");
......@@ -5641,14 +5625,12 @@ fn readU32Be() u32 {}
56415625 {#header_open|Grammar#}
56425626 <pre><code class="nohighlight">Root = many(TopLevelItem) EOF
56435627
5644TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
5628TopLevelItem = CompTimeExpression(Block) | TopLevelDecl | TestDecl
56455629
56465630TestDecl = "test" String Block
56475631
56485632TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
56495633
5650ErrorValueDecl = "error" Symbol ";"
5651
56525634GlobalVarDecl = option("export") VariableDeclaration ";"
56535635
56545636LocalVarDecl = option("comptime") VariableDeclaration
......@@ -5663,7 +5645,7 @@ UseDecl = "use" Expression ";"
56635645
56645646ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
56655647
5666FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") TypeExpr
5648FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr
56675649
56685650FnDef = option("inline" | "export") FnProto Block
56695651
......@@ -5675,7 +5657,9 @@ Block = option(Symbol ":") "{" many(Statement) "}"
56755657
56765658Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
56775659
5678TypeExpr = PrefixOpExpression | "var"
5660TypeExpr = ErrorSetExpr | "var"
5661
5662ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
56795663
56805664BlockOrExpression = Block | Expression
56815665
......@@ -5757,9 +5741,9 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
57575741
57585742CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
57595743
5760MultiplyOperator = "*" | "/" | "%" | "**" | "*%"
5744MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
57615745
5762PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
5746PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
57635747
57645748SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
57655749
......@@ -5777,9 +5761,9 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
57775761
57785762StructLiteralField = "." 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
57845768ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
57855769
......@@ -5787,6 +5771,8 @@ GroupedExpression = "(" Expression ")"
57875771
57885772KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
57895773
5774ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
5775
57905776ContainerDecl = option("extern" | "packed")
57915777 ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
57925778 "{" many(ContainerMember) "}"</code></pre>
example/cat/main.zig+5-5
......@@ -5,7 +5,7 @@ const os = std.os;
55const warn = std.debug.warn;
66const allocator = std.debug.global_allocator;
77
8pub fn main() %void {
8pub fn main() !void {
99 var args_it = os.args();
1010 const exe = try unwrapArg(??args_it.next(allocator));
1111 var catted_anything = false;
......@@ -20,7 +20,7 @@ pub fn main() %void {
2020 } else if (arg[0] == '-') {
2121 return usage(exe);
2222 } else {
23 var file = io.File.openRead(arg, null) catch |err| {
23 var file = os.File.openRead(allocator, arg) catch |err| {
2424 warn("Unable to open file: {}\n", @errorName(err));
2525 return err;
2626 };
......@@ -36,12 +36,12 @@ pub fn main() %void {
3636 }
3737}
3838
39fn usage(exe: []const u8) %void {
39fn usage(exe: []const u8) !void {
4040 warn("Usage: {} [FILE]...\n", exe);
4141 return error.Invalid;
4242}
4343
44fn cat_file(stdout: &io.File, file: &io.File) %void {
44fn cat_file(stdout: &os.File, file: &os.File) !void {
4545 var buf: [1024 * 4]u8 = undefined;
4646
4747 while (true) {
......@@ -61,7 +61,7 @@ fn cat_file(stdout: &io.File, file: &io.File) %void {
6161 }
6262}
6363
64fn unwrapArg(arg: %[]u8) %[]u8 {
64fn unwrapArg(arg: error![]u8) ![]u8 {
6565 return arg catch |err| {
6666 warn("Unable to parse command line: {}\n", err);
6767 return err;
example/guess_number/main.zig+1-1
......@@ -5,7 +5,7 @@ const fmt = std.fmt;
55const Rand = std.rand.Rand;
66const os = std.os;
77
8pub fn main() %void {
8pub fn main() !void {
99 var stdout_file = try io.getStdOut();
1010 var stdout_file_stream = io.FileOutStream.init(&stdout_file);
1111 const stdout = &stdout_file_stream.stream;
example/hello_world/hello.zig+1-1
......@@ -1,6 +1,6 @@
11const std = @import("std");
22
3pub fn main() %void {
3pub fn main() !void {
44 // If this program is run without stdout attached, exit with an error.
55 var stdout_file = try std.io.getStdOut();
66 // 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,
88 base64_decoder.decode(dest[0..decoded_size], src);
99 return decoded_size;
1010}
11
12var x: c_int = 1234;
13export var x_ptr = &x;
example/mix_o_files/build.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) void {
44 const obj = b.addObject("base64", "base64.zig");
55
66 const exe = b.addCExecutable("test");
example/mix_o_files/test.c+4
......@@ -4,6 +4,8 @@
44#include <assert.h>
55#include <string.h>
66
7extern int *x_ptr;
8
79int main(int argc, char **argv) {
810 const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz";
911 char buf[200];
......@@ -12,5 +14,7 @@ int main(int argc, char **argv) {
1214 buf[len] = 0;
1315 assert(strcmp(buf, "all your base are belong to us") == 0);
1416
17 assert(*x_ptr == 1234);
18
1519 return 0;
1620}
example/shared_library/build.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) void {
44 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
55
66 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");
1414const ArrayList = std.ArrayList;
1515const c = @import("c.zig");
1616
17error InvalidCommandLineArguments;
18error ZigLibDirNotFound;
19error ZigInstallationNotFound;
20
2117const 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
3219const Cmd = enum {
3320 None,
3421 Build,
......@@ -39,21 +26,25 @@ const Cmd = enum {
3926 Targets,
4027};
4128
42fn badArgs(comptime format: []const u8, args: ...) error {
43 var stderr = try io.getStdErr();
29fn badArgs(comptime format: []const u8, args: ...) noreturn {
30 var stderr = io.getStdErr() catch std.os.exit(1);
4431 var stderr_stream_adapter = io.FileOutStream.init(&stderr);
4532 const stderr_stream = &stderr_stream_adapter.stream;
46 try stderr_stream.print(format ++ "\n\n", args);
47 try printUsage(&stderr_stream_adapter.stream);
48 return error.InvalidCommandLineArguments;
33 stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
34 printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
35 std.os.exit(1);
4936}
5037
51pub fn main2() %void {
38pub fn main() !void {
5239 const allocator = std.heap.c_allocator;
5340
5441 const args = try os.argsAlloc(allocator);
5542 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
5748 var cmd = Cmd.None;
5849 var build_kind: Module.Kind = undefined;
5950 var build_mode: builtin.Mode = builtin.Mode.Debug;
......@@ -173,7 +164,7 @@ pub fn main2() %void {
173164 } else if (mem.eql(u8, arg, "--pkg-end")) {
174165 @panic("TODO --pkg-end");
175166 } else if (arg_i + 1 >= args.len) {
176 return badArgs("expected another argument after {}", arg);
167 badArgs("expected another argument after {}", arg);
177168 } else {
178169 arg_i += 1;
179170 if (mem.eql(u8, arg, "--output")) {
......@@ -188,7 +179,7 @@ pub fn main2() %void {
188179 } else if (mem.eql(u8, args[arg_i], "off")) {
189180 color = ErrColor.Off;
190181 } else {
191 return badArgs("--color options are 'auto', 'on', or 'off'");
182 badArgs("--color options are 'auto', 'on', or 'off'");
192183 }
193184 } else if (mem.eql(u8, arg, "--emit")) {
194185 if (mem.eql(u8, args[arg_i], "asm")) {
......@@ -198,7 +189,7 @@ pub fn main2() %void {
198189 } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
199190 emit_file_type = Emit.LlvmIr;
200191 } else {
201 return badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
192 badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
202193 }
203194 } else if (mem.eql(u8, arg, "--name")) {
204195 out_name_arg = args[arg_i];
......@@ -266,7 +257,7 @@ pub fn main2() %void {
266257 } else if (mem.eql(u8, arg, "--test-cmd")) {
267258 @panic("TODO --test-cmd");
268259 } else {
269 return badArgs("invalid argument: {}", arg);
260 badArgs("invalid argument: {}", arg);
270261 }
271262 }
272263 } else if (cmd == Cmd.None) {
......@@ -289,18 +280,18 @@ pub fn main2() %void {
289280 cmd = Cmd.Test;
290281 build_kind = Module.Kind.Exe;
291282 } else {
292 return badArgs("unrecognized command: {}", arg);
283 badArgs("unrecognized command: {}", arg);
293284 }
294285 } else switch (cmd) {
295286 Cmd.Build, Cmd.TranslateC, Cmd.Test => {
296287 if (in_file_arg == null) {
297288 in_file_arg = arg;
298289 } else {
299 return badArgs("unexpected extra parameter: {}", arg);
290 badArgs("unexpected extra parameter: {}", arg);
300291 }
301292 },
302293 Cmd.Version, Cmd.Zen, Cmd.Targets => {
303 return badArgs("unexpected extra parameter: {}", arg);
294 badArgs("unexpected extra parameter: {}", arg);
304295 },
305296 Cmd.None => unreachable,
306297 }
......@@ -337,15 +328,15 @@ pub fn main2() %void {
337328// }
338329
339330 switch (cmd) {
340 Cmd.None => return badArgs("expected command"),
331 Cmd.None => badArgs("expected command"),
341332 Cmd.Zen => return printZen(),
342333 Cmd.Build, Cmd.Test, Cmd.TranslateC => {
343334 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");
345336 } 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");
347338 } 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");
349340 }
350341
351342 const root_name = switch (cmd) {
......@@ -355,9 +346,9 @@ pub fn main2() %void {
355346 } else if (in_file_arg) |in_file_path| {
356347 const basename = os.path.basename(in_file_path);
357348 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");
359350 } else {
360 return badArgs("--name [name] not provided and unable to infer");
351 badArgs("--name [name] not provided and unable to infer");
361352 }
362353 },
363354 Cmd.Test => "test",
......@@ -432,7 +423,7 @@ pub fn main2() %void {
432423 module.linker_rdynamic = rdynamic;
433424
434425 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");
436427 }
437428
438429 if (mmacosx_version_min) |ver| {
......@@ -472,7 +463,7 @@ pub fn main2() %void {
472463 }
473464}
474465
475fn printUsage(stream: &io.OutStream) %void {
466fn printUsage(stream: var) !void {
476467 try stream.write(
477468 \\Usage: zig [command] [options]
478469 \\
......@@ -481,6 +472,7 @@ fn printUsage(stream: &io.OutStream) %void {
481472 \\ build-exe [source] create executable from source or object files
482473 \\ build-lib [source] create library from source or object files
483474 \\ build-obj [source] create object from source or assembly
475 \\ fmt [file] parse file and render in canonical zig format
484476 \\ translate-c [source] convert c code to zig code
485477 \\ targets list available compilation targets
486478 \\ test [source] create and run a test build
......@@ -548,7 +540,7 @@ fn printUsage(stream: &io.OutStream) %void {
548540 );
549541}
550542
551fn printZen() %void {
543fn printZen() !void {
552544 var stdout_file = try io.getStdErr();
553545 try stdout_file.write(
554546 \\
......@@ -568,8 +560,33 @@ fn printZen() %void {
568560 );
569561}
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
571588/// 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 {
573590 if (zig_install_prefix_arg) |zig_install_prefix| {
574591 return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {
575592 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
585602}
586603
587604/// Caller must free result
588fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) %[]u8 {
605fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
589606 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
590607 errdefer allocator.free(test_zig_dir);
591608
592609 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
593610 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);
596613 file.close();
597614
598615 return test_zig_dir;
599616}
600617
601618/// Caller must free result
602fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {
619fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
603620 const self_exe_path = try os.selfExeDirPath(allocator);
604621 defer allocator.free(self_exe_path);
605622
......@@ -626,8 +643,3 @@ fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {
626643
627644 return error.FileNotFound;
628645}
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");
88const builtin = @import("builtin");
99const Target = @import("target.zig").Target;
1010const warn = std.debug.warn;
11const Tokenizer = @import("tokenizer.zig").Tokenizer;
12const Token = @import("tokenizer.zig").Token;
13const Parser = @import("parser.zig").Parser;
11const Tokenizer = std.zig.Tokenizer;
12const Token = std.zig.Token;
13const Parser = std.zig.Parser;
1414const ArrayList = std.ArrayList;
1515
1616pub const Module = struct {
......@@ -110,7 +110,7 @@ pub const Module = struct {
110110 };
111111
112112 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) %&Module
113 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
114114 {
115115 var name_buffer = try Buffer.init(allocator, name);
116116 errdefer name_buffer.deinit();
......@@ -198,7 +198,7 @@ pub const Module = struct {
198198 self.allocator.destroy(self);
199199 }
200200
201 pub fn build(self: &Module) %void {
201 pub fn build(self: &Module) !void {
202202 if (self.llvm_argv.len != 0) {
203203 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator,
204204 [][]const []const u8 { [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, });
......@@ -213,14 +213,11 @@ pub const Module = struct {
213213 };
214214 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| {
217217 try printError("unable to open '{}': {}", root_src_real_path, err);
218218 return err;
219219 };
220220 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
225222 warn("====input:====\n");
226223
......@@ -263,11 +260,12 @@ pub const Module = struct {
263260
264261 }
265262
266 pub fn link(self: &Module, out_file: ?[]const u8) %void {
263 pub fn link(self: &Module, out_file: ?[]const u8) !void {
267264 warn("TODO link");
265 return error.Todo;
268266 }
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 {
271269 const is_libc = mem.eql(u8, name, "c");
272270
273271 if (is_libc) {
......@@ -297,7 +295,7 @@ pub const Module = struct {
297295 }
298296};
299297
300fn printError(comptime format: []const u8, args: ...) %void {
298fn printError(comptime format: []const u8, args: ...) !void {
301299 var stderr_file = try std.io.getStdErr();
302300 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
303301 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 {
236236 TypeTableEntry *x_type;
237237 ConstExprValue *x_maybe;
238238 ConstErrValue x_err_union;
239 ErrorTableEntry *x_pure_err;
239 ErrorTableEntry *x_err_set;
240240 BigInt x_enum_tag;
241241 ConstStructValue x_struct;
242242 ConstUnionValue x_union;
......@@ -353,7 +353,6 @@ enum NodeType {
353353 NodeTypeReturnExpr,
354354 NodeTypeDefer,
355355 NodeTypeVariableDeclaration,
356 NodeTypeErrorValueDecl,
357356 NodeTypeTestDecl,
358357 NodeTypeBinOpExpr,
359358 NodeTypeUnwrapErrorExpr,
......@@ -393,6 +392,7 @@ enum NodeType {
393392 NodeTypeVarLiteral,
394393 NodeTypeIfErrorExpr,
395394 NodeTypeTestExpr,
395 NodeTypeErrorSetDecl,
396396};
397397
398398struct AstNodeRoot {
......@@ -424,6 +424,8 @@ struct AstNodeFnProto {
424424 AstNode *align_expr;
425425 // populated if the "section(S)" is present
426426 AstNode *section_expr;
427
428 bool auto_err_set;
427429};
428430
429431struct AstNodeFnDef {
......@@ -486,12 +488,6 @@ struct AstNodeVariableDeclaration {
486488 AstNode *section_expr;
487489};
488490
489struct AstNodeErrorValueDecl {
490 Buf *name;
491
492 ErrorTableEntry *err;
493};
494
495491struct AstNodeTestDecl {
496492 Buf *name;
497493
......@@ -514,8 +510,7 @@ enum BinOpType {
514510 BinOpTypeAssignBitAnd,
515511 BinOpTypeAssignBitXor,
516512 BinOpTypeAssignBitOr,
517 BinOpTypeAssignBoolAnd,
518 BinOpTypeAssignBoolOr,
513 BinOpTypeAssignMergeErrorSets,
519514 BinOpTypeBoolOr,
520515 BinOpTypeBoolAnd,
521516 BinOpTypeCmpEq,
......@@ -540,6 +535,8 @@ enum BinOpType {
540535 BinOpTypeUnwrapMaybe,
541536 BinOpTypeArrayCat,
542537 BinOpTypeArrayMult,
538 BinOpTypeErrorUnion,
539 BinOpTypeMergeErrorSets,
543540};
544541
545542struct AstNodeBinOpExpr {
......@@ -563,6 +560,7 @@ enum CastOp {
563560 CastOpResizeSlice,
564561 CastOpBytesToSlice,
565562 CastOpNumLitToConcrete,
563 CastOpErrSet,
566564};
567565
568566struct AstNodeFnCallExpr {
......@@ -595,7 +593,6 @@ enum PrefixOp {
595593 PrefixOpNegationWrap,
596594 PrefixOpDereference,
597595 PrefixOpMaybe,
598 PrefixOpError,
599596 PrefixOpUnwrapMaybe,
600597};
601598
......@@ -762,6 +759,10 @@ struct AstNodeContainerDecl {
762759 bool auto_enum; // union(enum)
763760};
764761
762struct AstNodeErrorSetDecl {
763 ZigList<AstNode *> decls;
764};
765
765766struct AstNodeStructField {
766767 VisibMod visib_mod;
767768 Buf *name;
......@@ -858,7 +859,6 @@ struct AstNode {
858859 AstNodeReturnExpr return_expr;
859860 AstNodeDefer defer;
860861 AstNodeVariableDeclaration variable_declaration;
861 AstNodeErrorValueDecl error_value_decl;
862862 AstNodeTestDecl test_decl;
863863 AstNodeBinOpExpr bin_op_expr;
864864 AstNodeCatchExpr unwrap_err_expr;
......@@ -899,6 +899,7 @@ struct AstNode {
899899 AstNodeArrayType array_type;
900900 AstNodeErrorType error_type;
901901 AstNodeVarLiteral var_literal;
902 AstNodeErrorSetDecl err_set_decl;
902903 } data;
903904};
904905
......@@ -993,8 +994,15 @@ struct TypeTableEntryMaybe {
993994 TypeTableEntry *child_type;
994995};
995996
996struct TypeTableEntryError {
997 TypeTableEntry *child_type;
997struct TypeTableEntryErrorUnion {
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;
9981006};
9991007
10001008struct TypeTableEntryEnum {
......@@ -1097,7 +1105,7 @@ enum TypeTableEntryId {
10971105 TypeTableEntryIdNullLit,
10981106 TypeTableEntryIdMaybe,
10991107 TypeTableEntryIdErrorUnion,
1100 TypeTableEntryIdPureError,
1108 TypeTableEntryIdErrorSet,
11011109 TypeTableEntryIdEnum,
11021110 TypeTableEntryIdUnion,
11031111 TypeTableEntryIdFn,
......@@ -1126,7 +1134,8 @@ struct TypeTableEntry {
11261134 TypeTableEntryArray array;
11271135 TypeTableEntryStruct structure;
11281136 TypeTableEntryMaybe maybe;
1129 TypeTableEntryError error;
1137 TypeTableEntryErrorUnion error_union;
1138 TypeTableEntryErrorSet error_set;
11301139 TypeTableEntryEnum enumeration;
11311140 TypeTableEntryUnion unionation;
11321141 TypeTableEntryFn fn;
......@@ -1136,7 +1145,6 @@ struct TypeTableEntry {
11361145 // use these fields to make sure we don't duplicate type table entries for the same type
11371146 TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const]
11381147 TypeTableEntry *maybe_parent;
1139 TypeTableEntry *error_parent;
11401148 // If we generate a constant name value for this type, we memoize it here.
11411149 // The type of this is array
11421150 ConstExprValue *cached_const_name_val;
......@@ -1340,6 +1348,10 @@ struct TypeId {
13401348 bool is_signed;
13411349 uint32_t bit_count;
13421350 } integer;
1351 struct {
1352 TypeTableEntry *err_set_type;
1353 TypeTableEntry *payload_type;
1354 } error_union;
13431355 } data;
13441356};
13451357
......@@ -1481,7 +1493,7 @@ struct CodeGen {
14811493 TypeTableEntry *entry_undef;
14821494 TypeTableEntry *entry_null;
14831495 TypeTableEntry *entry_var;
1484 TypeTableEntry *entry_pure_error;
1496 TypeTableEntry *entry_global_error_set;
14851497 TypeTableEntry *entry_arg_tuple;
14861498 } builtin_types;
14871499
......@@ -1570,7 +1582,6 @@ struct CodeGen {
15701582 LLVMValueRef return_address_fn_val;
15711583 LLVMValueRef frame_address_fn_val;
15721584 bool error_during_imports;
1573 TypeTableEntry *err_tag_type;
15741585
15751586 const char **clang_argv;
15761587 size_t clang_argv_len;
......@@ -1584,7 +1595,9 @@ struct CodeGen {
15841595
15851596 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;
15881601 bool generate_error_name_table;
15891602 LLVMValueRef err_name_table;
15901603 size_t largest_err_name_len;
......@@ -1617,6 +1630,10 @@ struct CodeGen {
16171630 TypeTableEntry *align_amt_type;
16181631 TypeTableEntry *stack_trace_type;
16191632 TypeTableEntry *ptr_to_stack_trace_type;
1633
1634 ZigList<ZigLLVMDIType **> error_di_types;
1635
1636 ZigList<Buf *> forbidden_libs;
16201637};
16211638
16221639enum VarLinkage {
......@@ -1653,6 +1670,7 @@ struct ErrorTableEntry {
16531670 Buf name;
16541671 uint32_t value;
16551672 AstNode *decl_node;
1673 TypeTableEntry *set_with_only_this_in_it;
16561674 // If we generate a constant error name value for this error, we memoize it here.
16571675 // The type of this is array
16581676 ConstExprValue *cached_error_name_val;
......@@ -1920,6 +1938,7 @@ enum IrInstructionId {
19201938 IrInstructionIdArgType,
19211939 IrInstructionIdExport,
19221940 IrInstructionIdErrorReturnTrace,
1941 IrInstructionIdErrorUnion,
19231942};
19241943
19251944struct IrInstruction {
......@@ -1996,7 +2015,6 @@ enum IrUnOp {
19962015 IrUnOpNegation,
19972016 IrUnOpNegationWrap,
19982017 IrUnOpDereference,
1999 IrUnOpError,
20002018 IrUnOpMaybe,
20012019};
20022020
......@@ -2039,6 +2057,7 @@ enum IrBinOp {
20392057 IrBinOpRemMod,
20402058 IrBinOpArrayCat,
20412059 IrBinOpArrayMult,
2060 IrBinOpMergeErrorSets,
20422061};
20432062
20442063struct IrInstructionBinOp {
......@@ -2750,6 +2769,13 @@ struct IrInstructionErrorReturnTrace {
27502769 IrInstruction base;
27512770};
27522771
2772struct IrInstructionErrorUnion {
2773 IrInstruction base;
2774
2775 IrInstruction *err_set;
2776 IrInstruction *payload;
2777};
2778
27532779static const size_t slice_ptr_index = 0;
27542780static const size_t slice_len_index = 1;
27552781
src/analyze.cpp+169-201
......@@ -224,7 +224,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {
224224 case TypeTableEntryIdNullLit:
225225 case TypeTableEntryIdMaybe:
226226 case TypeTableEntryIdErrorUnion:
227 case TypeTableEntryIdPureError:
227 case TypeTableEntryIdErrorSet:
228228 case TypeTableEntryIdFn:
229229 case TypeTableEntryIdNamespace:
230230 case TypeTableEntryIdBlock:
......@@ -260,7 +260,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
260260 case TypeTableEntryIdNullLit:
261261 case TypeTableEntryIdMaybe:
262262 case TypeTableEntryIdErrorUnion:
263 case TypeTableEntryIdPureError:
263 case TypeTableEntryIdErrorSet:
264264 case TypeTableEntryIdFn:
265265 case TypeTableEntryIdNamespace:
266266 case TypeTableEntryIdBlock:
......@@ -514,29 +514,47 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
514514 }
515515}
516516
517TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
518 if (child_type->error_parent)
519 return child_type->error_parent;
517TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type) {
518 assert(err_set_type->id == TypeTableEntryIdErrorSet);
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
521531 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
522532 entry->is_copyable = true;
523 assert(child_type->type_ref);
524 assert(child_type->di_type);
525 ensure_complete_type(g, child_type);
533 assert(payload_type->di_type);
534 ensure_complete_type(g, payload_type);
526535
527536 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;
531
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;
539 entry->data.error_union.err_set_type = err_set_type;
540 entry->data.error_union.payload_type = payload_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;
536554 } else {
537555 LLVMTypeRef elem_types[] = {
538 g->err_tag_type->type_ref,
539 child_type->type_ref,
556 err_set_type->type_ref,
557 payload_type->type_ref,
540558 };
541559 entry->type_ref = LLVMStructType(elem_types, 2, false);
542560
......@@ -547,12 +565,12 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
547565 ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),
548566 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);
551 uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(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);
569 uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_type->type_ref);
552570 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);
555 uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(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);
573 uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_type->type_ref);
556574 uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref,
557575 err_union_payload_index);
558576
......@@ -565,13 +583,13 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
565583 tag_debug_size_in_bits,
566584 tag_debug_align_in_bits,
567585 tag_offset_in_bits,
568 0, child_type->di_type),
586 0, err_set_type->di_type),
569587 ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
570588 "value", di_file, line,
571589 value_debug_size_in_bits,
572590 value_debug_align_in_bits,
573591 value_offset_in_bits,
574 0, child_type->di_type),
592 0, payload_type->di_type),
575593 };
576594
577595 ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
......@@ -587,7 +605,7 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
587605 entry->di_type = replacement_di_type;
588606 }
589607
590 child_type->error_parent = entry;
608 g->type_table.put(type_id, entry);
591609 return entry;
592610}
593611
......@@ -937,7 +955,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
937955 handle_is_ptr(fn_type_id->return_type);
938956 bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&
939957 (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
940 fn_type_id->return_type->id == TypeTableEntryIdPureError);
958 fn_type_id->return_type->id == TypeTableEntryIdErrorSet);
941959 // +1 for maybe making the first argument the return value
942960 // +1 for maybe last argument the error return trace
943961 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) {
11771195 case TypeTableEntryIdUndefLit:
11781196 case TypeTableEntryIdNullLit:
11791197 case TypeTableEntryIdErrorUnion:
1180 case TypeTableEntryIdPureError:
1198 case TypeTableEntryIdErrorSet:
11811199 case TypeTableEntryIdNamespace:
11821200 case TypeTableEntryIdBlock:
11831201 case TypeTableEntryIdBoundFn:
......@@ -1218,7 +1236,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
12181236 case TypeTableEntryIdUndefLit:
12191237 case TypeTableEntryIdNullLit:
12201238 case TypeTableEntryIdErrorUnion:
1221 case TypeTableEntryIdPureError:
1239 case TypeTableEntryIdErrorSet:
12221240 case TypeTableEntryIdNamespace:
12231241 case TypeTableEntryIdBlock:
12241242 case TypeTableEntryIdBoundFn:
......@@ -1263,7 +1281,23 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
12631281 zig_unreachable();
12641282}
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) {
12671301 assert(proto_node->type == NodeTypeFnProto);
12681302 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
13591393 case TypeTableEntryIdStruct:
13601394 case TypeTableEntryIdMaybe:
13611395 case TypeTableEntryIdErrorUnion:
1362 case TypeTableEntryIdPureError:
1396 case TypeTableEntryIdErrorSet:
13631397 case TypeTableEntryIdEnum:
13641398 case TypeTableEntryIdUnion:
13651399 case TypeTableEntryIdFn:
......@@ -1382,13 +1416,19 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
13821416 }
13831417 }
13841418
1385 fn_type_id.return_type = (fn_proto->return_type == nullptr) ?
1386 g->builtin_types.entry_void : analyze_type_expr(g, child_scope, fn_proto->return_type);
1387
1388 if (type_is_invalid(fn_type_id.return_type)) {
1419 TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
1420 if (type_is_invalid(specified_return_type)) {
1421 fn_type_id.return_type = g->builtin_types.entry_invalid;
13891422 return g->builtin_types.entry_invalid;
13901423 }
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
13921432 if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, fn_type_id.return_type)) {
13931433 add_node_error(g, fn_proto->return_type,
13941434 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
14341474 case TypeTableEntryIdStruct:
14351475 case TypeTableEntryIdMaybe:
14361476 case TypeTableEntryIdErrorUnion:
1437 case TypeTableEntryIdPureError:
1477 case TypeTableEntryIdErrorSet:
14381478 case TypeTableEntryIdEnum:
14391479 case TypeTableEntryIdUnion:
14401480 case TypeTableEntryIdFn:
......@@ -2756,7 +2796,8 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) {
27562796 return g->test_fn_type;
27572797
27582798 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);
27602801 g->test_fn_type = get_fn_type(g, &fn_type_id);
27612802 return g->test_fn_type;
27622803}
......@@ -2824,7 +2865,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
28242865
28252866 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
28292870 if (fn_proto->section_expr != nullptr) {
28302871 if (fn_table_entry->body_node == nullptr) {
......@@ -2949,29 +2990,6 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope
29492990 g->resolve_queue.append(&tld_fn->base);
29502991}
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
29752993static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
29762994 assert(node->type == NodeTypeCompTime);
29772995
......@@ -3045,10 +3063,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
30453063 import->use_decls.append(node);
30463064 break;
30473065 }
3048 case NodeTypeErrorValueDecl:
3049 // error value declarations do not depend on other top level decls
3050 preview_error_value_decl(g, node);
3051 break;
30523066 case NodeTypeTestDecl:
30533067 preview_test_decl(g, node, decls_scope);
30543068 break;
......@@ -3097,6 +3111,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
30973111 case NodeTypeVarLiteral:
30983112 case NodeTypeIfErrorExpr:
30993113 case NodeTypeTestExpr:
3114 case NodeTypeErrorSetDecl:
31003115 zig_unreachable();
31013116 }
31023117}
......@@ -3147,7 +3162,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
31473162 case TypeTableEntryIdStruct:
31483163 case TypeTableEntryIdMaybe:
31493164 case TypeTableEntryIdErrorUnion:
3150 case TypeTableEntryIdPureError:
3165 case TypeTableEntryIdErrorSet:
31513166 case TypeTableEntryIdEnum:
31523167 case TypeTableEntryIdUnion:
31533168 case TypeTableEntryIdFn:
......@@ -3362,108 +3377,6 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so
33623377 g->tld_ref_source_node_stack.pop();
33633378}
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
34673380Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
34683381 // we must resolve all the use decls
34693382 ImportTableEntry *import = get_scope_import(scope);
......@@ -3625,7 +3538,7 @@ static bool is_container(TypeTableEntry *type_entry) {
36253538 case TypeTableEntryIdNullLit:
36263539 case TypeTableEntryIdMaybe:
36273540 case TypeTableEntryIdErrorUnion:
3628 case TypeTableEntryIdPureError:
3541 case TypeTableEntryIdErrorSet:
36293542 case TypeTableEntryIdFn:
36303543 case TypeTableEntryIdNamespace:
36313544 case TypeTableEntryIdBlock:
......@@ -3673,7 +3586,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
36733586 case TypeTableEntryIdNullLit:
36743587 case TypeTableEntryIdMaybe:
36753588 case TypeTableEntryIdErrorUnion:
3676 case TypeTableEntryIdPureError:
3589 case TypeTableEntryIdErrorSet:
36773590 case TypeTableEntryIdFn:
36783591 case TypeTableEntryIdNamespace:
36793592 case TypeTableEntryIdBlock:
......@@ -3765,6 +3678,27 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari
37653678 }
37663679}
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
37683702void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {
37693703 TypeTableEntry *fn_type = fn_table_entry->type_entry;
37703704 assert(!fn_type->data.fn.is_generic);
......@@ -3774,14 +3708,49 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
37743708 &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
37753709 fn_table_entry->implicit_return_type = block_return_type;
37763710
3777 if (block_return_type->id == TypeTableEntryIdInvalid ||
3778 fn_table_entry->analyzed_executable.invalid)
3779 {
3711 if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
37803712 assert(g->errors.length > 0);
37813713 fn_table_entry->anal_state = FnAnalStateInvalid;
37823714 return;
37833715 }
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
37853754 if (g->verbose_ir) {
37863755 fprintf(stderr, "{ // (analyzed)\n");
37873756 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
37913760 fn_table_entry->anal_state = FnAnalStateComplete;
37923761}
37933762
3794static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
3763void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
37953764 assert(fn_table_entry->anal_state != FnAnalStateProbing);
37963765 if (fn_table_entry->anal_state != FnAnalStateReady)
37973766 return;
......@@ -4022,7 +3991,8 @@ void semantic_analyze(CodeGen *g) {
40223991 for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
40233992 Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
40243993 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);
40263996 }
40273997
40283998 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) {
41144084 case TypeTableEntryIdInt:
41154085 case TypeTableEntryIdFloat:
41164086 case TypeTableEntryIdPointer:
4117 case TypeTableEntryIdPureError:
4087 case TypeTableEntryIdErrorSet:
41184088 case TypeTableEntryIdFn:
41194089 case TypeTableEntryIdEnum:
41204090 return false;
......@@ -4122,7 +4092,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
41224092 case TypeTableEntryIdStruct:
41234093 return type_has_bits(type_entry);
41244094 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);
41264096 case TypeTableEntryIdMaybe:
41274097 return type_has_bits(type_entry->data.maybe.child_type) &&
41284098 type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
......@@ -4386,9 +4356,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
43864356 case TypeTableEntryIdErrorUnion:
43874357 // TODO better hashing algorithm
43884358 return 3415065496;
4389 case TypeTableEntryIdPureError:
4390 // TODO better hashing algorithm
4391 return 2630160122;
4359 case TypeTableEntryIdErrorSet:
4360 assert(const_val->data.x_err_set != nullptr);
4361 return const_val->data.x_err_set->value ^ 2630160122;
43924362 case TypeTableEntryIdFn:
43934363 return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry);
43944364 case TypeTableEntryIdNamespace:
......@@ -4515,7 +4485,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
45154485 case TypeTableEntryIdMaybe:
45164486 case TypeTableEntryIdErrorUnion:
45174487 case TypeTableEntryIdEnum:
4518 case TypeTableEntryIdPureError:
4488 case TypeTableEntryIdErrorSet:
45194489 case TypeTableEntryIdFn:
45204490 case TypeTableEntryIdBool:
45214491 case TypeTableEntryIdInt:
......@@ -4894,8 +4864,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
48944864 return a->data.x_type == b->data.x_type;
48954865 case TypeTableEntryIdVoid:
48964866 return true;
4897 case TypeTableEntryIdPureError:
4898 return a->data.x_pure_err == b->data.x_pure_err;
4867 case TypeTableEntryIdErrorSet:
4868 return a->data.x_err_set->value == b->data.x_err_set->value;
48994869 case TypeTableEntryIdFn:
49004870 return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry;
49014871 case TypeTableEntryIdBool:
......@@ -5256,9 +5226,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
52565226 buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name));
52575227 return;
52585228 }
5259 case TypeTableEntryIdPureError:
5229 case TypeTableEntryIdErrorSet:
52605230 {
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));
52625232 return;
52635233 }
52645234 case TypeTableEntryIdArgTuple:
......@@ -5319,8 +5289,7 @@ uint32_t type_id_hash(TypeId x) {
53195289 case TypeTableEntryIdUndefLit:
53205290 case TypeTableEntryIdNullLit:
53215291 case TypeTableEntryIdMaybe:
5322 case TypeTableEntryIdErrorUnion:
5323 case TypeTableEntryIdPureError:
5292 case TypeTableEntryIdErrorSet:
53245293 case TypeTableEntryIdEnum:
53255294 case TypeTableEntryIdUnion:
53265295 case TypeTableEntryIdFn:
......@@ -5329,6 +5298,8 @@ uint32_t type_id_hash(TypeId x) {
53295298 case TypeTableEntryIdBoundFn:
53305299 case TypeTableEntryIdArgTuple:
53315300 zig_unreachable();
5301 case TypeTableEntryIdErrorUnion:
5302 return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
53325303 case TypeTableEntryIdPointer:
53335304 return hash_ptr(x.data.pointer.child_type) +
53345305 (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
......@@ -5363,8 +5334,7 @@ bool type_id_eql(TypeId a, TypeId b) {
53635334 case TypeTableEntryIdUndefLit:
53645335 case TypeTableEntryIdNullLit:
53655336 case TypeTableEntryIdMaybe:
5366 case TypeTableEntryIdErrorUnion:
5367 case TypeTableEntryIdPureError:
5337 case TypeTableEntryIdErrorSet:
53685338 case TypeTableEntryIdEnum:
53695339 case TypeTableEntryIdUnion:
53705340 case TypeTableEntryIdFn:
......@@ -5374,6 +5344,10 @@ bool type_id_eql(TypeId a, TypeId b) {
53745344 case TypeTableEntryIdArgTuple:
53755345 case TypeTableEntryIdOpaque:
53765346 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
53775351 case TypeTableEntryIdPointer:
53785352 return a.data.pointer.child_type == b.data.pointer.child_type &&
53795353 a.data.pointer.is_const == b.data.pointer.is_const &&
......@@ -5478,7 +5452,7 @@ static const TypeTableEntryId all_type_ids[] = {
54785452 TypeTableEntryIdNullLit,
54795453 TypeTableEntryIdMaybe,
54805454 TypeTableEntryIdErrorUnion,
5481 TypeTableEntryIdPureError,
5455 TypeTableEntryIdErrorSet,
54825456 TypeTableEntryIdEnum,
54835457 TypeTableEntryIdUnion,
54845458 TypeTableEntryIdFn,
......@@ -5533,7 +5507,7 @@ size_t type_id_index(TypeTableEntryId id) {
55335507 return 13;
55345508 case TypeTableEntryIdErrorUnion:
55355509 return 14;
5536 case TypeTableEntryIdPureError:
5510 case TypeTableEntryIdErrorSet:
55375511 return 15;
55385512 case TypeTableEntryIdEnum:
55395513 return 16;
......@@ -5590,8 +5564,8 @@ const char *type_id_name(TypeTableEntryId id) {
55905564 return "Nullable";
55915565 case TypeTableEntryIdErrorUnion:
55925566 return "ErrorUnion";
5593 case TypeTableEntryIdPureError:
5594 return "Error";
5567 case TypeTableEntryIdErrorSet:
5568 return "ErrorSet";
55955569 case TypeTableEntryIdEnum:
55965570 return "Enum";
55975571 case TypeTableEntryIdUnion:
......@@ -5640,17 +5614,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
56405614 return link_lib;
56415615}
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
56545617uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
56555618 type_ensure_zero_bits_known(g, type_entry);
56565619 if (type_entry->zero_bits) return 0;
......@@ -5696,3 +5659,8 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
56965659 return var_value;
56975660}
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);
3030TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
3131 AstNode *decl_node, const char *name, ContainerLayout layout);
3232TypeTableEntry *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);
3434TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry);
3535TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name);
3636TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
......@@ -46,8 +46,6 @@ bool type_has_bits(TypeTableEntry *type_entry);
4646ImportTableEntry *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);
5149VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name);
5250Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);
5351void 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
5856TypeTableEntry *container_ref_type(TypeTableEntry *type_entry);
5957bool type_is_complete(TypeTableEntry *type_entry);
6058bool type_is_invalid(TypeTableEntry *type_entry);
59bool type_is_global_error_set(TypeTableEntry *err_set_type);
6160bool type_has_zero_bits_known(TypeTableEntry *type_entry);
6261void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
6362ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
......@@ -176,7 +175,6 @@ bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
176175LinkLib *create_link_lib(Buf *name);
177176bool calling_convention_does_first_arg_return(CallingConvention cc);
178177LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
179void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name);
180178
181179uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry);
182180TypeTableEntry *get_align_amt_type(CodeGen *g);
......@@ -188,6 +186,8 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
188186
189187ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
190188TypeTableEntry *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
193193#endif
src/ast_render.cpp+26-7
......@@ -49,11 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) {
4949 case BinOpTypeAssignBitAnd: return "&=";
5050 case BinOpTypeAssignBitXor: return "^=";
5151 case BinOpTypeAssignBitOr: return "|=";
52 case BinOpTypeAssignBoolAnd: return "&&=";
53 case BinOpTypeAssignBoolOr: return "||=";
52 case BinOpTypeAssignMergeErrorSets: return "||=";
5453 case BinOpTypeUnwrapMaybe: return "??";
5554 case BinOpTypeArrayCat: return "++";
5655 case BinOpTypeArrayMult: return "**";
56 case BinOpTypeErrorUnion: return "!";
57 case BinOpTypeMergeErrorSets: return "||";
5758 }
5859 zig_unreachable();
5960}
......@@ -67,7 +68,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
6768 case PrefixOpBinNot: return "~";
6869 case PrefixOpDereference: return "*";
6970 case PrefixOpMaybe: return "?";
70 case PrefixOpError: return "%";
7171 case PrefixOpUnwrapMaybe: return "??";
7272 }
7373 zig_unreachable();
......@@ -174,8 +174,6 @@ static const char *node_type_str(NodeType node_type) {
174174 return "Defer";
175175 case NodeTypeVariableDeclaration:
176176 return "VariableDeclaration";
177 case NodeTypeErrorValueDecl:
178 return "ErrorValueDecl";
179177 case NodeTypeTestDecl:
180178 return "TestDecl";
181179 case NodeTypeIntLiteral:
......@@ -244,6 +242,8 @@ static const char *node_type_str(NodeType node_type) {
244242 return "IfErrorExpr";
245243 case NodeTypeTestExpr:
246244 return "TestExpr";
245 case NodeTypeErrorSetDecl:
246 return "ErrorSetDecl";
247247 }
248248 zig_unreachable();
249249}
......@@ -396,7 +396,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
396396
397397 if (child->type == NodeTypeUse ||
398398 child->type == NodeTypeVariableDeclaration ||
399 child->type == NodeTypeErrorValueDecl ||
400399 child->type == NodeTypeFnProto)
401400 {
402401 fprintf(ar->f, ";");
......@@ -452,6 +451,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
452451 AstNode *return_type_node = node->data.fn_proto.return_type;
453452 assert(return_type_node != nullptr);
454453 fprintf(ar->f, " ");
454 if (node->data.fn_proto.auto_err_set) {
455 fprintf(ar->f, "!");
456 }
455457 render_node_grouped(ar, return_type_node);
456458 break;
457459 }
......@@ -1017,9 +1019,26 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
10171019 render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
10181020 break;
10191021 }
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 }
10201040 case NodeTypeFnDecl:
10211041 case NodeTypeParamDecl:
1022 case NodeTypeErrorValueDecl:
10231042 case NodeTypeTestDecl:
10241043 case NodeTypeStructField:
10251044 case NodeTypeUse:
src/codegen.cpp+181-100
......@@ -92,9 +92,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
9292 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
9393 buf_resize(&g->global_asm, 0);
9494
95 // reserve index 0 to indicate no error
96 g->error_decls.append(nullptr);
97
9895 if (root_src_path) {
9996 Buf *src_basename = buf_alloc();
10097 Buf *src_dir = buf_alloc();
......@@ -256,6 +253,10 @@ LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) {
256253 return add_link_lib(g, name);
257254}
258255
256void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) {
257 codegen->forbidden_libs.append(lib);
258}
259
259260void codegen_add_framework(CodeGen *g, const char *framework) {
260261 g->darwin_frameworks.append(buf_create_from_str(framework));
261262}
......@@ -282,9 +283,9 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) {
282283}
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);
286287static 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);
288289static void generate_error_name_table(CodeGen *g);
289290
290291static 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
410411 }
411412 TypeTableEntry *fn_type = fn_table_entry->type_entry;
412413 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) {
414415 return UINT32_MAX;
415416 }
416417 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) {
873874 ConstExprValue *array_val = create_const_str_lit(g, buf_msg);
874875 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, "");
877878 render_const_val_global(g, val, "");
878879
879880 assert(val->global_refs->llvm_global);
......@@ -1412,7 +1413,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
14121413 if (!instruction->llvm_value) {
14131414 assert(instruction->value.special != ConstValSpecialRuntime);
14141415 assert(instruction->value.type);
1415 render_const_val(g, &instruction->value);
1416 render_const_val(g, &instruction->value, "");
14161417 // we might have to do some pointer casting here due to the way union
14171418 // values are rendered with a type other than the one we expect
14181419 if (handle_is_ptr(instruction->value.type)) {
......@@ -1442,7 +1443,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
14421443 is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
14431444 // TODO: emit a branch to check if the return value is an error
14441445 }
1445 } else if (return_type->id == TypeTableEntryIdPureError) {
1446 } else if (return_type->id == TypeTableEntryIdErrorSet) {
14461447 is_err_return = true;
14471448 }
14481449 if (is_err_return) {
......@@ -1789,7 +1790,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
17891790
17901791 assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy ||
17911792 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));
17931795 TypeTableEntry *type_entry = op1->value.type;
17941796
17951797 bool want_runtime_safety = bin_op_instruction->safety_check_on &&
......@@ -1802,6 +1804,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
18021804 case IrBinOpArrayCat:
18031805 case IrBinOpArrayMult:
18041806 case IrBinOpRemUnspecified:
1807 case IrBinOpMergeErrorSets:
18051808 zig_unreachable();
18061809 case IrBinOpBoolOr:
18071810 return LLVMBuildOr(g->builder, op1_value, op2_value, "");
......@@ -1823,7 +1826,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
18231826 } else if (type_entry->id == TypeTableEntryIdEnum) {
18241827 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
18251828 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
1826 } else if (type_entry->id == TypeTableEntryIdPureError ||
1829 } else if (type_entry->id == TypeTableEntryIdErrorSet ||
18271830 type_entry->id == TypeTableEntryIdPointer ||
18281831 type_entry->id == TypeTableEntryIdBool)
18291832 {
......@@ -1955,6 +1958,54 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
19551958 zig_unreachable();
19561959}
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
19582009static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
19592010 IrInstructionCast *cast_instruction)
19602011{
......@@ -2078,6 +2129,11 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
20782129 assert(wanted_type->id == TypeTableEntryIdInt);
20792130 assert(actual_type->id == TypeTableEntryIdBool);
20802131 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;
20812137 }
20822138 zig_unreachable();
20832139}
......@@ -2139,7 +2195,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
21392195
21402196static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) {
21412197 TypeTableEntry *wanted_type = instruction->base.value.type;
2142 assert(wanted_type->id == TypeTableEntryIdPureError);
2198 assert(wanted_type->id == TypeTableEntryIdErrorSet);
21432199
21442200 TypeTableEntry *actual_type = instruction->target->value.type;
21452201 assert(actual_type->id == TypeTableEntryIdInt);
......@@ -2148,32 +2204,7 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I
21482204 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
21492205
21502206 if (ir_want_runtime_safety(g, &instruction->base)) {
2151 LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
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);
2207 add_error_range_check(g, wanted_type, actual_type, target_val);
21772208 }
21782209
21792210 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
21872218 TypeTableEntry *actual_type = instruction->target->value.type;
21882219 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
21892220
2190 if (actual_type->id == TypeTableEntryIdPureError) {
2221 if (actual_type->id == TypeTableEntryIdErrorSet) {
21912222 return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
21922223 g->err_tag_type, wanted_type, target_val);
21932224 } 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)) {
21952229 return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
21962230 g->err_tag_type, wanted_type, target_val);
21972231 } else {
2198 zig_panic("TODO");
2232 zig_panic("TODO err to int when error union payload type not void");
21992233 }
22002234 } else {
22012235 zig_unreachable();
......@@ -2235,7 +2269,6 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
22352269
22362270 switch (op_id) {
22372271 case IrUnOpInvalid:
2238 case IrUnOpError:
22392272 case IrUnOpMaybe:
22402273 case IrUnOpDereference:
22412274 zig_unreachable();
......@@ -2489,7 +2522,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
24892522 TypeTableEntry *src_return_type = fn_type_id->return_type;
24902523 bool ret_has_bits = type_has_bits(src_return_type);
24912524 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);
24932526 size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
24942527 bool is_var_args = fn_type_id->is_var_args;
24952528 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
......@@ -2907,7 +2940,7 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru
29072940static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrInstructionErrName *instruction) {
29082941 assert(g->generate_error_name_table);
29092942
2910 if (g->error_decls.length == 1) {
2943 if (g->errors_by_index.length == 1) {
29112944 LLVMBuildUnreachable(g->builder);
29122945 return nullptr;
29132946 }
......@@ -2915,7 +2948,7 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
29152948 LLVMValueRef err_val = ir_llvm_value(g, instruction->value);
29162949 if (ir_want_runtime_safety(g, &instruction->base)) {
29172950 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);
29192952 add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
29202953 }
29212954
......@@ -3393,11 +3426,11 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable,
33933426
33943427static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) {
33953428 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;
33973430 LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->value);
33983431
33993432 LLVMValueRef err_val;
3400 if (type_has_bits(child_type)) {
3433 if (type_has_bits(payload_type)) {
34013434 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
34023435 err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");
34033436 } else {
......@@ -3412,11 +3445,11 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
34123445 TypeTableEntry *ptr_type = instruction->value->value.type;
34133446 assert(ptr_type->id == TypeTableEntryIdPointer);
34143447 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;
34163449 LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
34173450 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)) {
34203453 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
34213454 return gen_load_untyped(g, err_val_ptr, 0, false, "");
34223455 } else {
......@@ -3428,13 +3461,17 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
34283461 TypeTableEntry *ptr_type = instruction->value->value.type;
34293462 assert(ptr_type->id == TypeTableEntryIdPointer);
34303463 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;
34323465 LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
34333466 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) {
34363473 LLVMValueRef err_val;
3437 if (type_has_bits(child_type)) {
3474 if (type_has_bits(payload_type)) {
34383475 LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
34393476 err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");
34403477 } else {
......@@ -3452,7 +3489,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
34523489 LLVMPositionBuilderAtEnd(g->builder, ok_block);
34533490 }
34543491
3455 if (type_has_bits(child_type)) {
3492 if (type_has_bits(payload_type)) {
34563493 return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, "");
34573494 } else {
34583495 return nullptr;
......@@ -3493,10 +3530,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable
34933530
34943531 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
34973536 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))
35003539 return err_val;
35013540
35023541 assert(instruction->tmp_ptr);
......@@ -3512,11 +3551,16 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
35123551
35133552 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
35173561 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))
35203564 return ok_err_val;
35213565
35223566 assert(instruction->tmp_ptr);
......@@ -3527,7 +3571,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
35273571 gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false);
35283572
35293573 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
35323576 return instruction->tmp_ptr;
35333577}
......@@ -3700,6 +3744,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
37003744 case IrInstructionIdArgType:
37013745 case IrInstructionIdTagType:
37023746 case IrInstructionIdExport:
3747 case IrInstructionIdErrorUnion:
37033748 zig_unreachable();
37043749 case IrInstructionIdReturn:
37053750 return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
......@@ -3847,7 +3892,7 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *ar
38473892static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
38483893 switch (parent->id) {
38493894 case ConstParentIdNone:
3850 render_const_val(g, val);
3895 render_const_val(g, val, "");
38513896 render_const_val_global(g, val, "");
38523897 return val->global_refs->llvm_global;
38533898 case ConstParentIdStruct:
......@@ -3933,7 +3978,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
39333978 case TypeTableEntryIdUndefLit:
39343979 case TypeTableEntryIdNullLit:
39353980 case TypeTableEntryIdErrorUnion:
3936 case TypeTableEntryIdPureError:
3981 case TypeTableEntryIdErrorSet:
39373982 case TypeTableEntryIdNamespace:
39383983 case TypeTableEntryIdBlock:
39393984 case TypeTableEntryIdBoundFn:
......@@ -3946,17 +3991,17 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
39463991 case TypeTableEntryIdEnum:
39473992 {
39483993 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, "");
39503995 return LLVMConstZExt(int_val, big_int_type_ref);
39513996 }
39523997 case TypeTableEntryIdInt:
39533998 {
3954 LLVMValueRef int_val = gen_const_val(g, const_val);
3999 LLVMValueRef int_val = gen_const_val(g, const_val, "");
39554000 return LLVMConstZExt(int_val, big_int_type_ref);
39564001 }
39574002 case TypeTableEntryIdFloat:
39584003 {
3959 LLVMValueRef float_val = gen_const_val(g, const_val);
4004 LLVMValueRef float_val = gen_const_val(g, const_val, "");
39604005 LLVMValueRef int_val = LLVMConstFPToUI(float_val,
39614006 LLVMIntType((unsigned)type_entry->data.floating.bit_count));
39624007 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
39654010 case TypeTableEntryIdFn:
39664011 case TypeTableEntryIdMaybe:
39674012 {
3968 LLVMValueRef ptr_val = gen_const_val(g, const_val);
4013 LLVMValueRef ptr_val = gen_const_val(g, const_val, "");
39694014 LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref);
39704015 return LLVMConstZExt(ptr_size_int_val, big_int_type_ref);
39714016 }
......@@ -4010,7 +4055,7 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
40104055 return LLVMTypeOf(val) != type_entry->type_ref;
40114056}
40124057
4013static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
4058static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
40144059 TypeTableEntry *type_entry = const_val->type;
40154060 assert(!type_entry->zero_bits);
40164061
......@@ -4026,10 +4071,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
40264071 switch (type_entry->id) {
40274072 case TypeTableEntryIdInt:
40284073 return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);
4029 case TypeTableEntryIdPureError:
4030 assert(const_val->data.x_pure_err);
4031 return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref,
4032 const_val->data.x_pure_err->value, false);
4074 case TypeTableEntryIdErrorSet:
4075 assert(const_val->data.x_err_set != nullptr);
4076 return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref,
4077 const_val->data.x_err_set->value, false);
40334078 case TypeTableEntryIdFloat:
40344079 switch (type_entry->data.floating.bit_count) {
40354080 case 32:
......@@ -4063,7 +4108,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
40634108 child_type->id == TypeTableEntryIdFn)
40644109 {
40654110 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, "");
40674112 } else {
40684113 return LLVMConstNull(child_type->type_ref);
40694114 }
......@@ -4072,7 +4117,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
40724117 LLVMValueRef maybe_val;
40734118 bool make_unnamed_struct;
40744119 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, "");
40764121 maybe_val = LLVMConstAllOnes(LLVMInt1Type());
40774122
40784123 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) {
41164161
41174162 if (src_field_index + 1 == src_field_index_end) {
41184163 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, "");
41204165 fields[type_struct_field->gen_index] = val;
41214166 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
41224167 } else {
......@@ -4156,7 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
41564201 continue;
41574202 }
41584203 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, "");
41604205 fields[type_struct_field->gen_index] = val;
41614206 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
41624207 }
......@@ -4180,7 +4225,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
41804225 bool make_unnamed_struct = false;
41814226 for (uint64_t i = 0; i < len; i += 1) {
41824227 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, "");
41844229 values[i] = val;
41854230 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
41864231 }
......@@ -4215,7 +4260,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
42154260 } else {
42164261 uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);
42174262 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, "");
42194264 make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||
42204265 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) {
42604305 return fn_llvm_value(g, const_val->data.x_fn.fn_entry);
42614306 case TypeTableEntryIdPointer:
42624307 {
4263 render_const_val_global(g, const_val, "");
4308 render_const_val_global(g, const_val, name);
42644309 switch (const_val->data.x_ptr.special) {
42654310 case ConstPtrSpecialInvalid:
42664311 case ConstPtrSpecialDiscard:
......@@ -4268,7 +4313,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
42684313 case ConstPtrSpecialRef:
42694314 {
42704315 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
4271 render_const_val(g, pointee);
4316 render_const_val(g, pointee, "");
42724317 render_const_val_global(g, pointee, "");
42734318 ConstExprValue *other_val = pointee;
42744319 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) {
43304375 }
43314376 case TypeTableEntryIdErrorUnion:
43324377 {
4333 TypeTableEntry *child_type = type_entry->data.error.child_type;
4334 if (!type_has_bits(child_type)) {
4378 TypeTableEntry *payload_type = type_entry->data.error_union.payload_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));
43354382 uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0;
43364383 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, "");
43374387 } else {
43384388 LLVMValueRef err_tag_value;
43394389 LLVMValueRef err_payload_value;
43404390 bool make_unnamed_struct;
43414391 if (const_val->data.x_err_union.err) {
43424392 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);
43444394 make_unnamed_struct = false;
43454395 } else {
43464396 err_tag_value = LLVMConstNull(g->err_tag_type->type_ref);
43474397 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, "");
43494399 make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value);
43504400 }
43514401 LLVMValueRef fields[] = {
......@@ -4380,11 +4430,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
43804430 zig_unreachable();
43814431}
43824432
4383static void render_const_val(CodeGen *g, ConstExprValue *const_val) {
4433static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
43844434 if (!const_val->global_refs)
43854435 const_val->global_refs = allocate<ConstGlobalRefs>(1);
43864436 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
43894439 if (const_val->global_refs->llvm_global)
43904440 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
44104460}
44114461
44124462static 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) {
44144464 return;
44154465 }
44164466
4417 assert(g->error_decls.length > 0);
4467 assert(g->errors_by_index.length > 0);
44184468
44194469 TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
44204470 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);
44234473 values[0] = LLVMGetUndef(str_type->type_ref);
4424 for (size_t i = 1; i < g->error_decls.length; i += 1) {
4425 AstNode *error_decl_node = g->error_decls.at(i);
4426 assert(error_decl_node->type == NodeTypeErrorValueDecl);
4427 Buf *name = error_decl_node->data.error_value_decl.name;
4474 for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
4475 ErrorTableEntry *err_entry = g->errors_by_index.at(i);
4476 Buf *name = &err_entry->name;
44284477
44294478 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) {
44434492 values[i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
44444493 }
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
44484497 g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init),
44494498 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) {
45754624
45764625 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
45784649 generate_error_name_table(g);
45794650 generate_enum_name_tables(g);
45804651
......@@ -4592,7 +4663,7 @@ static void do_code_gen(CodeGen *g) {
45924663 coerced_value.special = ConstValSpecialStatic;
45934664 coerced_value.type = var_type;
45944665 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, "");
45964667 gen_global_var(g, var, init_val, var_type);
45974668 continue;
45984669 }
......@@ -4626,8 +4697,9 @@ static void do_code_gen(CodeGen *g) {
46264697 LLVMSetAlignment(global_value, var->align_bytes);
46274698 } else {
46284699 bool exported = (var->linkage == VarLinkageExport);
4629 render_const_val(g, var->value);
4630 render_const_val_global(g, var->value, buf_ptr(get_mangled_name(g, &var->name, exported)));
4700 const char *mangled_name = 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);
46314703 global_value = var->value->global_refs->llvm_global;
46324704
46334705 if (exported) {
......@@ -5176,16 +5248,24 @@ static void define_builtin_types(CodeGen *g) {
51765248 }
51775249
51785250 {
5179 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
5251 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorSet);
51805252 buf_init_from_str(&entry->name, "error");
5253 entry->data.error_set.err_count = UINT32_MAX;
51815254
51825255 // TODO allow overriding this type and keep track of max value and emit an
51835256 // error if there are too many errors declared
51845257 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;
51875260 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
51905270 g->primitive_type_table.put(&entry->name, entry);
51915271 }
......@@ -5815,7 +5895,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry
58155895 case TypeTableEntryIdBoundFn:
58165896 case TypeTableEntryIdArgTuple:
58175897 case TypeTableEntryIdErrorUnion:
5818 case TypeTableEntryIdPureError:
5898 case TypeTableEntryIdErrorSet:
58195899 zig_unreachable();
58205900 case TypeTableEntryIdVoid:
58215901 case TypeTableEntryIdUnreachable:
......@@ -5988,7 +6068,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
59886068 return;
59896069 }
59906070 case TypeTableEntryIdErrorUnion:
5991 case TypeTableEntryIdPureError:
6071 case TypeTableEntryIdErrorSet:
59926072 case TypeTableEntryIdFn:
59936073 zig_panic("TODO implement get_c_type for more types");
59946074 case TypeTableEntryIdInvalid:
......@@ -6155,7 +6235,7 @@ static void gen_h_file(CodeGen *g) {
61556235 case TypeTableEntryIdUndefLit:
61566236 case TypeTableEntryIdNullLit:
61576237 case TypeTableEntryIdErrorUnion:
6158 case TypeTableEntryIdPureError:
6238 case TypeTableEntryIdErrorSet:
61596239 case TypeTableEntryIdNamespace:
61606240 case TypeTableEntryIdBlock:
61616241 case TypeTableEntryIdBoundFn:
......@@ -6265,3 +6345,4 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
62656345 }
62666346 return pkg;
62676347}
6348
src/codegen.hpp+1
......@@ -36,6 +36,7 @@ void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir);
3636void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);
3737void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);
3838void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
39void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib);
3940LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);
4041void codegen_add_framework(CodeGen *codegen, const char *name);
4142void 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) {
4545 return { true, is_const, is_volatile };
4646}
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
48101static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
49102static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval);
50103static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
......@@ -580,6 +633,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace
580633 return IrInstructionIdErrorReturnTrace;
581634}
582635
636static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) {
637 return IrInstructionIdErrorUnion;
638}
639
583640template<typename T>
584641static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
585642 T *special_instruction = allocate<T>(1);
......@@ -2326,6 +2383,19 @@ static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope,
23262383 return &instruction->base;
23272384}
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
23292399static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
23302400 results[ReturnKindUnconditional] = 0;
23312401 results[ReturnKindError] = 0;
......@@ -2800,6 +2870,23 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
28002870 return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
28012871}
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
28032890static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) {
28042891 assert(node->type == NodeTypeBinOpExpr);
28052892
......@@ -2835,10 +2922,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
28352922 return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);
28362923 case BinOpTypeAssignBitOr:
28372924 return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);
2838 case BinOpTypeAssignBoolAnd:
2839 return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd);
2840 case BinOpTypeAssignBoolOr:
2841 return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr);
2925 case BinOpTypeAssignMergeErrorSets:
2926 return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets);
28422927 case BinOpTypeBoolOr:
28432928 return ir_gen_bool_or(irb, scope, node);
28442929 case BinOpTypeBoolAnd:
......@@ -2885,8 +2970,12 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
28852970 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
28862971 case BinOpTypeArrayMult:
28872972 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
2973 case BinOpTypeMergeErrorSets:
2974 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
28882975 case BinOpTypeUnwrapMaybe:
28892976 return ir_gen_maybe_ok_or(irb, scope, node);
2977 case BinOpTypeErrorUnion:
2978 return ir_gen_error_union(irb, scope, node);
28902979 }
28912980 zig_unreachable();
28922981}
......@@ -3990,8 +4079,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
39904079 return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval);
39914080 case PrefixOpMaybe:
39924081 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);
39954082 case PrefixOpUnwrapMaybe:
39964083 return ir_gen_maybe_assert_ok(irb, scope, node, lval);
39974084 }
......@@ -4713,12 +4800,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
47134800 IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse");
47144801 IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
47154802
4716 IrInstruction *is_comptime;
4717 if (ir_should_inline(irb->exec, scope)) {
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 }
4803 bool force_comptime = 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);
47224805 ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
47234806
47244807 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 *
47274810 if (var_symbol) {
47284811 IrInstruction *var_type = nullptr;
47294812 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);
47304814 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
47334817 IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false);
47344818 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
51655249
51665250static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
51675251 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);
51695253}
51705254
51715255static 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
52495333 Scope *err_scope;
52505334 if (var_node) {
52515335 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);
52545336 Buf *var_name = var_node->data.symbol_expr.symbol;
52555337 bool is_const = true;
52565338 bool is_shadowable = false;
......@@ -5258,7 +5340,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
52585340 is_const, is_const, is_shadowable, is_comptime);
52595341 err_scope = var->child_scope;
52605342 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);
52625344 } else {
52635345 err_scope = parent_scope;
52645346 }
......@@ -5348,6 +5430,135 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
53485430 return ir_build_const_type(irb, parent_scope, node, container_type);
53495431}
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
53515562static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
53525563 assert(node->type == NodeTypeFnProto);
53535564
......@@ -5401,7 +5612,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
54015612 case NodeTypeStructField:
54025613 case NodeTypeFnDef:
54035614 case NodeTypeFnDecl:
5404 case NodeTypeErrorValueDecl:
54055615 case NodeTypeTestDecl:
54065616 zig_unreachable();
54075617 case NodeTypeBlock:
......@@ -5482,6 +5692,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
54825692 return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
54835693 case NodeTypeFnProto:
54845694 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);
54855697 }
54865698 zig_unreachable();
54875699}
......@@ -6287,6 +6499,274 @@ static bool slice_is_const(TypeTableEntry *type) {
62876499 return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
62886500}
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
62906770enum ImplicitCastMatchResult {
62916771 ImplicitCastMatchResultNo,
62926772 ImplicitCastMatchResultYes,
......@@ -6296,10 +6776,46 @@ enum ImplicitCastMatchResult {
62966776static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type,
62976777 TypeTableEntry *actual_type, IrInstruction *value)
62986778{
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) {
63006782 return ImplicitCastMatchResultYes;
63016783 }
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
63036819 // implicit conversion from anything to var
63046820 if (expected_type->id == TypeTableEntryIdVar) {
63056821 return ImplicitCastMatchResultYes;
......@@ -6319,25 +6835,25 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
63196835 return ImplicitCastMatchResultYes;
63206836 }
63216837
6322 // implicit T to %T
6838 // implicit T to U!T
63236839 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))
63256841 {
63266842 return ImplicitCastMatchResultYes;
63276843 }
63286844
6329 // implicit conversion from pure error to error union type
6845 // implicit conversion from error set to error union type
63306846 if (expected_type->id == TypeTableEntryIdErrorUnion &&
6331 actual_type->id == TypeTableEntryIdPureError)
6847 actual_type->id == TypeTableEntryIdErrorSet)
63326848 {
63336849 return ImplicitCastMatchResultYes;
63346850 }
63356851
6336 // implicit conversion from T to %?T
6852 // implicit conversion from T to U!?T
63376853 if (expected_type->id == TypeTableEntryIdErrorUnion &&
6338 expected_type->data.error.child_type->id == TypeTableEntryIdMaybe &&
6854 expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
63396855 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,
63416857 actual_type, value))
63426858 {
63436859 return ImplicitCastMatchResultYes;
......@@ -6374,7 +6890,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
63746890 assert(ptr_type->id == TypeTableEntryIdPointer);
63756891
63766892 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)
63786894 {
63796895 return ImplicitCastMatchResultYes;
63806896 }
......@@ -6392,7 +6908,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
63926908 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
63936909
63946910 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)
63966912 {
63976913 return ImplicitCastMatchResultYes;
63986914 }
......@@ -6408,7 +6924,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
64086924 expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
64096925 assert(ptr_type->id == TypeTableEntryIdPointer);
64106926 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)
64126928 {
64136929 return ImplicitCastMatchResultYes;
64146930 }
......@@ -6423,7 +6939,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
64236939 expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
64246940 assert(ptr_type->id == TypeTableEntryIdPointer);
64256941 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)
64276943 {
64286944 return ImplicitCastMatchResultYes;
64296945 }
......@@ -6503,7 +7019,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
65037019 // implicitly take a const pointer to something
65047020 if (!type_requires_comptime(actual_type)) {
65057021 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) {
65077023 return ImplicitCastMatchResultYes;
65087024 }
65097025 }
......@@ -6511,13 +7027,39 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
65117027 return ImplicitCastMatchResultNo;
65127028}
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
65147036static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
65157037 assert(instruction_count >= 1);
65167038 IrInstruction *prev_inst = instructions[0];
65177039 if (type_is_invalid(prev_inst->value.type)) {
65187040 return ira->codegen->builtin_types.entry_invalid;
65197041 }
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
65217063 bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit);
65227064 bool convert_to_const_slice = false;
65237065 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
65387080 continue;
65397081 }
65407082
6541 if (prev_type->id == TypeTableEntryIdPureError) {
7083 if (prev_type->id == TypeTableEntryIdNullLit) {
65427084 prev_inst = cur_inst;
65437085 continue;
65447086 }
65457087
6546 if (prev_type->id == TypeTableEntryIdNullLit) {
6547 prev_inst = cur_inst;
7088 if (cur_type->id == TypeTableEntryIdNullLit) {
7089 any_are_null = true;
65487090 continue;
65497091 }
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) {
65527218 if (prev_type->id == TypeTableEntryIdArray) {
65537219 convert_to_const_slice = true;
65547220 }
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);
65567265 continue;
65577266 }
65587267
6559 if (cur_type->id == TypeTableEntryIdNullLit) {
6560 any_are_null = true;
6561 continue;
7268 if (prev_type->id == TypeTableEntryIdErrorUnion && cur_type->id == TypeTableEntryIdErrorUnion) {
7269 TypeTableEntry *prev_payload_type = prev_type->data.error_union.payload_type;
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 }
65627350 }
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) {
65657353 continue;
65667354 }
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) {
65697357 prev_inst = cur_inst;
65707358 continue;
65717359 }
......@@ -6588,26 +7376,41 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
65887376 }
65897377
65907378 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)
65927380 {
65937381 continue;
65947382 }
65957383
65967384 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)
65987386 {
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 }
65997402 prev_inst = cur_inst;
66007403 continue;
66017404 }
66027405
66037406 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)
66057408 {
66067409 continue;
66077410 }
66087411
66097412 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)
66117414 {
66127415 prev_inst = cur_inst;
66137416 continue;
......@@ -6645,7 +7448,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
66457448
66467449 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
66477450 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)
66497452 {
66507453 convert_to_const_slice = true;
66517454 prev_inst = cur_inst;
......@@ -6654,7 +7457,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
66547457
66557458 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
66567459 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)
66587461 {
66597462 convert_to_const_slice = true;
66607463 continue;
......@@ -6663,8 +7466,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
66637466 if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&
66647467 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
66657468 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,
6667 cur_type->data.array.child_type))
7469 types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
7470 cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
66687471 {
66697472 convert_to_const_slice = false;
66707473 continue;
......@@ -6673,8 +7476,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
66737476 if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&
66747477 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
66757478 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,
6677 prev_type->data.array.child_type))
7479 types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
7480 prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
66787481 {
66797482 prev_inst = cur_inst;
66807483 convert_to_const_slice = false;
......@@ -6714,30 +7517,37 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
67147517
67157518 return ira->codegen->builtin_types.entry_invalid;
67167519 }
7520
7521 free(errors);
7522
67177523 if (convert_to_const_slice) {
67187524 assert(prev_inst->value.type->id == TypeTableEntryIdArray);
67197525 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true);
67207526 TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type);
6721 if (any_are_pure_error) {
6722 return get_error_type(ira->codegen, slice_type);
7527 if (err_set_type != nullptr) {
7528 return get_error_union_type(ira->codegen, err_set_type, slice_type);
67237529 } else {
67247530 return slice_type;
67257531 }
6726 } else if (any_are_pure_error && prev_inst->value.type->id != TypeTableEntryIdPureError) {
6727 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
6728 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
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;
7532 } else if (err_set_type != nullptr) {
7533 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
7534 return err_set_type;
67397535 } 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 }
67417551 }
67427552 } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) {
67437553 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
......@@ -6783,6 +7593,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
67837593 switch (cast_op) {
67847594 case CastOpNoCast:
67857595 zig_unreachable();
7596 case CastOpErrSet:
7597 zig_panic("TODO");
67867598 case CastOpNoop:
67877599 {
67887600 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
72138025 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
72148026
72158027 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;
72178029 IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
72188030 if (type_is_invalid(casted_payload->value.type))
72198031 return ira->codegen->invalid_instruction;
......@@ -7238,19 +8050,64 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction
72388050 return result;
72398051}
72408052
7241static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
7242 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
8053static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
8054 TypeTableEntry *wanted_type)
8055{
8056 assert(value->value.type->id == TypeTableEntryIdErrorSet);
8057 assert(wanted_type->id == TypeTableEntryIdErrorSet);
72438058
72448059 if (instr_is_comptime(value)) {
72458060 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
72468061 if (!val)
72478062 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
72498083 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
72508084 source_instr->scope, source_instr->source_node);
72518085 const_instruction->base.value.type = wanted_type;
72528086 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;
72548111 const_instruction->base.value.data.x_err_union.payload = nullptr;
72558112 return &const_instruction->base;
72568113 }
......@@ -7630,36 +8487,68 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction
76308487 return result;
76318488}
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{
76348493 assert(target->value.type->id == TypeTableEntryIdInt);
76358494 assert(!target->value.type->data.integral.is_signed);
8495 assert(wanted_type->id == TypeTableEntryIdErrorSet);
76368496
76378497 if (instr_is_comptime(target)) {
76388498 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
76398499 if (!val)
76408500 return ira->codegen->invalid_instruction;
76418501
7642 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
7643 source_instr->source_node, ira->codegen->builtin_types.entry_pure_error);
8502 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
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;
7646 bigint_init_unsigned(&err_count, ira->codegen->error_decls.length);
7647 if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
7648 Buf *val_buf = buf_alloc();
7649 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
7650 ir_add_error(ira, source_instr,
7651 buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
7652 return ira->codegen->invalid_instruction;
7653 }
8537 if (err == nullptr) {
8538 Buf *val_buf = buf_alloc();
8539 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
8540 ir_add_error(ira, source_instr,
8541 buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name)));
8542 return ira->codegen->invalid_instruction;
8543 }
76548544
7655 size_t index = bigint_as_unsigned(&val->data.x_bigint);
7656 AstNode *error_decl_node = ira->codegen->error_decls.at(index);
7657 result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err;
7658 return result;
8545 result->value.data.x_err_set = err;
8546 return result;
8547 }
76598548 }
76608549
76618550 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;
76638552 return result;
76648553}
76658554
......@@ -7681,8 +8570,8 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
76818570 ErrorTableEntry *err;
76828571 if (err_type->id == TypeTableEntryIdErrorUnion) {
76838572 err = val->data.x_err_union.err;
7684 } else if (err_type->id == TypeTableEntryIdPureError) {
7685 err = val->data.x_pure_err;
8573 } else if (err_type->id == TypeTableEntryIdErrorSet) {
8574 err = val->data.x_err_set;
76868575 } else {
76878576 zig_unreachable();
76888577 }
......@@ -7702,8 +8591,36 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
77028591 return result;
77038592 }
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
77058622 BigInt bn;
7706 bigint_init_unsigned(&bn, ira->codegen->error_decls.length);
8623 bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length);
77078624 if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
77088625 ir_add_error_node(ira, source_instr->source_node,
77098626 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
77198636 TypeTableEntry *wanted_type, IrInstruction *value)
77208637{
77218638 TypeTableEntry *actual_type = value->value.type;
8639 AstNode *source_node = source_instr->source_node;
77228640
77238641 if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
77248642 return ira->codegen->invalid_instruction;
......@@ -7728,7 +8646,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
77288646 return value;
77298647
77308648 // 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) {
77328650 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
77338651 }
77348652
......@@ -7748,6 +8666,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
77488666 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
77498667 }
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
77518676 // explicit cast from int to float
77528677 if (wanted_type->id == TypeTableEntryIdFloat &&
77538678 actual_type->id == TypeTableEntryIdInt)
......@@ -7767,7 +8692,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
77678692 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
77688693 assert(ptr_type->id == TypeTableEntryIdPointer);
77698694 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)
77718696 {
77728697 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
77738698 }
......@@ -7785,7 +8710,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
77858710 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
77868711
77878712 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)
77898714 {
77908715 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
77918716 }
......@@ -7801,7 +8726,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
78018726 wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
78028727 assert(ptr_type->id == TypeTableEntryIdPointer);
78038728 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)
78058730 {
78068731 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
78078732 if (type_is_invalid(cast1->value.type))
......@@ -7824,7 +8749,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
78248749 wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
78258750 assert(ptr_type->id == TypeTableEntryIdPointer);
78268751 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)
78288753 {
78298754 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
78308755 if (type_is_invalid(cast1->value.type))
......@@ -7886,7 +8811,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
78868811
78878812 // explicit cast from child type of maybe type to maybe type
78888813 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) {
78908815 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
78918816 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
78928817 actual_type->id == TypeTableEntryIdNumLitFloat)
......@@ -7908,12 +8833,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
79088833
79098834 // explicit cast from child type of error type to error type
79108835 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) {
79128837 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
79138838 } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
79148839 actual_type->id == TypeTableEntryIdNumLitFloat)
79158840 {
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)) {
79178842 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
79188843 } else {
79198844 return ira->codegen->invalid_instruction;
......@@ -7923,16 +8848,16 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
79238848
79248849 // explicit cast from [N]T to %[]const T
79258850 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7926 is_slice(wanted_type->data.error.child_type) &&
8851 is_slice(wanted_type->data.error_union.payload_type) &&
79278852 actual_type->id == TypeTableEntryIdArray)
79288853 {
79298854 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;
79318856 assert(ptr_type->id == TypeTableEntryIdPointer);
79328857 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)
79348859 {
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);
79368861 if (type_is_invalid(cast1->value.type))
79378862 return ira->codegen->invalid_instruction;
79388863
......@@ -7944,25 +8869,25 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
79448869 }
79458870 }
79468871
7947 // explicit cast from pure error to error union type
8872 // explicit cast from error set to error union type
79488873 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7949 actual_type->id == TypeTableEntryIdPureError)
8874 actual_type->id == TypeTableEntryIdErrorSet)
79508875 {
79518876 return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
79528877 }
79538878
79548879 // explicit cast from T to %?T
79558880 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7956 wanted_type->data.error.child_type->id == TypeTableEntryIdMaybe &&
8881 wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
79578882 actual_type->id != TypeTableEntryIdMaybe)
79588883 {
7959 TypeTableEntry *wanted_child_type = wanted_type->data.error.child_type->data.maybe.child_type;
7960 if (types_match_const_cast_only(wanted_child_type, actual_type) ||
8884 TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
8885 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk ||
79618886 actual_type->id == TypeTableEntryIdNullLit ||
79628887 actual_type->id == TypeTableEntryIdNumLitInt ||
79638888 actual_type->id == TypeTableEntryIdNumLitFloat)
79648889 {
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);
79668891 if (type_is_invalid(cast1->value.type))
79678892 return ira->codegen->invalid_instruction;
79688893
......@@ -8031,21 +8956,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
80318956 return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
80328957 }
80338958
8034 // explicit cast from %void to integer type which can fit it
8959 // explicit cast from T!void to integer type which can fit it
80358960 bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
8036 !type_has_bits(actual_type->data.error.child_type);
8037 bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError;
8038 if ((actual_type_is_void_err || actual_type_is_pure_err) &&
8039 wanted_type->id == TypeTableEntryIdInt)
8040 {
8961 !type_has_bits(actual_type->data.error_union.payload_type);
8962 bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet;
8963 if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) {
80418964 return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);
80428965 }
80438966
8044 // explicit cast from integer to pure error
8045 if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt &&
8967 // explicit cast from integer to error set
8968 if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt &&
80468969 !actual_type->data.integral.is_signed)
80478970 {
8048 return ir_analyze_int_to_err(ira, source_instr, value);
8971 return ir_analyze_int_to_err(ira, source_instr, value, wanted_type);
80498972 }
80508973
80518974 // explicit cast from integer to enum type with no payload
......@@ -8109,7 +9032,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
81099032 // explicit cast from something to const pointer of it
81109033 if (!type_requires_comptime(actual_type)) {
81119034 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) {
81139036 return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
81149037 }
81159038 }
......@@ -8471,6 +9394,7 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
84719394static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
84729395 IrInstruction *op1 = bin_op_instruction->op1->other;
84739396 IrInstruction *op2 = bin_op_instruction->op2->other;
9397 AstNode *source_node = bin_op_instruction->base.source_node;
84749398
84759399 IrBinOp op_id = bin_op_instruction->op_id;
84769400 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
......@@ -8503,7 +9427,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
85039427 }
85049428
85059429 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);
85079431 is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
85089432
85099433 if (op_id == IrBinOpCmpEq) {
......@@ -8514,8 +9438,88 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
85149438 return ira->codegen->builtin_types.entry_bool;
85159439 }
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
85179521 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);
85199523 if (type_is_invalid(resolved_type))
85209524 return resolved_type;
85219525 type_ensure_zero_bits_known(ira->codegen, resolved_type);
......@@ -8523,7 +9527,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
85239527 return resolved_type;
85249528
85259529
8526 AstNode *source_node = bin_op_instruction->base.source_node;
85279530 switch (resolved_type->id) {
85289531 case TypeTableEntryIdInvalid:
85299532 zig_unreachable(); // handled above
......@@ -8538,7 +9541,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
85389541 case TypeTableEntryIdMetaType:
85399542 case TypeTableEntryIdVoid:
85409543 case TypeTableEntryIdPointer:
8541 case TypeTableEntryIdPureError:
9544 case TypeTableEntryIdErrorSet:
85429545 case TypeTableEntryIdFn:
85439546 case TypeTableEntryIdOpaque:
85449547 case TypeTableEntryIdNamespace:
......@@ -8692,6 +9695,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,
86929695 case IrBinOpArrayCat:
86939696 case IrBinOpArrayMult:
86949697 case IrBinOpRemUnspecified:
9698 case IrBinOpMergeErrorSets:
86959699 zig_unreachable();
86969700 case IrBinOpBinOr:
86979701 assert(is_int);
......@@ -9264,6 +10268,46 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp
926410268 return get_array_type(ira->codegen, child_type, new_array_len);
926510269}
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
926710311static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
926810312 IrBinOp op_id = bin_op_instruction->op_id;
926910313 switch (op_id) {
......@@ -9305,6 +10349,8 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
930510349 return ir_analyze_array_cat(ira, bin_op_instruction);
930610350 case IrBinOpArrayMult:
930710351 return ir_analyze_array_mult(ira, bin_op_instruction);
10352 case IrBinOpMergeErrorSets:
10353 return ir_analyze_merge_error_sets(ira, bin_op_instruction);
930810354 }
930910355 zig_unreachable();
931010356}
......@@ -9326,7 +10372,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
932610372 case TypeTableEntryIdInt:
932710373 case TypeTableEntryIdFloat:
932810374 case TypeTableEntryIdVoid:
9329 case TypeTableEntryIdPureError:
10375 case TypeTableEntryIdErrorSet:
933010376 case TypeTableEntryIdFn:
933110377 return VarClassRequiredAny;
933210378 case TypeTableEntryIdNumLitFloat:
......@@ -9352,7 +10398,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
935210398 case TypeTableEntryIdMaybe:
935310399 return get_var_class_required(type_entry->data.maybe.child_type);
935410400 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
935710403 case TypeTableEntryIdStruct:
935810404 case TypeTableEntryIdEnum:
......@@ -9587,7 +10633,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
958710633 case TypeTableEntryIdNullLit:
958810634 case TypeTableEntryIdMaybe:
958910635 case TypeTableEntryIdErrorUnion:
9590 case TypeTableEntryIdPureError:
10636 case TypeTableEntryIdErrorSet:
959110637 case TypeTableEntryIdNamespace:
959210638 case TypeTableEntryIdBlock:
959310639 case TypeTableEntryIdBoundFn:
......@@ -9610,7 +10656,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
961010656 case TypeTableEntryIdNullLit:
961110657 case TypeTableEntryIdMaybe:
961210658 case TypeTableEntryIdErrorUnion:
9613 case TypeTableEntryIdPureError:
10659 case TypeTableEntryIdErrorSet:
961410660 zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
961510661 case TypeTableEntryIdNamespace:
961610662 case TypeTableEntryIdBlock:
......@@ -9644,6 +10690,31 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
964410690 return nullable_type;
964510691}
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
964710718static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
964810719 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
964910720{
......@@ -9926,9 +10997,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
992610997 }
992710998
992810999 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);
9930 if (type_is_invalid(return_type))
11000 TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);
11001 if (type_is_invalid(specified_return_type))
993111002 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
993311012 IrInstruction *result;
993411013
......@@ -9942,6 +11021,23 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
994211021 ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
994311022 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
994511041 ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
994611042
994711043 if (type_is_invalid(result->value.type))
......@@ -10092,12 +11188,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1009211188
1009311189 {
1009411190 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);
10096 if (type_is_invalid(return_type))
11191 TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);
11192 if (type_is_invalid(specified_return_type))
1009711193 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)) {
1010111202 // Throw out our work and call the function as if it were comptime.
1010211203 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
1010311204 }
......@@ -10128,7 +11229,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1012811229 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
1012911230 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) {
1013211233 parent_fn_entry->calls_errorable_function = true;
1013311234 }
1013411235
......@@ -10138,7 +11239,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1013811239 FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
1013911240 assert(fn_type_id->return_type != nullptr);
1014011241 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) {
1014211243 parent_fn_entry->calls_errorable_function = true;
1014311244 }
1014411245
......@@ -10257,58 +11358,6 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
1025711358 }
1025811359}
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
1031211361static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
1031311362 IrInstruction *value = un_op_instruction->value->other;
1031411363
......@@ -10364,7 +11413,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
1036411413 case TypeTableEntryIdNullLit:
1036511414 case TypeTableEntryIdMaybe:
1036611415 case TypeTableEntryIdErrorUnion:
10367 case TypeTableEntryIdPureError:
11416 case TypeTableEntryIdErrorSet:
1036811417 case TypeTableEntryIdEnum:
1036911418 case TypeTableEntryIdUnion:
1037011419 case TypeTableEntryIdFn:
......@@ -10474,8 +11523,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
1047411523 return ir_analyze_dereference(ira, un_op_instruction);
1047511524 case IrUnOpMaybe:
1047611525 return ir_analyze_maybe(ira, un_op_instruction);
10477 case IrUnOpError:
10478 return ir_analyze_unary_prefix_op_err(ira, un_op_instruction);
1047911526 }
1048011527 zig_unreachable();
1048111528}
......@@ -10633,6 +11680,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
1063311680 IrInstruction *branch_instruction = predecessor->instruction_list.pop();
1063411681 ir_set_cursor_at_end(&ira->new_irb, predecessor);
1063511682 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 }
1063611686 new_incoming_values.items[i] = casted_value;
1063711687 predecessor->instruction_list.append(branch_instruction);
1063811688
......@@ -11048,6 +12098,25 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
1104812098 }
1104912099}
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
1105112120static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {
1105212121 bool pointer_only = false;
1105312122 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
1106312132 TldVar *tld_var = (TldVar *)tld;
1106412133 VariableTableEntry *var = tld_var->var;
1106512134 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);
1106712136 }
1106812137
1106912138 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
1108512154 const_val->data.x_fn.fn_entry = fn_entry;
1108612155
1108712156 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);
1108912158 }
1109012159
1109112160 bool ptr_is_const = true;
......@@ -11097,6 +12166,17 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
1109712166 zig_unreachable();
1109812167}
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
1110012180static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
1110112181 IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
1110212182 if (type_is_invalid(container_ptr->value.type))
......@@ -11238,23 +12318,52 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
1123812318 buf_sprintf("container '%s' has no member called '%s'",
1123912319 buf_ptr(&child_type->name), buf_ptr(field_name)));
1124012320 return ira->codegen->builtin_types.entry_invalid;
11241 } else if (child_type->id == TypeTableEntryIdPureError) {
11242 auto err_table_entry = ira->codegen->error_table.maybe_get(field_name);
11243 if (err_table_entry) {
11244 ConstExprValue *const_val = create_const_vals(1);
11245 const_val->special = ConstValSpecialStatic;
11246 const_val->type = child_type;
11247 const_val->data.x_pure_err = err_table_entry->value;
11248
11249 bool ptr_is_const = true;
11250 bool ptr_is_volatile = false;
11251 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
11252 child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
12321 } else if (child_type->id == TypeTableEntryIdErrorSet) {
12322 ErrorTableEntry *err_entry;
12323 TypeTableEntry *err_set_type;
12324 if (type_is_global_error_set(child_type)) {
12325 auto existing_entry = ira->codegen->error_table.maybe_get(field_name);
12326 if (existing_entry) {
12327 err_entry = existing_entry->value;
12328 } else {
12329 err_entry = allocate<ErrorTableEntry>(1);
12330 err_entry->decl_node = field_ptr_instruction->base.source_node;
12331 buf_init_from_buf(&err_entry->name, field_name);
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;
1125312357 }
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,
11256 buf_sprintf("use of undeclared error value '%s'", buf_ptr(field_name)));
11257 return ira->codegen->builtin_types.entry_invalid;
12363 bool ptr_is_const = true;
12364 bool ptr_is_volatile = false;
12365 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
12366 err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
1125812367 } else if (child_type->id == TypeTableEntryIdInt) {
1125912368 if (buf_eql_str(field_name, "bit_count")) {
1126012369 bool ptr_is_const = true;
......@@ -11337,11 +12446,18 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
1133712446 return ira->codegen->builtin_types.entry_invalid;
1133812447 }
1133912448 } 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")) {
1134112457 bool ptr_is_const = true;
1134212458 bool ptr_is_volatile = false;
1134312459 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),
1134512461 ira->codegen->builtin_types.entry_type,
1134612462 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
1134712463 } else {
......@@ -11528,7 +12644,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
1152812644 case TypeTableEntryIdStruct:
1152912645 case TypeTableEntryIdMaybe:
1153012646 case TypeTableEntryIdErrorUnion:
11531 case TypeTableEntryIdPureError:
12647 case TypeTableEntryIdErrorSet:
1153212648 case TypeTableEntryIdEnum:
1153312649 case TypeTableEntryIdUnion:
1153412650 case TypeTableEntryIdFn:
......@@ -11795,7 +12911,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
1179512911 case TypeTableEntryIdNumLitInt:
1179612912 case TypeTableEntryIdMaybe:
1179712913 case TypeTableEntryIdErrorUnion:
11798 case TypeTableEntryIdPureError:
12914 case TypeTableEntryIdErrorSet:
1179912915 case TypeTableEntryIdEnum:
1180012916 case TypeTableEntryIdUnion:
1180112917 case TypeTableEntryIdFn:
......@@ -11903,7 +13019,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
1190313019 case TypeTableEntryIdNumLitInt:
1190413020 case TypeTableEntryIdMaybe:
1190513021 case TypeTableEntryIdErrorUnion:
11906 case TypeTableEntryIdPureError:
13022 case TypeTableEntryIdErrorSet:
1190713023 case TypeTableEntryIdEnum:
1190813024 case TypeTableEntryIdUnion:
1190913025 case TypeTableEntryIdFn:
......@@ -11956,7 +13072,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
1195613072 case TypeTableEntryIdStruct:
1195713073 case TypeTableEntryIdMaybe:
1195813074 case TypeTableEntryIdErrorUnion:
11959 case TypeTableEntryIdPureError:
13075 case TypeTableEntryIdErrorSet:
1196013076 case TypeTableEntryIdEnum:
1196113077 case TypeTableEntryIdUnion:
1196213078 case TypeTableEntryIdFn:
......@@ -12291,7 +13407,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1229113407 case TypeTableEntryIdPointer:
1229213408 case TypeTableEntryIdFn:
1229313409 case TypeTableEntryIdNamespace:
12294 case TypeTableEntryIdPureError:
13410 case TypeTableEntryIdErrorSet:
1229513411 if (pointee_val) {
1229613412 ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
1229713413 copy_const_val(out_val, pointee_val, true);
......@@ -12361,8 +13477,6 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1236113477 return target_type;
1236213478 }
1236313479 case TypeTableEntryIdErrorUnion:
12364 // see https://github.com/andrewrk/zig/issues/632
12365 zig_panic("TODO switch on error union");
1236613480 case TypeTableEntryIdUnreachable:
1236713481 case TypeTableEntryIdArray:
1236813482 case TypeTableEntryIdStruct:
......@@ -12887,7 +14001,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
1288714001 case TypeTableEntryIdNullLit:
1288814002 case TypeTableEntryIdMaybe:
1288914003 case TypeTableEntryIdErrorUnion:
12890 case TypeTableEntryIdPureError:
14004 case TypeTableEntryIdErrorSet:
1289114005 case TypeTableEntryIdUnion:
1289214006 case TypeTableEntryIdFn:
1289314007 case TypeTableEntryIdNamespace:
......@@ -12975,7 +14089,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
1297514089 TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
1297614090 TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type);
1297714091 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;
1297914093 if (!err->cached_error_name_val) {
1298014094 ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name);
1298114095 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
1395615070 result = container_type->data.structure.src_field_count;
1395715071 } else if (container_type->id == TypeTableEntryIdUnion) {
1395815072 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;
1395915082 } else {
1396015083 ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));
1396115084 return ira->codegen->builtin_types.entry_invalid;
......@@ -14120,7 +15243,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc
1412015243 case TypeTableEntryIdStruct:
1412115244 case TypeTableEntryIdMaybe:
1412215245 case TypeTableEntryIdErrorUnion:
14123 case TypeTableEntryIdPureError:
15246 case TypeTableEntryIdErrorSet:
1412415247 case TypeTableEntryIdEnum:
1412515248 case TypeTableEntryIdUnion:
1412615249 case TypeTableEntryIdFn:
......@@ -14251,9 +15374,22 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc
1425115374 }
1425215375 }
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
1425415390 ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
1425515391 return ira->codegen->builtin_types.entry_bool;
14256 } else if (type_entry->id == TypeTableEntryIdPureError) {
15392 } else if (type_entry->id == TypeTableEntryIdErrorSet) {
1425715393 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
1425815394 out_val->data.x_bool = true;
1425915395 return ira->codegen->builtin_types.entry_bool;
......@@ -14289,13 +15425,13 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
1428915425 assert(err);
1429015426
1429115427 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
14292 out_val->data.x_pure_err = err;
14293 return ira->codegen->builtin_types.entry_pure_error;
15428 out_val->data.x_err_set = err;
15429 return type_entry->data.error_union.err_set_type;
1429415430 }
1429515431 }
1429615432
1429715433 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;
1429915435 } else {
1430015436 ir_add_error(ira, value,
1430115437 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,
1431915455 if (type_is_invalid(type_entry)) {
1432015456 return ira->codegen->builtin_types.entry_invalid;
1432115457 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
14322 TypeTableEntry *child_type = type_entry->data.error.child_type;
14323 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
15458 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
15459 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
1432415460 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);
1432615462 if (instr_is_comptime(value)) {
1432715463 ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
1432815464 if (!ptr_val)
......@@ -14332,7 +15468,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
1433215468 ErrorTableEntry *err = err_union_val->data.x_err_union.err;
1433315469 if (err != nullptr) {
1433415470 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)));
1433615472 return ira->codegen->builtin_types.entry_invalid;
1433715473 }
1433815474
......@@ -14357,6 +15493,12 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
1435715493 AstNode *proto_node = instruction->base.source_node;
1435815494 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
1436015502 FnTypeId fn_type_id = {0};
1436115503 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
1448215624 }
1448315625 }
1448415626 }
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);
1448515684 } else if (switch_type->id == TypeTableEntryIdInt) {
1448615685 RangeSet rs = {0};
1448715686 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
1477415973 zig_panic("TODO buf_write_value_bytes maybe type");
1477515974 case TypeTableEntryIdErrorUnion:
1477615975 zig_panic("TODO buf_write_value_bytes error union");
14777 case TypeTableEntryIdPureError:
15976 case TypeTableEntryIdErrorSet:
1477815977 zig_panic("TODO buf_write_value_bytes pure error type");
1477915978 case TypeTableEntryIdEnum:
1478015979 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
1483216031 zig_panic("TODO buf_read_value_bytes maybe type");
1483316032 case TypeTableEntryIdErrorUnion:
1483416033 zig_panic("TODO buf_read_value_bytes error union");
14835 case TypeTableEntryIdPureError:
16034 case TypeTableEntryIdErrorSet:
1483616035 zig_panic("TODO buf_read_value_bytes pure error type");
1483716036 case TypeTableEntryIdEnum:
1483816037 zig_panic("TODO buf_read_value_bytes enum type");
......@@ -15010,7 +16209,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
1501016209 return ira->codegen->builtin_types.entry_invalid;
1501116210
1501216211 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);
1501416213 }
1501516214
1501616215 if (lval.is_ptr) {
......@@ -15029,7 +16228,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
1502916228 assert(fn_entry->type_entry);
1503016229
1503116230 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);
1503316232 }
1503416233
1503516234 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
1544316642 return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
1544416643 case IrInstructionIdErrorReturnTrace:
1544516644 return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
16645 case IrInstructionIdErrorUnion:
16646 return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction);
1544616647 }
1544716648 zig_unreachable();
1544816649}
......@@ -15628,6 +16829,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1562816829 case IrInstructionIdArgType:
1562916830 case IrInstructionIdTagType:
1563016831 case IrInstructionIdErrorReturnTrace:
16832 case IrInstructionIdErrorUnion:
1563116833 return false;
1563216834 case IrInstructionIdAsm:
1563316835 {
src/ir_print.cpp+10-2
......@@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
130130 return "++";
131131 case IrBinOpArrayMult:
132132 return "**";
133 case IrBinOpMergeErrorSets:
134 return "||";
133135 }
134136 zig_unreachable();
135137}
......@@ -148,8 +150,6 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
148150 return "*";
149151 case IrUnOpMaybe:
150152 return "?";
151 case IrUnOpError:
152 return "%";
153153 }
154154 zig_unreachable();
155155}
......@@ -1004,6 +1004,11 @@ static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTr
10041004 fprintf(irp->f, "@errorReturnTrace()");
10051005}
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
10081013static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
10091014 ir_print_prefix(irp, instruction);
......@@ -1322,6 +1327,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
13221327 case IrInstructionIdErrorReturnTrace:
13231328 ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
13241329 break;
1330 case IrInstructionIdErrorUnion:
1331 ir_print_error_union(irp, (IrInstructionErrorUnion *)instruction);
1332 break;
13251333 }
13261334 fprintf(irp->f, "\n");
13271335}
src/main.cpp+31-10
......@@ -66,6 +66,7 @@ static int usage(const char *arg0) {
6666 " --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n"
6767 " --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n"
6868 " --library [lib] link against lib\n"
69 " --forbid-library [lib] make it an error to link against lib\n"
6970 " --library-path [dir] add a directory to the library search path\n"
7071 " --linker-script [path] use a custom linker script\n"
7172 " --object [obj] add object file to build\n"
......@@ -309,6 +310,7 @@ int main(int argc, char **argv) {
309310 ZigList<const char *> llvm_argv = {0};
310311 ZigList<const char *> lib_dirs = {0};
311312 ZigList<const char *> link_libs = {0};
313 ZigList<const char *> forbidden_link_libs = {0};
312314 ZigList<const char *> frameworks = {0};
313315 int err;
314316 const char *target_arch = nullptr;
......@@ -339,6 +341,7 @@ int main(int argc, char **argv) {
339341 const char *zig_exe_path = arg0;
340342 const char *build_file = "build.zig";
341343 bool asked_for_help = false;
344 bool asked_to_init = false;
342345
343346 init_all_targets();
344347
......@@ -350,6 +353,9 @@ int main(int argc, char **argv) {
350353 if (strcmp(argv[i], "--help") == 0) {
351354 asked_for_help = true;
352355 args.append(argv[i]);
356 } else if (strcmp(argv[i], "--init") == 0) {
357 asked_to_init = true;
358 args.append(argv[i]);
353359 } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) {
354360 build_file = argv[i + 1];
355361 i += 1;
......@@ -414,6 +420,7 @@ int main(int argc, char **argv) {
414420 "\n"
415421 "General Options:\n"
416422 " --help Print this help and exit\n"
423 " --init Generate a build.zig template\n"
417424 " --build-file [file] Override path to build.zig\n"
418425 " --cache-dir [path] Override path to cache directory\n"
419426 " --verbose Print commands before executing them\n"
......@@ -426,7 +433,6 @@ int main(int argc, char **argv) {
426433 " --prefix [path] Override default install prefix\n"
427434 "\n"
428435 "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"
430436 "\n"
431437 "Advanced Options:\n"
432438 " --build-file [file] Override path to build.zig\n"
......@@ -439,17 +445,26 @@ int main(int argc, char **argv) {
439445 " --verbose-cimport Enable compiler debug output for C imports\n"
440446 "\n"
441447 , zig_exe_path);
442 return 0;
443 }
444 Buf *build_template_path = buf_alloc();
445 os_path_join(special_dir, buf_create_from_str("build_file_template.zig"), build_template_path);
448 return EXIT_SUCCESS;
449 } else if (asked_to_init) {
450 Buf *build_template_path = buf_alloc();
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))) {
448 fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
449 } else {
450 fprintf(stderr, "Wrote build.zig template\n");
453 if ((err = os_copy_file(build_template_path, &build_file_abs))) {
454 fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
455 } else {
456 fprintf(stderr, "Wrote build.zig template\n");
457 }
458 return EXIT_SUCCESS;
451459 }
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;
453468 }
454469
455470 PackageTableEntry *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname),
......@@ -592,6 +607,8 @@ int main(int argc, char **argv) {
592607 lib_dirs.append(argv[i]);
593608 } else if (strcmp(arg, "--library") == 0) {
594609 link_libs.append(argv[i]);
610 } else if (strcmp(arg, "--forbid-library") == 0) {
611 forbidden_link_libs.append(argv[i]);
595612 } else if (strcmp(arg, "--object") == 0) {
596613 objects.append(argv[i]);
597614 } else if (strcmp(arg, "--assembly") == 0) {
......@@ -804,6 +821,10 @@ int main(int argc, char **argv) {
804821 LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));
805822 link_lib->provided_explicitly = true;
806823 }
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 }
807828 for (size_t i = 0; i < frameworks.length; i += 1) {
808829 codegen_add_framework(g, frameworks.at(i));
809830 }
src/parser.cpp+74-42
......@@ -221,6 +221,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bo
221221static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory);
222222static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory);
223223static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index);
224static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index);
224225
225226static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
226227 if (token->id == token_id) {
......@@ -240,7 +241,28 @@ static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token
240241}
241242
242243/*
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"
244266*/
245267static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
246268 Token *token = &pc->tokens->at(*token_index);
......@@ -249,7 +271,7 @@ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool
249271 *token_index += 1;
250272 return node;
251273 } else {
252 return ast_parse_prefix_op_expr(pc, token_index, mandatory);
274 return ast_parse_error_set_expr(pc, token_index, mandatory);
253275 }
254276}
255277
......@@ -651,8 +673,9 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
651673}
652674
653675/*
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
655677KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
678ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
656679*/
657680static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
658681 Token *token = &pc->tokens->at(*token_index);
......@@ -716,9 +739,31 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
716739 *token_index += 1;
717740 return node;
718741 } else if (token->id == TokenIdKeywordError) {
719 AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);
720 *token_index += 1;
721 return node;
742 Token *next_token = &pc->tokens->at(*token_index + 1);
743 if (next_token->id == TokenIdLBrace) {
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 }
722767 } else if (token->id == TokenIdAtSign) {
723768 *token_index += 1;
724769 Token *name_tok = &pc->tokens->at(*token_index);
......@@ -950,7 +995,6 @@ static PrefixOp tok_to_prefix_op(Token *token) {
950995 case TokenIdTilde: return PrefixOpBinNot;
951996 case TokenIdStar: return PrefixOpDereference;
952997 case TokenIdMaybe: return PrefixOpMaybe;
953 case TokenIdPercent: return PrefixOpError;
954998 case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
955999 case TokenIdStarStar: return PrefixOpDereference;
9561000 default: return PrefixOpInvalid;
......@@ -997,8 +1041,8 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {
9971041}
9981042
9991043/*
1000PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
1001PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" | "try"
1044PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
1045PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
10021046*/
10031047static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
10041048 Token *token = &pc->tokens->at(*token_index);
......@@ -1028,7 +1072,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index,
10281072 node->column += 1;
10291073 }
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);
10321076 node->data.prefix_op_expr.primary_expr = prefix_op_expr;
10331077 node->data.prefix_op_expr.prefix_op = prefix_op;
10341078
......@@ -1043,12 +1087,14 @@ static BinOpType tok_to_mult_op(Token *token) {
10431087 case TokenIdStarStar: return BinOpTypeArrayMult;
10441088 case TokenIdSlash: return BinOpTypeDiv;
10451089 case TokenIdPercent: return BinOpTypeMod;
1090 case TokenIdBang: return BinOpTypeErrorUnion;
1091 case TokenIdBarBar: return BinOpTypeMergeErrorSets;
10461092 default: return BinOpTypeInvalid;
10471093 }
10481094}
10491095
10501096/*
1051MultiplyOperator = "*" | "/" | "%" | "**" | "*%"
1097MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
10521098*/
10531099static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) {
10541100 Token *token = &pc->tokens->at(*token_index);
......@@ -2240,7 +2286,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
22402286}
22412287
22422288/*
2243FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") TypeExpr
2289FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr
22442290*/
22452291static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
22462292 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
23152361 ast_eat_token(pc, token_index, TokenIdRParen);
23162362 next_token = &pc->tokens->at(*token_index);
23172363 }
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 }
23182376 node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
23192377
23202378 return node;
......@@ -2531,7 +2589,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
25312589 Token *colon_token = &pc->tokens->at(*token_index);
25322590 if (colon_token->id == TokenIdColon) {
25332591 *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);
25352593 }
25362594 Token *eq_token = &pc->tokens->at(*token_index);
25372595 if (eq_token->id == TokenIdEq) {
......@@ -2559,26 +2617,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
25592617 return node;
25602618}
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
25822620/*
25832621TestDecl = "test" String Block
25842622*/
......@@ -2611,12 +2649,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
26112649 continue;
26122650 }
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
26202652 AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index);
26212653 if (test_decl_node) {
26222654 top_level_decls->append(test_decl_node);
......@@ -2744,9 +2776,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
27442776 visit_field(&node->data.variable_declaration.align_expr, visit, context);
27452777 visit_field(&node->data.variable_declaration.section_expr, visit, context);
27462778 break;
2747 case NodeTypeErrorValueDecl:
2748 // none
2749 break;
27502779 case NodeTypeTestDecl:
27512780 visit_field(&node->data.test_decl.body, visit, context);
27522781 break;
......@@ -2899,5 +2928,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
28992928 visit_field(&node->data.addr_of_expr.align_expr, visit, context);
29002929 visit_field(&node->data.addr_of_expr.op_expr, visit, context);
29012930 break;
2931 case NodeTypeErrorSetDecl:
2932 visit_node_list(&node->data.err_set_decl.decls, visit, context);
2933 break;
29022934 }
29032935}
src/tokenizer.cpp+25-4
......@@ -195,7 +195,8 @@ enum TokenizeState {
195195 TokenizeStateSawMinusPercent,
196196 TokenizeStateSawAmpersand,
197197 TokenizeStateSawCaret,
198 TokenizeStateSawPipe,
198 TokenizeStateSawBar,
199 TokenizeStateSawBarBar,
199200 TokenizeStateLineComment,
200201 TokenizeStateLineString,
201202 TokenizeStateLineStringEnd,
......@@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) {
594595 break;
595596 case '|':
596597 begin_token(&t, TokenIdBinOr);
597 t.state = TokenizeStateSawPipe;
598 t.state = TokenizeStateSawBar;
598599 break;
599600 case '=':
600601 begin_token(&t, TokenIdEq);
......@@ -888,13 +889,17 @@ void tokenize(Buf *buf, Tokenization *out) {
888889 continue;
889890 }
890891 break;
891 case TokenizeStateSawPipe:
892 case TokenizeStateSawBar:
892893 switch (c) {
893894 case '=':
894895 set_token_id(&t, t.cur_tok, TokenIdBitOrEq);
895896 end_token(&t);
896897 t.state = TokenizeStateStart;
897898 break;
899 case '|':
900 set_token_id(&t, t.cur_tok, TokenIdBarBar);
901 t.state = TokenizeStateSawBarBar;
902 break;
898903 default:
899904 t.pos -= 1;
900905 end_token(&t);
......@@ -902,6 +907,19 @@ void tokenize(Buf *buf, Tokenization *out) {
902907 continue;
903908 }
904909 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 }
905923 case TokenizeStateSawSlash:
906924 switch (c) {
907925 case '/':
......@@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) {
14281446 case TokenizeStateSawDash:
14291447 case TokenizeStateSawAmpersand:
14301448 case TokenizeStateSawCaret:
1431 case TokenizeStateSawPipe:
1449 case TokenizeStateSawBar:
14321450 case TokenizeStateSawEq:
14331451 case TokenizeStateSawBang:
14341452 case TokenizeStateSawLessThan:
......@@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) {
14431461 case TokenizeStateSawMinusPercent:
14441462 case TokenizeStateLineString:
14451463 case TokenizeStateLineStringEnd:
1464 case TokenizeStateSawBarBar:
14461465 end_token(&t);
14471466 break;
14481467 case TokenizeStateSawDotDot:
......@@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) {
14751494 case TokenIdArrow: return "->";
14761495 case TokenIdAtSign: return "@";
14771496 case TokenIdBang: return "!";
1497 case TokenIdBarBar: return "||";
14781498 case TokenIdBinOr: return "|";
14791499 case TokenIdBinXor: return "^";
14801500 case TokenIdBitAndEq: return "&=";
......@@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) {
15771597 case TokenIdTimesEq: return "*=";
15781598 case TokenIdTimesPercent: return "*%";
15791599 case TokenIdTimesPercentEq: return "*%=";
1600 case TokenIdBarBarEq: return "||=";
15801601 }
15811602 return "(invalid token)";
15821603}
src/tokenizer.hpp+2
......@@ -17,6 +17,8 @@ enum TokenId {
1717 TokenIdArrow,
1818 TokenIdAtSign,
1919 TokenIdBang,
20 TokenIdBarBar,
21 TokenIdBarBarEq,
2022 TokenIdBinOr,
2123 TokenIdBinXor,
2224 TokenIdBitAndEq,
src/util.hpp+11-8
......@@ -92,19 +92,22 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {
9292}
9393
9494template<typename T>
95static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
96#ifdef NDEBUG
95static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
9796 T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
9897 if (!ptr)
9998 zig_panic("allocation failed");
99 if (new_count > old_count) {
100 memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
101 }
100102 return ptr;
101#else
102 // manually assign every element to trigger compile error for non-copyable structs
103 T *ptr = allocate_nonzero<T>(new_count);
104 safe_memcpy(ptr, old, old_count);
105 free(old);
103}
104
105template<typename T>
106static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
107 T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
108 if (!ptr)
109 zig_panic("allocation failed");
106110 return ptr;
107#endif
108111}
109112
110113template <typename T, size_t n>
src/zig_llvm.cpp+4
......@@ -437,6 +437,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {
437437 return dwarf::DW_TAG_structure_type;
438438}
439439
440unsigned ZigLLVMTag_DW_enumeration_type(void) {
441 return dwarf::DW_TAG_enumeration_type;
442}
443
440444unsigned ZigLLVMTag_DW_union_type(void) {
441445 return dwarf::DW_TAG_union_type;
442446}
src/zig_llvm.h+1
......@@ -133,6 +133,7 @@ ZIG_EXTERN_C unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);
133133ZIG_EXTERN_C unsigned ZigLLVMLang_DW_LANG_C99(void);
134134ZIG_EXTERN_C unsigned ZigLLVMTag_DW_variable(void);
135135ZIG_EXTERN_C unsigned ZigLLVMTag_DW_structure_type(void);
136ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
136137ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
137138
138139ZIG_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{
4040 return l.items[0..l.len];
4141 }
4242
43 pub fn at(l: &const Self, n: usize) T {
44 return l.toSliceConst()[n];
45 }
46
4347 /// ArrayList takes ownership of the passed in slice. The slice must have been
4448 /// allocated with `allocator`.
4549 /// Deinitialize with `deinit` or use `toOwnedSlice`.
......@@ -59,18 +63,34 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
5963 return result;
6064 }
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 {
6383 const new_item_ptr = try l.addOne();
6484 *new_item_ptr = *item;
6585 }
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 {
6888 try l.ensureCapacity(l.len + items.len);
6989 mem.copy(T, l.items[l.len..], items);
7090 l.len += items.len;
7191 }
7292
73 pub fn resize(l: &Self, new_len: usize) %void {
93 pub fn resize(l: &Self, new_len: usize) !void {
7494 try l.ensureCapacity(new_len);
7595 l.len = new_len;
7696 }
......@@ -80,7 +100,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
80100 l.len = new_len;
81101 }
82102
83 pub fn ensureCapacity(l: &Self, new_capacity: usize) %void {
103 pub fn ensureCapacity(l: &Self, new_capacity: usize) !void {
84104 var better_capacity = l.items.len;
85105 if (better_capacity >= new_capacity) return;
86106 while (true) {
......@@ -90,7 +110,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
90110 l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity);
91111 }
92112
93 pub fn addOne(l: &Self) %&T {
113 pub fn addOne(l: &Self) !&T {
94114 const new_length = l.len + 1;
95115 try l.ensureCapacity(new_length);
96116 const result = &l.items[l.len];
......@@ -136,3 +156,22 @@ test "basic ArrayList test" {
136156 list.appendSlice([]const i32 {}) catch unreachable;
137157 assert(list.len == 9);
138158}
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 {
7979};
8080
8181pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
82error InvalidPadding;
83error InvalidCharacter;
8482
8583pub const Base64Decoder = struct {
8684 /// e.g. 'A' => 0.
......@@ -111,7 +109,7 @@ pub const Base64Decoder = struct {
111109 }
112110
113111 /// 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 {
115113 if (source.len % 4 != 0) return error.InvalidPadding;
116114 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
117115 }
......@@ -119,7 +117,7 @@ pub const Base64Decoder = struct {
119117 /// dest.len must be what you get from ::calcSize.
120118 /// invalid characters result in error.InvalidCharacter.
121119 /// 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 {
123121 assert(dest.len == (decoder.calcSize(source) catch unreachable));
124122 assert(source.len % 4 == 0);
125123
......@@ -163,8 +161,6 @@ pub const Base64Decoder = struct {
163161 }
164162};
165163
166error OutputTooSmall;
167
168164pub const Base64DecoderWithIgnore = struct {
169165 decoder: Base64Decoder,
170166 char_is_ignored: [256]bool,
......@@ -185,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct {
185181 }
186182
187183 /// 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 {
189185 return @divTrunc(encoded_len, 4) * 3;
190186 }
191187
......@@ -193,7 +189,7 @@ pub const Base64DecoderWithIgnore = struct {
193189 /// Invalid padding results in error.InvalidPadding.
194190 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
195191 /// 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 {
197193 const decoder = &decoder_with_ignore.decoder;
198194
199195 var src_cursor: usize = 0;
......@@ -378,7 +374,7 @@ test "base64" {
378374 comptime (testBase64() catch unreachable);
379375}
380376
381fn testBase64() %void {
377fn testBase64() !void {
382378 try testAllApis("", "");
383379 try testAllApis("f", "Zg==");
384380 try testAllApis("fo", "Zm8=");
......@@ -412,7 +408,7 @@ fn testBase64() %void {
412408 try testOutputTooSmallError("AAAAAA==");
413409}
414410
415fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void {
411fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void {
416412 // Base64Encoder
417413 {
418414 var buffer: [0x100]u8 = undefined;
......@@ -434,7 +430,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void
434430 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
435431 standard_alphabet_chars, standard_pad_char, "");
436432 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)];
438434 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
439435 assert(written <= decoded.len);
440436 assert(mem.eql(u8, decoded[0..written], expected_decoded));
......@@ -449,17 +445,16 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void
449445 }
450446}
451447
452fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) %void {
448fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {
453449 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
454450 standard_alphabet_chars, standard_pad_char, " ");
455451 var buffer: [0x100]u8 = undefined;
456 var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
452 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
457453 var written = try standard_decoder_ignore_space.decode(decoded, encoded);
458454 assert(mem.eql(u8, decoded[0..written], expected_decoded));
459455}
460456
461error ExpectedError;
462fn testError(encoded: []const u8, expected_err: error) %void {
457fn testError(encoded: []const u8, expected_err: error) !void {
463458 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
464459 standard_alphabet_chars, standard_pad_char, " ");
465460 var buffer: [0x100]u8 = undefined;
......@@ -475,7 +470,7 @@ fn testError(encoded: []const u8, expected_err: error) %void {
475470 } else |err| if (err != expected_err) return err;
476471}
477472
478fn testOutputTooSmallError(encoded: []const u8) %void {
473fn testOutputTooSmallError(encoded: []const u8) !void {
479474 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
480475 standard_alphabet_chars, standard_pad_char, " ");
481476 var buffer: [0x100]u8 = undefined;
std/buf_map.zig+2-2
......@@ -27,7 +27,7 @@ pub const BufMap = struct {
2727 self.hash_map.deinit();
2828 }
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 {
3131 if (self.hash_map.get(key)) |entry| {
3232 const value_copy = try self.copy(value);
3333 errdefer self.free(value_copy);
......@@ -67,7 +67,7 @@ pub const BufMap = struct {
6767 self.hash_map.allocator.free(mut_value);
6868 }
6969
70 fn copy(self: &BufMap, value: []const u8) %[]const u8 {
70 fn copy(self: &BufMap, value: []const u8) ![]const u8 {
7171 const result = try self.hash_map.allocator.alloc(u8, value.len);
7272 mem.copy(u8, result, value);
7373 return result;
std/buf_set.zig+2-2
......@@ -24,7 +24,7 @@ pub const BufSet = struct {
2424 self.hash_map.deinit();
2525 }
2626
27 pub fn put(self: &BufSet, key: []const u8) %void {
27 pub fn put(self: &BufSet, key: []const u8) !void {
2828 if (self.hash_map.get(key) == null) {
2929 const key_copy = try self.copy(key);
3030 errdefer self.free(key_copy);
......@@ -55,7 +55,7 @@ pub const BufSet = struct {
5555 self.hash_map.allocator.free(mut_value);
5656 }
5757
58 fn copy(self: &BufSet, value: []const u8) %[]const u8 {
58 fn copy(self: &BufSet, value: []const u8) ![]const u8 {
5959 const result = try self.hash_map.allocator.alloc(u8, value.len);
6060 mem.copy(u8, result, value);
6161 return result;
std/buffer.zig+9-9
......@@ -12,14 +12,14 @@ pub const Buffer = struct {
1212 list: ArrayList(u8),
1313
1414 /// Must deinitialize with deinit.
15 pub fn init(allocator: &Allocator, m: []const u8) %Buffer {
15 pub fn init(allocator: &Allocator, m: []const u8) !Buffer {
1616 var self = try initSize(allocator, m.len);
1717 mem.copy(u8, self.list.items, m);
1818 return self;
1919 }
2020
2121 /// Must deinitialize with deinit.
22 pub fn initSize(allocator: &Allocator, size: usize) %Buffer {
22 pub fn initSize(allocator: &Allocator, size: usize) !Buffer {
2323 var self = initNull(allocator);
2424 try self.resize(size);
2525 return self;
......@@ -37,7 +37,7 @@ pub const Buffer = struct {
3737 }
3838
3939 /// Must deinitialize with deinit.
40 pub fn initFromBuffer(buffer: &const Buffer) %Buffer {
40 pub fn initFromBuffer(buffer: &const Buffer) !Buffer {
4141 return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
4242 }
4343
......@@ -80,7 +80,7 @@ pub const Buffer = struct {
8080 self.list.items[self.len()] = 0;
8181 }
8282
83 pub fn resize(self: &Buffer, new_len: usize) %void {
83 pub fn resize(self: &Buffer, new_len: usize) !void {
8484 try self.list.resize(new_len + 1);
8585 self.list.items[self.len()] = 0;
8686 }
......@@ -93,24 +93,24 @@ pub const Buffer = struct {
9393 return self.list.len - 1;
9494 }
9595
96 pub fn append(self: &Buffer, m: []const u8) %void {
96 pub fn append(self: &Buffer, m: []const u8) !void {
9797 const old_len = self.len();
9898 try self.resize(old_len + m.len);
9999 mem.copy(u8, self.list.toSlice()[old_len..], m);
100100 }
101101
102102 // 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 {
104104 return fmt.format(self, append, format, args);
105105 }
106106
107107 // TODO: remove, use OutStream for this
108 pub fn appendByte(self: &Buffer, byte: u8) %void {
108 pub fn appendByte(self: &Buffer, byte: u8) !void {
109109 return self.appendByteNTimes(byte, 1);
110110 }
111111
112112 // 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 {
114114 var prev_size: usize = self.len();
115115 const new_size = prev_size + count;
116116 try self.resize(new_size);
......@@ -137,7 +137,7 @@ pub const Buffer = struct {
137137 return mem.eql(u8, self.list.items[start..l], m);
138138 }
139139
140 pub fn replaceContents(self: &const Buffer, m: []const u8) %void {
140 pub fn replaceContents(self: &const Buffer, m: []const u8) !void {
141141 try self.resize(m.len);
142142 mem.copy(u8, self.list.toSlice(), m);
143143 }
std/build.zig+35-39
......@@ -15,13 +15,6 @@ const BufSet = std.BufSet;
1515const BufMap = std.BufMap;
1616const fmt_lib = std.fmt;
1717
18error ExtraArg;
19error UncleanExit;
20error InvalidStepName;
21error DependencyLoopDetected;
22error NoCompilerFound;
23error NeedAnObject;
24
2518pub const Builder = struct {
2619 uninstall_tls: TopLevelStep,
2720 install_tls: TopLevelStep,
......@@ -242,7 +235,7 @@ pub const Builder = struct {
242235 self.lib_paths.append(path) catch unreachable;
243236 }
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 {
246239 var wanted_steps = ArrayList(&Step).init(self.allocator);
247240 defer wanted_steps.deinit();
248241
......@@ -278,7 +271,7 @@ pub const Builder = struct {
278271 return &self.uninstall_tls.step;
279272 }
280273
281 fn makeUninstall(uninstall_step: &Step) %void {
274 fn makeUninstall(uninstall_step: &Step) error!void {
282275 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
283276 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
284277
......@@ -292,7 +285,7 @@ pub const Builder = struct {
292285 // TODO remove empty directories
293286 }
294287
295 fn makeOneStep(self: &Builder, s: &Step) %void {
288 fn makeOneStep(self: &Builder, s: &Step) error!void {
296289 if (s.loop_flag) {
297290 warn("Dependency loop detected:\n {}\n", s.name);
298291 return error.DependencyLoopDetected;
......@@ -313,7 +306,7 @@ pub const Builder = struct {
313306 try s.make();
314307 }
315308
316 fn getTopLevelStepByName(self: &Builder, name: []const u8) %&Step {
309 fn getTopLevelStepByName(self: &Builder, name: []const u8) !&Step {
317310 for (self.top_level_steps.toSliceConst()) |top_level_step| {
318311 if (mem.eql(u8, top_level_step.step.name, name)) {
319312 return &top_level_step.step;
......@@ -548,7 +541,7 @@ pub const Builder = struct {
548541 return self.invalid_user_input;
549542 }
550543
551 fn spawnChild(self: &Builder, argv: []const []const u8) %void {
544 fn spawnChild(self: &Builder, argv: []const []const u8) !void {
552545 return self.spawnChildEnvMap(null, &self.env_map, argv);
553546 }
554547
......@@ -561,7 +554,7 @@ pub const Builder = struct {
561554 }
562555
563556 fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
564 argv: []const []const u8) %void
557 argv: []const []const u8) !void
565558 {
566559 if (self.verbose) {
567560 printCmd(cwd, argv);
......@@ -595,7 +588,7 @@ pub const Builder = struct {
595588 }
596589 }
597590
598 pub fn makePath(self: &Builder, path: []const u8) %void {
591 pub fn makePath(self: &Builder, path: []const u8) !void {
599592 os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| {
600593 warn("Unable to create path {}: {}\n", path, @errorName(err));
601594 return err;
......@@ -630,11 +623,11 @@ pub const Builder = struct {
630623 self.installed_files.append(full_path) catch unreachable;
631624 }
632625
633 fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) %void {
634 return self.copyFileMode(source_path, dest_path, 0o666);
626 fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void {
627 return self.copyFileMode(source_path, dest_path, os.default_file_mode);
635628 }
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 {
638631 if (self.verbose) {
639632 warn("cp {} {}\n", source_path, dest_path);
640633 }
......@@ -672,7 +665,7 @@ pub const Builder = struct {
672665 }
673666 }
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 {
676669 // TODO report error for ambiguous situations
677670 const exe_extension = (Target { .Native = {}}).exeFileExt();
678671 for (self.search_prefixes.toSliceConst()) |search_prefix| {
......@@ -721,7 +714,7 @@ pub const Builder = struct {
721714 return error.FileNotFound;
722715 }
723716
724 pub fn exec(self: &Builder, argv: []const []const u8) %[]u8 {
717 pub fn exec(self: &Builder, argv: []const []const u8) ![]u8 {
725718 const max_output_size = 100 * 1024;
726719 const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
727720 switch (result.term) {
......@@ -1180,12 +1173,12 @@ pub const LibExeObjStep = struct {
11801173 self.disable_libc = disable;
11811174 }
11821175
1183 fn make(step: &Step) %void {
1176 fn make(step: &Step) !void {
11841177 const self = @fieldParentPtr(LibExeObjStep, "step", step);
11851178 return if (self.is_zig) self.makeZig() else self.makeC();
11861179 }
11871180
1188 fn makeZig(self: &LibExeObjStep) %void {
1181 fn makeZig(self: &LibExeObjStep) !void {
11891182 const builder = self.builder;
11901183
11911184 assert(self.is_zig);
......@@ -1396,7 +1389,7 @@ pub const LibExeObjStep = struct {
13961389 }
13971390 }
13981391
1399 fn makeC(self: &LibExeObjStep) %void {
1392 fn makeC(self: &LibExeObjStep) !void {
14001393 const builder = self.builder;
14011394
14021395 const cc = builder.getCCExe();
......@@ -1687,7 +1680,7 @@ pub const TestStep = struct {
16871680 self.exec_cmd_args = args;
16881681 }
16891682
1690 fn make(step: &Step) %void {
1683 fn make(step: &Step) !void {
16911684 const self = @fieldParentPtr(TestStep, "step", step);
16921685 const builder = self.builder;
16931686
......@@ -1796,7 +1789,7 @@ pub const CommandStep = struct {
17961789 return self;
17971790 }
17981791
1799 fn make(step: &Step) %void {
1792 fn make(step: &Step) !void {
18001793 const self = @fieldParentPtr(CommandStep, "step", step);
18011794
18021795 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
......@@ -1836,14 +1829,17 @@ const InstallArtifactStep = struct {
18361829 return self;
18371830 }
18381831
1839 fn make(step: &Step) %void {
1832 fn make(step: &Step) !void {
18401833 const self = @fieldParentPtr(Self, "step", step);
18411834 const builder = self.builder;
18421835
1843 const mode = switch (self.artifact.kind) {
1844 LibExeObjStep.Kind.Obj => unreachable,
1845 LibExeObjStep.Kind.Exe => usize(0o755),
1846 LibExeObjStep.Kind.Lib => if (self.artifact.static) usize(0o666) else usize(0o755),
1836 const mode = switch (builtin.os) {
1837 builtin.Os.windows => {},
1838 else => switch (self.artifact.kind) {
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 },
18471843 };
18481844 try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode);
18491845 if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
......@@ -1868,7 +1864,7 @@ pub const InstallFileStep = struct {
18681864 };
18691865 }
18701866
1871 fn make(step: &Step) %void {
1867 fn make(step: &Step) !void {
18721868 const self = @fieldParentPtr(InstallFileStep, "step", step);
18731869 try self.builder.copyFile(self.src_path, self.dest_path);
18741870 }
......@@ -1889,7 +1885,7 @@ pub const WriteFileStep = struct {
18891885 };
18901886 }
18911887
1892 fn make(step: &Step) %void {
1888 fn make(step: &Step) !void {
18931889 const self = @fieldParentPtr(WriteFileStep, "step", step);
18941890 const full_path = self.builder.pathFromRoot(self.file_path);
18951891 const full_path_dir = os.path.dirname(full_path);
......@@ -1897,7 +1893,7 @@ pub const WriteFileStep = struct {
18971893 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
18981894 return err;
18991895 };
1900 io.writeFile(full_path, self.data, self.builder.allocator) catch |err| {
1896 io.writeFile(self.builder.allocator, full_path, self.data) catch |err| {
19011897 warn("unable to write {}: {}\n", full_path, @errorName(err));
19021898 return err;
19031899 };
......@@ -1917,7 +1913,7 @@ pub const LogStep = struct {
19171913 };
19181914 }
19191915
1920 fn make(step: &Step) %void {
1916 fn make(step: &Step) error!void {
19211917 const self = @fieldParentPtr(LogStep, "step", step);
19221918 warn("{}", self.data);
19231919 }
......@@ -1936,7 +1932,7 @@ pub const RemoveDirStep = struct {
19361932 };
19371933 }
19381934
1939 fn make(step: &Step) %void {
1935 fn make(step: &Step) !void {
19401936 const self = @fieldParentPtr(RemoveDirStep, "step", step);
19411937
19421938 const full_path = self.builder.pathFromRoot(self.dir_path);
......@@ -1949,12 +1945,12 @@ pub const RemoveDirStep = struct {
19491945
19501946pub const Step = struct {
19511947 name: []const u8,
1952 makeFn: fn(self: &Step) %void,
1948 makeFn: fn(self: &Step) error!void,
19531949 dependencies: ArrayList(&Step),
19541950 loop_flag: bool,
19551951 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 {
19581954 return Step {
19591955 .name = name,
19601956 .makeFn = makeFn,
......@@ -1967,7 +1963,7 @@ pub const Step = struct {
19671963 return init(name, allocator, makeNoOp);
19681964 }
19691965
1970 pub fn make(self: &Step) %void {
1966 pub fn make(self: &Step) !void {
19711967 if (self.done_flag)
19721968 return;
19731969
......@@ -1979,11 +1975,11 @@ pub const Step = struct {
19791975 self.dependencies.append(other) catch unreachable;
19801976 }
19811977
1982 fn makeNoOp(self: &Step) %void {}
1978 fn makeNoOp(self: &Step) error!void {}
19831979};
19841980
19851981fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8,
1986 filename_name_only: []const u8) %void
1982 filename_name_only: []const u8) !void
19871983{
19881984 const out_dir = os.path.dirname(output_path);
19891985 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;
2020pub extern "c" fn raise(sig: c_int) c_int;
2121pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize;
2222pub 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;
2424pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int,
2525 fd: c_int, offset: isize) ?&c_void;
2626pub 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({
1818
1919const Mb = 1024 * 1024;
2020
21pub fn main() %void {
21pub fn main() !void {
2222 var stdout_file = try std.io.getStdOut();
2323 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
2424 const stdout = &stdout_out_stream.stream;
std/cstr.zig+10-4
......@@ -1,8 +1,15 @@
11const std = @import("index.zig");
2const builtin = @import("builtin");
23const debug = std.debug;
34const mem = std.mem;
45const assert = debug.assert;
56
7pub const line_sep = switch (builtin.os) {
8 builtin.Os.windows => "\r\n",
9 else => "\n",
10};
11
12
613pub fn len(ptr: &const u8) usize {
714 var count: usize = 0;
815 while (ptr[count] != 0) : (count += 1) {}
......@@ -39,10 +46,9 @@ fn testCStrFnsImpl() void {
3946 assert(len(c"123456789") == 9);
4047}
4148
42/// Returns a mutable slice with exactly the same size which is guaranteed to
43/// have a null byte after it.
49/// Returns a mutable slice with 1 more byte of length which is a null byte.
4450/// 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 {
4652 const result = try allocator.alloc(u8, slice.len + 1);
4753 mem.copy(u8, result, slice);
4854 result[slice.len] = 0;
......@@ -56,7 +62,7 @@ pub const NullTerminated2DArray = struct {
5662
5763 /// Takes N lists of strings, concatenates the lists together, and adds a null terminator
5864 /// 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 {
6066 var new_len: usize = 1; // 1 for the list null
6167 var byte_count: usize = 0;
6268 for (slices) |slice| {
std/debug/failing_allocator.zig+2-2
......@@ -28,7 +28,7 @@ pub const FailingAllocator = struct {
2828 };
2929 }
3030
31 fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) %[]u8 {
31 fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 {
3232 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
3333 if (self.index == self.fail_index) {
3434 return error.OutOfMemory;
......@@ -39,7 +39,7 @@ pub const FailingAllocator = struct {
3939 return result;
4040 }
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 {
4343 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
4444 if (new_size <= old_mem.len) {
4545 self.freed_bytes += old_mem.len - new_size;
std/debug/index.zig+55-55
......@@ -10,26 +10,17 @@ const builtin = @import("builtin");
1010
1111pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
1212
13error MissingDebugInfo;
14error InvalidDebugInfo;
15error UnsupportedDebugInfo;
16error UnknownObjectFormat;
17error TodoSupportCoffDebugInfo;
18error TodoSupportMachoDebugInfo;
19error TodoSupportCOFFDebugInfo;
20
21
2213/// Tries to write to stderr, unbuffered, and ignores any error returned.
2314/// Does not append a newline.
2415/// TODO atomic/multithread support
25var stderr_file: io.File = undefined;
16var stderr_file: os.File = undefined;
2617var stderr_file_out_stream: io.FileOutStream = undefined;
27var stderr_stream: ?&io.OutStream = null;
18var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null;
2819pub fn warn(comptime fmt: []const u8, args: ...) void {
2920 const stderr = getStderrStream() catch return;
3021 stderr.print(fmt, args) catch return;
3122}
32fn getStderrStream() %&io.OutStream {
23fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) {
3324 if (stderr_stream) |st| {
3425 return st;
3526 } else {
......@@ -42,7 +33,7 @@ fn getStderrStream() %&io.OutStream {
4233}
4334
4435var self_debug_info: ?&ElfStackTrace = null;
45pub fn getSelfDebugInfo() %&ElfStackTrace {
36pub fn getSelfDebugInfo() !&ElfStackTrace {
4637 if (self_debug_info) |info| {
4738 return info;
4839 } else {
......@@ -149,11 +140,8 @@ const WHITE = "\x1b[37;1m";
149140const DIM = "\x1b[2m";
150141const RESET = "\x1b[0m";
151142
152error PathNotFound;
153error InvalidDebugInfo;
154
155pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
156 debug_info: &ElfStackTrace, tty_color: bool) %void
143pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator,
144 debug_info: &ElfStackTrace, tty_color: bool) !void
157145{
158146 var frame_index: usize = undefined;
159147 var frames_left: usize = undefined;
......@@ -174,8 +162,8 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.O
174162 }
175163}
176164
177pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
178 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) %void
165pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
166 debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) !void
179167{
180168 var ignored_count: usize = 0;
181169
......@@ -191,7 +179,7 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
191179 }
192180}
193181
194fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) %void {
182fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
195183 if (builtin.os == builtin.Os.windows) {
196184 return error.UnsupportedDebugInfo;
197185 }
......@@ -221,7 +209,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
221209 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
222210 }
223211 } else |err| switch (err) {
224 error.EndOfFile, error.PathNotFound => {},
212 error.EndOfFile => {},
225213 else => return err,
226214 }
227215 } else |err| switch (err) {
......@@ -232,7 +220,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
232220 }
233221}
234222
235pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {
223pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
236224 switch (builtin.object_format) {
237225 builtin.ObjectFormat.elf => {
238226 const st = try allocator.create(ElfStackTrace);
......@@ -276,8 +264,8 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {
276264 }
277265}
278266
279fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) %void {
280 var f = try io.File.openRead(line_info.file_name, allocator);
267fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void {
268 var f = try os.File.openRead(allocator, line_info.file_name);
281269 defer f.close();
282270 // 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_
310298}
311299
312300pub const ElfStackTrace = struct {
313 self_exe_file: io.File,
301 self_exe_file: os.File,
314302 elf: elf.Elf,
315303 debug_info: &elf.SectionHeader,
316304 debug_abbrev: &elf.SectionHeader,
......@@ -324,7 +312,7 @@ pub const ElfStackTrace = struct {
324312 return self.abbrev_table_list.allocator;
325313 }
326314
327 pub fn readString(self: &ElfStackTrace) %[]u8 {
315 pub fn readString(self: &ElfStackTrace) ![]u8 {
328316 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
329317 const in_stream = &in_file_stream.stream;
330318 return readStringRaw(self.allocator(), in_stream);
......@@ -387,7 +375,7 @@ const Constant = struct {
387375 payload: []u8,
388376 signed: bool,
389377
390 fn asUnsignedLe(self: &const Constant) %u64 {
378 fn asUnsignedLe(self: &const Constant) !u64 {
391379 if (self.payload.len > @sizeOf(u64))
392380 return error.InvalidDebugInfo;
393381 if (self.signed)
......@@ -414,7 +402,7 @@ const Die = struct {
414402 return null;
415403 }
416404
417 fn getAttrAddr(self: &const Die, id: u64) %u64 {
405 fn getAttrAddr(self: &const Die, id: u64) !u64 {
418406 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
419407 return switch (*form_value) {
420408 FormValue.Address => |value| value,
......@@ -422,7 +410,7 @@ const Die = struct {
422410 };
423411 }
424412
425 fn getAttrSecOffset(self: &const Die, id: u64) %u64 {
413 fn getAttrSecOffset(self: &const Die, id: u64) !u64 {
426414 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
427415 return switch (*form_value) {
428416 FormValue.Const => |value| value.asUnsignedLe(),
......@@ -431,7 +419,7 @@ const Die = struct {
431419 };
432420 }
433421
434 fn getAttrUnsignedLe(self: &const Die, id: u64) %u64 {
422 fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 {
435423 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
436424 return switch (*form_value) {
437425 FormValue.Const => |value| value.asUnsignedLe(),
......@@ -439,7 +427,7 @@ const Die = struct {
439427 };
440428 }
441429
442 fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) %[]u8 {
430 fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 {
443431 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
444432 return switch (*form_value) {
445433 FormValue.String => |value| value,
......@@ -512,7 +500,7 @@ const LineNumberProgram = struct {
512500 };
513501 }
514502
515 pub fn checkLineMatch(self: &LineNumberProgram) %?LineInfo {
503 pub fn checkLineMatch(self: &LineNumberProgram) !?LineInfo {
516504 if (self.target_address >= self.prev_address and self.target_address < self.address) {
517505 const file_entry = if (self.prev_file == 0) {
518506 return error.MissingDebugInfo;
......@@ -544,7 +532,7 @@ const LineNumberProgram = struct {
544532 }
545533};
546534
547fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {
535fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 {
548536 var buf = ArrayList(u8).init(allocator);
549537 while (true) {
550538 const byte = try in_stream.readByte();
......@@ -555,58 +543,70 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {
555543 return buf.toSlice();
556544}
557545
558fn getString(st: &ElfStackTrace, offset: u64) %[]u8 {
546fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
559547 const pos = st.debug_str.offset + offset;
560548 try st.self_exe_file.seekTo(pos);
561549 return st.readString();
562550}
563551
564fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %[]u8 {
552fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
565553 const buf = try global_allocator.alloc(u8, size);
566554 errdefer global_allocator.free(buf);
567555 if ((try in_stream.read(buf)) < size) return error.EndOfFile;
568556 return buf;
569557}
570558
571fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {
559fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
572560 const buf = try readAllocBytes(allocator, in_stream, size);
573561 return FormValue { .Block = buf };
574562}
575563
576fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {
564fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
577565 const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size);
578566 return parseFormValueBlockLen(allocator, in_stream, block_len);
579567}
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 {
582570 return FormValue { .Const = Constant {
583571 .signed = signed,
584572 .payload = try readAllocBytes(allocator, in_stream, size),
585573 }};
586574}
587575
588fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) %u64 {
576fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
589577 return if (is_64) try in_stream.readIntLe(u64)
590578 else u64(try in_stream.readIntLe(u32)) ;
591579}
592580
593fn parseFormValueTargetAddrSize(in_stream: &io.InStream) %u64 {
581fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
594582 return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32))
595583 else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64)
596584 else unreachable;
597585}
598586
599fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {
587fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
600588 const buf = try readAllocBytes(allocator, in_stream, size);
601589 return FormValue { .Ref = buf };
602590}
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 {
605593 const block_len = try in_stream.readIntLe(T);
606594 return parseFormValueRefLen(allocator, in_stream, block_len);
607595}
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{
610610 return switch (form_id) {
611611 DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) },
612612 DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
......@@ -656,7 +656,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
656656 };
657657}
658658
659fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {
659fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable {
660660 const in_file = &st.self_exe_file;
661661 var in_file_stream = io.FileInStream.init(in_file);
662662 const in_stream = &in_file_stream.stream;
......@@ -688,7 +688,7 @@ fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {
688688
689689/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
690690/// 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 {
692692 for (st.abbrev_table_list.toSlice()) |*header| {
693693 if (header.offset == abbrev_offset) {
694694 return &header.table;
......@@ -710,7 +710,7 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&con
710710 return null;
711711}
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 {
714714 const in_file = &st.self_exe_file;
715715 var in_file_stream = io.FileInStream.init(in_file);
716716 const in_stream = &in_file_stream.stream;
......@@ -732,7 +732,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) %
732732 return result;
733733}
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 {
736736 const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);
737737
738738 const in_file = &st.self_exe_file;
......@@ -747,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
747747 try in_file.seekTo(this_offset);
748748
749749 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);
751751 if (unit_length == 0)
752752 return error.MissingDebugInfo;
753753 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
910910 return error.MissingDebugInfo;
911911}
912912
913fn scanAllCompileUnits(st: &ElfStackTrace) %void {
913fn scanAllCompileUnits(st: &ElfStackTrace) !void {
914914 const debug_info_end = st.debug_info.offset + st.debug_info.size;
915915 var this_unit_offset = st.debug_info.offset;
916916 var cu_index: usize = 0;
......@@ -922,7 +922,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {
922922 try st.self_exe_file.seekTo(this_unit_offset);
923923
924924 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);
926926 if (unit_length == 0)
927927 return;
928928 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
......@@ -986,7 +986,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {
986986 }
987987}
988988
989fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit {
989fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit {
990990 var in_file_stream = io.FileInStream.init(&st.self_exe_file);
991991 const in_stream = &in_file_stream.stream;
992992 for (st.compile_unit_list.toSlice()) |*compile_unit| {
......@@ -1022,7 +1022,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit
10221022 return error.MissingDebugInfo;
10231023}
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 {
10261026 const first_32_bits = try in_stream.readIntLe(u32);
10271027 *is_64 = (first_32_bits == 0xffffffff);
10281028 if (*is_64) {
......@@ -1033,7 +1033,7 @@ fn readInitialLength(in_stream: &io.InStream, is_64: &bool) %u64 {
10331033 }
10341034}
10351035
1036fn readULeb128(in_stream: &io.InStream) %u64 {
1036fn readULeb128(in_stream: var) !u64 {
10371037 var result: u64 = 0;
10381038 var shift: usize = 0;
10391039
......@@ -1054,7 +1054,7 @@ fn readULeb128(in_stream: &io.InStream) %u64 {
10541054 }
10551055}
10561056
1057fn readILeb128(in_stream: &io.InStream) %i64 {
1057fn readILeb128(in_stream: var) !i64 {
10581058 var result: i64 = 0;
10591059 var shift: usize = 0;
10601060
std/elf.zig+7-8
......@@ -1,13 +1,12 @@
11const builtin = @import("builtin");
22const std = @import("index.zig");
33const io = std.io;
4const os = std.os;
45const math = std.math;
56const mem = std.mem;
67const debug = std.debug;
78const InStream = std.stream.InStream;
89
9error InvalidFormat;
10
1110pub const SHT_NULL = 0;
1211pub const SHT_PROGBITS = 1;
1312pub const SHT_SYMTAB = 2;
......@@ -65,7 +64,7 @@ pub const SectionHeader = struct {
6564};
6665
6766pub const Elf = struct {
68 in_file: &io.File,
67 in_file: &os.File,
6968 auto_close_stream: bool,
7069 is_64: bool,
7170 endian: builtin.Endian,
......@@ -78,17 +77,17 @@ pub const Elf = struct {
7877 string_section: &SectionHeader,
7978 section_headers: []SectionHeader,
8079 allocator: &mem.Allocator,
81 prealloc_file: io.File,
80 prealloc_file: os.File,
8281
8382 /// 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 {
8584 try elf.prealloc_file.open(path);
8685 try elf.openFile(allocator, &elf.prealloc_file);
8786 elf.auto_close_stream = true;
8887 }
8988
9089 /// 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 {
9291 elf.allocator = allocator;
9392 elf.in_file = file;
9493 elf.auto_close_stream = false;
......@@ -239,7 +238,7 @@ pub const Elf = struct {
239238 elf.in_file.close();
240239 }
241240
242 pub fn findSection(elf: &Elf, name: []const u8) %?&SectionHeader {
241 pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader {
243242 var file_stream = io.FileInStream.init(elf.in_file);
244243 const in = &file_stream.stream;
245244
......@@ -263,7 +262,7 @@ pub const Elf = struct {
263262 return null;
264263 }
265264
266 pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) %void {
265 pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void {
267266 try elf.in_file.seekTo(elf_section.offset);
268267 }
269268};
std/fmt/index.zig+56-52
......@@ -24,8 +24,8 @@ const State = enum { // TODO put inside format function and make sure the name a
2424/// Renders fmt string with args, calling output with slices of bytes.
2525/// If `output` returns an error, the error is returned from `format` and
2626/// `output` is not called again.
27pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
28 comptime fmt: []const u8, args: ...) %void
27pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
28 comptime fmt: []const u8, args: ...) Errors!void
2929{
3030 comptime var start_index = 0;
3131 comptime var state = State.Start;
......@@ -58,7 +58,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
5858 start_index = i;
5959 },
6060 '}' => {
61 try formatValue(args[next_arg], context, output);
61 try formatValue(args[next_arg], context, Errors, output);
6262 next_arg += 1;
6363 state = State.Start;
6464 start_index = i + 1;
......@@ -110,7 +110,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
110110 },
111111 State.Integer => switch (c) {
112112 '}' => {
113 try formatInt(args[next_arg], radix, uppercase, width, context, output);
113 try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
114114 next_arg += 1;
115115 state = State.Start;
116116 start_index = i + 1;
......@@ -124,7 +124,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
124124 State.IntegerWidth => switch (c) {
125125 '}' => {
126126 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);
128128 next_arg += 1;
129129 state = State.Start;
130130 start_index = i + 1;
......@@ -134,7 +134,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
134134 },
135135 State.Float => switch (c) {
136136 '}' => {
137 try formatFloatDecimal(args[next_arg], 0, context, output);
137 try formatFloatDecimal(args[next_arg], 0, context, Errors, output);
138138 next_arg += 1;
139139 state = State.Start;
140140 start_index = i + 1;
......@@ -148,7 +148,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
148148 State.FloatWidth => switch (c) {
149149 '}' => {
150150 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);
152152 next_arg += 1;
153153 state = State.Start;
154154 start_index = i + 1;
......@@ -159,7 +159,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
159159 State.BufWidth => switch (c) {
160160 '}' => {
161161 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);
163163 next_arg += 1;
164164 state = State.Start;
165165 start_index = i + 1;
......@@ -169,7 +169,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
169169 },
170170 State.Character => switch (c) {
171171 '}' => {
172 try formatAsciiChar(args[next_arg], context, output);
172 try formatAsciiChar(args[next_arg], context, Errors, output);
173173 next_arg += 1;
174174 state = State.Start;
175175 start_index = i + 1;
......@@ -191,14 +191,14 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
191191 }
192192}
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 {
195195 const T = @typeOf(value);
196196 switch (@typeId(T)) {
197197 builtin.TypeId.Int => {
198 return formatInt(value, 10, false, 0, context, output);
198 return formatInt(value, 10, false, 0, context, Errors, output);
199199 },
200200 builtin.TypeId.Float => {
201 return formatFloat(value, context, output);
201 return formatFloat(value, context, Errors, output);
202202 },
203203 builtin.TypeId.Void => {
204204 return output(context, "void");
......@@ -208,19 +208,19 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
208208 },
209209 builtin.TypeId.Nullable => {
210210 if (value) |payload| {
211 return formatValue(payload, context, output);
211 return formatValue(payload, context, Errors, output);
212212 } else {
213213 return output(context, "null");
214214 }
215215 },
216216 builtin.TypeId.ErrorUnion => {
217217 if (value) |payload| {
218 return formatValue(payload, context, output);
218 return formatValue(payload, context, Errors, output);
219219 } else |err| {
220 return formatValue(err, context, output);
220 return formatValue(err, context, Errors, output);
221221 }
222222 },
223 builtin.TypeId.Error => {
223 builtin.TypeId.ErrorSet => {
224224 try output(context, "error.");
225225 return output(context, @errorName(value));
226226 },
......@@ -228,7 +228,7 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
228228 if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) {
229229 return output(context, (*value)[0..]);
230230 } else {
231 return format(context, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
231 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
232232 }
233233 },
234234 else => if (@canImplicitCast([]const u8, value)) {
......@@ -240,12 +240,12 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
240240 }
241241}
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 {
244244 return output(context, (&c)[0..1]);
245245}
246246
247247pub fn formatBuf(buf: []const u8, width: usize,
248 context: var, output: fn(@typeOf(context), []const u8)%void) %void
248 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
249249{
250250 try output(context, buf);
251251
......@@ -256,7 +256,7 @@ pub fn formatBuf(buf: []const u8, width: usize,
256256 }
257257}
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 {
260260 var x = f64(value);
261261
262262 // Errol doesn't handle these special cases.
......@@ -290,11 +290,11 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
290290
291291 if (float_decimal.exp != 1) {
292292 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);
294294 }
295295}
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 {
298298 var x = f64(value);
299299
300300 // Errol doesn't handle these special cases.
......@@ -336,17 +336,17 @@ pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn
336336
337337
338338pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
339 context: var, output: fn(@typeOf(context), []const u8)%void) %void
339 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
340340{
341341 if (@typeOf(value).is_signed) {
342 return formatIntSigned(value, base, uppercase, width, context, output);
342 return formatIntSigned(value, base, uppercase, width, context, Errors, output);
343343 } else {
344 return formatIntUnsigned(value, base, uppercase, width, context, output);
344 return formatIntUnsigned(value, base, uppercase, width, context, Errors, output);
345345 }
346346}
347347
348348fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
349 context: var, output: fn(@typeOf(context), []const u8)%void) %void
349 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
350350{
351351 const uint = @IntType(false, @typeOf(value).bit_count);
352352 if (value < 0) {
......@@ -354,20 +354,20 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
354354 try output(context, (&minus_sign)[0..1]);
355355 const new_value = uint(-(value + 1)) + 1;
356356 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);
358358 } 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);
360360 } else {
361361 const plus_sign: u8 = '+';
362362 try output(context, (&plus_sign)[0..1]);
363363 const new_value = uint(value);
364364 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);
366366 }
367367}
368368
369369fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
370 context: var, output: fn(@typeOf(context), []const u8)%void) %void
370 context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
371371{
372372 // max_int_digits accounts for the minus sign. when printing an unsigned
373373 // 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:
410410 .out_buf = out_buf,
411411 .index = 0,
412412 };
413 formatInt(value, base, uppercase, width, &context, formatIntCallback) catch unreachable;
413 formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable;
414414 return context.index;
415415}
416416const FormatIntBuf = struct {
417417 out_buf: []u8,
418418 index: usize,
419419};
420fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) %void {
420fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) {
421421 mem.copy(u8, context.out_buf[context.index..], bytes);
422422 context.index += bytes.len;
423423}
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 {
426426 if (!T.is_signed)
427427 return parseUnsigned(T, buf, radix);
428428 if (buf.len == 0)
......@@ -439,14 +439,21 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T {
439439test "fmt.parseInt" {
440440 assert((parseInt(i32, "-10", 10) catch unreachable) == -10);
441441 assert((parseInt(i32, "+10", 10) catch unreachable) == 10);
442 assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidChar);
443 assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidChar);
444 assert(if (parseInt(u32, "-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.InvalidCharacter);
444 assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter);
445445 assert((parseInt(u8, "255", 10) catch unreachable) == 255);
446446 assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
447447}
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 {
450457 var x: T = 0;
451458
452459 for (buf) |c| {
......@@ -458,17 +465,16 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T {
458465 return x;
459466}
460467
461error InvalidChar;
462fn charToDigit(c: u8, radix: u8) %u8 {
468fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
463469 const value = switch (c) {
464470 '0' ... '9' => c - '0',
465471 'A' ... 'Z' => c - 'A' + 10,
466472 'a' ... 'z' => c - 'a' + 10,
467 else => return error.InvalidChar,
473 else => return error.InvalidCharacter,
468474 };
469475
470476 if (value >= radix)
471 return error.InvalidChar;
477 return error.InvalidCharacter;
472478
473479 return value;
474480}
......@@ -485,28 +491,26 @@ const BufPrintContext = struct {
485491 remaining: []u8,
486492};
487493
488error BufferTooSmall;
489fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) %void {
494fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void {
490495 if (context.remaining.len < bytes.len) return error.BufferTooSmall;
491496 mem.copy(u8, context.remaining, bytes);
492497 context.remaining = context.remaining[bytes.len..];
493498}
494499
495pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) %[]u8 {
500pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
496501 var context = BufPrintContext { .remaining = buf, };
497 try format(&context, bufPrintWrite, fmt, args);
502 try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args);
498503 return buf[0..buf.len - context.remaining.len];
499504}
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 {
502507 var size: usize = 0;
503 // Cannot fail because `countSize` cannot fail.
504 format(&size, countSize, fmt, args) catch unreachable;
508 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
505509 const buf = try allocator.alloc(u8, size);
506510 return bufPrint(buf, fmt, args);
507511}
508512
509fn countSize(size: &usize, bytes: []const u8) %void {
513fn countSize(size: &usize, bytes: []const u8) (error{}!void) {
510514 *size += bytes.len;
511515}
512516
......@@ -534,7 +538,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u
534538
535539test "parse u64 digit too big" {
536540 _ = parseUnsigned(u64, "123a", 10) catch |err| {
537 if (err == error.InvalidChar) return;
541 if (err == error.InvalidCharacter) return;
538542 unreachable;
539543 };
540544 unreachable;
......@@ -567,13 +571,13 @@ test "fmt.format" {
567571 }
568572 {
569573 var buf1: [32]u8 = undefined;
570 const value: %i32 = 1234;
574 const value: error!i32 = 1234;
571575 const result = try bufPrint(buf1[0..], "error union: {}\n", value);
572576 assert(mem.eql(u8, result, "error union: 1234\n"));
573577 }
574578 {
575579 var buf1: [32]u8 = undefined;
576 const value: %i32 = error.InvalidChar;
580 const value: error!i32 = error.InvalidChar;
577581 const result = try bufPrint(buf1[0..], "error union: {}\n", value);
578582 assert(mem.eql(u8, result, "error union: error.InvalidChar\n"));
579583 }
std/hash_map.zig+2-2
......@@ -80,7 +80,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
8080 }
8181
8282 /// 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 {
8484 if (hm.entries.len == 0) {
8585 try hm.initCapacity(16);
8686 }
......@@ -151,7 +151,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
151151 };
152152 }
153153
154 fn initCapacity(hm: &Self, capacity: usize) %void {
154 fn initCapacity(hm: &Self, capacity: usize) !void {
155155 hm.entries = try hm.allocator.alloc(Entry, capacity);
156156 hm.size = 0;
157157 hm.max_distance_from_start_index = 0;
std/heap.zig+5-7
......@@ -9,8 +9,6 @@ const c = std.c;
99
1010const Allocator = mem.Allocator;
1111
12error OutOfMemory;
13
1412pub const c_allocator = &c_allocator_state;
1513var c_allocator_state = Allocator {
1614 .allocFn = cAlloc,
......@@ -18,14 +16,14 @@ var c_allocator_state = Allocator {
1816 .freeFn = cFree,
1917};
2018
21fn cAlloc(self: &Allocator, n: usize, alignment: u29) %[]u8 {
19fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 {
2220 return if (c.malloc(usize(n))) |buf|
2321 @ptrCast(&u8, buf)[0..n]
2422 else
2523 error.OutOfMemory;
2624}
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 {
2927 const old_ptr = @ptrCast(&c_void, old_mem.ptr);
3028 if (c.realloc(old_ptr, new_size)) |buf| {
3129 return @ptrCast(&u8, buf)[0..new_size];
......@@ -47,7 +45,7 @@ pub const IncrementingAllocator = struct {
4745 end_index: usize,
4846 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 {
5149 switch (builtin.os) {
5250 Os.linux, Os.macosx, Os.ios => {
5351 const p = os.posix;
......@@ -105,7 +103,7 @@ pub const IncrementingAllocator = struct {
105103 return self.bytes.len - self.end_index;
106104 }
107105
108 fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 {
106 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
109107 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
110108 const addr = @ptrToInt(&self.bytes[self.end_index]);
111109 const rem = @rem(addr, alignment);
......@@ -120,7 +118,7 @@ pub const IncrementingAllocator = struct {
120118 return result;
121119 }
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 {
124122 if (new_size <= old_mem.len) {
125123 return old_mem[0..new_size];
126124 } else {
std/index.zig+2
......@@ -28,6 +28,7 @@ pub const os = @import("os/index.zig");
2828pub const rand = @import("rand.zig");
2929pub const sort = @import("sort.zig");
3030pub const unicode = @import("unicode.zig");
31pub const zig = @import("zig/index.zig");
3132
3233test "std" {
3334 // run tests from these
......@@ -58,4 +59,5 @@ test "std" {
5859 _ = @import("rand.zig");
5960 _ = @import("sort.zig");
6061 _ = @import("unicode.zig");
62 _ = @import("zig/index.zig");
6163}
std/io.zig+230-456
......@@ -1,12 +1,6 @@
11const std = @import("index.zig");
22const builtin = @import("builtin");
33const 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};
104const c = std.c;
115
126const math = std.math;
......@@ -16,65 +10,38 @@ const os = std.os;
1610const mem = std.mem;
1711const Buffer = std.Buffer;
1812const fmt = std.fmt;
13const File = std.os.File;
1914
2015const is_posix = builtin.os != builtin.Os.windows;
2116const is_windows = builtin.os == builtin.Os.windows;
2217
23test "import io tests" {
24 comptime {
25 _ = @import("io_test.zig");
26 }
27}
18const GetStdIoErrs = os.WindowsGetStdHandleErrs;
2819
29/// The function received invalid input at runtime. An Invalid error means a
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 {
20pub fn getStdErr() GetStdIoErrs!File {
5421 const handle = if (is_windows)
55 try os.windowsGetStdHandle(system.STD_ERROR_HANDLE)
22 try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE)
5623 else if (is_posix)
57 system.STDERR_FILENO
24 os.posix.STDERR_FILENO
5825 else
5926 unreachable;
6027 return File.openHandle(handle);
6128}
6229
63pub fn getStdOut() %File {
30pub fn getStdOut() GetStdIoErrs!File {
6431 const handle = if (is_windows)
65 try os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)
32 try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE)
6633 else if (is_posix)
67 system.STDOUT_FILENO
34 os.posix.STDOUT_FILENO
6835 else
6936 unreachable;
7037 return File.openHandle(handle);
7138}
7239
73pub fn getStdIn() %File {
40pub fn getStdIn() GetStdIoErrs!File {
7441 const handle = if (is_windows)
75 try os.windowsGetStdHandle(system.STD_INPUT_HANDLE)
42 try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE)
7643 else if (is_posix)
77 system.STDIN_FILENO
44 os.posix.STDIN_FILENO
7845 else
7946 unreachable;
8047 return File.openHandle(handle);
......@@ -83,18 +50,21 @@ pub fn getStdIn() %File {
8350/// Implementation of InStream trait for File
8451pub const FileInStream = struct {
8552 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
8858 pub fn init(file: &File) FileInStream {
8959 return FileInStream {
9060 .file = file,
91 .stream = InStream {
61 .stream = Stream {
9262 .readFn = readFn,
9363 },
9464 };
9565 }
9666
97 fn readFn(in_stream: &InStream, buffer: []u8) %usize {
67 fn readFn(in_stream: &Stream, buffer: []u8) Error!usize {
9868 const self = @fieldParentPtr(FileInStream, "stream", in_stream);
9969 return self.file.read(buffer);
10070 }
......@@ -103,453 +73,202 @@ pub const FileInStream = struct {
10373/// Implementation of OutStream trait for File
10474pub const FileOutStream = struct {
10575 file: &File,
106 stream: OutStream,
76 stream: Stream,
77
78 pub const Error = File.WriteError;
79 pub const Stream = OutStream(Error);
10780
10881 pub fn init(file: &File) FileOutStream {
10982 return FileOutStream {
11083 .file = file,
111 .stream = OutStream {
84 .stream = Stream {
11285 .writeFn = writeFn,
11386 },
11487 };
11588 }
11689
117 fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {
90 fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
11891 const self = @fieldParentPtr(FileOutStream, "stream", out_stream);
11992 return self.file.write(bytes);
12093 }
12194};
12295
123pub const File = struct {
124 /// The OS-specific file descriptor or file handle.
125 handle: os.FileHandle,
126
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 }
96pub fn InStream(comptime ReadError: type) type {
97 return struct {
98 const Self = this;
99 pub const Error = ReadError;
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,
181 /// you must use the open() function.
182 pub fn close(self: &File) void {
183 os.close(self.handle);
184 self.handle = undefined;
185 }
106 /// Replaces `buffer` contents by reading from the stream until it is finished.
107 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
108 /// the contents read from the stream are lost.
109 pub fn readAllBuffer(self: &Self, buffer: &Buffer, max_size: usize) !void {
110 try buffer.resize(0);
186111
187 /// Calls `os.isTty` on `self.handle`.
188 pub fn isTty(self: &File) bool {
189 return os.isTty(self.handle);
190 }
112 var actual_buf_len: usize = 0;
113 while (true) {
114 const dest_slice = buffer.toSlice()[actual_buf_len..];
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 {
193 switch (builtin.os) {
194 Os.linux, Os.macosx, Os.ios => {
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 };
118 if (bytes_read != dest_slice.len) {
119 buffer.shrink(actual_buf_len);
120 return;
206121 }
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 {
222 switch (builtin.os) {
223 Os.linux, Os.macosx, Os.ios => {
224 const ipos = try math.cast(isize, pos);
225 const result = system.lseek(self.handle, ipos, system.SEEK_SET);
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)),
123 const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
124 if (new_buf_size == actual_buf_len)
125 return error.StreamTooLong;
126 try buffer.resize(new_buf_size);
127 }
249128 }
250 }
251129
252 pub fn getPos(self: &File) %usize {
253 switch (builtin.os) {
254 Os.linux, Os.macosx, Os.ios => {
255 const result = system.lseek(self.handle, 0, system.SEEK_CUR);
256 const err = system.getErrno(result);
257 if (err > 0) {
258 return switch (err) {
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 }
130 /// Allocates enough memory to hold all the contents of the stream. If the allocated
131 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
132 /// Caller owns returned memory.
133 /// If this function returns an error, the contents from the stream read so far are lost.
134 pub fn readAllAlloc(self: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 {
135 var buf = Buffer.initNull(allocator);
136 defer buf.deinit();
285137
286 return usize(pos);
287 },
288 else => @compileError("unsupported OS"),
138 try self.readAllBuffer(&buf, max_size);
139 return buf.toOwnedSlice();
289140 }
290 }
291141
292 pub fn getEndPos(self: &File) %usize {
293 if (is_posix) {
294 var stat: system.Stat = undefined;
295 const err = system.getErrno(system.fstat(self.handle, &stat));
296 if (err > 0) {
297 return switch (err) {
298 system.EBADF => error.BadFd,
299 system.ENOMEM => error.SystemResources,
300 else => os.unexpectedErrorPosix(err),
301 };
302 }
142 /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
143 /// Does not include the delimiter in the result.
144 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
145 /// read from the stream so far are lost.
146 pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
147 try buf.resize(0);
303148
304 return usize(stat.size);
305 } else if (is_windows) {
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 }
149 while (true) {
150 var byte: u8 = try self.readByte();
320151
321 pub fn read(self: &File, buffer: []u8) %usize {
322 if (is_posix) {
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 }
152 if (byte == delimiter) {
153 return;
336154 }
337 if (amt_read == 0) return index;
338 index += amt_read;
339 }
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 };
155
156 if (buf.len() == max_size) {
157 return error.StreamTooLong;
353158 }
354 if (amt_read == 0) return index;
355 index += amt_read;
159
160 try buf.appendByte(byte);
356161 }
357 return index;
358 } else {
359 unreachable;
360162 }
361 }
362163
363 fn write(self: &File, bytes: []const u8) %void {
364 if (is_posix) {
365 try os.posixWrite(self.handle, bytes);
366 } else if (is_windows) {
367 try os.windowsWrite(self.handle, bytes);
368 } else {
369 @compileError("Unsupported OS");
164 /// Allocates enough memory to read until `delimiter`. If the allocated
165 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
166 /// Caller owns returned memory.
167 /// If this function returns an error, the contents from the stream read so far are lost.
168 pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator,
169 delimiter: u8, max_size: usize) ![]u8
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();
370176 }
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);
401 if (new_buf_size == actual_buf_len)
402 return error.StreamTooLong;
403 try buffer.resize(new_buf_size);
178 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
179 /// means the stream reached the end. Reaching the end of a stream is not an error
180 /// condition.
181 pub fn read(self: &Self, buffer: []u8) !usize {
182 return self.readFn(self, buffer);
404183 }
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) {
434 return error.StreamTooLong;
435 }
436
437 try buf.appendByte(byte);
185 /// Same as `read` but end of stream returns `error.EndOfStream`.
186 pub fn readNoEof(self: &Self, buf: []u8) !void {
187 const amt_read = try self.read(buf);
188 if (amt_read < buf.len) return error.EndOfStream;
438189 }
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`.
469 pub fn readByte(self: &InStream) %u8 {
470 var result: [1]u8 = undefined;
471 try self.readNoEof(result[0..]);
472 return result[0];
473 }
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 }
191 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
192 pub fn readByte(self: &Self) !u8 {
193 var result: [1]u8 = undefined;
194 try self.readNoEof(result[0..]);
195 return result[0];
196 }
479197
480 pub fn readIntLe(self: &InStream, comptime T: type) %T {
481 return self.readInt(builtin.Endian.Little, T);
482 }
198 /// Same as `readByte` except the returned byte is signed.
199 pub fn readByteSigned(self: &Self) !i8 {
200 return @bitCast(i8, try self.readByte());
201 }
483202
484 pub fn readIntBe(self: &InStream, comptime T: type) %T {
485 return self.readInt(builtin.Endian.Big, T);
486 }
203 pub fn readIntLe(self: &Self, comptime T: type) !T {
204 return self.readInt(builtin.Endian.Little, T);
205 }
487206
488 pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) %T {
489 var bytes: [@sizeOf(T)]u8 = undefined;
490 try self.readNoEof(bytes[0..]);
491 return mem.readInt(bytes, T, endian);
492 }
207 pub fn readIntBe(self: &Self, comptime T: type) !T {
208 return self.readInt(builtin.Endian.Big, T);
209 }
493210
494 pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) %T {
495 assert(size <= @sizeOf(T));
496 assert(size <= 8);
497 var input_buf: [8]u8 = undefined;
498 const input_slice = input_buf[0..size];
499 try self.readNoEof(input_slice);
500 return mem.readInt(input_slice, T, endian);
501 }
211 pub fn readInt(self: &Self, endian: builtin.Endian, comptime T: type) !T {
212 var bytes: [@sizeOf(T)]u8 = undefined;
213 try self.readNoEof(bytes[0..]);
214 return mem.readInt(bytes, T, endian);
215 }
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 {
507 writeFn: fn(self: &OutStream, bytes: []const u8) %void,
233 writeFn: fn(self: &Self, bytes: []const u8) Error!void,
508234
509 pub fn print(self: &OutStream, comptime format: []const u8, args: ...) %void {
510 return std.fmt.format(self, self.writeFn, format, args);
511 }
235 pub fn print(self: &Self, comptime format: []const u8, args: ...) !void {
236 return std.fmt.format(self, Error, self.writeFn, format, args);
237 }
512238
513 pub fn write(self: &OutStream, bytes: []const u8) %void {
514 return self.writeFn(self, bytes);
515 }
239 pub fn write(self: &Self, bytes: []const u8) !void {
240 return self.writeFn(self, bytes);
241 }
516242
517 pub fn writeByte(self: &OutStream, byte: u8) %void {
518 const slice = (&byte)[0..1];
519 return self.writeFn(self, slice);
520 }
243 pub fn writeByte(self: &Self, byte: u8) !void {
244 const slice = (&byte)[0..1];
245 return self.writeFn(self, slice);
246 }
521247
522 pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) %void {
523 const slice = (&byte)[0..1];
524 var i: usize = 0;
525 while (i < n) : (i += 1) {
526 try self.writeFn(self, slice);
248 pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void {
249 const slice = (&byte)[0..1];
250 var i: usize = 0;
251 while (i < n) : (i += 1) {
252 try self.writeFn(self, slice);
253 }
527254 }
528 }
529};
255 };
256}
530257
531/// `path` may need to be copied in memory to add a null terminating byte. In this case
532/// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed
533/// size buffer is too small, and the provided allocator is null, `error.NameTooLong` is returned.
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);
258/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
259pub fn writeFile(allocator: &mem.Allocator, path: []const u8, data: []const u8) !void {
260 var file = try File.openWrite(allocator, path);
537261 defer file.close();
538262 try file.write(data);
539263}
540264
541265/// On success, caller owns returned buffer.
542pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) %[]u8 {
543 return readFileAllocExtra(path, allocator, 0);
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);
266pub fn readFileAlloc(allocator: &mem.Allocator, path: []const u8) ![]u8 {
267 var file = try File.openRead(allocator, path);
549268 defer file.close();
550269
551270 const size = try file.getEndPos();
552 const buf = try allocator.alloc(u8, size + extra_len);
271 const buf = try allocator.alloc(u8, size);
553272 errdefer allocator.free(buf);
554273
555274 var adapter = FileInStream.init(&file);
......@@ -557,21 +276,24 @@ pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len
557276 return buf;
558277}
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 {
563284 return struct {
564285 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
570292 buffer: [buffer_size]u8,
571293 start_index: usize,
572294 end_index: usize,
573295
574 pub fn init(unbuffered_in_stream: &InStream) Self {
296 pub fn init(unbuffered_in_stream: &Stream) Self {
575297 return Self {
576298 .unbuffered_in_stream = unbuffered_in_stream,
577299 .buffer = undefined,
......@@ -583,13 +305,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
583305 .start_index = buffer_size,
584306 .end_index = buffer_size,
585307
586 .stream = InStream {
308 .stream = Stream {
587309 .readFn = readFn,
588310 },
589311 };
590312 }
591313
592 fn readFn(in_stream: &InStream, dest: []u8) %usize {
314 fn readFn(in_stream: &Stream, dest: []u8) !usize {
593315 const self = @fieldParentPtr(Self, "stream", in_stream);
594316
595317 var dest_index: usize = 0;
......@@ -628,39 +350,40 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
628350 };
629351}
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 {
634358 return struct {
635359 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
641367 buffer: [buffer_size]u8,
642368 index: usize,
643369
644 pub fn init(unbuffered_out_stream: &OutStream) Self {
370 pub fn init(unbuffered_out_stream: &Stream) Self {
645371 return Self {
646372 .unbuffered_out_stream = unbuffered_out_stream,
647373 .buffer = undefined,
648374 .index = 0,
649 .stream = OutStream {
375 .stream = Stream {
650376 .writeFn = writeFn,
651377 },
652378 };
653379 }
654380
655 pub fn flush(self: &Self) %void {
656 if (self.index == 0)
657 return;
658
381 pub fn flush(self: &Self) !void {
659382 try self.unbuffered_out_stream.write(self.buffer[0..self.index]);
660383 self.index = 0;
661384 }
662385
663 fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {
386 fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
664387 const self = @fieldParentPtr(Self, "stream", out_stream);
665388
666389 if (bytes.len >= self.buffer.len) {
......@@ -687,20 +410,71 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type {
687410/// Implementation of OutStream trait for Buffer
688411pub const BufferOutStream = struct {
689412 buffer: &Buffer,
690 stream: OutStream,
413 stream: Stream,
414
415 pub const Error = error{OutOfMemory};
416 pub const Stream = OutStream(Error);
691417
692418 pub fn init(buffer: &Buffer) BufferOutStream {
693419 return BufferOutStream {
694420 .buffer = buffer,
695 .stream = OutStream {
421 .stream = Stream {
696422 .writeFn = writeFn,
697423 },
698424 };
699425 }
700426
701 fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {
427 fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
702428 const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
703429 return self.buffer.append(bytes);
704430 }
705431};
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" {
1313 rng.fillBytes(data[0..]);
1414 const tmp_file_name = "temp_test_file.txt";
1515 {
16 var file = try io.File.openWrite(tmp_file_name, allocator);
16 var file = try os.File.openWrite(allocator, tmp_file_name);
1717 defer file.close();
1818
1919 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);
2121 const st = &buf_stream.stream;
2222 try st.print("begin");
2323 try st.write(data[0..]);
......@@ -25,7 +25,7 @@ test "write a file, read it, then delete it" {
2525 try buf_stream.flush();
2626 }
2727 {
28 var file = try io.File.openRead(tmp_file_name, allocator);
28 var file = try os.File.openRead(allocator, tmp_file_name);
2929 defer file.close();
3030
3131 const file_size = try file.getEndPos();
......@@ -33,7 +33,7 @@ test "write a file, read it, then delete it" {
3333 assert(file_size == expected_file_size);
3434
3535 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);
3737 const st = &buf_stream.stream;
3838 const contents = try st.readAllAlloc(allocator, 2 * 1024);
3939 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
190190 ///
191191 /// Returns:
192192 /// A pointer to the new node.
193 pub fn allocateNode(list: &Self, allocator: &Allocator) %&Node {
193 pub fn allocateNode(list: &Self, allocator: &Allocator) !&Node {
194194 comptime assert(!isIntrusive());
195195 return allocator.create(Node);
196196 }
......@@ -213,7 +213,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
213213 ///
214214 /// Returns:
215215 /// 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 {
217217 comptime assert(!isIntrusive());
218218 var node = try list.allocateNode(allocator);
219219 *node = Node.init(data);
std/math/index.zig+13-31
......@@ -191,30 +191,26 @@ test "math.max" {
191191 assert(max(i32(-1), i32(2)) == 2);
192192}
193193
194error Overflow;
195pub fn mul(comptime T: type, a: T, b: T) %T {
194pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
196195 var answer: T = undefined;
197196 return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;
198197}
199198
200error Overflow;
201pub fn add(comptime T: type, a: T, b: T) %T {
199pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
202200 var answer: T = undefined;
203201 return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
204202}
205203
206error Overflow;
207pub fn sub(comptime T: type, a: T, b: T) %T {
204pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
208205 var answer: T = undefined;
209206 return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
210207}
211208
212pub fn negate(x: var) %@typeOf(x) {
209pub fn negate(x: var) !@typeOf(x) {
213210 return sub(@typeOf(x), 0, x);
214211}
215212
216error Overflow;
217pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) %T {
213pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
218214 var answer: T = undefined;
219215 return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;
220216}
......@@ -323,8 +319,7 @@ fn testOverflow() void {
323319}
324320
325321
326error Overflow;
327pub fn absInt(x: var) %@typeOf(x) {
322pub fn absInt(x: var) !@typeOf(x) {
328323 const T = @typeOf(x);
329324 comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
330325 comptime assert(T.is_signed); // must pass a signed integer to absInt
......@@ -347,9 +342,7 @@ fn testAbsInt() void {
347342
348343pub const absFloat = @import("fabs.zig").fabs;
349344
350error DivisionByZero;
351error Overflow;
352pub fn divTrunc(comptime T: type, numerator: T, denominator: T) %T {
345pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
353346 @setRuntimeSafety(false);
354347 if (denominator == 0)
355348 return error.DivisionByZero;
......@@ -372,9 +365,7 @@ fn testDivTrunc() void {
372365 assert((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0);
373366}
374367
375error DivisionByZero;
376error Overflow;
377pub fn divFloor(comptime T: type, numerator: T, denominator: T) %T {
368pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
378369 @setRuntimeSafety(false);
379370 if (denominator == 0)
380371 return error.DivisionByZero;
......@@ -397,10 +388,7 @@ fn testDivFloor() void {
397388 assert((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0);
398389}
399390
400error DivisionByZero;
401error Overflow;
402error UnexpectedRemainder;
403pub fn divExact(comptime T: type, numerator: T, denominator: T) %T {
391pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
404392 @setRuntimeSafety(false);
405393 if (denominator == 0)
406394 return error.DivisionByZero;
......@@ -428,9 +416,7 @@ fn testDivExact() void {
428416 if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder);
429417}
430418
431error DivisionByZero;
432error NegativeDenominator;
433pub fn mod(comptime T: type, numerator: T, denominator: T) %T {
419pub fn mod(comptime T: type, numerator: T, denominator: T) !T {
434420 @setRuntimeSafety(false);
435421 if (denominator == 0)
436422 return error.DivisionByZero;
......@@ -455,9 +441,7 @@ fn testMod() void {
455441 if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
456442}
457443
458error DivisionByZero;
459error NegativeDenominator;
460pub fn rem(comptime T: type, numerator: T, denominator: T) %T {
444pub fn rem(comptime T: type, numerator: T, denominator: T) !T {
461445 @setRuntimeSafety(false);
462446 if (denominator == 0)
463447 return error.DivisionByZero;
......@@ -505,8 +489,7 @@ test "math.absCast" {
505489
506490/// Returns the negation of the integer parameter.
507491/// Result is a signed integer.
508error Overflow;
509pub fn negateCast(x: var) %@IntType(true, @typeOf(x).bit_count) {
492pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) {
510493 if (@typeOf(x).is_signed)
511494 return negate(x);
512495
......@@ -532,8 +515,7 @@ test "math.negateCast" {
532515
533516/// Cast an integer to a different integer type. If the value doesn't fit,
534517/// return an error.
535error Overflow;
536pub fn cast(comptime T: type, x: var) %T {
518pub fn cast(comptime T: type, x: var) !T {
537519 comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer
538520 if (x > @maxValue(T)) {
539521 return error.Overflow;
std/mem.zig+15-15
......@@ -4,13 +4,13 @@ const assert = debug.assert;
44const math = std.math;
55const builtin = @import("builtin");
66
7error OutOfMemory;
8
97pub const Allocator = struct {
8 const Error = error {OutOfMemory};
9
1010 /// Allocate byte_count bytes and return them in a slice, with the
1111 /// slice's pointer aligned at least to alignment bytes.
1212 /// 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
1515 /// If `new_byte_count > old_mem.len`:
1616 /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
......@@ -21,12 +21,12 @@ pub const Allocator = struct {
2121 /// * alignment <= alignment of old_mem.ptr
2222 ///
2323 /// 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
2626 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
2727 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 {
3030 const slice = try self.alloc(T, 1);
3131 return &slice[0];
3232 }
......@@ -35,14 +35,14 @@ pub const Allocator = struct {
3535 self.free(ptr[0..1]);
3636 }
3737
38 fn alloc(self: &Allocator, comptime T: type, n: usize) %[]T {
38 fn alloc(self: &Allocator, comptime T: type, n: usize) ![]T {
3939 return self.alignedAlloc(T, @alignOf(T), n);
4040 }
4141
4242 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
43 n: usize) %[]align(alignment) T
43 n: usize) ![]align(alignment) T
4444 {
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;
4646 const byte_slice = try self.allocFn(self, byte_count, alignment);
4747 // This loop should get optimized out in ReleaseFast mode
4848 for (byte_slice) |*byte| {
......@@ -51,19 +51,19 @@ pub const Allocator = struct {
5151 return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
5252 }
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 {
5555 return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
5656 }
5757
5858 fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,
59 old_mem: []align(alignment) T, n: usize) %[]align(alignment) T
59 old_mem: []align(alignment) T, n: usize) ![]align(alignment) T
6060 {
6161 if (old_mem.len == 0) {
6262 return self.alloc(T, n);
6363 }
6464
6565 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;
6767 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
6868 // This loop should get optimized out in ReleaseFast mode
6969 for (byte_slice[old_byte_slice.len..]) |*byte| {
......@@ -123,7 +123,7 @@ pub const FixedBufferAllocator = struct {
123123 };
124124 }
125125
126 fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 {
126 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
127127 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
128128 const addr = @ptrToInt(&self.buffer[self.end_index]);
129129 const rem = @rem(addr, alignment);
......@@ -138,7 +138,7 @@ pub const FixedBufferAllocator = struct {
138138 return result;
139139 }
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 {
142142 if (new_size <= old_mem.len) {
143143 return old_mem[0..new_size];
144144 } else {
......@@ -197,7 +197,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
197197}
198198
199199/// 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 {
201201 const new_buf = try allocator.alloc(T, m.len);
202202 copy(T, new_buf, m);
203203 return new_buf;
......@@ -428,7 +428,7 @@ const SplitIterator = struct {
428428
429429/// Naively combines a series of strings with a separator.
430430/// 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 {
432432 comptime assert(strings.len >= 1);
433433 var total_strings_len: usize = strings.len; // 1 sep per string
434434 {
std/net.zig+9-25
......@@ -5,19 +5,10 @@ const endian = std.endian;
55
66// 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
178const Connection = struct {
189 socket_fd: i32,
1910
20 pub fn send(c: Connection, buf: []const u8) %usize {
11 pub fn send(c: Connection, buf: []const u8) !usize {
2112 const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
2213 const send_err = linux.getErrno(send_ret);
2314 switch (send_err) {
......@@ -31,7 +22,7 @@ const Connection = struct {
3122 }
3223 }
3324
34 pub fn recv(c: Connection, buf: []u8) %[]u8 {
25 pub fn recv(c: Connection, buf: []u8) ![]u8 {
3526 const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
3627 const recv_err = linux.getErrno(recv_ret);
3728 switch (recv_err) {
......@@ -48,7 +39,7 @@ const Connection = struct {
4839 }
4940 }
5041
51 pub fn close(c: Connection) %void {
42 pub fn close(c: Connection) !void {
5243 switch (linux.getErrno(linux.close(c.socket_fd))) {
5344 0 => return,
5445 linux.EBADF => unreachable,
......@@ -66,7 +57,7 @@ const Address = struct {
6657 sort_key: i32,
6758};
6859
69pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {
60pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
7061 if (hostname.len == 0) {
7162
7263 unreachable; // TODO
......@@ -75,7 +66,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {
7566 unreachable; // TODO
7667}
7768
78pub fn connectAddr(addr: &Address, port: u16) %Connection {
69pub fn connectAddr(addr: &Address, port: u16) !Connection {
7970 const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
8071 const socket_err = linux.getErrno(socket_ret);
8172 if (socket_err > 0) {
......@@ -118,7 +109,7 @@ pub fn connectAddr(addr: &Address, port: u16) %Connection {
118109 };
119110}
120111
121pub fn connect(hostname: []const u8, port: u16) %Connection {
112pub fn connect(hostname: []const u8, port: u16) !Connection {
122113 var addrs_buf: [1]Address = undefined;
123114 const addrs_slice = try lookup(hostname, addrs_buf[0..]);
124115 const main_addr = &addrs_slice[0];
......@@ -126,9 +117,7 @@ pub fn connect(hostname: []const u8, port: u16) %Connection {
126117 return connectAddr(main_addr, port);
127118}
128119
129error InvalidIpLiteral;
130
131pub fn parseIpLiteral(buf: []const u8) %Address {
120pub fn parseIpLiteral(buf: []const u8) !Address {
132121
133122 return error.InvalidIpLiteral;
134123}
......@@ -146,12 +135,7 @@ fn hexDigit(c: u8) u8 {
146135 }
147136}
148137
149error InvalidChar;
150error Overflow;
151error JunkAtEnd;
152error Incomplete;
153
154fn parseIp6(buf: []const u8) %Address {
138fn parseIp6(buf: []const u8) !Address {
155139 var result: Address = undefined;
156140 result.family = linux.AF_INET6;
157141 result.scope_id = 0;
......@@ -232,7 +216,7 @@ fn parseIp6(buf: []const u8) %Address {
232216 return error.Incomplete;
233217}
234218
235fn parseIp4(buf: []const u8) %u32 {
219fn parseIp4(buf: []const u8) !u32 {
236220 var result: u32 = undefined;
237221 const out_ptr = ([]u8)((&result)[0..1]);
238222
std/os/child_process.zig+75-54
......@@ -13,10 +13,6 @@ const builtin = @import("builtin");
1313const Os = builtin.Os;
1414const LinkedList = std.LinkedList;
1515
16error PermissionDenied;
17error ProcessNotFound;
18error InvalidName;
19
2016var children_nodes = LinkedList(&ChildProcess).init();
2117
2218const is_windows = builtin.os == Os.windows;
......@@ -28,11 +24,11 @@ pub const ChildProcess = struct {
2824
2925 pub allocator: &mem.Allocator,
3026
31 pub stdin: ?io.File,
32 pub stdout: ?io.File,
33 pub stderr: ?io.File,
27 pub stdin: ?os.File,
28 pub stdout: ?os.File,
29 pub stderr: ?os.File,
3430
35 pub term: ?%Term,
31 pub term: ?(SpawnError!Term),
3632
3733 pub argv: []const []const u8,
3834
......@@ -58,6 +54,25 @@ pub const ChildProcess = struct {
5854 err_pipe: if (is_windows) void else [2]i32,
5955 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
6176 pub const Term = union(enum) {
6277 Exited: i32,
6378 Signal: i32,
......@@ -74,7 +89,7 @@ pub const ChildProcess = struct {
7489
7590 /// First argument in argv is the executable.
7691 /// 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 {
7893 const child = try allocator.create(ChildProcess);
7994 errdefer allocator.destroy(child);
8095
......@@ -103,7 +118,7 @@ pub const ChildProcess = struct {
103118 return child;
104119 }
105120
106 pub fn setUserName(self: &ChildProcess, name: []const u8) %void {
121 pub fn setUserName(self: &ChildProcess, name: []const u8) !void {
107122 const user_info = try os.getUserInfo(name);
108123 self.uid = user_info.uid;
109124 self.gid = user_info.gid;
......@@ -111,7 +126,7 @@ pub const ChildProcess = struct {
111126
112127 /// onTerm can be called before `spawn` returns.
113128 /// On success must call `kill` or `wait`.
114 pub fn spawn(self: &ChildProcess) %void {
129 pub fn spawn(self: &ChildProcess) !void {
115130 if (is_windows) {
116131 return self.spawnWindows();
117132 } else {
......@@ -119,13 +134,13 @@ pub const ChildProcess = struct {
119134 }
120135 }
121136
122 pub fn spawnAndWait(self: &ChildProcess) %Term {
137 pub fn spawnAndWait(self: &ChildProcess) !Term {
123138 try self.spawn();
124139 return self.wait();
125140 }
126141
127142 /// Forcibly terminates child process and then cleans up all resources.
128 pub fn kill(self: &ChildProcess) %Term {
143 pub fn kill(self: &ChildProcess) !Term {
129144 if (is_windows) {
130145 return self.killWindows(1);
131146 } else {
......@@ -133,7 +148,7 @@ pub const ChildProcess = struct {
133148 }
134149 }
135150
136 pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) %Term {
151 pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) !Term {
137152 if (self.term) |term| {
138153 self.cleanupStreams();
139154 return term;
......@@ -145,11 +160,11 @@ pub const ChildProcess = struct {
145160 else => os.unexpectedErrorWindows(err),
146161 };
147162 }
148 self.waitUnwrappedWindows();
163 try self.waitUnwrappedWindows();
149164 return ??self.term;
150165 }
151166
152 pub fn killPosix(self: &ChildProcess) %Term {
167 pub fn killPosix(self: &ChildProcess) !Term {
153168 block_SIGCHLD();
154169 defer restore_SIGCHLD();
155170
......@@ -172,7 +187,7 @@ pub const ChildProcess = struct {
172187 }
173188
174189 /// 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 {
176191 if (is_windows) {
177192 return self.waitWindows();
178193 } else {
......@@ -189,7 +204,7 @@ pub const ChildProcess = struct {
189204 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
190205 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
191206 pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8,
192 env_map: ?&const BufMap, max_output_size: usize) %ExecResult
207 env_map: ?&const BufMap, max_output_size: usize) !ExecResult
193208 {
194209 const child = try ChildProcess.init(argv, allocator);
195210 defer child.deinit();
......@@ -220,7 +235,7 @@ pub const ChildProcess = struct {
220235 };
221236 }
222237
223 fn waitWindows(self: &ChildProcess) %Term {
238 fn waitWindows(self: &ChildProcess) !Term {
224239 if (self.term) |term| {
225240 self.cleanupStreams();
226241 return term;
......@@ -230,7 +245,7 @@ pub const ChildProcess = struct {
230245 return ??self.term;
231246 }
232247
233 fn waitPosix(self: &ChildProcess) %Term {
248 fn waitPosix(self: &ChildProcess) !Term {
234249 block_SIGCHLD();
235250 defer restore_SIGCHLD();
236251
......@@ -247,10 +262,10 @@ pub const ChildProcess = struct {
247262 self.allocator.destroy(self);
248263 }
249264
250 fn waitUnwrappedWindows(self: &ChildProcess) %void {
265 fn waitUnwrappedWindows(self: &ChildProcess) !void {
251266 const result = os.windowsWaitSingle(self.handle, windows.INFINITE);
252267
253 self.term = (%Term)(x: {
268 self.term = (SpawnError!Term)(x: {
254269 var exit_code: windows.DWORD = undefined;
255270 if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {
256271 break :x Term { .Unknown = 0 };
......@@ -295,7 +310,7 @@ pub const ChildProcess = struct {
295310 if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; }
296311 }
297312
298 fn cleanupAfterWait(self: &ChildProcess, status: i32) %Term {
313 fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term {
299314 children_nodes.remove(&self.llnode);
300315
301316 defer {
......@@ -313,7 +328,7 @@ pub const ChildProcess = struct {
313328 // Here we potentially return the fork child's error
314329 // from the parent pid.
315330 if (err_int != @maxValue(ErrInt)) {
316 return error(err_int);
331 return SpawnError(err_int);
317332 }
318333
319334 return statusToTerm(status);
......@@ -331,7 +346,7 @@ pub const ChildProcess = struct {
331346 ;
332347 }
333348
334 fn spawnPosix(self: &ChildProcess) %void {
349 fn spawnPosix(self: &ChildProcess) !void {
335350 // TODO atomically set a flag saying that we already did this
336351 install_SIGCHLD_handler();
337352
......@@ -345,11 +360,14 @@ pub const ChildProcess = struct {
345360 errdefer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
346361
347362 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)
349 try os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
350 else
351 undefined
352 ;
363 const dev_null_fd = if (any_ignore) blk: {
364 const dev_null_path = "/dev/null";
365 var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined;
366 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
367 break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0);
368 } else blk: {
369 break :blk undefined;
370 };
353371 defer { if (any_ignore) os.close(dev_null_fd); }
354372
355373 var env_map_owned: BufMap = undefined;
......@@ -410,17 +428,17 @@ pub const ChildProcess = struct {
410428 // we are the parent
411429 const pid = i32(pid_result);
412430 if (self.stdin_behavior == StdIo.Pipe) {
413 self.stdin = io.File.openHandle(stdin_pipe[1]);
431 self.stdin = os.File.openHandle(stdin_pipe[1]);
414432 } else {
415433 self.stdin = null;
416434 }
417435 if (self.stdout_behavior == StdIo.Pipe) {
418 self.stdout = io.File.openHandle(stdout_pipe[0]);
436 self.stdout = os.File.openHandle(stdout_pipe[0]);
419437 } else {
420438 self.stdout = null;
421439 }
422440 if (self.stderr_behavior == StdIo.Pipe) {
423 self.stderr = io.File.openHandle(stderr_pipe[0]);
441 self.stderr = os.File.openHandle(stderr_pipe[0]);
424442 } else {
425443 self.stderr = null;
426444 }
......@@ -440,7 +458,7 @@ pub const ChildProcess = struct {
440458 if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
441459 }
442460
443 fn spawnWindows(self: &ChildProcess) %void {
461 fn spawnWindows(self: &ChildProcess) !void {
444462 const saAttr = windows.SECURITY_ATTRIBUTES {
445463 .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
446464 .bInheritHandle = windows.TRUE,
......@@ -451,12 +469,15 @@ pub const ChildProcess = struct {
451469 self.stdout_behavior == StdIo.Ignore or
452470 self.stderr_behavior == StdIo.Ignore);
453471
454 const nul_handle = if (any_ignore)
455 try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
456 windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null)
457 else
458 undefined
459 ;
472 const nul_handle = if (any_ignore) blk: {
473 const nul_file_path = "NUL";
474 var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined;
475 var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
476 break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
477 windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
478 } else blk: {
479 break :blk undefined;
480 };
460481 defer { if (any_ignore) os.close(nul_handle); }
461482 if (any_ignore) {
462483 try windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);
......@@ -599,17 +620,17 @@ pub const ChildProcess = struct {
599620 };
600621
601622 if (g_hChildStd_IN_Wr) |h| {
602 self.stdin = io.File.openHandle(h);
623 self.stdin = os.File.openHandle(h);
603624 } else {
604625 self.stdin = null;
605626 }
606627 if (g_hChildStd_OUT_Rd) |h| {
607 self.stdout = io.File.openHandle(h);
628 self.stdout = os.File.openHandle(h);
608629 } else {
609630 self.stdout = null;
610631 }
611632 if (g_hChildStd_ERR_Rd) |h| {
612 self.stderr = io.File.openHandle(h);
633 self.stderr = os.File.openHandle(h);
613634 } else {
614635 self.stderr = null;
615636 }
......@@ -623,7 +644,7 @@ pub const ChildProcess = struct {
623644 if (self.stdout_behavior == StdIo.Pipe) { os.close(??g_hChildStd_OUT_Wr); }
624645 }
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 {
627648 switch (stdio) {
628649 StdIo.Pipe => try os.posixDup2(pipe_fd, std_fileno),
629650 StdIo.Close => os.close(std_fileno),
......@@ -635,7 +656,7 @@ pub const ChildProcess = struct {
635656};
636657
637658fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8,
638 lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) %void
659 lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void
639660{
640661 if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0,
641662 @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: ?
655676
656677/// Caller must dealloc.
657678/// 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 {
659680 var buf = try Buffer.initSize(allocator, 0);
660681 defer buf.deinit();
661682
......@@ -700,7 +721,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
700721// a namespace field lookup
701722const 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 {
704725 if (windows.CreatePipe(rd, wr, sattr, 0) == 0) {
705726 const err = windows.GetLastError();
706727 return switch (err) {
......@@ -709,7 +730,7 @@ fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECUR
709730 }
710731}
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 {
713734 if (windows.SetHandleInformation(h, mask, flags) == 0) {
714735 const err = windows.GetLastError();
715736 return switch (err) {
......@@ -718,7 +739,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D
718739 }
719740}
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 {
722743 var rd_h: windows.HANDLE = undefined;
723744 var wr_h: windows.HANDLE = undefined;
724745 try windowsMakePipe(&rd_h, &wr_h, sattr);
......@@ -728,7 +749,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S
728749 *wr = wr_h;
729750}
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 {
732753 var rd_h: windows.HANDLE = undefined;
733754 var wr_h: windows.HANDLE = undefined;
734755 try windowsMakePipe(&rd_h, &wr_h, sattr);
......@@ -738,7 +759,7 @@ fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const
738759 *wr = wr_h;
739760}
740761
741fn makePipe() %[2]i32 {
762fn makePipe() ![2]i32 {
742763 var fds: [2]i32 = undefined;
743764 const err = posix.getErrno(posix.pipe(&fds));
744765 if (err > 0) {
......@@ -757,20 +778,20 @@ fn destroyPipe(pipe: &const [2]i32) void {
757778
758779// Child of fork calls this to report an error to the fork parent.
759780// Then the child exits.
760fn forkChildErrReport(fd: i32, err: error) noreturn {
781fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
761782 _ = writeIntFd(fd, ErrInt(err));
762783 posix.exit(1);
763784}
764785
765786const ErrInt = @IntType(false, @sizeOf(error) * 8);
766787
767fn writeIntFd(fd: i32, value: ErrInt) %void {
788fn writeIntFd(fd: i32, value: ErrInt) !void {
768789 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
769790 mem.writeInt(bytes[0..], value, builtin.endian);
770791 os.posixWrite(fd, bytes[0..]) catch return error.SystemResources;
771792}
772793
773fn readIntFd(fd: i32) %ErrInt {
794fn readIntFd(fd: i32) !ErrInt {
774795 var bytes: [@sizeOf(ErrInt)]u8 = undefined;
775796 os.posixRead(fd, bytes[0..]) catch return error.SystemResources;
776797 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 {
99};
1010
1111/// POSIX function which gets a uid from username.
12pub fn getUserInfo(name: []const u8) %UserInfo {
12pub fn getUserInfo(name: []const u8) !UserInfo {
1313 return switch (builtin.os) {
1414 Os.linux, Os.macosx, Os.ios => posixGetUserInfo(name),
1515 else => @compileError("Unsupported OS"),
......@@ -24,13 +24,10 @@ const State = enum {
2424 ReadGroupId,
2525};
2626
27error UserNotFound;
28error CorruptPasswordFile;
29
3027// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
3128// 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 {
3431 var in_stream = try io.InStream.open("/etc/passwd", null);
3532 defer in_stream.close();
3633
std/os/index.zig+341-181
......@@ -15,13 +15,18 @@ pub const posix = switch(builtin.os) {
1515 else => @compileError("Unsupported OS"),
1616};
1717
18pub const max_noalloc_path_len = 1024;
1918pub const ChildProcess = @import("child_process.zig").ChildProcess;
2019pub const path = @import("path.zig");
20pub const File = @import("file.zig").File;
2121
22pub const line_sep = switch (builtin.os) {
23 Os.windows => "\r\n",
24 else => "\n",
22pub const FileMode = switch (builtin.os) {
23 Os.windows => void,
24 else => u32,
25};
26
27pub const default_file_mode = switch (builtin.os) {
28 Os.windows => {},
29 else => 0o666,
2530};
2631
2732pub const page_size = 4 * 1024;
......@@ -38,6 +43,10 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;
3843pub const windowsUnloadDll = windows_util.windowsUnloadDll;
3944pub 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
4150pub const FileHandle = if (is_windows) windows.HANDLE else i32;
4251
4352const debug = std.debug;
......@@ -57,25 +66,10 @@ const ArrayList = std.ArrayList;
5766const Buffer = std.Buffer;
5867const 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
7569/// Fills `buf` with random bytes. If linking against libc, this calls the
7670/// appropriate OS-specific library call. Otherwise it uses the zig standard
7771/// library implementation.
78pub fn getRandomBytes(buf: []u8) %void {
72pub fn getRandomBytes(buf: []u8) !void {
7973 switch (builtin.os) {
8074 Os.linux => while (true) {
8175 // TODO check libc version and potentially call c.getrandom.
......@@ -188,7 +182,7 @@ pub fn close(handle: FileHandle) void {
188182}
189183
190184/// 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 {
192186 // Linux can return EINVAL when read amount is > 0x7ffff000
193187 // See https://github.com/zig-lang/zig/pull/743#issuecomment-363158274
194188 const max_buf_len = 0x7ffff000;
......@@ -214,17 +208,21 @@ pub fn posixRead(fd: i32, buf: []u8) %void {
214208 }
215209}
216210
217error WouldBlock;
218error FileClosed;
219error DestinationAddressRequired;
220error DiskQuota;
221error FileTooBig;
222error InputOutput;
223error NoSpaceLeft;
224error BrokenPipe;
211pub const PosixWriteError = error {
212 WouldBlock,
213 FileClosed,
214 DestinationAddressRequired,
215 DiskQuota,
216 FileTooBig,
217 InputOutput,
218 NoSpaceLeft,
219 AccessDenied,
220 BrokenPipe,
221 Unexpected,
222};
225223
226224/// 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 {
228226 // Linux can return EINVAL when write amount is > 0x7ffff000
229227 // See https://github.com/zig-lang/zig/pull/743#issuecomment-363165856
230228 const max_bytes_len = 0x7ffff000;
......@@ -238,15 +236,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {
238236 return switch (write_err) {
239237 posix.EINTR => continue,
240238 posix.EINVAL, posix.EFAULT => unreachable,
241 posix.EAGAIN => error.WouldBlock,
242 posix.EBADF => error.FileClosed,
243 posix.EDESTADDRREQ => error.DestinationAddressRequired,
244 posix.EDQUOT => error.DiskQuota,
245 posix.EFBIG => error.FileTooBig,
246 posix.EIO => error.InputOutput,
247 posix.ENOSPC => error.NoSpaceLeft,
248 posix.EPERM => error.AccessDenied,
249 posix.EPIPE => error.BrokenPipe,
239 posix.EAGAIN => PosixWriteError.WouldBlock,
240 posix.EBADF => PosixWriteError.FileClosed,
241 posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
242 posix.EDQUOT => PosixWriteError.DiskQuota,
243 posix.EFBIG => PosixWriteError.FileTooBig,
244 posix.EIO => PosixWriteError.InputOutput,
245 posix.ENOSPC => PosixWriteError.NoSpaceLeft,
246 posix.EPERM => PosixWriteError.AccessDenied,
247 posix.EPIPE => PosixWriteError.BrokenPipe,
250248 else => unexpectedErrorPosix(write_err),
251249 };
252250 }
......@@ -254,66 +252,66 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {
254252 }
255253}
256254
257/// ::file_path may need to be copied in memory to add a null terminating byte. In this case
258/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
259/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
260/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
255pub const PosixOpenError = error {
256 OutOfMemory,
257 AccessDenied,
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.
261274/// Calls POSIX open, keeps trying if it gets interrupted, and translates
262275/// the return value into zig errors.
263pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) %i32 {
264 var stack_buf: [max_noalloc_path_len]u8 = undefined;
265 var path0: []u8 = undefined;
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;
276pub fn posixOpen(allocator: &Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
277 const path_with_null = try cstr.addNullByte(allocator, file_path);
278 defer allocator.free(path_with_null);
281279
282 return posixOpenC(path0.ptr, flags, perm);
280 return posixOpenC(path_with_null.ptr, flags, perm);
283281}
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 {
286284 while (true) {
287285 const result = posix.open(file_path, flags, perm);
288286 const err = posix.getErrno(result);
289287 if (err > 0) {
290 return switch (err) {
288 switch (err) {
291289 posix.EINTR => continue,
292290
293291 posix.EFAULT => unreachable,
294292 posix.EINVAL => unreachable,
295 posix.EACCES => error.AccessDenied,
296 posix.EFBIG, posix.EOVERFLOW => error.FileTooBig,
297 posix.EISDIR => error.IsDir,
298 posix.ELOOP => error.SymLinkLoop,
299 posix.EMFILE => error.ProcessFdQuotaExceeded,
300 posix.ENAMETOOLONG => error.NameTooLong,
301 posix.ENFILE => error.SystemFdQuotaExceeded,
302 posix.ENODEV => error.NoDevice,
303 posix.ENOENT => error.PathNotFound,
304 posix.ENOMEM => error.SystemResources,
305 posix.ENOSPC => error.NoSpaceLeft,
306 posix.ENOTDIR => error.NotDir,
307 posix.EPERM => error.AccessDenied,
308 posix.EEXIST => error.PathAlreadyExists,
309 else => unexpectedErrorPosix(err),
310 };
293 posix.EACCES => return PosixOpenError.AccessDenied,
294 posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,
295 posix.EISDIR => return PosixOpenError.IsDir,
296 posix.ELOOP => return PosixOpenError.SymLinkLoop,
297 posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
298 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
299 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
300 posix.ENODEV => return PosixOpenError.NoDevice,
301 posix.ENOENT => return PosixOpenError.PathNotFound,
302 posix.ENOMEM => return PosixOpenError.SystemResources,
303 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
304 posix.ENOTDIR => return PosixOpenError.NotDir,
305 posix.EPERM => return PosixOpenError.AccessDenied,
306 posix.EEXIST => return PosixOpenError.PathAlreadyExists,
307 else => return unexpectedErrorPosix(err),
308 }
311309 }
312310 return i32(result);
313311 }
314312}
315313
316pub fn posixDup2(old_fd: i32, new_fd: i32) %void {
314pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
317315 while (true) {
318316 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
319317 if (err > 0) {
......@@ -328,7 +326,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) %void {
328326 }
329327}
330328
331pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) %[]?&u8 {
329pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) ![]?&u8 {
332330 const envp_count = env_map.count();
333331 const envp_buf = try allocator.alloc(?&u8, envp_count + 1);
334332 mem.set(?&u8, envp_buf, null);
......@@ -365,7 +363,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
365363/// `argv[0]` is the executable path.
366364/// This function also uses the PATH environment variable to get the full path to the executable.
367365pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
368 allocator: &Allocator) %void
366 allocator: &Allocator) !void
369367{
370368 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
371369 mem.set(?&u8, argv_buf, null);
......@@ -421,7 +419,19 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
421419 return posixExecveErrnoToErr(err);
422420}
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 {
425435 assert(err > 0);
426436 return switch (err) {
427437 posix.EFAULT => unreachable,
......@@ -440,7 +450,7 @@ fn posixExecveErrnoToErr(err: usize) error {
440450pub var posix_environ_raw: []&u8 = undefined;
441451
442452/// Caller must free result when done.
443pub fn getEnvMap(allocator: &Allocator) %BufMap {
453pub fn getEnvMap(allocator: &Allocator) !BufMap {
444454 var result = BufMap.init(allocator);
445455 errdefer result.deinit();
446456
......@@ -501,10 +511,8 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
501511 return null;
502512}
503513
504error EnvironmentVariableNotFound;
505
506514/// 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 {
508516 if (is_windows) {
509517 const key_with_null = try cstr.addNullByte(allocator, key);
510518 defer allocator.free(key_with_null);
......@@ -538,7 +546,7 @@ pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) %[]u8 {
538546}
539547
540548/// Caller must free the returned memory.
541pub fn getCwd(allocator: &Allocator) %[]u8 {
549pub fn getCwd(allocator: &Allocator) ![]u8 {
542550 switch (builtin.os) {
543551 Os.windows => {
544552 var buf = try allocator.alloc(u8, 256);
......@@ -585,7 +593,9 @@ test "os.getCwd" {
585593 _ = getCwd(debug.global_allocator);
586594}
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 {
589599 if (is_windows) {
590600 return symLinkWindows(allocator, existing_path, new_path);
591601 } else {
......@@ -593,7 +603,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
593603 }
594604}
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 {
597612 const existing_with_null = try cstr.addNullByte(allocator, existing_path);
598613 defer allocator.free(existing_with_null);
599614 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
607622 }
608623}
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 {
611642 const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
612643 defer allocator.free(full_buf);
613644
......@@ -644,36 +675,36 @@ const b64_fs_encoder = base64.Base64Encoder.init(
644675 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
645676 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 {
648679 if (symLink(allocator, existing_path, new_path)) {
649680 return;
650 } else |err| {
651 if (err != error.PathAlreadyExists) {
652 return err;
653 }
681 } else |err| switch (err) {
682 error.PathAlreadyExists => {},
683 else => return err, // TODO zig should know this set does not include PathAlreadyExists
654684 }
655685
686 const dirname = os.path.dirname(new_path);
687
656688 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));
658690 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;
660693 while (true) {
661694 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
663697 if (symLink(allocator, existing_path, tmp_path)) {
664698 return rename(allocator, tmp_path, new_path);
665 } else |err| {
666 if (err == error.PathAlreadyExists) {
667 continue;
668 } else {
669 return err;
670 }
699 } else |err| switch (err) {
700 error.PathAlreadyExists => continue,
701 else => return err, // TODO zig should know this set does not include PathAlreadyExists
671702 }
672703 }
673704
674705}
675706
676pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {
707pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
677708 if (builtin.os == Os.windows) {
678709 return deleteFileWindows(allocator, file_path);
679710 } else {
......@@ -681,10 +712,7 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {
681712 }
682713}
683714
684error FileNotFound;
685error AccessDenied;
686
687pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {
715pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
688716 const buf = try allocator.alloc(u8, file_path.len + 1);
689717 defer allocator.free(buf);
690718
......@@ -702,7 +730,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {
702730 }
703731}
704732
705pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {
733pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
706734 const buf = try allocator.alloc(u8, file_path.len + 1);
707735 defer allocator.free(buf);
708736
......@@ -728,38 +756,109 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {
728756 }
729757}
730758
731/// Calls ::copyFileMode with 0o666 for the mode.
732pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) %void {
733 return copyFileMode(allocator, source_path, dest_path, 0o666);
734}
759/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
760/// merged and readily available,
761/// there is a possibility of power loss or application termination leaving temporary files present
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 open
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);
768 const mode = try in_file.mode();
745769
746 var out_file = try io.File.openWriteMode(tmp_path, mode, allocator);
747 defer out_file.close();
748 errdefer _ = deleteFile(allocator, tmp_path);
770 var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
771 defer atomic_file.deinit();
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);
751788 defer in_file.close();
752789
790 var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
791 defer atomic_file.deinit();
792
753793 var buf: [page_size]u8 = undefined;
754794 while (true) {
755795 const amt = try in_file.read(buf[0..]);
756 try out_file.write(buf[0..amt]);
757 if (amt != buf.len)
758 return rename(allocator, tmp_path, dest_path);
796 try atomic_file.file.write(buf[0..amt]);
797 if (amt != buf.len) {
798 return atomic_file.finish();
799 }
759800 }
760801}
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 {
763862 const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);
764863 defer allocator.free(full_buf);
765864
......@@ -804,7 +903,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
804903 }
805904}
806905
807pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {
906pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void {
808907 if (is_windows) {
809908 return makeDirWindows(allocator, dir_path);
810909 } else {
......@@ -812,7 +911,7 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {
812911 }
813912}
814913
815pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {
914pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void {
816915 const path_buf = try cstr.addNullByte(allocator, dir_path);
817916 defer allocator.free(path_buf);
818917
......@@ -826,7 +925,7 @@ pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {
826925 }
827926}
828927
829pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {
928pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
830929 const path_buf = try cstr.addNullByte(allocator, dir_path);
831930 defer allocator.free(path_buf);
832931
......@@ -852,7 +951,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {
852951
853952/// Calls makeDir recursively to make an entire path. Returns success if the path
854953/// 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 {
856955 const resolved_path = try path.resolve(allocator, full_path);
857956 defer allocator.free(resolved_path);
858957
......@@ -890,7 +989,7 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) %void {
890989
891990/// Returns ::error.DirNotEmpty if the directory is not empty.
892991/// 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 {
894993 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
895994 defer allocator.free(path_buf);
896995
......@@ -919,24 +1018,68 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) %void {
9191018/// removes it. If it cannot be removed because it is a non-empty directory,
9201019/// this function recursively removes its entries and then tries again.
9211020// 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 {
9231044 start_over: while (true) {
9241045 // First, try deleting the item as a file. This way we don't follow sym links.
9251046 if (deleteFile(allocator, full_path)) {
9261047 return;
927 } else |err| {
928 if (err == error.FileNotFound)
929 return;
930 if (err != error.IsDir)
931 return err;
1048 } else |err| switch (err) {
1049 error.FileNotFound => return,
1050 error.IsDir => {},
1051
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,
9321063 }
9331064 {
934 var dir = Dir.open(allocator, full_path) catch |err| {
935 if (err == error.FileNotFound)
936 return;
937 if (err == error.NotDir)
938 continue :start_over;
939 return err;
1065 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
1066 error.NotDir => continue :start_over,
1067
1068 error.OutOfMemory,
1069 error.AccessDenied,
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,
9401083 };
9411084 defer dir.close();
9421085
......@@ -988,8 +1131,8 @@ pub const Dir = struct {
9881131 };
9891132 };
9901133
991 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);
1134 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
1135 const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0);
9931136 return Dir {
9941137 .allocator = allocator,
9951138 .fd = fd,
......@@ -1006,7 +1149,7 @@ pub const Dir = struct {
10061149
10071150 /// Memory such as file names referenced in this returned entry becomes invalid
10081151 /// 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 {
10101153 start_over: while (true) {
10111154 if (self.index >= self.end_index) {
10121155 if (self.buf.len == 0) {
......@@ -1063,7 +1206,7 @@ pub const Dir = struct {
10631206 }
10641207};
10651208
1066pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {
1209pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void {
10671210 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
10681211 defer allocator.free(path_buf);
10691212
......@@ -1087,7 +1230,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {
10871230}
10881231
10891232/// Read value of a symbolic link.
1090pub fn readLink(allocator: &Allocator, pathname: []const u8) %[]u8 {
1233pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
10911234 const path_buf = try allocator.alloc(u8, pathname.len + 1);
10921235 defer allocator.free(path_buf);
10931236
......@@ -1164,11 +1307,7 @@ test "os.sleep" {
11641307 sleep(0, 1);
11651308}
11661309
1167error ResourceLimitReached;
1168error InvalidUserId;
1169error PermissionDenied;
1170
1171pub fn posix_setuid(uid: u32) %void {
1310pub fn posix_setuid(uid: u32) !void {
11721311 const err = posix.getErrno(posix.setuid(uid));
11731312 if (err == 0) return;
11741313 return switch (err) {
......@@ -1179,7 +1318,7 @@ pub fn posix_setuid(uid: u32) %void {
11791318 };
11801319}
11811320
1182pub fn posix_setreuid(ruid: u32, euid: u32) %void {
1321pub fn posix_setreuid(ruid: u32, euid: u32) !void {
11831322 const err = posix.getErrno(posix.setreuid(ruid, euid));
11841323 if (err == 0) return;
11851324 return switch (err) {
......@@ -1190,7 +1329,7 @@ pub fn posix_setreuid(ruid: u32, euid: u32) %void {
11901329 };
11911330}
11921331
1193pub fn posix_setgid(gid: u32) %void {
1332pub fn posix_setgid(gid: u32) !void {
11941333 const err = posix.getErrno(posix.setgid(gid));
11951334 if (err == 0) return;
11961335 return switch (err) {
......@@ -1201,7 +1340,7 @@ pub fn posix_setgid(gid: u32) %void {
12011340 };
12021341}
12031342
1204pub fn posix_setregid(rgid: u32, egid: u32) %void {
1343pub fn posix_setregid(rgid: u32, egid: u32) !void {
12051344 const err = posix.getErrno(posix.setregid(rgid, egid));
12061345 if (err == 0) return;
12071346 return switch (err) {
......@@ -1212,8 +1351,12 @@ pub fn posix_setregid(rgid: u32, egid: u32) %void {
12121351 };
12131352}
12141353
1215error NoStdHandles;
1216pub fn windowsGetStdHandle(handle_id: windows.DWORD) %windows.HANDLE {
1354pub const WindowsGetStdHandleErrs = error {
1355 NoStdHandles,
1356 Unexpected,
1357};
1358
1359pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE {
12171360 if (windows.GetStdHandle(handle_id)) |handle| {
12181361 if (handle == windows.INVALID_HANDLE_VALUE) {
12191362 const err = windows.GetLastError();
......@@ -1267,6 +1410,8 @@ pub const ArgIteratorWindows = struct {
12671410 quote_count: usize,
12681411 seen_quote_count: usize,
12691412
1413 pub const NextError = error{OutOfMemory};
1414
12701415 pub fn init() ArgIteratorWindows {
12711416 return initWithCmdLine(windows.GetCommandLineA());
12721417 }
......@@ -1282,7 +1427,7 @@ pub const ArgIteratorWindows = struct {
12821427 }
12831428
12841429 /// 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) {
12861431 // march forward over whitespace
12871432 while (true) : (self.index += 1) {
12881433 const byte = self.cmd_line[self.index];
......@@ -1335,7 +1480,7 @@ pub const ArgIteratorWindows = struct {
13351480 }
13361481 }
13371482
1338 fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) %[]u8 {
1483 fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) NextError![]u8 {
13391484 var buf = try Buffer.initSize(allocator, 0);
13401485 defer buf.deinit();
13411486
......@@ -1379,7 +1524,7 @@ pub const ArgIteratorWindows = struct {
13791524 }
13801525 }
13811526
1382 fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) %void {
1527 fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) !void {
13831528 var i: usize = 0;
13841529 while (i < emit_count) : (i += 1) {
13851530 try buf.appendByte('\\');
......@@ -1409,16 +1554,20 @@ pub const ArgIteratorWindows = struct {
14091554};
14101555
14111556pub 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
14141561 pub fn init() ArgIterator {
14151562 return ArgIterator {
1416 .inner = if (builtin.os == Os.windows) ArgIteratorWindows.init() else ArgIteratorPosix.init(),
1563 .inner = InnerType.init(),
14171564 };
14181565 }
1566
1567 pub const NextError = ArgIteratorWindows.NextError;
14191568
14201569 /// 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) {
14221571 if (builtin.os == Os.windows) {
14231572 return self.inner.next(allocator);
14241573 } else {
......@@ -1443,7 +1592,7 @@ pub fn args() ArgIterator {
14431592}
14441593
14451594/// Caller must call freeArgs on result.
1446pub fn argsAlloc(allocator: &mem.Allocator) %[]const []u8 {
1595pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 {
14471596 // TODO refactor to only make 1 allocation.
14481597 var it = args();
14491598 var contents = try Buffer.initSize(allocator, 0);
......@@ -1525,14 +1674,12 @@ test "std.os" {
15251674}
15261675
15271676
1528error Unexpected;
1529
15301677// TODO make this a build variable that you can set
15311678const unexpected_error_tracing = false;
15321679
15331680/// Call this when you made a syscall or something that sets errno
15341681/// and you get an unexpected error.
1535pub fn unexpectedErrorPosix(errno: usize) error {
1682pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
15361683 if (unexpected_error_tracing) {
15371684 debug.warn("unexpected errno: {}\n", errno);
15381685 debug.dumpStackTrace();
......@@ -1542,7 +1689,7 @@ pub fn unexpectedErrorPosix(errno: usize) error {
15421689
15431690/// Call this when you made a windows DLL call or something that does SetLastError
15441691/// and you get an unexpected error.
1545pub fn unexpectedErrorWindows(err: windows.DWORD) error {
1692pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
15461693 if (unexpected_error_tracing) {
15471694 debug.warn("unexpected GetLastError(): {}\n", err);
15481695 debug.dumpStackTrace();
......@@ -1550,25 +1697,38 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) error {
15501697 return error.Unexpected;
15511698}
15521699
1553pub fn openSelfExe() %io.File {
1700pub fn openSelfExe() !os.File {
15541701 switch (builtin.os) {
15551702 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);
15571707 },
15581708 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);
15601713 },
15611714 else => @compileError("Unsupported OS"),
15621715 }
15631716}
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
15651725/// Get the path to the current executable.
15661726/// If you only need the directory, use selfExeDirPath.
15671727/// If you only want an open file handle, use openSelfExe.
15681728/// This function may return an error if the current executable
15691729/// was deleted after spawning.
15701730/// Caller owns returned memory.
1571pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {
1731pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
15721732 switch (builtin.os) {
15731733 Os.linux => {
15741734 // If the currently executing binary has been deleted,
......@@ -1611,7 +1771,7 @@ pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {
16111771
16121772/// Get the directory path that contains the current executable.
16131773/// Caller owns returned memory.
1614pub fn selfExeDirPath(allocator: &mem.Allocator) %[]u8 {
1774pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
16151775 switch (builtin.os) {
16161776 Os.linux => {
16171777 // 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:
720720// error SystemResources;
721721// error Io;
722722//
723// pub fn if_nametoindex(name: []u8) %u32 {
723// pub fn if_nametoindex(name: []u8) !u32 {
724724// var ifr: ifreq = undefined;
725725//
726726// if (name.len >= ifr.ifr_name.len) {
std/os/path.zig+12-19
......@@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool {
3232
3333/// Naively combines a series of paths with the native path seperator.
3434/// 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 {
3636 if (is_windows) {
3737 return joinWindows(allocator, paths);
3838 } else {
......@@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) %[]u8 {
4040 }
4141}
4242
43pub fn joinWindows(allocator: &Allocator, paths: ...) %[]u8 {
43pub fn joinWindows(allocator: &Allocator, paths: ...) ![]u8 {
4444 return mem.join(allocator, sep_windows, paths);
4545}
4646
47pub fn joinPosix(allocator: &Allocator, paths: ...) %[]u8 {
47pub fn joinPosix(allocator: &Allocator, paths: ...) ![]u8 {
4848 return mem.join(allocator, sep_posix, paths);
4949}
5050
......@@ -313,7 +313,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
313313}
314314
315315/// 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 {
317317 var paths: [args.len][]const u8 = undefined;
318318 comptime var arg_i = 0;
319319 inline while (arg_i < args.len) : (arg_i += 1) {
......@@ -323,7 +323,7 @@ pub fn resolve(allocator: &Allocator, args: ...) %[]u8 {
323323}
324324
325325/// 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 {
327327 if (is_windows) {
328328 return resolveWindows(allocator, paths);
329329 } else {
......@@ -337,7 +337,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 {
337337/// If all paths are relative it uses the current working directory as a starting point.
338338/// Each drive has its own current working directory.
339339/// 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 {
341341 if (paths.len == 0) {
342342 assert(is_windows); // resolveWindows called on non windows can't use getCwd
343343 return os.getCwd(allocator);
......@@ -520,7 +520,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 {
520520/// It resolves "." and "..".
521521/// The result does not have a trailing path separator.
522522/// 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 {
524524 if (paths.len == 0) {
525525 assert(!is_windows); // resolvePosix called on windows can't use getCwd
526526 return os.getCwd(allocator);
......@@ -890,7 +890,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void {
890890/// resolve to the same path (after calling `resolve` on each), a zero-length
891891/// string is returned.
892892/// 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 {
894894 if (is_windows) {
895895 return relativeWindows(allocator, from, to);
896896 } else {
......@@ -898,7 +898,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {
898898 }
899899}
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 {
902902 const resolved_from = try resolveWindows(allocator, [][]const u8{from});
903903 defer allocator.free(resolved_from);
904904
......@@ -971,7 +971,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8)
971971 return []u8{};
972972}
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 {
975975 const resolved_from = try resolvePosix(allocator, [][]const u8{from});
976976 defer allocator.free(resolved_from);
977977
......@@ -1066,18 +1066,11 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
10661066 assert(mem.eql(u8, result, expected_output));
10671067}
10681068
1069error AccessDenied;
1070error FileNotFound;
1071error NotSupported;
1072error NotDir;
1073error NameTooLong;
1074error SymLinkLoop;
1075error InputOutput;
10761069/// Return the canonicalized absolute pathname.
10771070/// Expands all symbolic links and resolves references to `.`, `..`, and
10781071/// extra `/` characters in ::pathname.
10791072/// Caller must deallocate result.
1080pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {
1073pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 {
10811074 switch (builtin.os) {
10821075 Os.windows => {
10831076 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
......@@ -1168,7 +1161,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {
11681161 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
11691162 },
11701163 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);
11721165 defer os.close(fd);
11731166
11741167 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
std/os/windows/util.zig+47-48
......@@ -1,4 +1,5 @@
11const std = @import("../../index.zig");
2const builtin = @import("builtin");
23const os = std.os;
34const windows = std.os.windows;
45const assert = std.debug.assert;
......@@ -6,11 +7,13 @@ const mem = std.mem;
67const BufMap = std.BufMap;
78const cstr = std.cstr;
89
9error WaitAbandoned;
10error WaitTimeOut;
11error Unexpected;
10pub const WaitError = error {
11 WaitAbandoned,
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 {
1417 const result = windows.WaitForSingleObject(handle, milliseconds);
1518 return switch (result) {
1619 windows.WAIT_ABANDONED => error.WaitAbandoned,
......@@ -30,21 +33,24 @@ pub fn windowsClose(handle: windows.HANDLE) void {
3033 assert(windows.CloseHandle(handle) != 0);
3134}
3235
33error SystemResources;
34error OperationAborted;
35error IoPending;
36error BrokenPipe;
36pub const WriteError = error {
37 SystemResources,
38 OperationAborted,
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 {
3945 if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {
4046 const err = windows.GetLastError();
4147 return switch (err) {
42 windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,
43 windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,
44 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
45 windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,
46 windows.ERROR.IO_PENDING => error.IoPending,
47 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
49 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
50 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
51 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
52 windows.ERROR.IO_PENDING => WriteError.IoPending,
53 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
4854 else => os.unexpectedErrorWindows(err),
4955 };
5056 }
......@@ -75,43 +81,35 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {
7581 mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null;
7682}
7783
78error SharingViolation;
79error PipeBusy;
80
81/// `file_path` may need to be copied in memory to add a null terminating byte. In this case
82/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
83/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
84/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
85pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD,
86 creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) %windows.HANDLE
84pub const OpenError = error {
85 SharingViolation,
86 PathAlreadyExists,
87 FileNotFound,
88 AccessDenied,
89 PipeBusy,
90 Unexpected,
91 OutOfMemory,
92};
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
8798{
88 var stack_buf: [os.max_noalloc_path_len]u8 = undefined;
89 var path0: []u8 = undefined;
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;
99 const path_with_null = try cstr.addNullByte(allocator, file_path);
100 defer allocator.free(path_with_null);
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,
105103 flags_and_attrs, null);
106104
107105 if (result == windows.INVALID_HANDLE_VALUE) {
108106 const err = windows.GetLastError();
109107 return switch (err) {
110 windows.ERROR.SHARING_VIOLATION => error.SharingViolation,
111 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists,
112 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
113 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
114 windows.ERROR.PIPE_BUSY => error.PipeBusy,
108 windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
109 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
110 windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
111 windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
112 windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
115113 else => os.unexpectedErrorWindows(err),
116114 };
117115 }
......@@ -120,7 +118,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
120118}
121119
122120/// 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 {
124122 // count bytes needed
125123 const bytes_needed = x: {
126124 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)
151149 return result;
152150}
153151
154error DllNotFound;
155pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) %windows.HMODULE {
152pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows.HMODULE {
156153 const padded_buff = try cstr.addNullByte(allocator, dll_path);
157154 defer allocator.free(padded_buff);
158155 return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
......@@ -164,6 +161,8 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
164161
165162
166163test "InvalidDll" {
164 if (builtin.os != builtin.Os.windows) return;
165
167166 const DllName = "asdf.dll";
168167 const allocator = std.debug.global_allocator;
169168 const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
std/special/bootstrap.zig+2-2
......@@ -77,7 +77,7 @@ fn callMain() u8 {
7777 },
7878 builtin.TypeId.Int => {
7979 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'");
8181 }
8282 return root.main();
8383 },
......@@ -91,6 +91,6 @@ fn callMain() u8 {
9191 };
9292 return 0;
9393 },
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'"),
9595 }
9696}
std/special/build_file_template.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) !void {
44 const mode = b.standardReleaseOptions();
55 const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
66 exe.setBuildMode(mode);
std/special/build_runner.zig+20-10
......@@ -1,5 +1,6 @@
11const root = @import("@build");
22const std = @import("std");
3const builtin = @import("builtin");
34const io = std.io;
45const fmt = std.fmt;
56const os = std.os;
......@@ -8,9 +9,7 @@ const mem = std.mem;
89const ArrayList = std.ArrayList;
910const warn = std.debug.warn;
1011
11error InvalidArgs;
12
13pub fn main() %void {
12pub fn main() !void {
1413 var arg_it = os.args();
1514
1615 // TODO use a more general purpose allocator here
......@@ -45,14 +44,14 @@ pub fn main() %void {
4544
4645 var stderr_file = io.getStdErr();
4746 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: {
4948 stderr_file_stream = io.FileOutStream.init(f);
5049 break :x &stderr_file_stream.stream;
5150 } else |err| err;
5251
5352 var stdout_file = io.getStdOut();
5453 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: {
5655 stdout_file_stream = io.FileOutStream.init(f);
5756 break :x &stdout_file_stream.stream;
5857 } else |err| err;
......@@ -112,7 +111,7 @@ pub fn main() %void {
112111 }
113112
114113 builder.setInstallPrefix(prefix);
115 try root.build(&builder);
114 try runBuild(&builder);
116115
117116 if (builder.validateUserInputDidItFail())
118117 return usageAndErr(&builder, true, try stderr_stream);
......@@ -125,11 +124,19 @@ pub fn main() %void {
125124 };
126125}
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 {
129136 // run the build script to collect the options
130137 if (!already_ran_build) {
131138 builder.setInstallPrefix(null);
132 try root.build(builder);
139 try runBuild(builder);
133140 }
134141
135142 // 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)
149156 \\
150157 \\General Options:
151158 \\ --help Print this help and exit
159 \\ --init Generate a build.zig template
152160 \\ --verbose Print commands before executing them
153161 \\ --prefix [path] Override default install prefix
154162 \\ --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)
183191 );
184192}
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 {
187195 usage(builder, already_ran_build, out_stream) catch {};
188196 return error.InvalidArgs;
189197}
190198
191fn unwrapArg(arg: %[]u8) %[]u8 {
199const UnwrapArgError = error {OutOfMemory};
200
201fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
192202 return arg catch |err| {
193203 warn("Unable to parse command line: {}\n", err);
194204 return err;
std/special/test_runner.zig+1-1
......@@ -4,7 +4,7 @@ const builtin = @import("builtin");
44const test_fn_list = builtin.__zig_test_fn_slice;
55const warn = std.debug.warn;
66
7pub fn main() %void {
7pub fn main() !void {
88 for (test_fn_list) |test_fn, i| {
99 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1010
std/unicode.zig+6-14
......@@ -1,11 +1,9 @@
11const std = @import("./index.zig");
22
3error Utf8InvalidStartByte;
4
53/// Given the first byte of a UTF-8 codepoint,
64/// returns a number 1-4 indicating the total length of the codepoint in bytes.
75/// 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 {
97 if (first_byte < 0b10000000) return u3(1);
108 if (first_byte & 0b11100000 == 0b11000000) return u3(2);
119 if (first_byte & 0b11110000 == 0b11100000) return u3(3);
......@@ -13,16 +11,11 @@ pub fn utf8ByteSequenceLength(first_byte: u8) %u3 {
1311 return error.Utf8InvalidStartByte;
1412}
1513
16error Utf8OverlongEncoding;
17error Utf8ExpectedContinuation;
18error Utf8EncodesSurrogateHalf;
19error Utf8CodepointTooLarge;
20
2114/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
2215/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
2316/// If you already know the length at comptime, you can call one of
2417/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
25pub fn utf8Decode(bytes: []const u8) %u32 {
18pub fn utf8Decode(bytes: []const u8) !u32 {
2619 return switch (bytes.len) {
2720 1 => u32(bytes[0]),
2821 2 => utf8Decode2(bytes),
......@@ -31,7 +24,7 @@ pub fn utf8Decode(bytes: []const u8) %u32 {
3124 else => unreachable,
3225 };
3326}
34pub fn utf8Decode2(bytes: []const u8) %u32 {
27pub fn utf8Decode2(bytes: []const u8) !u32 {
3528 std.debug.assert(bytes.len == 2);
3629 std.debug.assert(bytes[0] & 0b11100000 == 0b11000000);
3730 var value: u32 = bytes[0] & 0b00011111;
......@@ -44,7 +37,7 @@ pub fn utf8Decode2(bytes: []const u8) %u32 {
4437
4538 return value;
4639}
47pub fn utf8Decode3(bytes: []const u8) %u32 {
40pub fn utf8Decode3(bytes: []const u8) !u32 {
4841 std.debug.assert(bytes.len == 3);
4942 std.debug.assert(bytes[0] & 0b11110000 == 0b11100000);
5043 var value: u32 = bytes[0] & 0b00001111;
......@@ -62,7 +55,7 @@ pub fn utf8Decode3(bytes: []const u8) %u32 {
6255
6356 return value;
6457}
65pub fn utf8Decode4(bytes: []const u8) %u32 {
58pub fn utf8Decode4(bytes: []const u8) !u32 {
6659 std.debug.assert(bytes.len == 4);
6760 std.debug.assert(bytes[0] & 0b11111000 == 0b11110000);
6861 var value: u32 = bytes[0] & 0b00000111;
......@@ -85,7 +78,6 @@ pub fn utf8Decode4(bytes: []const u8) %u32 {
8578 return value;
8679}
8780
88error UnexpectedEof;
8981test "valid utf8" {
9082 testValid("\x00", 0x0);
9183 testValid("\x20", 0x20);
......@@ -161,7 +153,7 @@ fn testValid(bytes: []const u8, expected_codepoint: u32) void {
161153 std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
162154}
163155
164fn testDecode(bytes: []const u8) %u32 {
156fn testDecode(bytes: []const u8) !u32 {
165157 const length = try utf8ByteSequenceLength(bytes[0]);
166158 if (bytes.len < length) return error.UnexpectedEof;
167159 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 {
3232 **x += 1;
3333}
3434
35error ItBroke;
3635test "explicit cast from integer to error type" {
3736 testCastIntToErr(error.ItBroke);
3837 comptime testCastIntToErr(error.ItBroke);
......@@ -75,7 +74,7 @@ test "string literal to &const []const u8" {
7574 assert(mem.eql(u8, *x, "hello"));
7675}
7776
78test "implicitly cast from T to %?T" {
77test "implicitly cast from T to error!?T" {
7978 castToMaybeTypeError(1);
8079 comptime castToMaybeTypeError(1);
8180}
......@@ -84,37 +83,37 @@ const A = struct {
8483};
8584fn castToMaybeTypeError(z: i32) void {
8685 const x = i32(1);
87 const y: %?i32 = x;
86 const y: error!?i32 = x;
8887 assert(??(try y) == 1);
8988
9089 const f = z;
91 const g: %?i32 = f;
90 const g: error!?i32 = f;
9291
9392 const a = A{ .a = z };
94 const b: %?A = a;
93 const b: error!?A = a;
9594 assert((??(b catch unreachable)).a == 1);
9695}
9796
98test "implicitly cast from int to %?T" {
97test "implicitly cast from int to error!?T" {
9998 implicitIntLitToMaybe();
10099 comptime implicitIntLitToMaybe();
101100}
102101fn implicitIntLitToMaybe() void {
103102 const f: ?i32 = 1;
104 const g: %?i32 = 1;
103 const g: error!?i32 = 1;
105104}
106105
107106
108test "return null from fn() %?&T" {
107test "return null from fn() error!?&T" {
109108 const a = returnNullFromMaybeTypeErrorRef();
110109 const b = returnNullLitFromMaybeTypeErrorRef();
111110 assert((try a) == null and (try b) == null);
112111}
113fn returnNullFromMaybeTypeErrorRef() %?&A {
112fn returnNullFromMaybeTypeErrorRef() error!?&A {
114113 const a: ?&A = null;
115114 return a;
116115}
117fn returnNullLitFromMaybeTypeErrorRef() %?&A {
116fn returnNullLitFromMaybeTypeErrorRef() error!?&A {
118117 return null;
119118}
120119
......@@ -161,7 +160,7 @@ fn castToMaybeSlice() ?[]const u8 {
161160}
162161
163162
164test "implicitly cast from [0]T to %[]T" {
163test "implicitly cast from [0]T to error![]T" {
165164 testCastZeroArrayToErrSliceMut();
166165 comptime testCastZeroArrayToErrSliceMut();
167166}
......@@ -170,11 +169,11 @@ fn testCastZeroArrayToErrSliceMut() void {
170169 assert((gimmeErrOrSlice() catch unreachable).len == 0);
171170}
172171
173fn gimmeErrOrSlice() %[]u8 {
172fn gimmeErrOrSlice() error![]u8 {
174173 return []u8{};
175174}
176175
177test "peer type resolution: [0]u8, []const u8, and %[]u8" {
176test "peer type resolution: [0]u8, []const u8, and error![]u8" {
178177 {
179178 var data = "hi";
180179 const slice = data[0..];
......@@ -188,7 +187,7 @@ test "peer type resolution: [0]u8, []const u8, and %[]u8" {
188187 assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
189188 }
190189}
191fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) %[]u8 {
190fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) error![]u8 {
192191 if (a) {
193192 return []u8{};
194193 }
......@@ -230,7 +229,7 @@ fn foo(args: ...) void {
230229
231230
232231test "peer type resolution: error and [N]T" {
233 // TODO: implicit %T to %U where T can implicitly cast to U
232 // TODO: implicit error!T to error!U where T can implicitly cast to U
234233 //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
235234 //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
236235
......@@ -238,14 +237,13 @@ test "peer type resolution: error and [N]T" {
238237 comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK"));
239238}
240239
241error BadValue;
242//fn testPeerErrorAndArray(x: u8) %[]const u8 {
240//fn testPeerErrorAndArray(x: u8) error![]const u8 {
243241// return switch (x) {
244242// 0x00 => "OK",
245243// else => error.BadValue,
246244// };
247245//}
248fn testPeerErrorAndArray2(x: u8) %[]const u8 {
246fn testPeerErrorAndArray2(x: u8) error![]const u8 {
249247 return switch (x) {
250248 0x00 => "OK",
251249 0x01 => "OKK",
test/cases/defer.zig+1-3
......@@ -3,9 +3,7 @@ const assert = @import("std").debug.assert;
33var result: [3]u8 = undefined;
44var index: usize = undefined;
55
6error FalseNotAllowed;
7
8fn runSomeErrorDefers(x: bool) %bool {
6fn runSomeErrorDefers(x: bool) !bool {
97 index = 0;
108 defer {result[index] = 'a'; index += 1;}
119 errdefer {result[index] = 'b'; index += 1;}
test/cases/enum_with_members.zig+1-1
......@@ -6,7 +6,7 @@ const ET = union(enum) {
66 SINT: i32,
77 UINT: u32,
88
9 pub fn print(a: &const ET, buf: []u8) %usize {
9 pub fn print(a: &const ET, buf: []u8) error!usize {
1010 return switch (*a) {
1111 ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
1212 ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
test/cases/error.zig+110-18
......@@ -1,16 +1,18 @@
1const assert = @import("std").debug.assert;
2const mem = @import("std").mem;
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4const builtin = @import("builtin");
35
4pub fn foo() %i32 {
6pub fn foo() error!i32 {
57 const x = try bar();
68 return x + 1;
79}
810
9pub fn bar() %i32 {
11pub fn bar() error!i32 {
1012 return 13;
1113}
1214
13pub fn baz() %i32 {
15pub fn baz() error!i32 {
1416 const y = foo() catch 1234;
1517 return y + 1;
1618}
......@@ -19,7 +21,6 @@ test "error wrapping" {
1921 assert((baz() catch unreachable) == 15);
2022}
2123
22error ItBroke;
2324fn gimmeItBroke() []const u8 {
2425 return @errorName(error.ItBroke);
2526}
......@@ -28,8 +29,6 @@ test "@errorName" {
2829 assert(mem.eql(u8, @errorName(error.AnError), "AnError"));
2930 assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
3031}
31error AnError;
32error ALongerErrorName;
3332
3433
3534test "error values" {
......@@ -37,16 +36,11 @@ test "error values" {
3736 const b = i32(error.err2);
3837 assert(a != b);
3938}
40error err1;
41error err2;
4239
4340
4441test "redefinition of error values allowed" {
4542 shouldBeNotEqual(error.AnError, error.SecondError);
4643}
47error AnError;
48error AnError;
49error SecondError;
5044fn shouldBeNotEqual(a: error, b: error) void {
5145 if (a == b) unreachable;
5246}
......@@ -58,8 +52,7 @@ test "error binary operator" {
5852 assert(a == 3);
5953 assert(b == 10);
6054}
61error ItBroke;
62fn errBinaryOperatorG(x: bool) %isize {
55fn errBinaryOperatorG(x: bool) error!isize {
6356 return if (x) error.ItBroke else isize(10);
6457}
6558
......@@ -68,18 +61,117 @@ test "unwrap simple value from error" {
6861 const i = unwrapSimpleValueFromErrorDo() catch unreachable;
6962 assert(i == 13);
7063}
71fn unwrapSimpleValueFromErrorDo() %isize { return 13; }
64fn unwrapSimpleValueFromErrorDo() error!isize { return 13; }
7265
7366
7467test "error return in assignment" {
7568 doErrReturnInAssignment() catch unreachable;
7669}
7770
78fn doErrReturnInAssignment() %void {
71fn doErrReturnInAssignment() error!void {
7972 var x : i32 = undefined;
8073 x = try makeANonErr();
8174}
8275
83fn makeANonErr() %i32 {
76fn makeANonErr() error!i32 {
8477 return 1;
8578}
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 @@
11const assert = @import("std").debug.assert;
22
3fn foo(id: u64) %i32 {
3fn foo(id: u64) !i32 {
44 return switch (id) {
55 1 => getErrInt(),
66 2 => {
......@@ -11,9 +11,7 @@ fn foo(id: u64) %i32 {
1111 };
1212}
1313
14fn getErrInt() %i32 { return 0; }
15
16error ItBroke;
14fn getErrInt() error!i32 { return 0; }
1715
1816test "ir block deps" {
1917 assert((foo(1) catch unreachable) == 0);
test/cases/misc.zig+4-4
......@@ -262,7 +262,7 @@ test "generic malloc free" {
262262 memFree(u8, a);
263263}
264264const some_mem : [100]u8 = undefined;
265fn memAlloc(comptime T: type, n: usize) %[]T {
265fn memAlloc(comptime T: type, n: usize) error![]T {
266266 return @ptrCast(&T, &some_mem[0])[0..n];
267267}
268268fn memFree(comptime T: type, memory: []T) void { }
......@@ -419,7 +419,7 @@ test "cast slice to u8 slice" {
419419test "pointer to void return type" {
420420 testPointerToVoidReturnType() catch unreachable;
421421}
422fn testPointerToVoidReturnType() %void {
422fn testPointerToVoidReturnType() error!void {
423423 const a = testPointerToVoidReturnType2();
424424 return *a;
425425}
......@@ -475,8 +475,8 @@ test "@typeId" {
475475 assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral);
476476 assert(@typeId(@typeOf(null)) == Tid.NullLiteral);
477477 assert(@typeId(?i32) == Tid.Nullable);
478 assert(@typeId(%i32) == Tid.ErrorUnion);
479 assert(@typeId(error) == Tid.Error);
478 assert(@typeId(error!i32) == Tid.ErrorUnion);
479 assert(@typeId(error) == Tid.ErrorSet);
480480 assert(@typeId(AnEnum) == Tid.Enum);
481481 assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum);
482482 assert(@typeId(AUnionEnum) == Tid.Union);
test/cases/reflection.zig+1-1
......@@ -5,7 +5,7 @@ test "reflection: array, pointer, nullable, error union type child" {
55 comptime {
66 assert(([10]u8).Child == u8);
77 assert((&u8).Child == u8);
8 assert((%u8).Child == u8);
8 assert((error!u8).Payload == u8);
99 assert((?u8).Child == u8);
1010 }
1111}
test/cases/switch.zig+1-1
......@@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 {
225225 return 10;
226226}
227227
228fn return_a_number() %i32 {
228fn return_a_number() error!i32 {
229229 return 1;
230230}
231231
test/cases/switch_prong_err_enum.zig+2-4
......@@ -2,19 +2,17 @@ const assert = @import("std").debug.assert;
22
33var read_count: u64 = 0;
44
5fn readOnce() %u64 {
5fn readOnce() error!u64 {
66 read_count += 1;
77 return read_count;
88}
99
10error InvalidDebugInfo;
11
1210const FormValue = union(enum) {
1311 Address: u64,
1412 Other: bool,
1513};
1614
17fn doThing(form_id: u64) %FormValue {
15fn doThing(form_id: u64) error!FormValue {
1816 return switch (form_id) {
1917 17 => FormValue { .Address = try readOnce() },
2018 else => error.InvalidDebugInfo,
test/cases/switch_prong_implicit_cast.zig+1-3
......@@ -5,9 +5,7 @@ const FormValue = union(enum) {
55 Two: bool,
66};
77
8error Whatever;
9
10fn foo(id: u64) %FormValue {
8fn foo(id: u64) !FormValue {
119 return switch (id) {
1210 2 => FormValue { .Two = true },
1311 1 => FormValue { .One = {} },
test/cases/try.zig+2-5
......@@ -17,10 +17,7 @@ fn tryOnErrorUnionImpl() void {
1717 assert(x == 11);
1818}
1919
20error ItBroke;
21error NoMem;
22error CrappedOut;
23fn returnsTen() %i32 {
20fn returnsTen() error!i32 {
2421 return 10;
2522}
2623
......@@ -32,7 +29,7 @@ test "try without vars" {
3229 assert(result2 == 1);
3330}
3431
35fn failIfTrue(ok: bool) %void {
32fn failIfTrue(ok: bool) error!void {
3633 if (ok) {
3734 return error.ItBroke;
3835 } else {
test/cases/union.zig+1-1
......@@ -13,7 +13,7 @@ const Agg = struct {
1313const v1 = Value { .Int = 1234 };
1414const v2 = Value { .Array = []u8{3} ** 9 };
1515
16const err = (%Agg)(Agg {
16const err = (error!Agg)(Agg {
1717 .val1 = v1,
1818 .val2 = v2,
1919});
test/cases/while.zig+4-6
......@@ -50,7 +50,7 @@ fn runContinueAndBreakTest() void {
5050test "return with implicit cast from while loop" {
5151 returnWithImplicitCastFromWhileLoopTest() catch unreachable;
5252}
53fn returnWithImplicitCastFromWhileLoopTest() %void {
53fn returnWithImplicitCastFromWhileLoopTest() error!void {
5454 while (true) {
5555 return;
5656 }
......@@ -116,8 +116,7 @@ test "while with error union condition" {
116116}
117117
118118var numbers_left: i32 = undefined;
119error OutOfNumbers;
120fn getNumberOrErr() %i32 {
119fn getNumberOrErr() error!i32 {
121120 return if (numbers_left == 0)
122121 error.OutOfNumbers
123122 else x: {
......@@ -205,8 +204,7 @@ fn testContinueOuter() void {
205204
206205fn returnNull() ?i32 { return null; }
207206fn returnMaybe(x: i32) ?i32 { return x; }
208error YouWantedAnError;
209fn returnError() %i32 { return error.YouWantedAnError; }
210fn returnSuccess(x: i32) %i32 { return x; }
207fn returnError() error!i32 { return error.YouWantedAnError; }
208fn returnSuccess(x: i32) error!i32 { return x; }
211209fn returnFalse() bool { return false; }
212210fn returnTrue() bool { return true; }
test/compare_output.zig+21-20
......@@ -1,4 +1,6 @@
1const os = @import("std").os;
1const builtin = @import("builtin");
2const std = @import("std");
3const os = std.os;
24const tests = @import("tests.zig");
35
46pub fn addCases(cases: &tests.CompareOutputContext) void {
......@@ -8,14 +10,14 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
810 \\ _ = c.puts(c"Hello, world!");
911 \\ return 0;
1012 \\}
11 , "Hello, world!" ++ os.line_sep);
13 , "Hello, world!" ++ std.cstr.line_sep);
1214
1315 cases.addCase(x: {
1416 var tc = cases.create("multiple files with private function",
1517 \\use @import("std").io;
1618 \\use @import("foo.zig");
1719 \\
18 \\pub fn main() %void {
20 \\pub fn main() void {
1921 \\ privateFunction();
2022 \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
2123 \\ stdout.print("OK 2\n") catch unreachable;
......@@ -49,7 +51,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
4951 \\use @import("foo.zig");
5052 \\use @import("bar.zig");
5153 \\
52 \\pub fn main() %void {
54 \\pub fn main() void {
5355 \\ foo_function();
5456 \\ bar_function();
5557 \\}
......@@ -89,7 +91,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
8991 var tc = cases.create("two files use import each other",
9092 \\use @import("a.zig");
9193 \\
92 \\pub fn main() %void {
94 \\pub fn main() void {
9395 \\ ok();
9496 \\}
9597 , "OK\n");
......@@ -118,7 +120,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
118120 cases.add("hello world without libc",
119121 \\const io = @import("std").io;
120122 \\
121 \\pub fn main() %void {
123 \\pub fn main() void {
122124 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
123125 \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
124126 \\}
......@@ -268,7 +270,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
268270 \\const z = io.stdin_fileno;
269271 \\const x : @typeOf(y) = 1234;
270272 \\const y : u16 = 5678;
271 \\pub fn main() %void {
273 \\pub fn main() void {
272274 \\ var x_local : i32 = print_ok(x);
273275 \\}
274276 \\fn print_ok(val: @typeOf(x)) @typeOf(foo) {
......@@ -351,7 +353,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
351353 \\ fn method(b: &const Bar) bool { return true; }
352354 \\};
353355 \\
354 \\pub fn main() %void {
356 \\pub fn main() void {
355357 \\ const bar = Bar {.field2 = 13,};
356358 \\ const foo = Foo {.field1 = bar,};
357359 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
......@@ -367,7 +369,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
367369
368370 cases.add("defer with only fallthrough",
369371 \\const io = @import("std").io;
370 \\pub fn main() %void {
372 \\pub fn main() void {
371373 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
372374 \\ stdout.print("before\n") catch unreachable;
373375 \\ defer stdout.print("defer1\n") catch unreachable;
......@@ -380,7 +382,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
380382 cases.add("defer with return",
381383 \\const io = @import("std").io;
382384 \\const os = @import("std").os;
383 \\pub fn main() %void {
385 \\pub fn main() void {
384386 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
385387 \\ stdout.print("before\n") catch unreachable;
386388 \\ defer stdout.print("defer1\n") catch unreachable;
......@@ -394,10 +396,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
394396
395397 cases.add("errdefer and it fails",
396398 \\const io = @import("std").io;
397 \\pub fn main() %void {
399 \\pub fn main() void {
398400 \\ do_test() catch return;
399401 \\}
400 \\fn do_test() %void {
402 \\fn do_test() !void {
401403 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
402404 \\ stdout.print("before\n") catch unreachable;
403405 \\ defer stdout.print("defer1\n") catch unreachable;
......@@ -406,18 +408,17 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
406408 \\ defer stdout.print("defer3\n") catch unreachable;
407409 \\ stdout.print("after\n") catch unreachable;
408410 \\}
409 \\error IToldYouItWouldFail;
410 \\fn its_gonna_fail() %void {
411 \\fn its_gonna_fail() !void {
411412 \\ return error.IToldYouItWouldFail;
412413 \\}
413414 , "before\ndeferErr\ndefer1\n");
414415
415416 cases.add("errdefer and it passes",
416417 \\const io = @import("std").io;
417 \\pub fn main() %void {
418 \\pub fn main() void {
418419 \\ do_test() catch return;
419420 \\}
420 \\fn do_test() %void {
421 \\fn do_test() !void {
421422 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
422423 \\ stdout.print("before\n") catch unreachable;
423424 \\ defer stdout.print("defer1\n") catch unreachable;
......@@ -426,7 +427,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
426427 \\ defer stdout.print("defer3\n") catch unreachable;
427428 \\ stdout.print("after\n") catch unreachable;
428429 \\}
429 \\fn its_gonna_pass() %void { }
430 \\fn its_gonna_pass() error!void { }
430431 , "before\nafter\ndefer3\ndefer1\n");
431432
432433 cases.addCase(x: {
......@@ -434,7 +435,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
434435 \\const foo_txt = @embedFile("foo.txt");
435436 \\const io = @import("std").io;
436437 \\
437 \\pub fn main() %void {
438 \\pub fn main() void {
438439 \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
439440 \\ stdout.print(foo_txt) catch unreachable;
440441 \\}
......@@ -452,7 +453,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
452453 \\const os = std.os;
453454 \\const allocator = std.debug.global_allocator;
454455 \\
455 \\pub fn main() %void {
456 \\pub fn main() !void {
456457 \\ var args_it = os.args();
457458 \\ var stdout_file = try io.getStdOut();
458459 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
......@@ -493,7 +494,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
493494 \\const os = std.os;
494495 \\const allocator = std.debug.global_allocator;
495496 \\
496 \\pub fn main() %void {
497 \\pub fn main() !void {
497498 \\ var args_it = os.args();
498499 \\ var stdout_file = try io.getStdOut();
499500 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
test/compile_errors.zig+231-18
......@@ -1,6 +1,220 @@
11const tests = @import("tests.zig");
22
33pub 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
4218 cases.add("cast negative integer literal to usize",
5219 \\export fn entry() void {
6220 \\ const x = usize(-10);
......@@ -112,12 +326,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
112326
113327 cases.add("wrong return type for main",
114328 \\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
117331 cases.add("double ?? on main return value",
118332 \\pub fn main() ??void {
119333 \\}
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
122336 cases.add("bad identifier in function with struct defined inside function which references local const",
123337 \\export fn entry() void {
......@@ -1173,7 +1387,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
11731387 \\export fn f() void {
11741388 \\ try something();
11751389 \\}
1176 \\fn something() %void { }
1390 \\fn something() error!void { }
11771391 ,
11781392 ".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
11791393
......@@ -1264,7 +1478,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
12641478 , ".tmp_source.zig:3:11: error: cannot assign to constant");
12651479
12661480 cases.add("main function with bogus args type",
1267 \\pub fn main(args: [][]bogus) %void {}
1481 \\pub fn main(args: [][]bogus) !void {}
12681482 , ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'");
12691483
12701484 cases.add("for loop missing element param",
......@@ -1396,7 +1610,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
13961610 , ".tmp_source.zig:6:13: error: cannot assign to constant");
13971611
13981612 cases.add("return from defer expression",
1399 \\pub fn testTrickyDefer() %void {
1613 \\pub fn testTrickyDefer() !void {
14001614 \\ defer canFail() catch {};
14011615 \\
14021616 \\ defer try canFail();
......@@ -1404,7 +1618,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
14041618 \\ const a = maybeInt() ?? return;
14051619 \\}
14061620 \\
1407 \\fn canFail() %void { }
1621 \\fn canFail() error!void { }
14081622 \\
14091623 \\pub fn maybeInt() ?i32 {
14101624 \\ return 0;
......@@ -1534,7 +1748,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
15341748 \\export fn foo() void {
15351749 \\ bar() catch unreachable;
15361750 \\}
1537 \\fn bar() %i32 { return 0; }
1751 \\fn bar() error!i32 { return 0; }
15381752 , ".tmp_source.zig:2:11: error: expression value is ignored");
15391753
15401754 cases.add("ignored statement value",
......@@ -1565,7 +1779,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
15651779 \\export fn foo() void {
15661780 \\ defer bar();
15671781 \\}
1568 \\fn bar() %i32 { return 0; }
1782 \\fn bar() error!i32 { return 0; }
15691783 , ".tmp_source.zig:2:14: error: expression value is ignored");
15701784
15711785 cases.add("dereference an array",
......@@ -1632,13 +1846,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
16321846 , ".tmp_source.zig:2:21: error: expected pointer, found 'usize'");
16331847
16341848 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;
1636 \\const u2 = @IntType(false, 2);
1637 \\fn foo(e: error) u2 {
1849 \\const Error = error { A, B, C, D, E, F, G, H };
1850 \\fn foo(e: Error) u2 {
16381851 \\ return u2(e);
16391852 \\}
16401853 \\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
16431856 cases.add("asm at compile time",
16441857 \\comptime {
......@@ -1821,9 +2034,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
18212034 \\export fn foo() void {
18222035 \\ while (bar()) {}
18232036 \\}
1824 \\fn bar() %i32 { return 1; }
2037 \\fn bar() error!i32 { return 1; }
18252038 ,
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
18282041 cases.add("while expected nullable, got bool",
18292042 \\export fn foo() void {
......@@ -1837,9 +2050,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
18372050 \\export fn foo() void {
18382051 \\ while (bar()) |x| {}
18392052 \\}
1840 \\fn bar() %i32 { return 1; }
2053 \\fn bar() error!i32 { return 1; }
18412054 ,
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
18442057 cases.add("while expected error union, got bool",
18452058 \\export fn foo() void {
......@@ -1983,7 +2196,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
19832196 \\fn foo1(args: ...) void {}
19842197 \\fn foo2(args: ...) void {}
19852198 \\
1986 \\pub fn main() %void {
2199 \\pub fn main() !void {
19872200 \\ foos[0]();
19882201 \\}
19892202 ,
......@@ -1995,7 +2208,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
19952208 \\fn foo1(arg: var) void {}
19962209 \\fn foo2(arg: var) void {}
19972210 \\
1998 \\pub fn main() %void {
2211 \\pub fn main() !void {
19992212 \\ foos[0](true);
20002213 \\}
20012214 ,
test/runtime_safety.zig+36-38
......@@ -5,7 +5,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
55 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
66 \\ @import("std").os.exit(126);
77 \\}
8 \\pub fn main() %void {
8 \\pub fn main() void {
99 \\ @panic("oh no");
1010 \\}
1111 );
......@@ -14,7 +14,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
1414 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
1515 \\ @import("std").os.exit(126);
1616 \\}
17 \\pub fn main() %void {
17 \\pub fn main() void {
1818 \\ const a = []i32{1, 2, 3, 4};
1919 \\ baz(bar(a));
2020 \\}
......@@ -28,8 +28,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
2828 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
2929 \\ @import("std").os.exit(126);
3030 \\}
31 \\error Whatever;
32 \\pub fn main() %void {
31 \\pub fn main() !void {
3332 \\ const x = add(65530, 10);
3433 \\ if (x == 0) return error.Whatever;
3534 \\}
......@@ -42,8 +41,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
4241 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
4342 \\ @import("std").os.exit(126);
4443 \\}
45 \\error Whatever;
46 \\pub fn main() %void {
44 \\pub fn main() !void {
4745 \\ const x = sub(10, 20);
4846 \\ if (x == 0) return error.Whatever;
4947 \\}
......@@ -56,8 +54,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
5654 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
5755 \\ @import("std").os.exit(126);
5856 \\}
59 \\error Whatever;
60 \\pub fn main() %void {
57 \\pub fn main() !void {
6158 \\ const x = mul(300, 6000);
6259 \\ if (x == 0) return error.Whatever;
6360 \\}
......@@ -70,8 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
7067 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
7168 \\ @import("std").os.exit(126);
7269 \\}
73 \\error Whatever;
74 \\pub fn main() %void {
70 \\pub fn main() !void {
7571 \\ const x = neg(-32768);
7672 \\ if (x == 32767) return error.Whatever;
7773 \\}
......@@ -84,8 +80,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
8480 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
8581 \\ @import("std").os.exit(126);
8682 \\}
87 \\error Whatever;
88 \\pub fn main() %void {
83 \\pub fn main() !void {
8984 \\ const x = div(-32768, -1);
9085 \\ if (x == 32767) return error.Whatever;
9186 \\}
......@@ -98,8 +93,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
9893 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
9994 \\ @import("std").os.exit(126);
10095 \\}
101 \\error Whatever;
102 \\pub fn main() %void {
96 \\pub fn main() !void {
10397 \\ const x = shl(-16385, 1);
10498 \\ if (x == 0) return error.Whatever;
10599 \\}
......@@ -112,8 +106,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
112106 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
113107 \\ @import("std").os.exit(126);
114108 \\}
115 \\error Whatever;
116 \\pub fn main() %void {
109 \\pub fn main() !void {
117110 \\ const x = shl(0b0010111111111111, 3);
118111 \\ if (x == 0) return error.Whatever;
119112 \\}
......@@ -126,8 +119,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
126119 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
127120 \\ @import("std").os.exit(126);
128121 \\}
129 \\error Whatever;
130 \\pub fn main() %void {
122 \\pub fn main() !void {
131123 \\ const x = shr(-16385, 1);
132124 \\ if (x == 0) return error.Whatever;
133125 \\}
......@@ -140,8 +132,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
140132 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
141133 \\ @import("std").os.exit(126);
142134 \\}
143 \\error Whatever;
144 \\pub fn main() %void {
135 \\pub fn main() !void {
145136 \\ const x = shr(0b0010111111111111, 3);
146137 \\ if (x == 0) return error.Whatever;
147138 \\}
......@@ -154,8 +145,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
154145 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
155146 \\ @import("std").os.exit(126);
156147 \\}
157 \\error Whatever;
158 \\pub fn main() %void {
148 \\pub fn main() void {
159149 \\ const x = div0(999, 0);
160150 \\}
161151 \\fn div0(a: i32, b: i32) i32 {
......@@ -167,8 +157,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
167157 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
168158 \\ @import("std").os.exit(126);
169159 \\}
170 \\error Whatever;
171 \\pub fn main() %void {
160 \\pub fn main() !void {
172161 \\ const x = divExact(10, 3);
173162 \\ if (x == 0) return error.Whatever;
174163 \\}
......@@ -181,8 +170,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
181170 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
182171 \\ @import("std").os.exit(126);
183172 \\}
184 \\error Whatever;
185 \\pub fn main() %void {
173 \\pub fn main() !void {
186174 \\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
187175 \\ if (x.len == 0) return error.Whatever;
188176 \\}
......@@ -195,8 +183,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
195183 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
196184 \\ @import("std").os.exit(126);
197185 \\}
198 \\error Whatever;
199 \\pub fn main() %void {
186 \\pub fn main() !void {
200187 \\ const x = shorten_cast(200);
201188 \\ if (x == 0) return error.Whatever;
202189 \\}
......@@ -209,8 +196,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
209196 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
210197 \\ @import("std").os.exit(126);
211198 \\}
212 \\error Whatever;
213 \\pub fn main() %void {
199 \\pub fn main() !void {
214200 \\ const x = unsigned_cast(-10);
215201 \\ if (x == 0) return error.Whatever;
216202 \\}
......@@ -226,20 +212,19 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
226212 \\ }
227213 \\ @import("std").os.exit(0); // test failed
228214 \\}
229 \\error Whatever;
230 \\pub fn main() %void {
215 \\pub fn main() void {
231216 \\ bar() catch unreachable;
232217 \\}
233 \\fn bar() %void {
218 \\fn bar() !void {
234219 \\ return error.Whatever;
235220 \\}
236221 );
237222
238 cases.addRuntimeSafety("cast integer to error and no code matches",
223 cases.addRuntimeSafety("cast integer to global error and no code matches",
239224 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
240225 \\ @import("std").os.exit(126);
241226 \\}
242 \\pub fn main() %void {
227 \\pub fn main() void {
243228 \\ _ = bar(9999);
244229 \\}
245230 \\fn bar(x: u32) error {
......@@ -247,12 +232,25 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
247232 \\}
248233 );
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
250249 cases.addRuntimeSafety("@alignCast misaligned",
251250 \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
252251 \\ @import("std").os.exit(126);
253252 \\}
254 \\error Wrong;
255 \\pub fn main() %void {
253 \\pub fn main() !void {
256254 \\ var array align(4) = []u32{0x11111111, 0x11111111};
257255 \\ const bytes = ([]u8)(array[0..]);
258256 \\ if (foo(bytes) != 0x11111111) return error.Wrong;
......@@ -274,7 +272,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
274272 \\ int: u32,
275273 \\};
276274 \\
277 \\pub fn main() %void {
275 \\pub fn main() void {
278276 \\ var f = Foo { .int = 42 };
279277 \\ bar(&f);
280278 \\}
test/standalone/brace_expansion/build.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) void {
44 const main = b.addTest("main.zig");
55 main.setBuildMode(b.standardReleaseOptions());
66
test/standalone/brace_expansion/main.zig+14-8
......@@ -6,9 +6,6 @@ const assert = debug.assert;
66const Buffer = std.Buffer;
77const ArrayList = std.ArrayList;
88
9error InvalidInput;
10error OutOfMem;
11
129const Token = union(enum) {
1310 Word: []const u8,
1411 OpenBrace,
......@@ -19,7 +16,7 @@ const Token = union(enum) {
1916
2017var global_allocator: &mem.Allocator = undefined;
2118
22fn tokenize(input:[] const u8) %ArrayList(Token) {
19fn tokenize(input:[] const u8) !ArrayList(Token) {
2320 const State = enum {
2421 Start,
2522 Word,
......@@ -71,7 +68,12 @@ const Node = union(enum) {
7168 Combine: []Node,
7269};
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 {
7577 const first_token = tokens.items[*token_index];
7678 *token_index += 1;
7779
......@@ -107,7 +109,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node {
107109 }
108110}
109111
110fn expandString(input: []const u8, output: &Buffer) %void {
112fn expandString(input: []const u8, output: &Buffer) !void {
111113 const tokens = try tokenize(input);
112114 if (tokens.len == 1) {
113115 return output.resize(0);
......@@ -135,7 +137,11 @@ fn expandString(input: []const u8, output: &Buffer) %void {
135137 }
136138}
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 {
139145 assert(output.len == 0);
140146 switch (*node) {
141147 Node.Scalar => |scalar| {
......@@ -172,7 +178,7 @@ fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void {
172178 }
173179}
174180
175pub fn main() %void {
181pub fn main() !void {
176182 var stdin_file = try io.getStdIn();
177183 var stdout_file = try io.getStdOut();
178184
test/standalone/issue_339/build.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) void {
44 const obj = b.addObject("test", "test.zig");
55
66 const test_step = b.step("test", "Test the program");
test/standalone/issue_339/test.zig+1-1
......@@ -1,7 +1,7 @@
11const StackTrace = @import("builtin").StackTrace;
22pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} }
33
4fn bar() %void {}
4fn bar() error!void {}
55
66export fn foo() void {
77 bar() catch unreachable;
test/standalone/pkg_import/build.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) void {
44 const exe = b.addExecutable("test", "test.zig");
55 exe.addPackagePath("my_pkg", "pkg.zig");
66
test/standalone/pkg_import/test.zig+1-1
......@@ -1,6 +1,6 @@
11const my_pkg = @import("my_pkg");
22const assert = @import("std").debug.assert;
33
4pub fn main() %void {
4pub fn main() void {
55 assert(my_pkg.add(10, 20) == 30);
66}
test/standalone/use_alias/build.zig+1-1
......@@ -1,6 +1,6 @@
11const Builder = @import("std").build.Builder;
22
3pub fn build(b: &Builder) %void {
3pub fn build(b: &Builder) void {
44 b.addCIncludePath(".");
55
66 const main = b.addTest("main.zig");
test/tests.zig+6-9
......@@ -45,9 +45,6 @@ const test_targets = []TestTarget {
4545 },
4646};
4747
48error TestFailed;
49error CompilationIncorrectlySucceeded;
50
5148const max_stdout_size = 1 * 1024 * 1024; // 1 MB
5249
5350pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step {
......@@ -248,7 +245,7 @@ pub const CompareOutputContext = struct {
248245 return ptr;
249246 }
250247
251 fn make(step: &build.Step) %void {
248 fn make(step: &build.Step) !void {
252249 const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
253250 const b = self.context.b;
254251
......@@ -337,7 +334,7 @@ pub const CompareOutputContext = struct {
337334 return ptr;
338335 }
339336
340 fn make(step: &build.Step) %void {
337 fn make(step: &build.Step) !void {
341338 const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step);
342339 const b = self.context.b;
343340
......@@ -563,7 +560,7 @@ pub const CompileErrorContext = struct {
563560 return ptr;
564561 }
565562
566 fn make(step: &build.Step) %void {
563 fn make(step: &build.Step) !void {
567564 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
568565 const b = self.context.b;
569566
......@@ -847,7 +844,7 @@ pub const TranslateCContext = struct {
847844 return ptr;
848845 }
849846
850 fn make(step: &build.Step) %void {
847 fn make(step: &build.Step) !void {
851848 const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
852849 const b = self.context.b;
853850
......@@ -1045,14 +1042,14 @@ pub const GenHContext = struct {
10451042 return ptr;
10461043 }
10471044
1048 fn make(step: &build.Step) %void {
1045 fn make(step: &build.Step) !void {
10491046 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
10501047 const b = self.context.b;
10511048
10521049 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
10531050
10541051 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
10571054 for (self.case.expected_lines.toSliceConst()) |expected_line| {
10581055 if (mem.indexOf(u8, actual_h, expected_line) == null) {