diff --git a/CMakeLists.txt b/CMakeLists.txt index 8aa6dff6a3..c23c0b6e17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,11 +480,12 @@ set(ZIG_STD_FILES "os/child_process.zig" "os/darwin.zig" "os/darwin_errno.zig" + "os/file.zig" "os/get_user_id.zig" "os/index.zig" - "os/linux/index.zig" "os/linux/errno.zig" "os/linux/i386.zig" + "os/linux/index.zig" "os/linux/x86_64.zig" "os/path.zig" "os/windows/error.zig" @@ -520,6 +521,10 @@ set(ZIG_STD_FILES "special/panic.zig" "special/test_runner.zig" "unicode.zig" + "zig/ast.zig" + "zig/index.zig" + "zig/parser.zig" + "zig/tokenizer.zig" ) set(ZIG_C_HEADER_FILES diff --git a/build.zig b/build.zig index 0a898776f8..0a7795d615 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; const Buffer = std.Buffer; const io = std.io; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const mode = b.standardReleaseOptions(); var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); @@ -78,7 +78,6 @@ pub fn build(b: &Builder) %void { exe.linkSystemLibrary("c"); b.default_step.dependOn(&exe.step); - b.default_step.dependOn(docs_step); const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false; if (!skip_self_hosted) { @@ -108,10 +107,6 @@ pub fn build(b: &Builder) %void { "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", with_lldb)); - test_step.dependOn(tests.addPkgTests(b, test_filter, - "src-self-hosted/main.zig", "fmt", "Run the fmt tests", - with_lldb)); - test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); @@ -149,7 +144,7 @@ const LibraryDep = struct { includes: ArrayList([]const u8), }; -fn findLLVM(b: &Builder, llvm_config_exe: []const u8) %LibraryDep { +fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep { const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); diff --git a/ci/appveyor/build_script.bat b/ci/appveyor/build_script.bat index 235b0e515e..d0d3f61b34 100644 --- a/ci/appveyor/build_script.bat +++ b/ci/appveyor/build_script.bat @@ -24,18 +24,6 @@ cd %ZIGBUILDDIR% cmake.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 msbuild /p:Configuration=Release INSTALL.vcxproj || exit /b -bin\zig.exe build --build-file ..\build.zig test -Dverbose-link || exit /b +bin\zig.exe build --build-file ..\build.zig test || exit /b -@echo "MSVC build succeeded, proceeding with MinGW build" -cd %APPVEYOR_BUILD_FOLDER% -SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%" -SET "MSYSTEM=MINGW64" - -bash -lc "pacman -Syu --needed --noconfirm" -bash -lc "pacman -Su --needed --noconfirm" - -bash -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" - -bash -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" - -@echo "MinGW build successful" +@echo "MSVC build succeeded" diff --git a/doc/docgen.zig b/doc/docgen.zig index a7b4084e7e..f9aac826ad 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -12,7 +12,7 @@ const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt(); const obj_ext = std.build.Target(std.build.Target.Native).oFileExt(); const tmp_dir_name = "docgen_tmp"; -pub fn main() %void { +pub fn main() !void { // TODO use a more general purpose allocator here var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size); defer inc_allocator.deinit(); @@ -31,10 +31,10 @@ pub fn main() %void { const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg")); defer allocator.free(out_file_name); - var in_file = try io.File.openRead(in_file_name, allocator); + var in_file = try os.File.openRead(allocator, in_file_name); defer in_file.close(); - var out_file = try io.File.openWrite(out_file_name, allocator); + var out_file = try os.File.openWrite(allocator, out_file_name); defer out_file.close(); var file_in_stream = io.FileInStream.init(&in_file); @@ -42,7 +42,7 @@ pub fn main() %void { const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size); var file_out_stream = io.FileOutStream.init(&out_file); - var buffered_out_stream = io.BufferedOutStream.init(&file_out_stream.stream); + var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); var toc = try genToc(allocator, &tokenizer); @@ -218,8 +218,6 @@ const Tokenizer = struct { } }; -error ParseError; - fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error { const loc = tokenizer.getTokenLocation(token); 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 return error.ParseError; } -fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) %void { +fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void { if (token.id != id) { return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } } -fn eatToken(tokenizer: &Tokenizer, id: Token.Id) %Token { +fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token { const token = tokenizer.next(); try assertToken(tokenizer, token, id); return token; @@ -316,7 +314,7 @@ const Action = enum { Close, }; -fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc { +fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc { var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator); errdefer urls.deinit(); @@ -540,7 +538,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc { }; } -fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 { +fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -560,7 +558,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 { return buf.toOwnedSlice(); } -fn escapeHtml(allocator: &mem.Allocator, input: []const u8) %[]u8 { +fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -596,15 +594,13 @@ const TermState = enum { ExpectEnd, }; -error UnsupportedEscape; - test "term color" { const input_bytes = "A\x1b[32;1mgreen\x1b[0mB"; const result = try termColor(std.debug.global_allocator, input_bytes); assert(mem.eql(u8, result, "AgreenB")); } -fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 { +fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -684,9 +680,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 { return buf.toOwnedSlice(); } -error ExampleFailedToCompile; - -fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io.OutStream, zig_exe: []const u8) %void { +fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; for (toc.nodes) |node| { switch (node) { @@ -729,7 +723,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io try out.print("
{}", escaped_source);
const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
- try io.writeFile(tmp_source_file_name, trimmed_raw_source, null);
+ try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);
switch (code.id) {
Code.Id.Exe => |expected_outcome| {
@@ -974,10 +968,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io
}
-error ChildCrashed;
-error ChildExitError;
-
-fn exec(allocator: &mem.Allocator, args: []const []const u8) %os.ChildProcess.ExecResult {
+fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {
const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 29a40c86a5..2b09ca81bd 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -108,7 +108,7 @@
{#code_begin|exe|hello#}
const std = @import("std");
-pub fn main() %void {
+pub fn main() !void {
// If this program is run without stdout attached, exit with an error.
var stdout_file = try std.io.getStdOut();
// If this program encounters pipe failure when printing to stdout, exit
@@ -129,8 +129,8 @@ pub fn main() void {
}
{#code_end#}
- Note that we also left off the % from the return type.
- In Zig, if your main function cannot fail, you may use the void return type.
+ Note that we also left off the ! from the return type.
+ In Zig, if your main function cannot fail, you must use the void return type.
err is the error and is in scope of the expression b.
const value: %u32 = null;
+ const value: error!u32 = error.Broken;
const unwrapped = value catch 1234;
unwrapped == 1234
x() x[] x.y
-!x -x -%x ~x *x &x ?x %x ??x
+a!b
+!x -x -%x ~x *x &x ?x ??x
x{}
-* / % ** *%
+! * / % ** *%
+ - ++ +% -%
<< >>
&
@@ -2268,8 +2266,8 @@ fn eventuallyNullSequence() ?u32 {
break :blk numbers_left;
};
}
-error ReachedZero;
-fn eventuallyErrorSequence() %u32 {
+
+fn eventuallyErrorSequence() error!u32 {
return if (numbers_left == 0) error.ReachedZero else blk: {
numbers_left -= 1;
break :blk numbers_left;
@@ -2398,7 +2396,7 @@ fn typeNameLength(comptime T: type) usize {
// If expressions have three uses, corresponding to the three types:
// * bool
// * ?T
-// * %T
+// * error!T
const assert = @import("std").debug.assert;
@@ -2459,20 +2457,18 @@ test "if nullable" {
}
}
-error BadValue;
-error LessBadValue;
test "if error union" {
// If expressions test for errors.
// Note the |err| capture on the else.
- const a: %u32 = 0;
+ const a: error!u32 = 0;
if (a) |value| {
assert(value == 0);
} else |err| {
unreachable;
}
- const b: %u32 = error.BadValue;
+ const b: error!u32 = error.BadValue;
if (b) |value| {
unreachable;
} else |err| {
@@ -2490,7 +2486,7 @@ test "if error union" {
}
// Access the value by reference using a pointer capture.
- var c: %u32 = 3;
+ var c: error!u32 = 3;
if (c) |*value| {
*value = 9;
} else |err| {
@@ -2558,8 +2554,7 @@ test "defer unwinding" {
//
// This is especially useful in allowing a function to clean up properly
// on error, and replaces goto error handling tactics as seen in c.
-error DeferError;
-fn deferErrorExample(is_error: bool) %void {
+fn deferErrorExample(is_error: bool) !void {
warn("\nstart of function\n");
// This will always be executed on exit
@@ -2668,7 +2663,7 @@ test "foo" {
assert(value == 1234);
}
-fn bar() %u32 {
+fn bar() error!u32 {
return 1234;
}
@@ -2791,13 +2786,8 @@ test "fn reflection" {
One of the distinguishing features of Zig is its exception handling strategy.
- Among the top level declarations available is the error value declaration:
+ TODO rewrite the errors section to take into account error sets
- {#code_begin|syntax#}
-error FileNotFound;
-error OutOfMemory;
-error UnexpectedToken;
- {#code_end#}
These error values are assigned an unsigned integer value greater than 0 at
compile time. You are allowed to declare the same error value more than once,
@@ -2809,26 +2799,23 @@ error UnexpectedToken;
Each error value across the entire compilation unit gets a unique integer,
- and this determines the size of the pure error type.
+ and this determines the size of the error set type.
- The pure error type is one of the error values, and in the same way that pointers
- cannot be null, a pure error is always an error.
+ The error set type is one of the error values, and in the same way that pointers
+ cannot be null, a error set instance is always an error.
{#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}
- Most of the time you will not find yourself using a pure error type. Instead,
- likely you will be using the error union type. This is when you take a normal type,
- and prefix it with the % operator.
+ Most of the time you will not find yourself using an error set type. Instead,
+ likely you will be using the error union type. This is when you take an error set
+ and a normal type, and create an error union with the ! binary operator.
Here is a function to parse a string into a 64-bit integer:
{#code_begin|test#}
-error InvalidChar;
-error Overflow;
-
-pub fn parseU64(buf: []const u8, radix: u8) %u64 {
+pub fn parseU64(buf: []const u8, radix: u8) !u64 {
var x: u64 = 0;
for (buf) |c| {
@@ -2867,13 +2854,14 @@ test "parse u64" {
}
{#code_end#}
- Notice the return type is %u64. This means that the function
- either returns an unsigned 64 bit integer, or an error.
+ Notice the return type is !u64. This means that the function
+ either returns an unsigned 64 bit integer, or an error. We left off the error set
+ to the left of the !, so the error set is inferred.
Within the function definition, you can see some return statements that return
- a pure error, and at the bottom a return statement that returns a u64.
- Both types implicitly cast to %u64.
+ an error, and at the bottom a return statement that returns a u64.
+ Both types implicitly cast to error!u64.
What it looks like to use this function varies depending on what you're
@@ -2900,7 +2888,7 @@ fn doAThing(str: []u8) void {
Let's say you wanted to return the error if you got one, otherwise continue with the
function logic:
{#code_begin|syntax#}
-fn doAThing(str: []u8) %void {
+fn doAThing(str: []u8) !void {
const number = parseU64(str, 10) catch |err| return err;
// ...
}
@@ -2909,7 +2897,7 @@ fn doAThing(str: []u8) %void {
There is a shortcut for this. The try expression:
{#code_begin|syntax#}
-fn doAThing(str: []u8) %void {
+fn doAThing(str: []u8) !void {
const number = try parseU64(str, 10);
// ...
}
@@ -2959,7 +2947,7 @@ fn doAThing(str: []u8) void {
Example:
{#code_begin|syntax#}
-fn createFoo(param: i32) %Foo {
+fn createFoo(param: i32) !Foo {
const foo = try tryToAllocateFoo();
// now we have allocated foo. we need to free it if the function fails.
// but we want to return it if the function succeeds.
@@ -2999,15 +2987,13 @@ fn createFoo(param: i32) %Foo {
{#see_also|defer|if|switch#}
{#header_open|Error Union Type#}
- An error union is created by putting a % in front of a type.
+
An error union is created with the ! binary operator.
You can use compile-time reflection to access the child type of an error union:
{#code_begin|test#}
const assert = @import("std").debug.assert;
-error SomeError;
-
test "error union" {
- var foo: %i32 = undefined;
+ var foo: error!i32 = undefined;
// Implicitly cast from child type of an error union:
foo = 1234;
@@ -3015,8 +3001,11 @@ test "error union" {
// Implicitly cast from an error set:
foo = error.SomeError;
- // Use compile-time reflection to access the child type of an error union:
- comptime assert(@typeOf(foo).Child == i32);
+ // Use compile-time reflection to access the payload type of an error union:
+ comptime assert(@typeOf(foo).Payload == i32);
+
+ // Use compile-time reflection to access the error set type of an error union:
+ comptime assert(@typeOf(foo).ErrorSet == error);
}
{#code_end#}
{#header_close#}
@@ -3610,7 +3599,7 @@ pub fn main() void {
{#code_begin|syntax#}
/// Calls print and then flushes the buffer.
-pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void {
+pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) error!void {
const State = enum {
Start,
OpenBrace,
@@ -3682,7 +3671,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void {
and emits a function that actually looks like this:
{#code_begin|syntax#}
-pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void {
+pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void {
try self.write("here is a string: '");
try self.printValue(arg0);
try self.write("' here is a number: ");
@@ -3696,7 +3685,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void {
on the type:
{#code_begin|syntax#}
-pub fn printValue(self: &OutStream, value: var) %void {
+pub fn printValue(self: &OutStream, value: var) !void {
const T = @typeOf(value);
if (@isInteger(T)) {
return self.printInt(T, value);
@@ -4647,7 +4636,7 @@ pub const TypeId = enum {
{#code_begin|syntax#}
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const exe = b.addExecutable("example", "example.zig");
exe.setBuildMode(b.standardReleaseOptions());
b.default_step.dependOn(&exe.step);
@@ -4789,7 +4778,7 @@ comptime {
{#code_begin|exe_err#}
const math = @import("std").math;
const warn = @import("std").debug.warn;
-pub fn main() %void {
+pub fn main() !void {
var byte: u8 = 255;
byte = if (math.add(u8, byte, 1)) |result| result else |err| {
@@ -4817,7 +4806,7 @@ pub fn main() %void {
{#code_begin|exe#}
const warn = @import("std").debug.warn;
-pub fn main() %void {
+pub fn main() void {
var byte: u8 = 255;
var result: u8 = undefined;
@@ -4926,14 +4915,12 @@ pub fn main() void {
{#header_close#}
{#header_open|Attempt to Unwrap Error#}
At compile-time:
- {#code_begin|test_err|unable to unwrap error 'UnableToReturnNumber'#}
+ {#code_begin|test_err|caught unexpected error 'UnableToReturnNumber'#}
comptime {
const number = getNumberOrFail() catch unreachable;
}
-error UnableToReturnNumber;
-
-fn getNumberOrFail() %i32 {
+fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
@@ -4953,9 +4940,7 @@ pub fn main() void {
}
}
-error UnableToReturnNumber;
-
-fn getNumberOrFail() %i32 {
+fn getNumberOrFail() !i32 {
return error.UnableToReturnNumber;
}
{#code_end#}
@@ -4963,7 +4948,6 @@ fn getNumberOrFail() %i32 {
{#header_open|Invalid Error Code#}
At compile-time:
{#code_begin|test_err|integer value 11 represents no error#}
-error AnError;
comptime {
const err = error.AnError;
const number = u32(err) + 10;
@@ -5363,7 +5347,7 @@ int main(int argc, char **argv) {
{#code_begin|syntax#}
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addCExecutable("test");
@@ -5641,14 +5625,12 @@ fn readU32Be() u32 {}
{#header_open|Grammar#}
Root = many(TopLevelItem) EOF
-TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
+TopLevelItem = CompTimeExpression(Block) | TopLevelDecl | TestDecl
TestDecl = "test" String Block
TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
-ErrorValueDecl = "error" Symbol ";"
-
GlobalVarDecl = option("export") VariableDeclaration ";"
LocalVarDecl = option("comptime") VariableDeclaration
@@ -5663,7 +5645,7 @@ UseDecl = "use" Expression ";"
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
-FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") TypeExpr
+FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr
FnDef = option("inline" | "export") FnProto Block
@@ -5675,7 +5657,9 @@ Block = option(Symbol ":") "{" many(Statement) "}"
Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
-TypeExpr = PrefixOpExpression | "var"
+TypeExpr = ErrorSetExpr | "var"
+
+ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
BlockOrExpression = Block | Expression
@@ -5757,9 +5741,9 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
-MultiplyOperator = "*" | "/" | "%" | "**" | "*%"
+MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
-PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
+PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
@@ -5777,9 +5761,9 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
StructLiteralField = "." Symbol "=" Expression
-PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "??" | "-%" | "try"
+PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
@@ -5787,6 +5771,8 @@ GroupedExpression = "(" Expression ")"
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
+ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
+
ContainerDecl = option("extern" | "packed")
("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
"{" many(ContainerMember) "}"
diff --git a/example/cat/main.zig b/example/cat/main.zig
index f519ca3f18..de0d323bed 100644
--- a/example/cat/main.zig
+++ b/example/cat/main.zig
@@ -5,7 +5,7 @@ const os = std.os;
const warn = std.debug.warn;
const allocator = std.debug.global_allocator;
-pub fn main() %void {
+pub fn main() !void {
var args_it = os.args();
const exe = try unwrapArg(??args_it.next(allocator));
var catted_anything = false;
@@ -20,7 +20,7 @@ pub fn main() %void {
} else if (arg[0] == '-') {
return usage(exe);
} else {
- var file = io.File.openRead(arg, null) catch |err| {
+ var file = os.File.openRead(allocator, arg) catch |err| {
warn("Unable to open file: {}\n", @errorName(err));
return err;
};
@@ -36,12 +36,12 @@ pub fn main() %void {
}
}
-fn usage(exe: []const u8) %void {
+fn usage(exe: []const u8) !void {
warn("Usage: {} [FILE]...\n", exe);
return error.Invalid;
}
-fn cat_file(stdout: &io.File, file: &io.File) %void {
+fn cat_file(stdout: &os.File, file: &os.File) !void {
var buf: [1024 * 4]u8 = undefined;
while (true) {
@@ -61,7 +61,7 @@ fn cat_file(stdout: &io.File, file: &io.File) %void {
}
}
-fn unwrapArg(arg: %[]u8) %[]u8 {
+fn unwrapArg(arg: error![]u8) ![]u8 {
return arg catch |err| {
warn("Unable to parse command line: {}\n", err);
return err;
diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig
index 482fb2f61e..d6a7b94b6c 100644
--- a/example/guess_number/main.zig
+++ b/example/guess_number/main.zig
@@ -5,7 +5,7 @@ const fmt = std.fmt;
const Rand = std.rand.Rand;
const os = std.os;
-pub fn main() %void {
+pub fn main() !void {
var stdout_file = try io.getStdOut();
var stdout_file_stream = io.FileOutStream.init(&stdout_file);
const stdout = &stdout_file_stream.stream;
diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig
index 9fd050d701..8e65e06a96 100644
--- a/example/hello_world/hello.zig
+++ b/example/hello_world/hello.zig
@@ -1,6 +1,6 @@
const std = @import("std");
-pub fn main() %void {
+pub fn main() !void {
// If this program is run without stdout attached, exit with an error.
var stdout_file = try std.io.getStdOut();
// If this program encounters pipe failure when printing to stdout, exit
diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig
index 553c780b98..e682a97055 100644
--- a/example/mix_o_files/base64.zig
+++ b/example/mix_o_files/base64.zig
@@ -8,3 +8,6 @@ export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8,
base64_decoder.decode(dest[0..decoded_size], src);
return decoded_size;
}
+
+var x: c_int = 1234;
+export var x_ptr = &x;
diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig
index 25d0fdd2ff..4380486867 100644
--- a/example/mix_o_files/build.zig
+++ b/example/mix_o_files/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addCExecutable("test");
diff --git a/example/mix_o_files/test.c b/example/mix_o_files/test.c
index 8a4758acc4..d821bbe108 100644
--- a/example/mix_o_files/test.c
+++ b/example/mix_o_files/test.c
@@ -4,6 +4,8 @@
#include
#include
+extern int *x_ptr;
+
int main(int argc, char **argv) {
const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz";
char buf[200];
@@ -12,5 +14,7 @@ int main(int argc, char **argv) {
buf[len] = 0;
assert(strcmp(buf, "all your base are belong to us") == 0);
+ assert(*x_ptr == 1234);
+
return 0;
}
diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig
index 52d60da819..2b5a178b35 100644
--- a/example/shared_library/build.zig
+++ b/example/shared_library/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addCExecutable("test");
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 7f9e5073a5..4d59783098 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -14,21 +14,8 @@ const builtin = @import("builtin");
const ArrayList = std.ArrayList;
const c = @import("c.zig");
-error InvalidCommandLineArguments;
-error ZigLibDirNotFound;
-error ZigInstallationNotFound;
-
const default_zig_cache_name = "zig-cache";
-pub fn main() %void {
- main2() catch |err| {
- if (err != error.InvalidCommandLineArguments) {
- warn("{}\n", @errorName(err));
- }
- return err;
- };
-}
-
const Cmd = enum {
None,
Build,
@@ -39,21 +26,25 @@ const Cmd = enum {
Targets,
};
-fn badArgs(comptime format: []const u8, args: ...) error {
- var stderr = try io.getStdErr();
+fn badArgs(comptime format: []const u8, args: ...) noreturn {
+ var stderr = io.getStdErr() catch std.os.exit(1);
var stderr_stream_adapter = io.FileOutStream.init(&stderr);
const stderr_stream = &stderr_stream_adapter.stream;
- try stderr_stream.print(format ++ "\n\n", args);
- try printUsage(&stderr_stream_adapter.stream);
- return error.InvalidCommandLineArguments;
+ stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
+ printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
+ std.os.exit(1);
}
-pub fn main2() %void {
+pub fn main() !void {
const allocator = std.heap.c_allocator;
const args = try os.argsAlloc(allocator);
defer os.argsFree(allocator, args);
+ if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
+ return fmtMain(allocator, args[2..]);
+ }
+
var cmd = Cmd.None;
var build_kind: Module.Kind = undefined;
var build_mode: builtin.Mode = builtin.Mode.Debug;
@@ -173,7 +164,7 @@ pub fn main2() %void {
} else if (mem.eql(u8, arg, "--pkg-end")) {
@panic("TODO --pkg-end");
} else if (arg_i + 1 >= args.len) {
- return badArgs("expected another argument after {}", arg);
+ badArgs("expected another argument after {}", arg);
} else {
arg_i += 1;
if (mem.eql(u8, arg, "--output")) {
@@ -188,7 +179,7 @@ pub fn main2() %void {
} else if (mem.eql(u8, args[arg_i], "off")) {
color = ErrColor.Off;
} else {
- return badArgs("--color options are 'auto', 'on', or 'off'");
+ badArgs("--color options are 'auto', 'on', or 'off'");
}
} else if (mem.eql(u8, arg, "--emit")) {
if (mem.eql(u8, args[arg_i], "asm")) {
@@ -198,7 +189,7 @@ pub fn main2() %void {
} else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
emit_file_type = Emit.LlvmIr;
} else {
- return badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
+ badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
}
} else if (mem.eql(u8, arg, "--name")) {
out_name_arg = args[arg_i];
@@ -266,7 +257,7 @@ pub fn main2() %void {
} else if (mem.eql(u8, arg, "--test-cmd")) {
@panic("TODO --test-cmd");
} else {
- return badArgs("invalid argument: {}", arg);
+ badArgs("invalid argument: {}", arg);
}
}
} else if (cmd == Cmd.None) {
@@ -289,18 +280,18 @@ pub fn main2() %void {
cmd = Cmd.Test;
build_kind = Module.Kind.Exe;
} else {
- return badArgs("unrecognized command: {}", arg);
+ badArgs("unrecognized command: {}", arg);
}
} else switch (cmd) {
Cmd.Build, Cmd.TranslateC, Cmd.Test => {
if (in_file_arg == null) {
in_file_arg = arg;
} else {
- return badArgs("unexpected extra parameter: {}", arg);
+ badArgs("unexpected extra parameter: {}", arg);
}
},
Cmd.Version, Cmd.Zen, Cmd.Targets => {
- return badArgs("unexpected extra parameter: {}", arg);
+ badArgs("unexpected extra parameter: {}", arg);
},
Cmd.None => unreachable,
}
@@ -337,15 +328,15 @@ pub fn main2() %void {
// }
switch (cmd) {
- Cmd.None => return badArgs("expected command"),
+ Cmd.None => badArgs("expected command"),
Cmd.Zen => return printZen(),
Cmd.Build, Cmd.Test, Cmd.TranslateC => {
if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
- return badArgs("expected source file argument or at least one --object or --assembly argument");
+ badArgs("expected source file argument or at least one --object or --assembly argument");
} else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
- return badArgs("expected source file argument");
+ badArgs("expected source file argument");
} else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
- return badArgs("When building an object file, --object arguments are invalid");
+ badArgs("When building an object file, --object arguments are invalid");
}
const root_name = switch (cmd) {
@@ -355,9 +346,9 @@ pub fn main2() %void {
} else if (in_file_arg) |in_file_path| {
const basename = os.path.basename(in_file_path);
var it = mem.split(basename, ".");
- break :x it.next() ?? return badArgs("file name cannot be empty");
+ break :x it.next() ?? badArgs("file name cannot be empty");
} else {
- return badArgs("--name [name] not provided and unable to infer");
+ badArgs("--name [name] not provided and unable to infer");
}
},
Cmd.Test => "test",
@@ -432,7 +423,7 @@ pub fn main2() %void {
module.linker_rdynamic = rdynamic;
if (mmacosx_version_min != null and mios_version_min != null) {
- return badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
+ badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
}
if (mmacosx_version_min) |ver| {
@@ -472,7 +463,7 @@ pub fn main2() %void {
}
}
-fn printUsage(stream: &io.OutStream) %void {
+fn printUsage(stream: var) !void {
try stream.write(
\\Usage: zig [command] [options]
\\
@@ -481,6 +472,7 @@ fn printUsage(stream: &io.OutStream) %void {
\\ build-exe [source] create executable from source or object files
\\ build-lib [source] create library from source or object files
\\ build-obj [source] create object from source or assembly
+ \\ fmt [file] parse file and render in canonical zig format
\\ translate-c [source] convert c code to zig code
\\ targets list available compilation targets
\\ test [source] create and run a test build
@@ -548,7 +540,7 @@ fn printUsage(stream: &io.OutStream) %void {
);
}
-fn printZen() %void {
+fn printZen() !void {
var stdout_file = try io.getStdErr();
try stdout_file.write(
\\
@@ -568,8 +560,33 @@ fn printZen() %void {
);
}
+fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
+ for (file_paths) |file_path| {
+ var file = try os.File.openRead(allocator, file_path);
+ defer file.close();
+
+ const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
+ warn("unable to open '{}': {}", file_path, err);
+ continue;
+ };
+ defer allocator.free(source_code);
+
+ var tokenizer = std.zig.Tokenizer.init(source_code);
+ var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
+ defer parser.deinit();
+
+ const tree = try parser.parse();
+ defer tree.deinit();
+
+ const baf = try io.BufferedAtomicFile.create(allocator, file_path);
+ defer baf.destroy();
+
+ try parser.renderSource(baf.stream(), tree.root_node);
+ }
+}
+
/// Caller must free result
-fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) %[]u8 {
+fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {
if (zig_install_prefix_arg) |zig_install_prefix| {
return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {
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
}
/// Caller must free result
-fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) %[]u8 {
+fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
errdefer allocator.free(test_zig_dir);
const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
defer allocator.free(test_index_file);
- var file = try io.File.openRead(test_index_file, allocator);
+ var file = try os.File.openRead(allocator, test_index_file);
file.close();
return test_zig_dir;
}
/// Caller must free result
-fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {
+fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
const self_exe_path = try os.selfExeDirPath(allocator);
defer allocator.free(self_exe_path);
@@ -626,8 +643,3 @@ fn findZigLibDir(allocator: &mem.Allocator) %[]u8 {
return error.FileNotFound;
}
-
-test "import tests" {
- _ = @import("tokenizer.zig");
- _ = @import("parser.zig");
-}
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index f7bf6e0de7..43bba22757 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -8,9 +8,9 @@ const c = @import("c.zig");
const builtin = @import("builtin");
const Target = @import("target.zig").Target;
const warn = std.debug.warn;
-const Tokenizer = @import("tokenizer.zig").Tokenizer;
-const Token = @import("tokenizer.zig").Token;
-const Parser = @import("parser.zig").Parser;
+const Tokenizer = std.zig.Tokenizer;
+const Token = std.zig.Token;
+const Parser = std.zig.Parser;
const ArrayList = std.ArrayList;
pub const Module = struct {
@@ -110,7 +110,7 @@ pub const Module = struct {
};
pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
- kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) %&Module
+ kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
{
var name_buffer = try Buffer.init(allocator, name);
errdefer name_buffer.deinit();
@@ -198,7 +198,7 @@ pub const Module = struct {
self.allocator.destroy(self);
}
- pub fn build(self: &Module) %void {
+ pub fn build(self: &Module) !void {
if (self.llvm_argv.len != 0) {
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator,
[][]const []const u8 { [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, });
@@ -213,14 +213,11 @@ pub const Module = struct {
};
errdefer self.allocator.free(root_src_real_path);
- const source_code = io.readFileAllocExtra(root_src_real_path, self.allocator, 3) catch |err| {
+ const source_code = io.readFileAlloc(self.allocator, root_src_real_path) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
errdefer self.allocator.free(source_code);
- source_code[source_code.len - 3] = '\n';
- source_code[source_code.len - 2] = '\n';
- source_code[source_code.len - 1] = '\n';
warn("====input:====\n");
@@ -263,11 +260,12 @@ pub const Module = struct {
}
- pub fn link(self: &Module, out_file: ?[]const u8) %void {
+ pub fn link(self: &Module, out_file: ?[]const u8) !void {
warn("TODO link");
+ return error.Todo;
}
- pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) %&LinkLib {
+ pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib {
const is_libc = mem.eql(u8, name, "c");
if (is_libc) {
@@ -297,7 +295,7 @@ pub const Module = struct {
}
};
-fn printError(comptime format: []const u8, args: ...) %void {
+fn printError(comptime format: []const u8, args: ...) !void {
var stderr_file = try std.io.getStdErr();
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
const out_stream = &stderr_file_out_stream.stream;
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 0f41760718..04b781a598 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -236,7 +236,7 @@ struct ConstExprValue {
TypeTableEntry *x_type;
ConstExprValue *x_maybe;
ConstErrValue x_err_union;
- ErrorTableEntry *x_pure_err;
+ ErrorTableEntry *x_err_set;
BigInt x_enum_tag;
ConstStructValue x_struct;
ConstUnionValue x_union;
@@ -353,7 +353,6 @@ enum NodeType {
NodeTypeReturnExpr,
NodeTypeDefer,
NodeTypeVariableDeclaration,
- NodeTypeErrorValueDecl,
NodeTypeTestDecl,
NodeTypeBinOpExpr,
NodeTypeUnwrapErrorExpr,
@@ -393,6 +392,7 @@ enum NodeType {
NodeTypeVarLiteral,
NodeTypeIfErrorExpr,
NodeTypeTestExpr,
+ NodeTypeErrorSetDecl,
};
struct AstNodeRoot {
@@ -424,6 +424,8 @@ struct AstNodeFnProto {
AstNode *align_expr;
// populated if the "section(S)" is present
AstNode *section_expr;
+
+ bool auto_err_set;
};
struct AstNodeFnDef {
@@ -486,12 +488,6 @@ struct AstNodeVariableDeclaration {
AstNode *section_expr;
};
-struct AstNodeErrorValueDecl {
- Buf *name;
-
- ErrorTableEntry *err;
-};
-
struct AstNodeTestDecl {
Buf *name;
@@ -514,8 +510,7 @@ enum BinOpType {
BinOpTypeAssignBitAnd,
BinOpTypeAssignBitXor,
BinOpTypeAssignBitOr,
- BinOpTypeAssignBoolAnd,
- BinOpTypeAssignBoolOr,
+ BinOpTypeAssignMergeErrorSets,
BinOpTypeBoolOr,
BinOpTypeBoolAnd,
BinOpTypeCmpEq,
@@ -540,6 +535,8 @@ enum BinOpType {
BinOpTypeUnwrapMaybe,
BinOpTypeArrayCat,
BinOpTypeArrayMult,
+ BinOpTypeErrorUnion,
+ BinOpTypeMergeErrorSets,
};
struct AstNodeBinOpExpr {
@@ -563,6 +560,7 @@ enum CastOp {
CastOpResizeSlice,
CastOpBytesToSlice,
CastOpNumLitToConcrete,
+ CastOpErrSet,
};
struct AstNodeFnCallExpr {
@@ -595,7 +593,6 @@ enum PrefixOp {
PrefixOpNegationWrap,
PrefixOpDereference,
PrefixOpMaybe,
- PrefixOpError,
PrefixOpUnwrapMaybe,
};
@@ -762,6 +759,10 @@ struct AstNodeContainerDecl {
bool auto_enum; // union(enum)
};
+struct AstNodeErrorSetDecl {
+ ZigList decls;
+};
+
struct AstNodeStructField {
VisibMod visib_mod;
Buf *name;
@@ -858,7 +859,6 @@ struct AstNode {
AstNodeReturnExpr return_expr;
AstNodeDefer defer;
AstNodeVariableDeclaration variable_declaration;
- AstNodeErrorValueDecl error_value_decl;
AstNodeTestDecl test_decl;
AstNodeBinOpExpr bin_op_expr;
AstNodeCatchExpr unwrap_err_expr;
@@ -899,6 +899,7 @@ struct AstNode {
AstNodeArrayType array_type;
AstNodeErrorType error_type;
AstNodeVarLiteral var_literal;
+ AstNodeErrorSetDecl err_set_decl;
} data;
};
@@ -993,8 +994,15 @@ struct TypeTableEntryMaybe {
TypeTableEntry *child_type;
};
-struct TypeTableEntryError {
- TypeTableEntry *child_type;
+struct TypeTableEntryErrorUnion {
+ TypeTableEntry *err_set_type;
+ TypeTableEntry *payload_type;
+};
+
+struct TypeTableEntryErrorSet {
+ uint32_t err_count;
+ ErrorTableEntry **errors;
+ FnTableEntry *infer_fn;
};
struct TypeTableEntryEnum {
@@ -1097,7 +1105,7 @@ enum TypeTableEntryId {
TypeTableEntryIdNullLit,
TypeTableEntryIdMaybe,
TypeTableEntryIdErrorUnion,
- TypeTableEntryIdPureError,
+ TypeTableEntryIdErrorSet,
TypeTableEntryIdEnum,
TypeTableEntryIdUnion,
TypeTableEntryIdFn,
@@ -1126,7 +1134,8 @@ struct TypeTableEntry {
TypeTableEntryArray array;
TypeTableEntryStruct structure;
TypeTableEntryMaybe maybe;
- TypeTableEntryError error;
+ TypeTableEntryErrorUnion error_union;
+ TypeTableEntryErrorSet error_set;
TypeTableEntryEnum enumeration;
TypeTableEntryUnion unionation;
TypeTableEntryFn fn;
@@ -1136,7 +1145,6 @@ struct TypeTableEntry {
// use these fields to make sure we don't duplicate type table entries for the same type
TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const]
TypeTableEntry *maybe_parent;
- TypeTableEntry *error_parent;
// If we generate a constant name value for this type, we memoize it here.
// The type of this is array
ConstExprValue *cached_const_name_val;
@@ -1340,6 +1348,10 @@ struct TypeId {
bool is_signed;
uint32_t bit_count;
} integer;
+ struct {
+ TypeTableEntry *err_set_type;
+ TypeTableEntry *payload_type;
+ } error_union;
} data;
};
@@ -1481,7 +1493,7 @@ struct CodeGen {
TypeTableEntry *entry_undef;
TypeTableEntry *entry_null;
TypeTableEntry *entry_var;
- TypeTableEntry *entry_pure_error;
+ TypeTableEntry *entry_global_error_set;
TypeTableEntry *entry_arg_tuple;
} builtin_types;
@@ -1570,7 +1582,6 @@ struct CodeGen {
LLVMValueRef return_address_fn_val;
LLVMValueRef frame_address_fn_val;
bool error_during_imports;
- TypeTableEntry *err_tag_type;
const char **clang_argv;
size_t clang_argv_len;
@@ -1584,7 +1595,9 @@ struct CodeGen {
bool each_lib_rpath;
- ZigList error_decls;
+ TypeTableEntry *err_tag_type;
+ ZigList err_enumerators;
+ ZigList errors_by_index;
bool generate_error_name_table;
LLVMValueRef err_name_table;
size_t largest_err_name_len;
@@ -1617,6 +1630,10 @@ struct CodeGen {
TypeTableEntry *align_amt_type;
TypeTableEntry *stack_trace_type;
TypeTableEntry *ptr_to_stack_trace_type;
+
+ ZigList error_di_types;
+
+ ZigList forbidden_libs;
};
enum VarLinkage {
@@ -1653,6 +1670,7 @@ struct ErrorTableEntry {
Buf name;
uint32_t value;
AstNode *decl_node;
+ TypeTableEntry *set_with_only_this_in_it;
// If we generate a constant error name value for this error, we memoize it here.
// The type of this is array
ConstExprValue *cached_error_name_val;
@@ -1920,6 +1938,7 @@ enum IrInstructionId {
IrInstructionIdArgType,
IrInstructionIdExport,
IrInstructionIdErrorReturnTrace,
+ IrInstructionIdErrorUnion,
};
struct IrInstruction {
@@ -1996,7 +2015,6 @@ enum IrUnOp {
IrUnOpNegation,
IrUnOpNegationWrap,
IrUnOpDereference,
- IrUnOpError,
IrUnOpMaybe,
};
@@ -2039,6 +2057,7 @@ enum IrBinOp {
IrBinOpRemMod,
IrBinOpArrayCat,
IrBinOpArrayMult,
+ IrBinOpMergeErrorSets,
};
struct IrInstructionBinOp {
@@ -2750,6 +2769,13 @@ struct IrInstructionErrorReturnTrace {
IrInstruction base;
};
+struct IrInstructionErrorUnion {
+ IrInstruction base;
+
+ IrInstruction *err_set;
+ IrInstruction *payload;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 4c982c160c..bf7b6e363f 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -224,7 +224,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
@@ -260,7 +260,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
@@ -514,29 +514,47 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
}
}
-TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
- if (child_type->error_parent)
- return child_type->error_parent;
+TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type) {
+ assert(err_set_type->id == TypeTableEntryIdErrorSet);
+ assert(!type_is_invalid(payload_type));
+
+ TypeId type_id = {};
+ type_id.id = TypeTableEntryIdErrorUnion;
+ type_id.data.error_union.err_set_type = err_set_type;
+ type_id.data.error_union.payload_type = payload_type;
+
+ auto existing_entry = g->type_table.maybe_get(type_id);
+ if (existing_entry) {
+ return existing_entry->value;
+ }
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
entry->is_copyable = true;
- assert(child_type->type_ref);
- assert(child_type->di_type);
- ensure_complete_type(g, child_type);
+ assert(payload_type->di_type);
+ ensure_complete_type(g, payload_type);
buf_resize(&entry->name, 0);
- buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name));
+ buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
- entry->data.error.child_type = child_type;
-
- if (!type_has_bits(child_type)) {
- entry->type_ref = g->err_tag_type->type_ref;
- entry->di_type = g->err_tag_type->di_type;
+ entry->data.error_union.err_set_type = err_set_type;
+ entry->data.error_union.payload_type = payload_type;
+ if (!type_has_bits(payload_type)) {
+ if (type_has_bits(err_set_type)) {
+ entry->type_ref = err_set_type->type_ref;
+ entry->di_type = err_set_type->di_type;
+ g->error_di_types.append(&entry->di_type);
+ } else {
+ entry->zero_bits = true;
+ entry->di_type = g->builtin_types.entry_void->di_type;
+ }
+ } else if (!type_has_bits(err_set_type)) {
+ entry->type_ref = payload_type->type_ref;
+ entry->di_type = payload_type->di_type;
} else {
LLVMTypeRef elem_types[] = {
- g->err_tag_type->type_ref,
- child_type->type_ref,
+ err_set_type->type_ref,
+ payload_type->type_ref,
};
entry->type_ref = LLVMStructType(elem_types, 2, false);
@@ -547,12 +565,12 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name),
compile_unit_scope, di_file, line);
- uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref);
- uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, g->err_tag_type->type_ref);
+ uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_type->type_ref);
+ uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_type->type_ref);
uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_err_index);
- uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref);
- uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref);
+ uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_type->type_ref);
+ uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_type->type_ref);
uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref,
err_union_payload_index);
@@ -565,13 +583,13 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
tag_debug_size_in_bits,
tag_debug_align_in_bits,
tag_offset_in_bits,
- 0, child_type->di_type),
+ 0, err_set_type->di_type),
ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type),
"value", di_file, line,
value_debug_size_in_bits,
value_debug_align_in_bits,
value_offset_in_bits,
- 0, child_type->di_type),
+ 0, payload_type->di_type),
};
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
@@ -587,7 +605,7 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
entry->di_type = replacement_di_type;
}
- child_type->error_parent = entry;
+ g->type_table.put(type_id, entry);
return entry;
}
@@ -937,7 +955,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
handle_is_ptr(fn_type_id->return_type);
bool prefix_arg_error_return_trace = g->have_err_ret_tracing &&
(fn_type_id->return_type->id == TypeTableEntryIdErrorUnion ||
- fn_type_id->return_type->id == TypeTableEntryIdPureError);
+ fn_type_id->return_type->id == TypeTableEntryIdErrorSet);
// +1 for maybe making the first argument the return value
// +1 for maybe last argument the error return trace
LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count);
@@ -1177,7 +1195,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
@@ -1218,7 +1236,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
@@ -1263,7 +1281,23 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
zig_unreachable();
}
-static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
+TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) {
+ TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
+ buf_resize(&err_set_type->name, 0);
+ buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name));
+ err_set_type->is_copyable = true;
+ err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref;
+ err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
+ err_set_type->data.error_set.err_count = 0;
+ err_set_type->data.error_set.errors = nullptr;
+ err_set_type->data.error_set.infer_fn = fn_entry;
+
+ g->error_di_types.append(&err_set_type->di_type);
+
+ return err_set_type;
+}
+
+static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
@@ -1359,7 +1393,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -1382,13 +1416,19 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
}
}
- fn_type_id.return_type = (fn_proto->return_type == nullptr) ?
- g->builtin_types.entry_void : analyze_type_expr(g, child_scope, fn_proto->return_type);
-
- if (type_is_invalid(fn_type_id.return_type)) {
+ TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type);
+ if (type_is_invalid(specified_return_type)) {
+ fn_type_id.return_type = g->builtin_types.entry_invalid;
return g->builtin_types.entry_invalid;
}
+ if (fn_proto->auto_err_set) {
+ TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(g, fn_entry);
+ fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type);
+ } else {
+ fn_type_id.return_type = specified_return_type;
+ }
+
if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, fn_type_id.return_type)) {
add_node_error(g, fn_proto->return_type,
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
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -2756,7 +2796,8 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) {
return g->test_fn_type;
FnTypeId fn_type_id = {0};
- fn_type_id.return_type = get_error_type(g, g->builtin_types.entry_void);
+ fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set,
+ g->builtin_types.entry_void);
g->test_fn_type = get_fn_type(g, &fn_type_id);
return g->test_fn_type;
}
@@ -2824,7 +2865,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope;
- fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope);
+ fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry);
if (fn_proto->section_expr != nullptr) {
if (fn_table_entry->body_node == nullptr) {
@@ -2949,29 +2990,6 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope
g->resolve_queue.append(&tld_fn->base);
}
-static void preview_error_value_decl(CodeGen *g, AstNode *node) {
- assert(node->type == NodeTypeErrorValueDecl);
-
- ErrorTableEntry *err = allocate(1);
-
- err->decl_node = node;
- buf_init_from_buf(&err->name, node->data.error_value_decl.name);
-
- auto existing_entry = g->error_table.maybe_get(&err->name);
- if (existing_entry) {
- // duplicate error definitions allowed and they get the same value
- err->value = existing_entry->value->value;
- } else {
- size_t error_value_count = g->error_decls.length;
- assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)g->err_tag_type->data.integral.bit_count));
- err->value = (uint32_t)error_value_count;
- g->error_decls.append(node);
- g->error_table.put(&err->name, err);
- }
-
- node->data.error_value_decl.err = err;
-}
-
static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) {
assert(node->type == NodeTypeCompTime);
@@ -3045,10 +3063,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
import->use_decls.append(node);
break;
}
- case NodeTypeErrorValueDecl:
- // error value declarations do not depend on other top level decls
- preview_error_value_decl(g, node);
- break;
case NodeTypeTestDecl:
preview_test_decl(g, node, decls_scope);
break;
@@ -3097,6 +3111,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
case NodeTypeVarLiteral:
case NodeTypeIfErrorExpr:
case NodeTypeTestExpr:
+ case NodeTypeErrorSetDecl:
zig_unreachable();
}
}
@@ -3147,7 +3162,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -3362,108 +3377,6 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so
g->tld_ref_source_node_stack.pop();
}
-bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) {
- if (expected_type == actual_type)
- return true;
-
- // pointer const
- if (expected_type->id == TypeTableEntryIdPointer &&
- actual_type->id == TypeTableEntryIdPointer &&
- (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) &&
- (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) &&
- actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset &&
- actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count &&
- actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment)
- {
- return types_match_const_cast_only(expected_type->data.pointer.child_type,
- actual_type->data.pointer.child_type);
- }
-
- // slice const
- if (is_slice(expected_type) && is_slice(actual_type)) {
- TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
- TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;
- if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) &&
- (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) &&
- actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset &&
- actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count &&
- actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment)
- {
- return types_match_const_cast_only(expected_ptr_type->data.pointer.child_type,
- actual_ptr_type->data.pointer.child_type);
- }
- }
-
- // maybe
- if (expected_type->id == TypeTableEntryIdMaybe &&
- actual_type->id == TypeTableEntryIdMaybe)
- {
- return types_match_const_cast_only(
- expected_type->data.maybe.child_type,
- actual_type->data.maybe.child_type);
- }
-
- // error
- if (expected_type->id == TypeTableEntryIdErrorUnion &&
- actual_type->id == TypeTableEntryIdErrorUnion)
- {
- return types_match_const_cast_only(
- expected_type->data.error.child_type,
- actual_type->data.error.child_type);
- }
-
- // fn
- if (expected_type->id == TypeTableEntryIdFn &&
- actual_type->id == TypeTableEntryIdFn)
- {
- if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
- return false;
- }
- if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
- return false;
- }
- if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
- return false;
- }
- if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
- return false;
- }
- if (!expected_type->data.fn.is_generic &&
- actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable &&
- !types_match_const_cast_only(
- expected_type->data.fn.fn_type_id.return_type,
- actual_type->data.fn.fn_type_id.return_type))
- {
- return false;
- }
- if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
- return false;
- }
- if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
- return false;
- }
- assert(expected_type->data.fn.is_generic ||
- expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count);
- for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) {
- // note it's reversed for parameters
- FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
- FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
-
- if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) {
- return false;
- }
-
- if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
- return false;
- }
- }
- return true;
- }
-
-
- return false;
-}
-
Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) {
// we must resolve all the use decls
ImportTableEntry *import = get_scope_import(scope);
@@ -3625,7 +3538,7 @@ static bool is_container(TypeTableEntry *type_entry) {
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
@@ -3673,7 +3586,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
@@ -3765,6 +3678,27 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari
}
}
+static bool analyze_resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) {
+ FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn;
+ if (infer_fn != nullptr) {
+ if (infer_fn->anal_state == FnAnalStateInvalid) {
+ return false;
+ } else if (infer_fn->anal_state == FnAnalStateReady) {
+ analyze_fn_body(g, infer_fn);
+ if (err_set_type->data.error_set.infer_fn != nullptr) {
+ assert(g->errors.length != 0);
+ return false;
+ }
+ } else {
+ add_node_error(g, source_node,
+ buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
+ buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
+ return false;
+ }
+ }
+ return true;
+}
+
void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {
TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic);
@@ -3774,14 +3708,49 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
&fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
fn_table_entry->implicit_return_type = block_return_type;
- if (block_return_type->id == TypeTableEntryIdInvalid ||
- fn_table_entry->analyzed_executable.invalid)
- {
+ if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
assert(g->errors.length > 0);
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
+ if (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
+ TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
+ if (return_err_set_type->data.error_set.infer_fn != nullptr) {
+ TypeTableEntry *inferred_err_set_type;
+ if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) {
+ inferred_err_set_type = fn_table_entry->implicit_return_type;
+ } else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) {
+ inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type;
+ } else {
+ add_node_error(g, return_type_node,
+ buf_sprintf("function with inferred error set must return at least one possible error"));
+ fn_table_entry->anal_state = FnAnalStateInvalid;
+ return;
+ }
+
+ if (inferred_err_set_type->data.error_set.infer_fn != nullptr) {
+ if (!analyze_resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
+ fn_table_entry->anal_state = FnAnalStateInvalid;
+ return;
+ }
+ }
+
+ return_err_set_type->data.error_set.infer_fn = nullptr;
+ if (type_is_global_error_set(inferred_err_set_type)) {
+ return_err_set_type->data.error_set.err_count = UINT32_MAX;
+ } else {
+ return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count;
+ if (inferred_err_set_type->data.error_set.err_count > 0) {
+ return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count);
+ for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) {
+ return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i];
+ }
+ }
+ }
+ }
+ }
+
if (g->verbose_ir) {
fprintf(stderr, "{ // (analyzed)\n");
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
fn_table_entry->anal_state = FnAnalStateComplete;
}
-static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
+void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
assert(fn_table_entry->anal_state != FnAnalStateProbing);
if (fn_table_entry->anal_state != FnAnalStateReady)
return;
@@ -4022,7 +3991,8 @@ void semantic_analyze(CodeGen *g) {
for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) {
Tld *tld = g->resolve_queue.at(g->resolve_queue_index);
bool pointer_only = false;
- resolve_top_level_decl(g, tld, pointer_only, nullptr);
+ AstNode *source_node = nullptr;
+ resolve_top_level_decl(g, tld, pointer_only, source_node);
}
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) {
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdEnum:
return false;
@@ -4122,7 +4092,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
case TypeTableEntryIdStruct:
return type_has_bits(type_entry);
case TypeTableEntryIdErrorUnion:
- return type_has_bits(type_entry->data.error.child_type);
+ return type_has_bits(type_entry->data.error_union.payload_type);
case TypeTableEntryIdMaybe:
return type_has_bits(type_entry->data.maybe.child_type) &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
@@ -4386,9 +4356,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
case TypeTableEntryIdErrorUnion:
// TODO better hashing algorithm
return 3415065496;
- case TypeTableEntryIdPureError:
- // TODO better hashing algorithm
- return 2630160122;
+ case TypeTableEntryIdErrorSet:
+ assert(const_val->data.x_err_set != nullptr);
+ return const_val->data.x_err_set->value ^ 2630160122;
case TypeTableEntryIdFn:
return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry);
case TypeTableEntryIdNamespace:
@@ -4515,7 +4485,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdEnum:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdBool:
case TypeTableEntryIdInt:
@@ -4894,8 +4864,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
return a->data.x_type == b->data.x_type;
case TypeTableEntryIdVoid:
return true;
- case TypeTableEntryIdPureError:
- return a->data.x_pure_err == b->data.x_pure_err;
+ case TypeTableEntryIdErrorSet:
+ return a->data.x_err_set->value == b->data.x_err_set->value;
case TypeTableEntryIdFn:
return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry;
case TypeTableEntryIdBool:
@@ -5256,9 +5226,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name));
return;
}
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
{
- buf_appendf(buf, "(pure error constant)");
+ buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name));
return;
}
case TypeTableEntryIdArgTuple:
@@ -5319,8 +5289,7 @@ uint32_t type_id_hash(TypeId x) {
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
- case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -5329,6 +5298,8 @@ uint32_t type_id_hash(TypeId x) {
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
zig_unreachable();
+ case TypeTableEntryIdErrorUnion:
+ return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
case TypeTableEntryIdPointer:
return hash_ptr(x.data.pointer.child_type) +
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
@@ -5363,8 +5334,7 @@ bool type_id_eql(TypeId a, TypeId b) {
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
- case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -5374,6 +5344,10 @@ bool type_id_eql(TypeId a, TypeId b) {
case TypeTableEntryIdArgTuple:
case TypeTableEntryIdOpaque:
zig_unreachable();
+ case TypeTableEntryIdErrorUnion:
+ return a.data.error_union.err_set_type == b.data.error_union.err_set_type &&
+ a.data.error_union.payload_type == b.data.error_union.payload_type;
+
case TypeTableEntryIdPointer:
return a.data.pointer.child_type == b.data.pointer.child_type &&
a.data.pointer.is_const == b.data.pointer.is_const &&
@@ -5478,7 +5452,7 @@ static const TypeTableEntryId all_type_ids[] = {
TypeTableEntryIdNullLit,
TypeTableEntryIdMaybe,
TypeTableEntryIdErrorUnion,
- TypeTableEntryIdPureError,
+ TypeTableEntryIdErrorSet,
TypeTableEntryIdEnum,
TypeTableEntryIdUnion,
TypeTableEntryIdFn,
@@ -5533,7 +5507,7 @@ size_t type_id_index(TypeTableEntryId id) {
return 13;
case TypeTableEntryIdErrorUnion:
return 14;
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
return 15;
case TypeTableEntryIdEnum:
return 16;
@@ -5590,8 +5564,8 @@ const char *type_id_name(TypeTableEntryId id) {
return "Nullable";
case TypeTableEntryIdErrorUnion:
return "ErrorUnion";
- case TypeTableEntryIdPureError:
- return "Error";
+ case TypeTableEntryIdErrorSet:
+ return "ErrorSet";
case TypeTableEntryIdEnum:
return "Enum";
case TypeTableEntryIdUnion:
@@ -5640,17 +5614,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
return link_lib;
}
-void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name) {
- LinkLib *link_lib = add_link_lib(g, lib_name);
- for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
- Buf *existing_symbol_name = link_lib->symbols.at(i);
- if (buf_eql_buf(existing_symbol_name, symbol_name)) {
- return;
- }
- }
- link_lib->symbols.append(symbol_name);
-}
-
uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
type_ensure_zero_bits_known(g, type_entry);
if (type_entry->zero_bits) return 0;
@@ -5696,3 +5659,8 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
return var_value;
}
+bool type_is_global_error_set(TypeTableEntry *err_set_type) {
+ assert(err_set_type->id == TypeTableEntryIdErrorSet);
+ assert(err_set_type->data.error_set.infer_fn == nullptr);
+ return err_set_type->data.error_set.err_count == UINT32_MAX;
+}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index dab6d17d0c..c0c89cf36b 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -30,7 +30,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type);
TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
AstNode *decl_node, const char *name, ContainerLayout layout);
TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
-TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type);
+TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type);
TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry);
TypeTableEntry *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name);
TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[],
@@ -46,8 +46,6 @@ bool type_has_bits(TypeTableEntry *type_entry);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
-// TODO move these over, these used to be static
-bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type);
VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name);
Tld *find_decl(CodeGen *g, Scope *scope, Buf *name);
void 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
TypeTableEntry *container_ref_type(TypeTableEntry *type_entry);
bool type_is_complete(TypeTableEntry *type_entry);
bool type_is_invalid(TypeTableEntry *type_entry);
+bool type_is_global_error_set(TypeTableEntry *err_set_type);
bool type_has_zero_bits_known(TypeTableEntry *type_entry);
void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
@@ -176,7 +175,6 @@ bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
LinkLib *create_link_lib(Buf *name);
bool calling_convention_does_first_arg_return(CallingConvention cc);
LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
-void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name);
uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry);
TypeTableEntry *get_align_amt_type(CodeGen *g);
@@ -188,6 +186,8 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
+void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
+TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry);
#endif
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 79cbc1b49a..aed4b3e6db 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -49,11 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeAssignBitAnd: return "&=";
case BinOpTypeAssignBitXor: return "^=";
case BinOpTypeAssignBitOr: return "|=";
- case BinOpTypeAssignBoolAnd: return "&&=";
- case BinOpTypeAssignBoolOr: return "||=";
+ case BinOpTypeAssignMergeErrorSets: return "||=";
case BinOpTypeUnwrapMaybe: return "??";
case BinOpTypeArrayCat: return "++";
case BinOpTypeArrayMult: return "**";
+ case BinOpTypeErrorUnion: return "!";
+ case BinOpTypeMergeErrorSets: return "||";
}
zig_unreachable();
}
@@ -67,7 +68,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
case PrefixOpBinNot: return "~";
case PrefixOpDereference: return "*";
case PrefixOpMaybe: return "?";
- case PrefixOpError: return "%";
case PrefixOpUnwrapMaybe: return "??";
}
zig_unreachable();
@@ -174,8 +174,6 @@ static const char *node_type_str(NodeType node_type) {
return "Defer";
case NodeTypeVariableDeclaration:
return "VariableDeclaration";
- case NodeTypeErrorValueDecl:
- return "ErrorValueDecl";
case NodeTypeTestDecl:
return "TestDecl";
case NodeTypeIntLiteral:
@@ -244,6 +242,8 @@ static const char *node_type_str(NodeType node_type) {
return "IfErrorExpr";
case NodeTypeTestExpr:
return "TestExpr";
+ case NodeTypeErrorSetDecl:
+ return "ErrorSetDecl";
}
zig_unreachable();
}
@@ -396,7 +396,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
if (child->type == NodeTypeUse ||
child->type == NodeTypeVariableDeclaration ||
- child->type == NodeTypeErrorValueDecl ||
child->type == NodeTypeFnProto)
{
fprintf(ar->f, ";");
@@ -452,6 +451,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
AstNode *return_type_node = node->data.fn_proto.return_type;
assert(return_type_node != nullptr);
fprintf(ar->f, " ");
+ if (node->data.fn_proto.auto_err_set) {
+ fprintf(ar->f, "!");
+ }
render_node_grouped(ar, return_type_node);
break;
}
@@ -1017,9 +1019,26 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
break;
}
+ case NodeTypeErrorSetDecl:
+ {
+ fprintf(ar->f, "error {\n");
+ ar->indent += ar->indent_size;
+
+ for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) {
+ AstNode *field_node = node->data.err_set_decl.decls.at(i);
+ assert(field_node->type == NodeTypeSymbol);
+ print_indent(ar);
+ print_symbol(ar, field_node->data.symbol_expr.symbol);
+ fprintf(ar->f, ",\n");
+ }
+
+ ar->indent -= ar->indent_size;
+ print_indent(ar);
+ fprintf(ar->f, "}");
+ break;
+ }
case NodeTypeFnDecl:
case NodeTypeParamDecl:
- case NodeTypeErrorValueDecl:
case NodeTypeTestDecl:
case NodeTypeStructField:
case NodeTypeUse:
diff --git a/src/codegen.cpp b/src/codegen.cpp
index b1412b2b59..4f100d75ad 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -92,9 +92,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
buf_resize(&g->global_asm, 0);
- // reserve index 0 to indicate no error
- g->error_decls.append(nullptr);
-
if (root_src_path) {
Buf *src_basename = buf_alloc();
Buf *src_dir = buf_alloc();
@@ -256,6 +253,10 @@ LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) {
return add_link_lib(g, name);
}
+void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) {
+ codegen->forbidden_libs.append(lib);
+}
+
void codegen_add_framework(CodeGen *g, const char *framework) {
g->darwin_frameworks.append(buf_create_from_str(framework));
}
@@ -282,9 +283,9 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) {
}
-static void render_const_val(CodeGen *g, ConstExprValue *const_val);
+static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name);
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name);
-static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val);
+static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name);
static void generate_error_name_table(CodeGen *g);
static 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
}
TypeTableEntry *fn_type = fn_table_entry->type_entry;
TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
- if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) {
+ if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdErrorSet) {
return UINT32_MAX;
}
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) {
ConstExprValue *array_val = create_const_str_lit(g, buf_msg);
init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true);
- render_const_val(g, val);
+ render_const_val(g, val, "");
render_const_val_global(g, val, "");
assert(val->global_refs->llvm_global);
@@ -1412,7 +1413,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
if (!instruction->llvm_value) {
assert(instruction->value.special != ConstValSpecialRuntime);
assert(instruction->value.type);
- render_const_val(g, &instruction->value);
+ render_const_val(g, &instruction->value, "");
// we might have to do some pointer casting here due to the way union
// values are rendered with a type other than the one we expect
if (handle_is_ptr(instruction->value.type)) {
@@ -1442,7 +1443,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
// TODO: emit a branch to check if the return value is an error
}
- } else if (return_type->id == TypeTableEntryIdPureError) {
+ } else if (return_type->id == TypeTableEntryIdErrorSet) {
is_err_return = true;
}
if (is_err_return) {
@@ -1789,7 +1790,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy ||
op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy ||
- op_id == IrBinOpBitShiftRightExact);
+ op_id == IrBinOpBitShiftRightExact ||
+ (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet));
TypeTableEntry *type_entry = op1->value.type;
bool want_runtime_safety = bin_op_instruction->safety_check_on &&
@@ -1802,6 +1804,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpArrayCat:
case IrBinOpArrayMult:
case IrBinOpRemUnspecified:
+ case IrBinOpMergeErrorSets:
zig_unreachable();
case IrBinOpBoolOr:
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
@@ -1823,7 +1826,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
} else if (type_entry->id == TypeTableEntryIdEnum) {
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
- } else if (type_entry->id == TypeTableEntryIdPureError ||
+ } else if (type_entry->id == TypeTableEntryIdErrorSet ||
type_entry->id == TypeTableEntryIdPointer ||
type_entry->id == TypeTableEntryIdBool)
{
@@ -1955,6 +1958,54 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
zig_unreachable();
}
+static void add_error_range_check(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *int_type, LLVMValueRef target_val) {
+ assert(err_set_type->id == TypeTableEntryIdErrorSet);
+
+ if (type_is_global_error_set(err_set_type)) {
+ LLVMValueRef zero = LLVMConstNull(int_type->type_ref);
+ LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, "");
+ LLVMValueRef ok_bit;
+
+ BigInt biggest_possible_err_val = {0};
+ eval_min_max_value_int(g, int_type, &biggest_possible_err_val, true);
+
+ if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) &&
+ bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length)
+ {
+ ok_bit = neq_zero_bit;
+ } else {
+ LLVMValueRef error_value_count = LLVMConstInt(int_type->type_ref, g->errors_by_index.length, false);
+ LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, "");
+ ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, "");
+ }
+
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
+
+ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_safety_crash(g, PanicMsgIdInvalidErrorCode);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ } else {
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
+
+ uint32_t err_count = err_set_type->data.error_set.err_count;
+ LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_val, fail_block, err_count);
+ for (uint32_t i = 0; i < err_count; i += 1) {
+ LLVMValueRef case_value = LLVMConstInt(g->err_tag_type->type_ref, err_set_type->data.error_set.errors[i]->value, false);
+ LLVMAddCase(switch_instr, case_value, ok_block);
+ }
+
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_safety_crash(g, PanicMsgIdInvalidErrorCode);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ }
+}
+
static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
IrInstructionCast *cast_instruction)
{
@@ -2078,6 +2129,11 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
assert(wanted_type->id == TypeTableEntryIdInt);
assert(actual_type->id == TypeTableEntryIdBool);
return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
+ case CastOpErrSet:
+ if (ir_want_runtime_safety(g, &cast_instruction->base)) {
+ add_error_range_check(g, wanted_type, g->err_tag_type, expr_val);
+ }
+ return expr_val;
}
zig_unreachable();
}
@@ -2139,7 +2195,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) {
TypeTableEntry *wanted_type = instruction->base.value.type;
- assert(wanted_type->id == TypeTableEntryIdPureError);
+ assert(wanted_type->id == TypeTableEntryIdErrorSet);
TypeTableEntry *actual_type = instruction->target->value.type;
assert(actual_type->id == TypeTableEntryIdInt);
@@ -2148,32 +2204,7 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
if (ir_want_runtime_safety(g, &instruction->base)) {
- LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
- LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, "");
- LLVMValueRef ok_bit;
-
- BigInt biggest_possible_err_val = {0};
- eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true);
-
- if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) &&
- bigint_as_unsigned(&biggest_possible_err_val) < g->error_decls.length)
- {
- ok_bit = neq_zero_bit;
- } else {
- LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false);
- LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, "");
- ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, "");
- }
-
- LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
- LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
-
- LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
-
- LLVMPositionBuilderAtEnd(g->builder, fail_block);
- gen_safety_crash(g, PanicMsgIdInvalidErrorCode);
-
- LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ add_error_range_check(g, wanted_type, actual_type, target_val);
}
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
TypeTableEntry *actual_type = instruction->target->value.type;
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
- if (actual_type->id == TypeTableEntryIdPureError) {
+ if (actual_type->id == TypeTableEntryIdErrorSet) {
return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
g->err_tag_type, wanted_type, target_val);
} else if (actual_type->id == TypeTableEntryIdErrorUnion) {
- if (!type_has_bits(actual_type->data.error.child_type)) {
+ // this should have been a compile time constant
+ assert(type_has_bits(actual_type->data.error_union.err_set_type));
+
+ if (!type_has_bits(actual_type->data.error_union.payload_type)) {
return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
g->err_tag_type, wanted_type, target_val);
} else {
- zig_panic("TODO");
+ zig_panic("TODO err to int when error union payload type not void");
}
} else {
zig_unreachable();
@@ -2235,7 +2269,6 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
switch (op_id) {
case IrUnOpInvalid:
- case IrUnOpError:
case IrUnOpMaybe:
case IrUnOpDereference:
zig_unreachable();
@@ -2489,7 +2522,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
TypeTableEntry *src_return_type = fn_type_id->return_type;
bool ret_has_bits = type_has_bits(src_return_type);
bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
- bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError);
+ bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdErrorSet);
size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0);
bool is_var_args = fn_type_id->is_var_args;
LLVMValueRef *gen_param_values = allocate(actual_param_count);
@@ -2907,7 +2940,7 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru
static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrInstructionErrName *instruction) {
assert(g->generate_error_name_table);
- if (g->error_decls.length == 1) {
+ if (g->errors_by_index.length == 1) {
LLVMBuildUnreachable(g->builder);
return nullptr;
}
@@ -2915,7 +2948,7 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
LLVMValueRef err_val = ir_llvm_value(g, instruction->value);
if (ir_want_runtime_safety(g, &instruction->base)) {
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));
- LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);
+ LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->errors_by_index.length, false);
add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
}
@@ -3393,11 +3426,11 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable,
static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) {
TypeTableEntry *err_union_type = instruction->value->value.type;
- TypeTableEntry *child_type = err_union_type->data.error.child_type;
+ TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type;
LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->value);
LLVMValueRef err_val;
- if (type_has_bits(child_type)) {
+ if (type_has_bits(payload_type)) {
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");
} else {
@@ -3412,11 +3445,11 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab
TypeTableEntry *ptr_type = instruction->value->value.type;
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type;
- TypeTableEntry *child_type = err_union_type->data.error.child_type;
+ TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type;
LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
- if (type_has_bits(child_type)) {
+ if (type_has_bits(payload_type)) {
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
return gen_load_untyped(g, err_val_ptr, 0, false, "");
} else {
@@ -3428,13 +3461,17 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
TypeTableEntry *ptr_type = instruction->value->value.type;
assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type;
- TypeTableEntry *child_type = err_union_type->data.error.child_type;
+ TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type;
LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value);
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type);
- if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->error_decls.length > 1) {
+ if (!type_has_bits(err_union_type->data.error_union.err_set_type)) {
+ return err_union_handle;
+ }
+
+ if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) {
LLVMValueRef err_val;
- if (type_has_bits(child_type)) {
+ if (type_has_bits(payload_type)) {
LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, "");
err_val = gen_load_untyped(g, err_val_ptr, 0, false, "");
} else {
@@ -3452,7 +3489,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
LLVMPositionBuilderAtEnd(g->builder, ok_block);
}
- if (type_has_bits(child_type)) {
+ if (type_has_bits(payload_type)) {
return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, "");
} else {
return nullptr;
@@ -3493,10 +3530,12 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
- TypeTableEntry *child_type = wanted_type->data.error.child_type;
+ TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type;
+ TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type;
+
LLVMValueRef err_val = ir_llvm_value(g, instruction->value);
- if (!type_has_bits(child_type))
+ if (!type_has_bits(payload_type) || !type_has_bits(err_set_type))
return err_val;
assert(instruction->tmp_ptr);
@@ -3512,11 +3551,16 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
- TypeTableEntry *child_type = wanted_type->data.error.child_type;
+ TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type;
+ TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type;
+
+ if (!type_has_bits(err_set_type)) {
+ return ir_llvm_value(g, instruction->value);
+ }
LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
- if (!type_has_bits(child_type))
+ if (!type_has_bits(payload_type))
return ok_err_val;
assert(instruction->tmp_ptr);
@@ -3527,7 +3571,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false);
LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, "");
- gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, child_type, false), payload_val);
+ gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val);
return instruction->tmp_ptr;
}
@@ -3700,6 +3744,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdArgType:
case IrInstructionIdTagType:
case IrInstructionIdExport:
+ case IrInstructionIdErrorUnion:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -3847,7 +3892,7 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *ar
static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
switch (parent->id) {
case ConstParentIdNone:
- render_const_val(g, val);
+ render_const_val(g, val, "");
render_const_val_global(g, val, "");
return val->global_refs->llvm_global;
case ConstParentIdStruct:
@@ -3933,7 +3978,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
@@ -3946,17 +3991,17 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
case TypeTableEntryIdEnum:
{
assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr);
- LLVMValueRef int_val = gen_const_val(g, const_val);
+ LLVMValueRef int_val = gen_const_val(g, const_val, "");
return LLVMConstZExt(int_val, big_int_type_ref);
}
case TypeTableEntryIdInt:
{
- LLVMValueRef int_val = gen_const_val(g, const_val);
+ LLVMValueRef int_val = gen_const_val(g, const_val, "");
return LLVMConstZExt(int_val, big_int_type_ref);
}
case TypeTableEntryIdFloat:
{
- LLVMValueRef float_val = gen_const_val(g, const_val);
+ LLVMValueRef float_val = gen_const_val(g, const_val, "");
LLVMValueRef int_val = LLVMConstFPToUI(float_val,
LLVMIntType((unsigned)type_entry->data.floating.bit_count));
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
case TypeTableEntryIdFn:
case TypeTableEntryIdMaybe:
{
- LLVMValueRef ptr_val = gen_const_val(g, const_val);
+ LLVMValueRef ptr_val = gen_const_val(g, const_val, "");
LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref);
return LLVMConstZExt(ptr_size_int_val, big_int_type_ref);
}
@@ -4010,7 +4055,7 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
return LLVMTypeOf(val) != type_entry->type_ref;
}
-static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
+static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
TypeTableEntry *type_entry = const_val->type;
assert(!type_entry->zero_bits);
@@ -4026,10 +4071,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
switch (type_entry->id) {
case TypeTableEntryIdInt:
return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint);
- case TypeTableEntryIdPureError:
- assert(const_val->data.x_pure_err);
- return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref,
- const_val->data.x_pure_err->value, false);
+ case TypeTableEntryIdErrorSet:
+ assert(const_val->data.x_err_set != nullptr);
+ return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref,
+ const_val->data.x_err_set->value, false);
case TypeTableEntryIdFloat:
switch (type_entry->data.floating.bit_count) {
case 32:
@@ -4063,7 +4108,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
child_type->id == TypeTableEntryIdFn)
{
if (const_val->data.x_maybe) {
- return gen_const_val(g, const_val->data.x_maybe);
+ return gen_const_val(g, const_val->data.x_maybe, "");
} else {
return LLVMConstNull(child_type->type_ref);
}
@@ -4072,7 +4117,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
LLVMValueRef maybe_val;
bool make_unnamed_struct;
if (const_val->data.x_maybe) {
- child_val = gen_const_val(g, const_val->data.x_maybe);
+ child_val = gen_const_val(g, const_val->data.x_maybe, "");
maybe_val = LLVMConstAllOnes(LLVMInt1Type());
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) {
if (src_field_index + 1 == src_field_index_end) {
ConstExprValue *field_val = &const_val->data.x_struct.fields[src_field_index];
- LLVMValueRef val = gen_const_val(g, field_val);
+ LLVMValueRef val = gen_const_val(g, field_val, "");
fields[type_struct_field->gen_index] = val;
make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
} else {
@@ -4156,7 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
continue;
}
ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
- LLVMValueRef val = gen_const_val(g, field_val);
+ LLVMValueRef val = gen_const_val(g, field_val, "");
fields[type_struct_field->gen_index] = val;
make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
}
@@ -4180,7 +4225,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
bool make_unnamed_struct = false;
for (uint64_t i = 0; i < len; i += 1) {
ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
- LLVMValueRef val = gen_const_val(g, elem_value);
+ LLVMValueRef val = gen_const_val(g, elem_value, "");
values[i] = val;
make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
}
@@ -4215,7 +4260,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
} else {
uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);
uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes;
- LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value);
+ LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value, "");
make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||
payload_value->type != type_entry->data.unionation.most_aligned_union_member;
@@ -4260,7 +4305,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
return fn_llvm_value(g, const_val->data.x_fn.fn_entry);
case TypeTableEntryIdPointer:
{
- render_const_val_global(g, const_val, "");
+ render_const_val_global(g, const_val, name);
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
@@ -4268,7 +4313,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
case ConstPtrSpecialRef:
{
ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
- render_const_val(g, pointee);
+ render_const_val(g, pointee, "");
render_const_val_global(g, pointee, "");
ConstExprValue *other_val = pointee;
const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
@@ -4330,22 +4375,27 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
}
case TypeTableEntryIdErrorUnion:
{
- TypeTableEntry *child_type = type_entry->data.error.child_type;
- if (!type_has_bits(child_type)) {
+ TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
+ TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
+ if (!type_has_bits(payload_type)) {
+ assert(type_has_bits(err_set_type));
uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0;
return LLVMConstInt(g->err_tag_type->type_ref, value, false);
+ } else if (!type_has_bits(err_set_type)) {
+ assert(type_has_bits(payload_type));
+ return gen_const_val(g, const_val->data.x_err_union.payload, "");
} else {
LLVMValueRef err_tag_value;
LLVMValueRef err_payload_value;
bool make_unnamed_struct;
if (const_val->data.x_err_union.err) {
err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false);
- err_payload_value = LLVMConstNull(child_type->type_ref);
+ err_payload_value = LLVMConstNull(payload_type->type_ref);
make_unnamed_struct = false;
} else {
err_tag_value = LLVMConstNull(g->err_tag_type->type_ref);
ConstExprValue *payload_val = const_val->data.x_err_union.payload;
- err_payload_value = gen_const_val(g, payload_val);
+ err_payload_value = gen_const_val(g, payload_val, "");
make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value);
}
LLVMValueRef fields[] = {
@@ -4380,11 +4430,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
zig_unreachable();
}
-static void render_const_val(CodeGen *g, ConstExprValue *const_val) {
+static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
if (!const_val->global_refs)
const_val->global_refs = allocate(1);
if (!const_val->global_refs->llvm_value)
- const_val->global_refs->llvm_value = gen_const_val(g, const_val);
+ const_val->global_refs->llvm_value = gen_const_val(g, const_val, name);
if (const_val->global_refs->llvm_global)
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
}
static void generate_error_name_table(CodeGen *g) {
- if (g->err_name_table != nullptr || !g->generate_error_name_table || g->error_decls.length == 1) {
+ if (g->err_name_table != nullptr || !g->generate_error_name_table || g->errors_by_index.length == 1) {
return;
}
- assert(g->error_decls.length > 0);
+ assert(g->errors_by_index.length > 0);
TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);
- LLVMValueRef *values = allocate(g->error_decls.length);
+ LLVMValueRef *values = allocate(g->errors_by_index.length);
values[0] = LLVMGetUndef(str_type->type_ref);
- for (size_t i = 1; i < g->error_decls.length; i += 1) {
- AstNode *error_decl_node = g->error_decls.at(i);
- assert(error_decl_node->type == NodeTypeErrorValueDecl);
- Buf *name = error_decl_node->data.error_value_decl.name;
+ for (size_t i = 1; i < g->errors_by_index.length; i += 1) {
+ ErrorTableEntry *err_entry = g->errors_by_index.at(i);
+ Buf *name = &err_entry->name;
g->largest_err_name_len = max(g->largest_err_name_len, buf_len(name));
@@ -4443,7 +4492,7 @@ static void generate_error_name_table(CodeGen *g) {
values[i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
}
- LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)g->error_decls.length);
+ LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)g->errors_by_index.length);
g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init),
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) {
codegen_add_time_event(g, "Code Generation");
+ {
+ // create debug type for error sets
+ assert(g->err_enumerators.length == g->errors_by_index.length);
+ uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref);
+ uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, g->err_tag_type->type_ref);
+ ZigLLVMDIFile *err_set_di_file = nullptr;
+ ZigLLVMDIType *err_set_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
+ ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&g->builtin_types.entry_global_error_set->name),
+ err_set_di_file, 0,
+ tag_debug_size_in_bits,
+ tag_debug_align_in_bits,
+ g->err_enumerators.items, g->err_enumerators.length,
+ g->err_tag_type->di_type, "");
+ ZigLLVMReplaceTemporary(g->dbuilder, g->builtin_types.entry_global_error_set->di_type, err_set_di_type);
+ g->builtin_types.entry_global_error_set->di_type = err_set_di_type;
+
+ for (size_t i = 0; i < g->error_di_types.length; i += 1) {
+ ZigLLVMDIType **di_type_ptr = g->error_di_types.at(i);
+ *di_type_ptr = err_set_di_type;
+ }
+ }
+
generate_error_name_table(g);
generate_enum_name_tables(g);
@@ -4592,7 +4663,7 @@ static void do_code_gen(CodeGen *g) {
coerced_value.special = ConstValSpecialStatic;
coerced_value.type = var_type;
coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat);
- LLVMValueRef init_val = gen_const_val(g, &coerced_value);
+ LLVMValueRef init_val = gen_const_val(g, &coerced_value, "");
gen_global_var(g, var, init_val, var_type);
continue;
}
@@ -4626,8 +4697,9 @@ static void do_code_gen(CodeGen *g) {
LLVMSetAlignment(global_value, var->align_bytes);
} else {
bool exported = (var->linkage == VarLinkageExport);
- render_const_val(g, var->value);
- render_const_val_global(g, var->value, buf_ptr(get_mangled_name(g, &var->name, exported)));
+ const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));
+ render_const_val(g, var->value, mangled_name);
+ render_const_val_global(g, var->value, mangled_name);
global_value = var->value->global_refs->llvm_global;
if (exported) {
@@ -5176,16 +5248,24 @@ static void define_builtin_types(CodeGen *g) {
}
{
- TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
+ TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorSet);
buf_init_from_str(&entry->name, "error");
+ entry->data.error_set.err_count = UINT32_MAX;
// TODO allow overriding this type and keep track of max value and emit an
// error if there are too many errors declared
g->err_tag_type = g->builtin_types.entry_u16;
- g->builtin_types.entry_pure_error = entry;
+ g->builtin_types.entry_global_error_set = entry;
entry->type_ref = g->err_tag_type->type_ref;
- entry->di_type = g->err_tag_type->di_type;
+
+ entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
+ ZigLLVMTag_DW_enumeration_type(), "error",
+ ZigLLVMCompileUnitToScope(g->compile_unit), nullptr, 0);
+
+ // reserve index 0 to indicate no error
+ g->err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0));
+ g->errors_by_index.append(nullptr);
g->primitive_type_table.put(&entry->name, entry);
}
@@ -5815,7 +5895,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry
case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
zig_unreachable();
case TypeTableEntryIdVoid:
case TypeTableEntryIdUnreachable:
@@ -5988,7 +6068,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
return;
}
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
zig_panic("TODO implement get_c_type for more types");
case TypeTableEntryIdInvalid:
@@ -6155,7 +6235,7 @@ static void gen_h_file(CodeGen *g) {
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
@@ -6265,3 +6345,4 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
}
return pkg;
}
+
diff --git a/src/codegen.hpp b/src/codegen.hpp
index b29cadee55..a7a4b748c4 100644
--- a/src/codegen.hpp
+++ b/src/codegen.hpp
@@ -36,6 +36,7 @@ void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir);
void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker);
void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);
void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
+void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib);
LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib);
void codegen_add_framework(CodeGen *codegen, const char *name);
void codegen_add_rpath(CodeGen *codegen, const char *name);
diff --git a/src/ir.cpp b/src/ir.cpp
index ae8ef00f2f..2bb40c7e15 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -45,6 +45,59 @@ static LVal make_lval_addr(bool is_const, bool is_volatile) {
return { true, is_const, is_volatile };
}
+enum ConstCastResultId {
+ ConstCastResultIdOk,
+ ConstCastResultIdErrSet,
+ ConstCastResultIdErrSetGlobal,
+ ConstCastResultIdPointerChild,
+ ConstCastResultIdSliceChild,
+ ConstCastResultIdNullableChild,
+ ConstCastResultIdErrorUnionPayload,
+ ConstCastResultIdErrorUnionErrorSet,
+ ConstCastResultIdFnAlign,
+ ConstCastResultIdFnCC,
+ ConstCastResultIdFnVarArgs,
+ ConstCastResultIdFnIsGeneric,
+ ConstCastResultIdFnReturnType,
+ ConstCastResultIdFnArgCount,
+ ConstCastResultIdFnGenericArgCount,
+ ConstCastResultIdFnArg,
+ ConstCastResultIdFnArgNoAlias,
+ ConstCastResultIdType,
+ ConstCastResultIdUnresolvedInferredErrSet,
+};
+
+struct ConstCastErrSetMismatch {
+ ZigList missing_errors;
+};
+
+struct ConstCastOnly;
+
+struct ConstCastArg {
+ size_t arg_index;
+ ConstCastOnly *child;
+};
+
+struct ConstCastArgNoAlias {
+ size_t arg_index;
+};
+
+struct ConstCastOnly {
+ ConstCastResultId id;
+ union {
+ ConstCastErrSetMismatch error_set;
+ ConstCastOnly *pointer_child;
+ ConstCastOnly *slice_child;
+ ConstCastOnly *nullable_child;
+ ConstCastOnly *error_union_payload;
+ ConstCastOnly *error_union_error_set;
+ ConstCastOnly *return_type;
+ ConstCastArg fn_arg;
+ ConstCastArgNoAlias arg_no_alias;
+ } data;
+};
+
+
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval);
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
@@ -580,6 +633,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace
return IrInstructionIdErrorReturnTrace;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) {
+ return IrInstructionIdErrorUnion;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2326,6 +2383,19 @@ static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope,
return &instruction->base;
}
+static IrInstruction *ir_build_error_union(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *err_set, IrInstruction *payload)
+{
+ IrInstructionErrorUnion *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->err_set = err_set;
+ instruction->payload = payload;
+
+ ir_ref_instruction(err_set, irb->current_basic_block);
+ ir_ref_instruction(payload, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -2800,6 +2870,23 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
}
+static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
+ assert(node->type == NodeTypeBinOpExpr);
+
+ AstNode *op1_node = node->data.bin_op_expr.op1;
+ AstNode *op2_node = node->data.bin_op_expr.op2;
+
+ IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope);
+ if (err_set == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+
+ IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope);
+ if (payload == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+
+ return ir_build_error_union(irb, parent_scope, node, err_set, payload);
+}
+
static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
@@ -2835,10 +2922,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);
case BinOpTypeAssignBitOr:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);
- case BinOpTypeAssignBoolAnd:
- return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd);
- case BinOpTypeAssignBoolOr:
- return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr);
+ case BinOpTypeAssignMergeErrorSets:
+ return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets);
case BinOpTypeBoolOr:
return ir_gen_bool_or(irb, scope, node);
case BinOpTypeBoolAnd:
@@ -2885,8 +2970,12 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
case BinOpTypeArrayMult:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
+ case BinOpTypeMergeErrorSets:
+ return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
case BinOpTypeUnwrapMaybe:
return ir_gen_maybe_ok_or(irb, scope, node);
+ case BinOpTypeErrorUnion:
+ return ir_gen_error_union(irb, scope, node);
}
zig_unreachable();
}
@@ -3990,8 +4079,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval);
case PrefixOpMaybe:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval);
- case PrefixOpError:
- return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpError), lval);
case PrefixOpUnwrapMaybe:
return ir_gen_maybe_assert_ok(irb, scope, node, lval);
}
@@ -4713,12 +4800,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse");
IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
- IrInstruction *is_comptime;
- if (ir_should_inline(irb->exec, scope)) {
- is_comptime = ir_build_const_bool(irb, scope, node, true);
- } else {
- is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
- }
+ bool force_comptime = ir_should_inline(irb->exec, scope);
+ IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
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 *
if (var_symbol) {
IrInstruction *var_type = nullptr;
bool is_shadowable = false;
+ IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, err_val);
VariableTableEntry *var = ir_create_var(irb, node, scope,
- var_symbol, var_is_const, var_is_const, is_shadowable, is_comptime);
+ var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime);
IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false);
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
static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeErrorType);
- return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error);
+ return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set);
}
static 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
Scope *err_scope;
if (var_node) {
assert(var_node->type == NodeTypeSymbol);
- IrInstruction *var_type = ir_build_const_type(irb, parent_scope, node,
- irb->codegen->builtin_types.entry_pure_error);
Buf *var_name = var_node->data.symbol_expr.symbol;
bool is_const = true;
bool is_shadowable = false;
@@ -5258,7 +5340,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
is_const, is_const, is_shadowable, is_comptime);
err_scope = var->child_scope;
IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
- ir_build_var_decl(irb, err_scope, var_node, var, var_type, nullptr, err_val);
+ ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val);
} else {
err_scope = parent_scope;
}
@@ -5348,6 +5430,135 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
return ir_build_const_type(irb, parent_scope, node, container_type);
}
+// errors should be populated with set1's values
+static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, TypeTableEntry *set1, TypeTableEntry *set2) {
+ assert(set1->id == TypeTableEntryIdErrorSet);
+ assert(set2->id == TypeTableEntryIdErrorSet);
+
+ TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
+ buf_resize(&err_set_type->name, 0);
+ buf_appendf(&err_set_type->name, "error{");
+
+ for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) {
+ assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]);
+ }
+
+ uint32_t count = set1->data.error_set.err_count;
+ for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
+ if (errors[error_entry->value] == nullptr) {
+ count += 1;
+ }
+ }
+
+ err_set_type->is_copyable = true;
+ err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref;
+ err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
+ err_set_type->data.error_set.err_count = count;
+ err_set_type->data.error_set.errors = allocate(count);
+
+ for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
+ buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
+ err_set_type->data.error_set.errors[i] = error_entry;
+ }
+
+ uint32_t index = set1->data.error_set.err_count;
+ for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
+ if (errors[error_entry->value] == nullptr) {
+ errors[error_entry->value] = error_entry;
+ buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
+ err_set_type->data.error_set.errors[index] = error_entry;
+ index += 1;
+ }
+ }
+ assert(index == count);
+ assert(count != 0);
+
+ buf_appendf(&err_set_type->name, "}");
+
+ g->error_di_types.append(&err_set_type->di_type);
+
+ return err_set_type;
+
+}
+
+static TypeTableEntry *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node,
+ ErrorTableEntry *err_entry)
+{
+ TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
+ buf_resize(&err_set_type->name, 0);
+ buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name));
+ err_set_type->is_copyable = true;
+ err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref;
+ err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
+ err_set_type->data.error_set.err_count = 1;
+ err_set_type->data.error_set.errors = allocate(1);
+
+ g->error_di_types.append(&err_set_type->di_type);
+
+ err_set_type->data.error_set.errors[0] = err_entry;
+
+ return err_set_type;
+}
+
+static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
+ assert(node->type == NodeTypeErrorSetDecl);
+
+ uint32_t err_count = node->data.err_set_decl.decls.length;
+
+ Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node);
+ TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
+ buf_init_from_buf(&err_set_type->name, type_name);
+ err_set_type->is_copyable = true;
+ err_set_type->data.error_set.err_count = err_count;
+
+ if (err_count == 0) {
+ err_set_type->zero_bits = true;
+ err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type;
+ } else {
+ err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
+ err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
+ irb->codegen->error_di_types.append(&err_set_type->di_type);
+ err_set_type->data.error_set.errors = allocate(err_count);
+ }
+
+ ErrorTableEntry **errors = allocate(irb->codegen->errors_by_index.length + err_count);
+
+ for (uint32_t i = 0; i < err_count; i += 1) {
+ AstNode *symbol_node = node->data.err_set_decl.decls.at(i);
+ assert(symbol_node->type == NodeTypeSymbol);
+ Buf *err_name = symbol_node->data.symbol_expr.symbol;
+ ErrorTableEntry *err = allocate(1);
+ err->decl_node = symbol_node;
+ buf_init_from_buf(&err->name, err_name);
+
+ auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
+ if (existing_entry) {
+ err->value = existing_entry->value->value;
+ } else {
+ size_t error_value_count = irb->codegen->errors_by_index.length;
+ assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
+ err->value = error_value_count;
+ irb->codegen->errors_by_index.append(err);
+ irb->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(irb->codegen->dbuilder,
+ buf_ptr(err_name), error_value_count));
+ }
+ err_set_type->data.error_set.errors[i] = err;
+
+ ErrorTableEntry *prev_err = errors[err->value];
+ if (prev_err != nullptr) {
+ ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
+ add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here"));
+ return irb->codegen->invalid_instruction;
+ }
+ errors[err->value] = err;
+ }
+ free(errors);
+ return ir_build_const_type(irb, parent_scope, node, err_set_type);
+}
+
static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeFnProto);
@@ -5401,7 +5612,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
case NodeTypeStructField:
case NodeTypeFnDef:
case NodeTypeFnDecl:
- case NodeTypeErrorValueDecl:
case NodeTypeTestDecl:
zig_unreachable();
case NodeTypeBlock:
@@ -5482,6 +5692,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
case NodeTypeFnProto:
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);
+ case NodeTypeErrorSetDecl:
+ return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval);
}
zig_unreachable();
}
@@ -6287,6 +6499,274 @@ static bool slice_is_const(TypeTableEntry *type) {
return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
}
+static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) {
+ assert(err_set_type->id == TypeTableEntryIdErrorSet);
+ FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn;
+ if (infer_fn != nullptr) {
+ if (infer_fn->anal_state == FnAnalStateInvalid) {
+ return false;
+ } else if (infer_fn->anal_state == FnAnalStateReady) {
+ analyze_fn_body(ira->codegen, infer_fn);
+ if (err_set_type->data.error_set.infer_fn != nullptr) {
+ assert(ira->codegen->errors.length != 0);
+ return false;
+ }
+ } else {
+ ir_add_error_node(ira, source_node,
+ buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
+ buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
+ return false;
+ }
+ }
+ return true;
+}
+
+static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry *set1, TypeTableEntry *set2,
+ AstNode *source_node)
+{
+ assert(set1->id == TypeTableEntryIdErrorSet);
+ assert(set2->id == TypeTableEntryIdErrorSet);
+
+ if (!resolve_inferred_error_set(ira, set1, source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (!resolve_inferred_error_set(ira, set2, source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (type_is_global_error_set(set1)) {
+ return set2;
+ }
+ if (type_is_global_error_set(set2)) {
+ return set1;
+ }
+ ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length);
+ for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ ZigList intersection_list = {};
+
+ TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
+ buf_resize(&err_set_type->name, 0);
+ buf_appendf(&err_set_type->name, "error{");
+
+ for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
+ ErrorTableEntry *existing_entry = errors[error_entry->value];
+ if (existing_entry != nullptr) {
+ intersection_list.append(existing_entry);
+ buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name));
+ }
+ }
+ free(errors);
+
+ err_set_type->is_copyable = true;
+ err_set_type->type_ref = ira->codegen->builtin_types.entry_global_error_set->type_ref;
+ err_set_type->di_type = ira->codegen->builtin_types.entry_global_error_set->di_type;
+ err_set_type->data.error_set.err_count = intersection_list.length;
+ err_set_type->data.error_set.errors = intersection_list.items;
+ err_set_type->zero_bits = intersection_list.length == 0;
+
+ buf_appendf(&err_set_type->name, "}");
+
+ ira->codegen->error_di_types.append(&err_set_type->di_type);
+
+ return err_set_type;
+}
+
+
+static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type,
+ TypeTableEntry *actual_type, AstNode *source_node)
+{
+ CodeGen *g = ira->codegen;
+ ConstCastOnly result = {};
+ result.id = ConstCastResultIdOk;
+
+ if (expected_type == actual_type)
+ return result;
+
+ // pointer const
+ if (expected_type->id == TypeTableEntryIdPointer &&
+ actual_type->id == TypeTableEntryIdPointer &&
+ (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) &&
+ (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) &&
+ actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset &&
+ actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count &&
+ actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment)
+ {
+ ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node);
+ if (child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdPointerChild;
+ result.data.pointer_child = allocate_nonzero(1);
+ *result.data.pointer_child = child;
+ }
+ return result;
+ }
+
+ // slice const
+ if (is_slice(expected_type) && is_slice(actual_type)) {
+ TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
+ TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;
+ if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) &&
+ (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) &&
+ actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset &&
+ actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count &&
+ actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment)
+ {
+ ConstCastOnly child = types_match_const_cast_only(ira, expected_ptr_type->data.pointer.child_type,
+ actual_ptr_type->data.pointer.child_type, source_node);
+ if (child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdSliceChild;
+ result.data.slice_child = allocate_nonzero(1);
+ *result.data.slice_child = child;
+ }
+ return result;
+ }
+ }
+
+ // maybe
+ if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) {
+ ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node);
+ if (child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdNullableChild;
+ result.data.nullable_child = allocate_nonzero(1);
+ *result.data.nullable_child = child;
+ }
+ return result;
+ }
+
+ // error union
+ if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) {
+ 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);
+ if (payload_child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdErrorUnionPayload;
+ result.data.error_union_payload = allocate_nonzero(1);
+ *result.data.error_union_payload = payload_child;
+ return result;
+ }
+ 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);
+ if (error_set_child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdErrorUnionErrorSet;
+ result.data.error_union_error_set = allocate_nonzero(1);
+ *result.data.error_union_error_set = error_set_child;
+ return result;
+ }
+ return result;
+ }
+
+ // error set
+ if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) {
+ TypeTableEntry *contained_set = actual_type;
+ TypeTableEntry *container_set = expected_type;
+
+ // if the container set is inferred, then this will always work.
+ if (container_set->data.error_set.infer_fn != nullptr) {
+ return result;
+ }
+ // if the container set is the global one, it will always work.
+ if (type_is_global_error_set(container_set)) {
+ return result;
+ }
+
+ if (!resolve_inferred_error_set(ira, contained_set, source_node)) {
+ result.id = ConstCastResultIdUnresolvedInferredErrSet;
+ return result;
+ }
+
+ if (type_is_global_error_set(contained_set)) {
+ result.id = ConstCastResultIdErrSetGlobal;
+ return result;
+ }
+
+ ErrorTableEntry **errors = allocate(g->errors_by_index.length);
+ for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ if (result.id == ConstCastResultIdOk) {
+ result.id = ConstCastResultIdErrSet;
+ }
+ result.data.error_set.missing_errors.append(contained_error_entry);
+ }
+ }
+ free(errors);
+ return result;
+ }
+
+ // fn
+ if (expected_type->id == TypeTableEntryIdFn &&
+ actual_type->id == TypeTableEntryIdFn)
+ {
+ if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
+ result.id = ConstCastResultIdFnAlign;
+ return result;
+ }
+ if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
+ result.id = ConstCastResultIdFnCC;
+ return result;
+ }
+ if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
+ result.id = ConstCastResultIdFnVarArgs;
+ return result;
+ }
+ if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
+ result.id = ConstCastResultIdFnIsGeneric;
+ return result;
+ }
+ if (!expected_type->data.fn.is_generic &&
+ actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable)
+ {
+ 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);
+ if (child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdFnReturnType;
+ result.data.return_type = allocate_nonzero(1);
+ *result.data.return_type = child;
+ }
+ return result;
+ }
+ if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
+ result.id = ConstCastResultIdFnArgCount;
+ return result;
+ }
+ if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
+ result.id = ConstCastResultIdFnGenericArgCount;
+ return result;
+ }
+ assert(expected_type->data.fn.is_generic ||
+ expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count);
+ for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) {
+ // note it's reversed for parameters
+ FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
+ FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
+
+ ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node);
+ if (arg_child.id != ConstCastResultIdOk) {
+ result.id = ConstCastResultIdFnArg;
+ result.data.fn_arg.arg_index = i;
+ result.data.fn_arg.child = allocate_nonzero(1);
+ *result.data.fn_arg.child = arg_child;
+ return result;
+ }
+
+ if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
+ result.id = ConstCastResultIdFnArgNoAlias;
+ result.data.arg_no_alias.arg_index = i;
+ return result;
+ }
+ }
+ return result;
+ }
+
+ result.id = ConstCastResultIdType;
+ return result;
+}
+
enum ImplicitCastMatchResult {
ImplicitCastMatchResultNo,
ImplicitCastMatchResultYes,
@@ -6296,10 +6776,46 @@ enum ImplicitCastMatchResult {
static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type,
TypeTableEntry *actual_type, IrInstruction *value)
{
- if (types_match_const_cast_only(expected_type, actual_type)) {
+ AstNode *source_node = value->source_node;
+ ConstCastOnly const_cast_result = types_match_const_cast_only(ira, expected_type, actual_type, source_node);
+ if (const_cast_result.id == ConstCastResultIdOk) {
return ImplicitCastMatchResultYes;
}
+ // if we got here with error sets, make an error showing the incompatibilities
+ ZigList *missing_errors = nullptr;
+ if (const_cast_result.id == ConstCastResultIdErrSet) {
+ missing_errors = &const_cast_result.data.error_set.missing_errors;
+ }
+ if (const_cast_result.id == ConstCastResultIdErrorUnionErrorSet) {
+ if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSet) {
+ missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors;
+ } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) {
+ ErrorMsg *msg = ir_add_error(ira, value,
+ buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
+ add_error_note(ira->codegen, msg, value->source_node,
+ buf_sprintf("unable to cast global error set into smaller set"));
+ return ImplicitCastMatchResultReportedError;
+ }
+ } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) {
+ ErrorMsg *msg = ir_add_error(ira, value,
+ buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
+ add_error_note(ira->codegen, msg, value->source_node,
+ buf_sprintf("unable to cast global error set into smaller set"));
+ return ImplicitCastMatchResultReportedError;
+ }
+ if (missing_errors != nullptr) {
+ ErrorMsg *msg = ir_add_error(ira, value,
+ buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
+ for (size_t i = 0; i < missing_errors->length; i += 1) {
+ ErrorTableEntry *error_entry = missing_errors->at(i);
+ add_error_note(ira->codegen, msg, error_entry->decl_node,
+ buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name)));
+ }
+
+ return ImplicitCastMatchResultReportedError;
+ }
+
// implicit conversion from anything to var
if (expected_type->id == TypeTableEntryIdVar) {
return ImplicitCastMatchResultYes;
@@ -6319,25 +6835,25 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
return ImplicitCastMatchResultYes;
}
- // implicit T to %T
+ // implicit T to U!T
if (expected_type->id == TypeTableEntryIdErrorUnion &&
- ir_types_match_with_implicit_cast(ira, expected_type->data.error.child_type, actual_type, value))
+ ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type, actual_type, value))
{
return ImplicitCastMatchResultYes;
}
- // implicit conversion from pure error to error union type
+ // implicit conversion from error set to error union type
if (expected_type->id == TypeTableEntryIdErrorUnion &&
- actual_type->id == TypeTableEntryIdPureError)
+ actual_type->id == TypeTableEntryIdErrorSet)
{
return ImplicitCastMatchResultYes;
}
- // implicit conversion from T to %?T
+ // implicit conversion from T to U!?T
if (expected_type->id == TypeTableEntryIdErrorUnion &&
- expected_type->data.error.child_type->id == TypeTableEntryIdMaybe &&
+ expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
ir_types_match_with_implicit_cast(ira,
- expected_type->data.error.child_type->data.maybe.child_type,
+ expected_type->data.error_union.payload_type->data.maybe.child_type,
actual_type, value))
{
return ImplicitCastMatchResultYes;
@@ -6374,7 +6890,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
return ImplicitCastMatchResultYes;
}
@@ -6392,7 +6908,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
TypeTableEntry *array_type = actual_type->data.pointer.child_type;
if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, array_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
return ImplicitCastMatchResultYes;
}
@@ -6408,7 +6924,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
return ImplicitCastMatchResultYes;
}
@@ -6423,7 +6939,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
return ImplicitCastMatchResultYes;
}
@@ -6503,7 +7019,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
// implicitly take a const pointer to something
if (!type_requires_comptime(actual_type)) {
TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
- if (types_match_const_cast_only(expected_type, const_ptr_actual)) {
+ if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) {
return ImplicitCastMatchResultYes;
}
}
@@ -6511,13 +7027,39 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
return ImplicitCastMatchResultNo;
}
+static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) {
+ size_t old_errors_count = *errors_count;
+ *errors_count = g->errors_by_index.length;
+ *errors = reallocate(*errors, old_errors_count, *errors_count);
+}
+
static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0];
if (type_is_invalid(prev_inst->value.type)) {
return ira->codegen->builtin_types.entry_invalid;
}
- bool any_are_pure_error = (prev_inst->value.type->id == TypeTableEntryIdPureError);
+ ErrorTableEntry **errors = nullptr;
+ size_t errors_count = 0;
+ TypeTableEntry *err_set_type = nullptr;
+ if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
+ if (type_is_global_error_set(prev_inst->value.type)) {
+ err_set_type = ira->codegen->builtin_types.entry_global_error_set;
+ } else {
+ err_set_type = prev_inst->value.type;
+ if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ update_errors_helper(ira->codegen, &errors, &errors_count);
+
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ }
+ }
+
bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit);
bool convert_to_const_slice = false;
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
continue;
}
- if (prev_type->id == TypeTableEntryIdPureError) {
- prev_inst = cur_inst;
- continue;
- }
-
if (prev_type->id == TypeTableEntryIdNullLit) {
prev_inst = cur_inst;
continue;
}
- if (cur_type->id == TypeTableEntryIdPureError) {
- if (prev_type->id == TypeTableEntryIdArray) {
- convert_to_const_slice = true;
- }
- any_are_pure_error = true;
- continue;
- }
-
if (cur_type->id == TypeTableEntryIdNullLit) {
any_are_null = true;
continue;
}
- if (types_match_const_cast_only(prev_type, cur_type)) {
+ if (prev_type->id == TypeTableEntryIdErrorSet) {
+ assert(err_set_type != nullptr);
+ if (cur_type->id == TypeTableEntryIdErrorSet) {
+ if (type_is_global_error_set(err_set_type)) {
+ continue;
+ }
+ if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (type_is_global_error_set(cur_type)) {
+ err_set_type = ira->codegen->builtin_types.entry_global_error_set;
+ prev_inst = cur_inst;
+ continue;
+ }
+
+ // number of declared errors might have increased now
+ update_errors_helper(ira->codegen, &errors, &errors_count);
+
+ // if err_set_type is a superset of cur_type, keep err_set_type.
+ // if cur_type is a superset of err_set_type, switch err_set_type to cur_type
+ bool prev_is_superset = true;
+ for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ prev_is_superset = false;
+ break;
+ }
+ }
+ if (prev_is_superset) {
+ continue;
+ }
+
+ // unset everything in errors
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+ errors[error_entry->value] = nullptr;
+ }
+ for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
+ assert(errors[i] == nullptr);
+ }
+ for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ bool cur_is_superset = true;
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ cur_is_superset = false;
+ break;
+ }
+ }
+ if (cur_is_superset) {
+ err_set_type = cur_type;
+ prev_inst = cur_inst;
+ assert(errors != nullptr);
+ continue;
+ }
+
+ // neither of them are supersets. so we invent a new error set type that is a union of both of them
+ err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type);
+ assert(errors != nullptr);
+ continue;
+ } else if (cur_type->id == TypeTableEntryIdErrorUnion) {
+ if (type_is_global_error_set(err_set_type)) {
+ prev_inst = cur_inst;
+ continue;
+ }
+ TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
+ if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (type_is_global_error_set(cur_err_set_type)) {
+ err_set_type = ira->codegen->builtin_types.entry_global_error_set;
+ prev_inst = cur_inst;
+ continue;
+ }
+
+ update_errors_helper(ira->codegen, &errors, &errors_count);
+
+ // test if err_set_type is a subset of cur_type's error set
+ // unset everything in errors
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+ errors[error_entry->value] = nullptr;
+ }
+ for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
+ assert(errors[i] == nullptr);
+ }
+ for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ bool cur_is_superset = true;
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ cur_is_superset = false;
+ break;
+ }
+ }
+ if (cur_is_superset) {
+ err_set_type = cur_err_set_type;
+ prev_inst = cur_inst;
+ assert(errors != nullptr);
+ continue;
+ }
+
+ // not a subset. invent new error set type, union of both of them
+ err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type);
+ prev_inst = cur_inst;
+ assert(errors != nullptr);
+ continue;
+ } else {
+ prev_inst = cur_inst;
+ continue;
+ }
+ }
+
+ if (cur_type->id == TypeTableEntryIdErrorSet) {
+ if (prev_type->id == TypeTableEntryIdArray) {
+ convert_to_const_slice = true;
+ }
+ if (type_is_global_error_set(cur_type)) {
+ err_set_type = ira->codegen->builtin_types.entry_global_error_set;
+ continue;
+ }
+ if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) {
+ continue;
+ }
+ if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ update_errors_helper(ira->codegen, &errors, &errors_count);
+
+ if (err_set_type == nullptr) {
+ if (prev_type->id == TypeTableEntryIdErrorUnion) {
+ err_set_type = prev_type->data.error_union.err_set_type;
+ } else {
+ err_set_type = cur_type;
+ }
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ if (err_set_type == cur_type) {
+ continue;
+ }
+ }
+ // check if the cur type error set is a subset
+ bool prev_is_superset = true;
+ for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ prev_is_superset = false;
+ break;
+ }
+ }
+ if (prev_is_superset) {
+ continue;
+ }
+ // not a subset. invent new error set type, union of both of them
+ err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type);
+ assert(errors != nullptr);
continue;
}
- if (types_match_const_cast_only(cur_type, prev_type)) {
+ if (prev_type->id == TypeTableEntryIdErrorUnion && cur_type->id == TypeTableEntryIdErrorUnion) {
+ TypeTableEntry *prev_payload_type = prev_type->data.error_union.payload_type;
+ TypeTableEntry *cur_payload_type = cur_type->data.error_union.payload_type;
+
+ bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type,
+ source_node).id == ConstCastResultIdOk;
+ bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type,
+ source_node).id == ConstCastResultIdOk;
+
+ if (const_cast_prev || const_cast_cur) {
+ if (const_cast_cur) {
+ prev_inst = cur_inst;
+ }
+
+ TypeTableEntry *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type;
+ TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
+
+ if (!resolve_inferred_error_set(ira, prev_err_set_type, cur_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (type_is_global_error_set(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) {
+ err_set_type = ira->codegen->builtin_types.entry_global_error_set;
+ continue;
+ }
+
+ update_errors_helper(ira->codegen, &errors, &errors_count);
+
+ if (err_set_type == nullptr) {
+ err_set_type = prev_err_set_type;
+ for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ }
+ bool prev_is_superset = true;
+ for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ prev_is_superset = false;
+ break;
+ }
+ }
+ if (prev_is_superset) {
+ continue;
+ }
+ // unset all the errors
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+ errors[error_entry->value] = nullptr;
+ }
+ for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
+ assert(errors[i] == nullptr);
+ }
+ for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ bool cur_is_superset = true;
+ for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i];
+ ErrorTableEntry *error_entry = errors[contained_error_entry->value];
+ if (error_entry == nullptr) {
+ cur_is_superset = false;
+ break;
+ }
+ }
+ if (cur_is_superset) {
+ err_set_type = cur_err_set_type;
+ continue;
+ }
+
+ err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type);
+ continue;
+ }
+ }
+
+ if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {
+ continue;
+ }
+
+ if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) {
prev_inst = cur_inst;
continue;
}
@@ -6588,26 +7376,41 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
}
if (prev_type->id == TypeTableEntryIdErrorUnion &&
- types_match_const_cast_only(prev_type->data.error.child_type, cur_type))
+ types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node).id == ConstCastResultIdOk)
{
continue;
}
if (cur_type->id == TypeTableEntryIdErrorUnion &&
- types_match_const_cast_only(cur_type->data.error.child_type, prev_type))
+ types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk)
{
+ if (err_set_type != nullptr) {
+ TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
+ if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) {
+ err_set_type = ira->codegen->builtin_types.entry_global_error_set;
+ prev_inst = cur_inst;
+ continue;
+ }
+
+ update_errors_helper(ira->codegen, &errors, &errors_count);
+
+ err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type);
+ }
prev_inst = cur_inst;
continue;
}
if (prev_type->id == TypeTableEntryIdMaybe &&
- types_match_const_cast_only(prev_type->data.maybe.child_type, cur_type))
+ types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk)
{
continue;
}
if (cur_type->id == TypeTableEntryIdMaybe &&
- types_match_const_cast_only(cur_type->data.maybe.child_type, prev_type))
+ types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
continue;
@@ -6645,7 +7448,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
cur_type->data.array.len != prev_type->data.array.len &&
- types_match_const_cast_only(cur_type->data.array.child_type, prev_type->data.array.child_type))
+ types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
convert_to_const_slice = true;
prev_inst = cur_inst;
@@ -6654,7 +7457,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
cur_type->data.array.len != prev_type->data.array.len &&
- types_match_const_cast_only(prev_type->data.array.child_type, cur_type->data.array.child_type))
+ types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
convert_to_const_slice = true;
continue;
@@ -6663,8 +7466,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&
(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
cur_type->data.array.len == 0) &&
- types_match_const_cast_only(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
- cur_type->data.array.child_type))
+ types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
+ cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
convert_to_const_slice = false;
continue;
@@ -6673,8 +7476,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&
(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
prev_type->data.array.len == 0) &&
- types_match_const_cast_only(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
- prev_type->data.array.child_type))
+ types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
+ prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
prev_inst = cur_inst;
convert_to_const_slice = false;
@@ -6714,30 +7517,37 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
return ira->codegen->builtin_types.entry_invalid;
}
+
+ free(errors);
+
if (convert_to_const_slice) {
assert(prev_inst->value.type->id == TypeTableEntryIdArray);
TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true);
TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type);
- if (any_are_pure_error) {
- return get_error_type(ira->codegen, slice_type);
+ if (err_set_type != nullptr) {
+ return get_error_union_type(ira->codegen, err_set_type, slice_type);
} else {
return slice_type;
}
- } else if (any_are_pure_error && prev_inst->value.type->id != TypeTableEntryIdPureError) {
- if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
- prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
- {
- ir_add_error_node(ira, source_node,
- buf_sprintf("unable to make error union out of number literal"));
- return ira->codegen->builtin_types.entry_invalid;
- } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) {
- ir_add_error_node(ira, source_node,
- buf_sprintf("unable to make error union out of null literal"));
- return ira->codegen->builtin_types.entry_invalid;
- } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
- return prev_inst->value.type;
+ } else if (err_set_type != nullptr) {
+ if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
+ return err_set_type;
} else {
- return get_error_type(ira->codegen, prev_inst->value.type);
+ if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
+ prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
+ {
+ ir_add_error_node(ira, source_node,
+ buf_sprintf("unable to make error union out of number literal"));
+ return ira->codegen->builtin_types.entry_invalid;
+ } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) {
+ ir_add_error_node(ira, source_node,
+ buf_sprintf("unable to make error union out of null literal"));
+ return ira->codegen->builtin_types.entry_invalid;
+ } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
+ return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
+ } else {
+ return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
+ }
}
} else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) {
if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
@@ -6783,6 +7593,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
switch (cast_op) {
case CastOpNoCast:
zig_unreachable();
+ case CastOpErrSet:
+ zig_panic("TODO");
case CastOpNoop:
{
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
assert(wanted_type->id == TypeTableEntryIdErrorUnion);
if (instr_is_comptime(value)) {
- TypeTableEntry *payload_type = wanted_type->data.error.child_type;
+ TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type;
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
if (type_is_invalid(casted_payload->value.type))
return ira->codegen->invalid_instruction;
@@ -7238,19 +8050,64 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction
return result;
}
-static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
- assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
+ TypeTableEntry *wanted_type)
+{
+ assert(value->value.type->id == TypeTableEntryIdErrorSet);
+ assert(wanted_type->id == TypeTableEntryIdErrorSet);
if (instr_is_comptime(value)) {
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
+ if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) {
+ return ira->codegen->invalid_instruction;
+ }
+ if (!type_is_global_error_set(wanted_type)) {
+ bool subset = false;
+ for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
+ if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) {
+ subset = true;
+ break;
+ }
+ }
+ if (!subset) {
+ ir_add_error(ira, source_instr,
+ buf_sprintf("error.%s not a member of error set '%s'",
+ buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+ }
+
+ IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb,
+ source_instr->scope, source_instr->source_node);
+ const_instruction->base.value.type = wanted_type;
+ const_instruction->base.value.special = ConstValSpecialStatic;
+ const_instruction->base.value.data.x_err_set = val->data.x_err_set;
+ return &const_instruction->base;
+ }
+
+ IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet);
+ result->value.type = wanted_type;
+ return result;
+}
+
+static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
+ assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+
+ IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type);
+
+ if (instr_is_comptime(casted_value)) {
+ ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad);
+ if (!val)
+ return ira->codegen->invalid_instruction;
+
IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb,
source_instr->scope, source_instr->source_node);
const_instruction->base.value.type = wanted_type;
const_instruction->base.value.special = ConstValSpecialStatic;
- const_instruction->base.value.data.x_err_union.err = val->data.x_pure_err;
+ const_instruction->base.value.data.x_err_union.err = val->data.x_err_set;
const_instruction->base.value.data.x_err_union.payload = nullptr;
return &const_instruction->base;
}
@@ -7630,9 +8487,12 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction
return result;
}
-static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) {
+static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
+ TypeTableEntry *wanted_type)
+{
assert(target->value.type->id == TypeTableEntryIdInt);
assert(!target->value.type->data.integral.is_signed);
+ assert(wanted_type->id == TypeTableEntryIdErrorSet);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
@@ -7640,26 +8500,55 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
- source_instr->source_node, ira->codegen->builtin_types.entry_pure_error);
+ source_instr->source_node, wanted_type);
- BigInt err_count;
- bigint_init_unsigned(&err_count, ira->codegen->error_decls.length);
- if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
- Buf *val_buf = buf_alloc();
- bigint_append_buf(val_buf, &val->data.x_bigint, 10);
- ir_add_error(ira, source_instr,
- buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
+ if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) {
return ira->codegen->invalid_instruction;
}
- size_t index = bigint_as_unsigned(&val->data.x_bigint);
- AstNode *error_decl_node = ira->codegen->error_decls.at(index);
- result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err;
- return result;
+ if (type_is_global_error_set(wanted_type)) {
+ BigInt err_count;
+ bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length);
+
+ if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
+ Buf *val_buf = buf_alloc();
+ bigint_append_buf(val_buf, &val->data.x_bigint, 10);
+ ir_add_error(ira, source_instr,
+ buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ size_t index = bigint_as_unsigned(&val->data.x_bigint);
+ result->value.data.x_err_set = ira->codegen->errors_by_index.at(index);
+ return result;
+ } else {
+ ErrorTableEntry *err = nullptr;
+ BigInt err_int;
+
+ for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
+ ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i];
+ bigint_init_unsigned(&err_int, this_err->value);
+ if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) {
+ err = this_err;
+ break;
+ }
+ }
+
+ if (err == nullptr) {
+ Buf *val_buf = buf_alloc();
+ bigint_append_buf(val_buf, &val->data.x_bigint, 10);
+ ir_add_error(ira, source_instr,
+ buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ result->value.data.x_err_set = err;
+ return result;
+ }
}
IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
- result->value.type = ira->codegen->builtin_types.entry_pure_error;
+ result->value.type = wanted_type;
return result;
}
@@ -7681,8 +8570,8 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
ErrorTableEntry *err;
if (err_type->id == TypeTableEntryIdErrorUnion) {
err = val->data.x_err_union.err;
- } else if (err_type->id == TypeTableEntryIdPureError) {
- err = val->data.x_pure_err;
+ } else if (err_type->id == TypeTableEntryIdErrorSet) {
+ err = val->data.x_err_set;
} else {
zig_unreachable();
}
@@ -7702,8 +8591,36 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
return result;
}
+ TypeTableEntry *err_set_type;
+ if (err_type->id == TypeTableEntryIdErrorUnion) {
+ err_set_type = err_type->data.error_union.err_set_type;
+ } else if (err_type->id == TypeTableEntryIdErrorSet) {
+ err_set_type = err_type;
+ } else {
+ zig_unreachable();
+ }
+ if (!type_is_global_error_set(err_set_type)) {
+ if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) {
+ return ira->codegen->invalid_instruction;
+ }
+ if (err_set_type->data.error_set.err_count == 0) {
+ IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, wanted_type);
+ result->value.type = wanted_type;
+ bigint_init_unsigned(&result->value.data.x_bigint, 0);
+ return result;
+ } else if (err_set_type->data.error_set.err_count == 1) {
+ IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, wanted_type);
+ result->value.type = wanted_type;
+ ErrorTableEntry *err = err_set_type->data.error_set.errors[0];
+ bigint_init_unsigned(&result->value.data.x_bigint, err->value);
+ return result;
+ }
+ }
+
BigInt bn;
- bigint_init_unsigned(&bn, ira->codegen->error_decls.length);
+ bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length);
if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
ir_add_error_node(ira, source_instr->source_node,
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
TypeTableEntry *wanted_type, IrInstruction *value)
{
TypeTableEntry *actual_type = value->value.type;
+ AstNode *source_node = source_instr->source_node;
if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
return ira->codegen->invalid_instruction;
@@ -7728,7 +8646,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return value;
// explicit match or non-const to const
- if (types_match_const_cast_only(wanted_type, actual_type)) {
+ if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) {
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
}
@@ -7748,6 +8666,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
}
+ // explicit error set cast
+ if (wanted_type->id == TypeTableEntryIdErrorSet &&
+ actual_type->id == TypeTableEntryIdErrorSet)
+ {
+ return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type);
+ }
+
// explicit cast from int to float
if (wanted_type->id == TypeTableEntryIdFloat &&
actual_type->id == TypeTableEntryIdInt)
@@ -7767,7 +8692,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
}
@@ -7785,7 +8710,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
TypeTableEntry *array_type = actual_type->data.pointer.child_type;
if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, array_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
}
@@ -7801,7 +8726,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
if (type_is_invalid(cast1->value.type))
@@ -7824,7 +8749,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
if (type_is_invalid(cast1->value.type))
@@ -7886,7 +8811,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
// explicit cast from child type of maybe type to maybe type
if (wanted_type->id == TypeTableEntryIdMaybe) {
- if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
+ if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) {
return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
actual_type->id == TypeTableEntryIdNumLitFloat)
@@ -7908,12 +8833,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
// explicit cast from child type of error type to error type
if (wanted_type->id == TypeTableEntryIdErrorUnion) {
- if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
+ if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) {
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
} else if (actual_type->id == TypeTableEntryIdNumLitInt ||
actual_type->id == TypeTableEntryIdNumLitFloat)
{
- if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type, true)) {
+ if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
} else {
return ira->codegen->invalid_instruction;
@@ -7923,16 +8848,16 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
// explicit cast from [N]T to %[]const T
if (wanted_type->id == TypeTableEntryIdErrorUnion &&
- is_slice(wanted_type->data.error.child_type) &&
+ is_slice(wanted_type->data.error_union.payload_type) &&
actual_type->id == TypeTableEntryIdArray)
{
TypeTableEntry *ptr_type =
- wanted_type->data.error.child_type->data.structure.fields[slice_ptr_index].type_entry;
+ wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
assert(ptr_type->id == TypeTableEntryIdPointer);
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
- types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type))
+ types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)
{
- IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error.child_type, value);
+ IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
@@ -7944,25 +8869,25 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
- // explicit cast from pure error to error union type
+ // explicit cast from error set to error union type
if (wanted_type->id == TypeTableEntryIdErrorUnion &&
- actual_type->id == TypeTableEntryIdPureError)
+ actual_type->id == TypeTableEntryIdErrorSet)
{
return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
}
// explicit cast from T to %?T
if (wanted_type->id == TypeTableEntryIdErrorUnion &&
- wanted_type->data.error.child_type->id == TypeTableEntryIdMaybe &&
+ wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&
actual_type->id != TypeTableEntryIdMaybe)
{
- TypeTableEntry *wanted_child_type = wanted_type->data.error.child_type->data.maybe.child_type;
- if (types_match_const_cast_only(wanted_child_type, actual_type) ||
+ TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
+ if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk ||
actual_type->id == TypeTableEntryIdNullLit ||
actual_type->id == TypeTableEntryIdNumLitInt ||
actual_type->id == TypeTableEntryIdNumLitFloat)
{
- IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error.child_type, value);
+ IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
if (type_is_invalid(cast1->value.type))
return ira->codegen->invalid_instruction;
@@ -8031,21 +8956,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
}
- // explicit cast from %void to integer type which can fit it
+ // explicit cast from T!void to integer type which can fit it
bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
- !type_has_bits(actual_type->data.error.child_type);
- bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError;
- if ((actual_type_is_void_err || actual_type_is_pure_err) &&
- wanted_type->id == TypeTableEntryIdInt)
- {
+ !type_has_bits(actual_type->data.error_union.payload_type);
+ bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet;
+ if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) {
return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);
}
- // explicit cast from integer to pure error
- if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt &&
+ // explicit cast from integer to error set
+ if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt &&
!actual_type->data.integral.is_signed)
{
- return ir_analyze_int_to_err(ira, source_instr, value);
+ return ir_analyze_int_to_err(ira, source_instr, value, wanted_type);
}
// explicit cast from integer to enum type with no payload
@@ -8109,7 +9032,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
// explicit cast from something to const pointer of it
if (!type_requires_comptime(actual_type)) {
TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
- if (types_match_const_cast_only(wanted_type, const_ptr_actual)) {
+ if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) {
return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
}
}
@@ -8471,6 +9394,7 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrInstruction *op1 = bin_op_instruction->op1->other;
IrInstruction *op2 = bin_op_instruction->op2->other;
+ AstNode *source_node = bin_op_instruction->base.source_node;
IrBinOp op_id = bin_op_instruction->op_id;
bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
@@ -8503,7 +9427,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
}
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
- bin_op_instruction->base.source_node, maybe_op);
+ source_node, maybe_op);
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
if (op_id == IrBinOpCmpEq) {
@@ -8514,8 +9438,88 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
return ira->codegen->builtin_types.entry_bool;
}
+ if (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet) {
+ if (!is_equality_cmp) {
+ ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ TypeTableEntry *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node);
+ if (type_is_invalid(intersect_type)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (!resolve_inferred_error_set(ira, intersect_type, source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ // exception if one of the operators has the type of the empty error set, we allow the comparison
+ // (and make it comptime known)
+ // this is a function which is evaluated at comptime and returns an inferred error set will have an empty
+ // error set.
+ if (op1->value.type->data.error_set.err_count == 0 || op2->value.type->data.error_set.err_count == 0) {
+ bool are_equal = false;
+ bool answer;
+ if (op_id == IrBinOpCmpEq) {
+ answer = are_equal;
+ } else if (op_id == IrBinOpCmpNotEq) {
+ answer = !are_equal;
+ } else {
+ zig_unreachable();
+ }
+ ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
+ out_val->data.x_bool = answer;
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
+ if (!type_is_global_error_set(intersect_type)) {
+ if (intersect_type->data.error_set.err_count == 0) {
+ ir_add_error_node(ira, source_node,
+ buf_sprintf("error sets '%s' and '%s' have no common errors",
+ buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (op1->value.type->data.error_set.err_count == 1 && op2->value.type->data.error_set.err_count == 1) {
+ bool are_equal = true;
+ bool answer;
+ if (op_id == IrBinOpCmpEq) {
+ answer = are_equal;
+ } else if (op_id == IrBinOpCmpNotEq) {
+ answer = !are_equal;
+ } else {
+ zig_unreachable();
+ }
+ ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
+ out_val->data.x_bool = answer;
+ return ira->codegen->builtin_types.entry_bool;
+ }
+ }
+
+ ConstExprValue *op1_val = &op1->value;
+ ConstExprValue *op2_val = &op2->value;
+ if (value_is_comptime(op1_val) && value_is_comptime(op2_val)) {
+ bool answer;
+ bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value;
+ if (op_id == IrBinOpCmpEq) {
+ answer = are_equal;
+ } else if (op_id == IrBinOpCmpNotEq) {
+ answer = !are_equal;
+ } else {
+ zig_unreachable();
+ }
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
+ out_val->data.x_bool = answer;
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
+ ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id,
+ op1, op2, bin_op_instruction->safety_check_on);
+
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
IrInstruction *instructions[] = {op1, op2};
- TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);
+ TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2);
if (type_is_invalid(resolved_type))
return resolved_type;
type_ensure_zero_bits_known(ira->codegen, resolved_type);
@@ -8523,7 +9527,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
return resolved_type;
- AstNode *source_node = bin_op_instruction->base.source_node;
switch (resolved_type->id) {
case TypeTableEntryIdInvalid:
zig_unreachable(); // handled above
@@ -8538,7 +9541,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
case TypeTableEntryIdMetaType:
case TypeTableEntryIdVoid:
case TypeTableEntryIdPointer:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
case TypeTableEntryIdOpaque:
case TypeTableEntryIdNamespace:
@@ -8692,6 +9695,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val,
case IrBinOpArrayCat:
case IrBinOpArrayMult:
case IrBinOpRemUnspecified:
+ case IrBinOpMergeErrorSets:
zig_unreachable();
case IrBinOpBinOr:
assert(is_int);
@@ -9264,6 +10268,46 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp
return get_array_type(ira->codegen, child_type, new_array_len);
}
+static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) {
+ TypeTableEntry *op1_type = ir_resolve_type(ira, instruction->op1->other);
+ if (type_is_invalid(op1_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ TypeTableEntry *op2_type = ir_resolve_type(ira, instruction->op2->other);
+ if (type_is_invalid(op2_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (type_is_global_error_set(op1_type) ||
+ type_is_global_error_set(op2_type))
+ {
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set;
+ return ira->codegen->builtin_types.entry_type;
+ }
+
+ if (!resolve_inferred_error_set(ira, op1_type, instruction->op1->other->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (!resolve_inferred_error_set(ira, op2_type, instruction->op2->other->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length);
+ for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
+ ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
+ assert(errors[error_entry->value] == nullptr);
+ errors[error_entry->value] = error_entry;
+ }
+ TypeTableEntry *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type);
+ free(errors);
+
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_type = result_type;
+ return ira->codegen->builtin_types.entry_type;
+}
+
static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrBinOp op_id = bin_op_instruction->op_id;
switch (op_id) {
@@ -9305,6 +10349,8 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
return ir_analyze_array_cat(ira, bin_op_instruction);
case IrBinOpArrayMult:
return ir_analyze_array_mult(ira, bin_op_instruction);
+ case IrBinOpMergeErrorSets:
+ return ir_analyze_merge_error_sets(ira, bin_op_instruction);
}
zig_unreachable();
}
@@ -9326,7 +10372,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
case TypeTableEntryIdInt:
case TypeTableEntryIdFloat:
case TypeTableEntryIdVoid:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn:
return VarClassRequiredAny;
case TypeTableEntryIdNumLitFloat:
@@ -9352,7 +10398,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
case TypeTableEntryIdMaybe:
return get_var_class_required(type_entry->data.maybe.child_type);
case TypeTableEntryIdErrorUnion:
- return get_var_class_required(type_entry->data.error.child_type);
+ return get_var_class_required(type_entry->data.error_union.payload_type);
case TypeTableEntryIdStruct:
case TypeTableEntryIdEnum:
@@ -9587,7 +10633,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
@@ -9610,7 +10656,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
@@ -9644,6 +10690,31 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
return nullable_type;
}
+static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira,
+ IrInstructionErrorUnion *instruction)
+{
+ TypeTableEntry *err_set_type = ir_resolve_type(ira, instruction->err_set->other);
+ if (type_is_invalid(err_set_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload->other);
+ if (type_is_invalid(payload_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (err_set_type->id != TypeTableEntryIdErrorSet) {
+ ir_add_error(ira, instruction->err_set->other,
+ buf_sprintf("expected error set type, found type '%s'",
+ buf_ptr(&err_set_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ TypeTableEntry *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_type = result_type;
+ return ira->codegen->builtin_types.entry_type;
+}
+
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
{
@@ -9926,9 +10997,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
}
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
- TypeTableEntry *return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);
- if (type_is_invalid(return_type))
+ TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node);
+ if (type_is_invalid(specified_return_type))
return ira->codegen->builtin_types.entry_invalid;
+ TypeTableEntry *return_type;
+ TypeTableEntry *inferred_err_set_type = nullptr;
+ if (fn_proto_node->data.fn_proto.auto_err_set) {
+ inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry);
+ return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
+ } else {
+ return_type = specified_return_type;
+ }
IrInstruction *result;
@@ -9942,6 +11021,23 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
+ if (inferred_err_set_type != nullptr) {
+ inferred_err_set_type->data.error_set.infer_fn = nullptr;
+ if (result->value.type->id == TypeTableEntryIdErrorUnion) {
+ if (result->value.data.x_err_union.err != nullptr) {
+ inferred_err_set_type->data.error_set.err_count = 1;
+ inferred_err_set_type->data.error_set.errors = allocate(1);
+ inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err;
+ }
+ TypeTableEntry *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type;
+ inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count;
+ inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors;
+ } else if (result->value.type->id == TypeTableEntryIdErrorSet) {
+ inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count;
+ inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors;
+ }
+ }
+
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
if (type_is_invalid(result->value.type))
@@ -10092,12 +11188,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
{
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
- TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);
- if (type_is_invalid(return_type))
+ TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node);
+ if (type_is_invalid(specified_return_type))
return ira->codegen->builtin_types.entry_invalid;
- inst_fn_type_id.return_type = return_type;
+ if (fn_proto_node->data.fn_proto.auto_err_set) {
+ TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn);
+ inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
+ } else {
+ inst_fn_type_id.return_type = specified_return_type;
+ }
- if (type_requires_comptime(return_type)) {
+ if (type_requires_comptime(specified_return_type)) {
// Throw out our work and call the function as if it were comptime.
return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
}
@@ -10128,7 +11229,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
- if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) {
+ if (return_type->id == TypeTableEntryIdErrorSet || return_type->id == TypeTableEntryIdErrorUnion) {
parent_fn_entry->calls_errorable_function = true;
}
@@ -10138,7 +11239,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
assert(fn_type_id->return_type != nullptr);
assert(parent_fn_entry != nullptr);
- if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
+ if (fn_type_id->return_type->id == TypeTableEntryIdErrorSet || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) {
parent_fn_entry->calls_errorable_function = true;
}
@@ -10257,58 +11358,6 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
}
}
-static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
- assert(un_op_instruction->op_id == IrUnOpError);
- IrInstruction *value = un_op_instruction->value->other;
-
- TypeTableEntry *meta_type = ir_resolve_type(ira, value);
- if (type_is_invalid(meta_type))
- return ira->codegen->builtin_types.entry_invalid;
-
-
- switch (meta_type->id) {
- case TypeTableEntryIdInvalid: // handled above
- zig_unreachable();
-
- case TypeTableEntryIdVoid:
- case TypeTableEntryIdBool:
- case TypeTableEntryIdInt:
- case TypeTableEntryIdFloat:
- case TypeTableEntryIdPointer:
- case TypeTableEntryIdArray:
- case TypeTableEntryIdStruct:
- case TypeTableEntryIdMaybe:
- case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
- case TypeTableEntryIdEnum:
- case TypeTableEntryIdUnion:
- case TypeTableEntryIdFn:
- case TypeTableEntryIdBoundFn:
- {
- ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base);
- TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type);
- out_val->data.x_type = result_type;
- return ira->codegen->builtin_types.entry_type;
- }
- case TypeTableEntryIdMetaType:
- case TypeTableEntryIdNumLitFloat:
- case TypeTableEntryIdNumLitInt:
- case TypeTableEntryIdUndefLit:
- case TypeTableEntryIdNullLit:
- case TypeTableEntryIdNamespace:
- case TypeTableEntryIdBlock:
- case TypeTableEntryIdUnreachable:
- case TypeTableEntryIdVar:
- case TypeTableEntryIdArgTuple:
- case TypeTableEntryIdOpaque:
- ir_add_error_node(ira, un_op_instruction->base.source_node,
- buf_sprintf("unable to wrap type '%s' in error type", buf_ptr(&meta_type->name)));
- return ira->codegen->builtin_types.entry_invalid;
- }
- zig_unreachable();
-}
-
-
static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrInstruction *value = un_op_instruction->value->other;
@@ -10364,7 +11413,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -10474,8 +11523,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
return ir_analyze_dereference(ira, un_op_instruction);
case IrUnOpMaybe:
return ir_analyze_maybe(ira, un_op_instruction);
- case IrUnOpError:
- return ir_analyze_unary_prefix_op_err(ira, un_op_instruction);
}
zig_unreachable();
}
@@ -10633,6 +11680,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
IrInstruction *branch_instruction = predecessor->instruction_list.pop();
ir_set_cursor_at_end(&ira->new_irb, predecessor);
IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type);
+ if (casted_value == ira->codegen->invalid_instruction) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
new_incoming_values.items[i] = casted_value;
predecessor->instruction_list.append(branch_instruction);
@@ -11048,6 +12098,25 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
}
}
+static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
+ LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);
+ for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
+ Buf *existing_symbol_name = link_lib->symbols.at(i);
+ if (buf_eql_buf(existing_symbol_name, symbol_name)) {
+ return;
+ }
+ }
+ for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) {
+ Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i);
+ if (buf_eql_buf(lib_name, forbidden_lib_name)) {
+ ir_add_error_node(ira, source_node,
+ buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name)));
+ }
+ }
+ link_lib->symbols.append(symbol_name);
+}
+
+
static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {
bool pointer_only = false;
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
TldVar *tld_var = (TldVar *)tld;
VariableTableEntry *var = tld_var->var;
if (tld_var->extern_lib_name != nullptr) {
- add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name);
+ add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node);
}
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
const_val->data.x_fn.fn_entry = fn_entry;
if (tld_fn->extern_lib_name != nullptr) {
- add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name);
+ add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node);
}
bool ptr_is_const = true;
@@ -11097,6 +12166,17 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source
zig_unreachable();
}
+static ErrorTableEntry *find_err_table_entry(TypeTableEntry *err_set_type, Buf *field_name) {
+ assert(err_set_type->id == TypeTableEntryIdErrorSet);
+ for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i];
+ if (buf_eql_buf(&err_table_entry->name, field_name)) {
+ return err_table_entry;
+ }
+ }
+ return nullptr;
+}
+
static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
if (type_is_invalid(container_ptr->value.type))
@@ -11238,23 +12318,52 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
buf_sprintf("container '%s' has no member called '%s'",
buf_ptr(&child_type->name), buf_ptr(field_name)));
return ira->codegen->builtin_types.entry_invalid;
- } else if (child_type->id == TypeTableEntryIdPureError) {
- auto err_table_entry = ira->codegen->error_table.maybe_get(field_name);
- if (err_table_entry) {
- ConstExprValue *const_val = create_const_vals(1);
- const_val->special = ConstValSpecialStatic;
- const_val->type = child_type;
- const_val->data.x_pure_err = err_table_entry->value;
-
- bool ptr_is_const = true;
- bool ptr_is_volatile = false;
- return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
- child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
+ } else if (child_type->id == TypeTableEntryIdErrorSet) {
+ ErrorTableEntry *err_entry;
+ TypeTableEntry *err_set_type;
+ if (type_is_global_error_set(child_type)) {
+ auto existing_entry = ira->codegen->error_table.maybe_get(field_name);
+ if (existing_entry) {
+ err_entry = existing_entry->value;
+ } else {
+ err_entry = allocate(1);
+ err_entry->decl_node = field_ptr_instruction->base.source_node;
+ buf_init_from_buf(&err_entry->name, field_name);
+ size_t error_value_count = ira->codegen->errors_by_index.length;
+ assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
+ err_entry->value = error_value_count;
+ ira->codegen->errors_by_index.append(err_entry);
+ ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder,
+ buf_ptr(field_name), error_value_count));
+ ira->codegen->error_table.put(field_name, err_entry);
+ }
+ if (err_entry->set_with_only_this_in_it == nullptr) {
+ err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen,
+ field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node,
+ err_entry);
+ }
+ err_set_type = err_entry->set_with_only_this_in_it;
+ } else {
+ if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ err_entry = find_err_table_entry(child_type, field_name);
+ if (err_entry == nullptr) {
+ ir_add_error(ira, &field_ptr_instruction->base,
+ buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ err_set_type = child_type;
}
+ ConstExprValue *const_val = create_const_vals(1);
+ const_val->special = ConstValSpecialStatic;
+ const_val->type = err_set_type;
+ const_val->data.x_err_set = err_entry;
- ir_add_error(ira, &field_ptr_instruction->base,
- buf_sprintf("use of undeclared error value '%s'", buf_ptr(field_name)));
- return ira->codegen->builtin_types.entry_invalid;
+ bool ptr_is_const = true;
+ bool ptr_is_volatile = false;
+ return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val,
+ err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
} else if (child_type->id == TypeTableEntryIdInt) {
if (buf_eql_str(field_name, "bit_count")) {
bool ptr_is_const = true;
@@ -11337,11 +12446,18 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
return ira->codegen->builtin_types.entry_invalid;
}
} else if (child_type->id == TypeTableEntryIdErrorUnion) {
- if (buf_eql_str(field_name, "Child")) {
+ if (buf_eql_str(field_name, "Payload")) {
bool ptr_is_const = true;
bool ptr_is_volatile = false;
return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
- create_const_type(ira->codegen, child_type->data.error.child_type),
+ create_const_type(ira->codegen, child_type->data.error_union.payload_type),
+ ira->codegen->builtin_types.entry_type,
+ ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
+ } else if (buf_eql_str(field_name, "ErrorSet")) {
+ bool ptr_is_const = true;
+ bool ptr_is_volatile = false;
+ return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
+ create_const_type(ira->codegen, child_type->data.error_union.err_set_type),
ira->codegen->builtin_types.entry_type,
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
} else {
@@ -11528,7 +12644,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -11795,7 +12911,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -11903,7 +13019,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -11956,7 +13072,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -12291,7 +13407,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
case TypeTableEntryIdPointer:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
if (pointee_val) {
ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
copy_const_val(out_val, pointee_val, true);
@@ -12361,8 +13477,6 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
return target_type;
}
case TypeTableEntryIdErrorUnion:
- // see https://github.com/andrewrk/zig/issues/632
- zig_panic("TODO switch on error union");
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
@@ -12887,7 +14001,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdNamespace:
@@ -12975,7 +14089,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type);
if (casted_value->value.special == ConstValSpecialStatic) {
- ErrorTableEntry *err = casted_value->value.data.x_pure_err;
+ ErrorTableEntry *err = casted_value->value.data.x_err_set;
if (!err->cached_error_name_val) {
ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name);
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
result = container_type->data.structure.src_field_count;
} else if (container_type->id == TypeTableEntryIdUnion) {
result = container_type->data.unionation.src_field_count;
+ } else if (container_type->id == TypeTableEntryIdErrorSet) {
+ if (!resolve_inferred_error_set(ira, container_type, instruction->base.source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (type_is_global_error_set(container_type)) {
+ ir_add_error(ira, &instruction->base, buf_sprintf("global error set member count not available at comptime"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ result = container_type->data.error_set.err_count;
} else {
ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));
return ira->codegen->builtin_types.entry_invalid;
@@ -14120,7 +15243,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc
case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
@@ -14251,9 +15374,22 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc
}
}
+ TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
+ if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ if (!type_is_global_error_set(err_set_type) &&
+ err_set_type->data.error_set.err_count == 0)
+ {
+ assert(err_set_type->data.error_set.infer_fn == nullptr);
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_bool = false;
+ return ira->codegen->builtin_types.entry_bool;
+ }
+
ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
return ira->codegen->builtin_types.entry_bool;
- } else if (type_entry->id == TypeTableEntryIdPureError) {
+ } else if (type_entry->id == TypeTableEntryIdErrorSet) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
out_val->data.x_bool = true;
return ira->codegen->builtin_types.entry_bool;
@@ -14289,13 +15425,13 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
assert(err);
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
- out_val->data.x_pure_err = err;
- return ira->codegen->builtin_types.entry_pure_error;
+ out_val->data.x_err_set = err;
+ return type_entry->data.error_union.err_set_type;
}
}
ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value);
- return ira->codegen->builtin_types.entry_pure_error;
+ return type_entry->data.error_union.err_set_type;
} else {
ir_add_error(ira, value,
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,
if (type_is_invalid(type_entry)) {
return ira->codegen->builtin_types.entry_invalid;
} else if (type_entry->id == TypeTableEntryIdErrorUnion) {
- TypeTableEntry *child_type = type_entry->data.error.child_type;
- TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
+ TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
+ TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
- get_abi_alignment(ira->codegen, child_type), 0, 0);
+ get_abi_alignment(ira->codegen, payload_type), 0, 0);
if (instr_is_comptime(value)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
if (!ptr_val)
@@ -14332,7 +15468,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
ErrorTableEntry *err = err_union_val->data.x_err_union.err;
if (err != nullptr) {
ir_add_error(ira, &instruction->base,
- buf_sprintf("unable to unwrap error '%s'", buf_ptr(&err->name)));
+ buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
return ira->codegen->builtin_types.entry_invalid;
}
@@ -14357,6 +15493,12 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
AstNode *proto_node = instruction->base.source_node;
assert(proto_node->type == NodeTypeFnProto);
+ if (proto_node->data.fn_proto.auto_err_set) {
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("inferring error set of return type valid only for function definitions"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
@@ -14482,6 +15624,63 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
}
}
}
+ } else if (switch_type->id == TypeTableEntryIdErrorSet) {
+ if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ AstNode **field_prev_uses = allocate(ira->codegen->errors_by_index.length);
+
+ for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
+ IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
+
+ IrInstruction *start_value = range->start->other;
+ if (type_is_invalid(start_value->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *end_value = range->end->other;
+ if (type_is_invalid(end_value->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ assert(start_value->value.type->id == TypeTableEntryIdErrorSet);
+ uint32_t start_index = start_value->value.data.x_err_set->value;
+
+ assert(end_value->value.type->id == TypeTableEntryIdErrorSet);
+ uint32_t end_index = end_value->value.data.x_err_set->value;
+
+ if (start_index != end_index) {
+ ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ AstNode *prev_node = field_prev_uses[start_index];
+ if (prev_node != nullptr) {
+ Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name;
+ ErrorMsg *msg = ir_add_error(ira, start_value,
+ buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name)));
+ add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here"));
+ }
+ field_prev_uses[start_index] = start_value->source_node;
+ }
+ if (!instruction->have_else_prong) {
+ if (type_is_global_error_set(switch_type)) {
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("else prong required when switching on type 'error'"));
+ return ira->codegen->builtin_types.entry_invalid;
+ } else {
+ for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
+ ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
+
+ AstNode *prev_node = field_prev_uses[err_entry->value];
+ if (prev_node == nullptr) {
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
+ }
+ }
+ }
+ }
+
+ free(field_prev_uses);
} else if (switch_type->id == TypeTableEntryIdInt) {
RangeSet rs = {0};
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
zig_panic("TODO buf_write_value_bytes maybe type");
case TypeTableEntryIdErrorUnion:
zig_panic("TODO buf_write_value_bytes error union");
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
zig_panic("TODO buf_write_value_bytes pure error type");
case TypeTableEntryIdEnum:
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
zig_panic("TODO buf_read_value_bytes maybe type");
case TypeTableEntryIdErrorUnion:
zig_panic("TODO buf_read_value_bytes error union");
- case TypeTableEntryIdPureError:
+ case TypeTableEntryIdErrorSet:
zig_panic("TODO buf_read_value_bytes pure error type");
case TypeTableEntryIdEnum:
zig_panic("TODO buf_read_value_bytes enum type");
@@ -15010,7 +16209,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
return ira->codegen->builtin_types.entry_invalid;
if (tld_var->extern_lib_name != nullptr) {
- add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name);
+ add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);
}
if (lval.is_ptr) {
@@ -15029,7 +16228,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
assert(fn_entry->type_entry);
if (tld_fn->extern_lib_name != nullptr) {
- add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name);
+ add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node);
}
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
return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
case IrInstructionIdErrorReturnTrace:
return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
+ case IrInstructionIdErrorUnion:
+ return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction);
}
zig_unreachable();
}
@@ -15628,6 +16829,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdArgType:
case IrInstructionIdTagType:
case IrInstructionIdErrorReturnTrace:
+ case IrInstructionIdErrorUnion:
return false;
case IrInstructionIdAsm:
{
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 8332212d34..f2c0d6a5b4 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
return "++";
case IrBinOpArrayMult:
return "**";
+ case IrBinOpMergeErrorSets:
+ return "||";
}
zig_unreachable();
}
@@ -148,8 +150,6 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
return "*";
case IrUnOpMaybe:
return "?";
- case IrUnOpError:
- return "%";
}
zig_unreachable();
}
@@ -1004,6 +1004,11 @@ static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTr
fprintf(irp->f, "@errorReturnTrace()");
}
+static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) {
+ ir_print_other_instruction(irp, instruction->err_set);
+ fprintf(irp->f, "!");
+ ir_print_other_instruction(irp, instruction->payload);
+}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
@@ -1322,6 +1327,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdErrorReturnTrace:
ir_print_error_return_trace(irp, (IrInstructionErrorReturnTrace *)instruction);
break;
+ case IrInstructionIdErrorUnion:
+ ir_print_error_union(irp, (IrInstructionErrorUnion *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/src/main.cpp b/src/main.cpp
index e3ef62be07..eab7f29b10 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -66,6 +66,7 @@ static int usage(const char *arg0) {
" --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n"
" --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n"
" --library [lib] link against lib\n"
+ " --forbid-library [lib] make it an error to link against lib\n"
" --library-path [dir] add a directory to the library search path\n"
" --linker-script [path] use a custom linker script\n"
" --object [obj] add object file to build\n"
@@ -309,6 +310,7 @@ int main(int argc, char **argv) {
ZigList llvm_argv = {0};
ZigList lib_dirs = {0};
ZigList link_libs = {0};
+ ZigList forbidden_link_libs = {0};
ZigList frameworks = {0};
int err;
const char *target_arch = nullptr;
@@ -339,6 +341,7 @@ int main(int argc, char **argv) {
const char *zig_exe_path = arg0;
const char *build_file = "build.zig";
bool asked_for_help = false;
+ bool asked_to_init = false;
init_all_targets();
@@ -350,6 +353,9 @@ int main(int argc, char **argv) {
if (strcmp(argv[i], "--help") == 0) {
asked_for_help = true;
args.append(argv[i]);
+ } else if (strcmp(argv[i], "--init") == 0) {
+ asked_to_init = true;
+ args.append(argv[i]);
} else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) {
build_file = argv[i + 1];
i += 1;
@@ -414,6 +420,7 @@ int main(int argc, char **argv) {
"\n"
"General Options:\n"
" --help Print this help and exit\n"
+ " --init Generate a build.zig template\n"
" --build-file [file] Override path to build.zig\n"
" --cache-dir [path] Override path to cache directory\n"
" --verbose Print commands before executing them\n"
@@ -426,7 +433,6 @@ int main(int argc, char **argv) {
" --prefix [path] Override default install prefix\n"
"\n"
"Project-specific options become available when the build file is found.\n"
- "Run this command with no options to generate a build.zig template.\n"
"\n"
"Advanced Options:\n"
" --build-file [file] Override path to build.zig\n"
@@ -439,17 +445,26 @@ int main(int argc, char **argv) {
" --verbose-cimport Enable compiler debug output for C imports\n"
"\n"
, zig_exe_path);
- return 0;
- }
- Buf *build_template_path = buf_alloc();
- os_path_join(special_dir, buf_create_from_str("build_file_template.zig"), build_template_path);
+ return EXIT_SUCCESS;
+ } else if (asked_to_init) {
+ Buf *build_template_path = buf_alloc();
+ os_path_join(special_dir, buf_create_from_str("build_file_template.zig"), build_template_path);
- if ((err = os_copy_file(build_template_path, &build_file_abs))) {
- fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
- } else {
- fprintf(stderr, "Wrote build.zig template\n");
+ if ((err = os_copy_file(build_template_path, &build_file_abs))) {
+ fprintf(stderr, "Unable to write build.zig template: %s\n", err_str(err));
+ } else {
+ fprintf(stderr, "Wrote build.zig template\n");
+ }
+ return EXIT_SUCCESS;
}
- return 1;
+
+ fprintf(stderr,
+ "No 'build.zig' file found.\n"
+ "Initialize a 'build.zig' template file with `zig build --init`,\n"
+ "or build an executable directly with `zig build-exe $FILENAME.zig`.\n"
+ "See: `zig build --help` or `zig help` for more options.\n"
+ );
+ return EXIT_FAILURE;
}
PackageTableEntry *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname),
@@ -592,6 +607,8 @@ int main(int argc, char **argv) {
lib_dirs.append(argv[i]);
} else if (strcmp(arg, "--library") == 0) {
link_libs.append(argv[i]);
+ } else if (strcmp(arg, "--forbid-library") == 0) {
+ forbidden_link_libs.append(argv[i]);
} else if (strcmp(arg, "--object") == 0) {
objects.append(argv[i]);
} else if (strcmp(arg, "--assembly") == 0) {
@@ -804,6 +821,10 @@ int main(int argc, char **argv) {
LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i)));
link_lib->provided_explicitly = true;
}
+ for (size_t i = 0; i < forbidden_link_libs.length; i += 1) {
+ Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i));
+ codegen_add_forbidden_lib(g, forbidden_link_lib);
+ }
for (size_t i = 0; i < frameworks.length; i += 1) {
codegen_add_framework(g, frameworks.at(i));
}
diff --git a/src/parser.cpp b/src/parser.cpp
index 12293bc61b..6ce9e25221 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -221,6 +221,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bo
static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory);
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory);
static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index);
+static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index);
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
if (token->id == token_id) {
@@ -240,7 +241,28 @@ static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token
}
/*
-TypeExpr = PrefixOpExpression | "var"
+ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
+*/
+static AstNode *ast_parse_error_set_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
+ AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory);
+ if (!prefix_op_expr) {
+ return nullptr;
+ }
+ Token *token = &pc->tokens->at(*token_index);
+ if (token->id == TokenIdBang) {
+ *token_index += 1;
+ AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
+ node->data.bin_op_expr.op1 = prefix_op_expr;
+ node->data.bin_op_expr.bin_op = BinOpTypeErrorUnion;
+ node->data.bin_op_expr.op2 = ast_parse_prefix_op_expr(pc, token_index, true);
+ return node;
+ } else {
+ return prefix_op_expr;
+ }
+}
+
+/*
+TypeExpr = ErrorSetExpr | "var"
*/
static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@@ -249,7 +271,7 @@ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool
*token_index += 1;
return node;
} else {
- return ast_parse_prefix_op_expr(pc, token_index, mandatory);
+ return ast_parse_error_set_expr(pc, token_index, mandatory);
}
}
@@ -651,8 +673,9 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
}
/*
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
+ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@@ -716,9 +739,31 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
*token_index += 1;
return node;
} else if (token->id == TokenIdKeywordError) {
- AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);
- *token_index += 1;
- return node;
+ Token *next_token = &pc->tokens->at(*token_index + 1);
+ if (next_token->id == TokenIdLBrace) {
+ AstNode *node = ast_create_node(pc, NodeTypeErrorSetDecl, token);
+ *token_index += 2;
+ for (;;) {
+ Token *item_tok = &pc->tokens->at(*token_index);
+ if (item_tok->id == TokenIdRBrace) {
+ *token_index += 1;
+ return node;
+ } else if (item_tok->id == TokenIdSymbol) {
+ AstNode *symbol_node = ast_parse_symbol(pc, token_index);
+ node->data.err_set_decl.decls.append(symbol_node);
+ Token *opt_comma_tok = &pc->tokens->at(*token_index);
+ if (opt_comma_tok->id == TokenIdComma) {
+ *token_index += 1;
+ }
+ } else {
+ ast_invalid_token_error(pc, item_tok);
+ }
+ }
+ } else {
+ AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);
+ *token_index += 1;
+ return node;
+ }
} else if (token->id == TokenIdAtSign) {
*token_index += 1;
Token *name_tok = &pc->tokens->at(*token_index);
@@ -950,7 +995,6 @@ static PrefixOp tok_to_prefix_op(Token *token) {
case TokenIdTilde: return PrefixOpBinNot;
case TokenIdStar: return PrefixOpDereference;
case TokenIdMaybe: return PrefixOpMaybe;
- case TokenIdPercent: return PrefixOpError;
case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
case TokenIdStarStar: return PrefixOpDereference;
default: return PrefixOpInvalid;
@@ -997,8 +1041,8 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {
}
/*
-PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
-PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" | "try"
+PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
+PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try"
*/
static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@@ -1028,7 +1072,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index,
node->column += 1;
}
- AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, true);
+ AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true);
node->data.prefix_op_expr.primary_expr = prefix_op_expr;
node->data.prefix_op_expr.prefix_op = prefix_op;
@@ -1043,12 +1087,14 @@ static BinOpType tok_to_mult_op(Token *token) {
case TokenIdStarStar: return BinOpTypeArrayMult;
case TokenIdSlash: return BinOpTypeDiv;
case TokenIdPercent: return BinOpTypeMod;
+ case TokenIdBang: return BinOpTypeErrorUnion;
+ case TokenIdBarBar: return BinOpTypeMergeErrorSets;
default: return BinOpTypeInvalid;
}
}
/*
-MultiplyOperator = "*" | "/" | "%" | "**" | "*%"
+MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
*/
static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@@ -2240,7 +2286,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
}
/*
-FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") TypeExpr
+FnProto = option("nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr
*/
static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
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
ast_eat_token(pc, token_index, TokenIdRParen);
next_token = &pc->tokens->at(*token_index);
}
+ if (next_token->id == TokenIdKeywordError) {
+ Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
+ if (maybe_lbrace_tok->id == TokenIdLBrace) {
+ *token_index += 1;
+ node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
+ return node;
+ }
+ } else if (next_token->id == TokenIdBang) {
+ *token_index += 1;
+ node->data.fn_proto.auto_err_set = true;
+ next_token = &pc->tokens->at(*token_index);
+ }
node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
return node;
@@ -2531,7 +2589,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
Token *colon_token = &pc->tokens->at(*token_index);
if (colon_token->id == TokenIdColon) {
*token_index += 1;
- field_node->data.struct_field.type = ast_parse_prefix_op_expr(pc, token_index, true);
+ field_node->data.struct_field.type = ast_parse_type_expr(pc, token_index, true);
}
Token *eq_token = &pc->tokens->at(*token_index);
if (eq_token->id == TokenIdEq) {
@@ -2559,26 +2617,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
return node;
}
-/*
-ErrorValueDecl : "error" "Symbol" ";"
-*/
-static AstNode *ast_parse_error_value_decl(ParseContext *pc, size_t *token_index) {
- Token *first_token = &pc->tokens->at(*token_index);
-
- if (first_token->id != TokenIdKeywordError) {
- return nullptr;
- }
- *token_index += 1;
-
- Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
- ast_eat_token(pc, token_index, TokenIdSemicolon);
-
- AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token);
- node->data.error_value_decl.name = token_buf(name_tok);
-
- return node;
-}
-
/*
TestDecl = "test" String Block
*/
@@ -2611,12 +2649,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
continue;
}
- AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index);
- if (error_value_node) {
- top_level_decls->append(error_value_node);
- continue;
- }
-
AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index);
if (test_decl_node) {
top_level_decls->append(test_decl_node);
@@ -2744,9 +2776,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.variable_declaration.align_expr, visit, context);
visit_field(&node->data.variable_declaration.section_expr, visit, context);
break;
- case NodeTypeErrorValueDecl:
- // none
- break;
case NodeTypeTestDecl:
visit_field(&node->data.test_decl.body, visit, context);
break;
@@ -2899,5 +2928,8 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.addr_of_expr.align_expr, visit, context);
visit_field(&node->data.addr_of_expr.op_expr, visit, context);
break;
+ case NodeTypeErrorSetDecl:
+ visit_node_list(&node->data.err_set_decl.decls, visit, context);
+ break;
}
}
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index f98c0c8344..44d838a723 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -195,7 +195,8 @@ enum TokenizeState {
TokenizeStateSawMinusPercent,
TokenizeStateSawAmpersand,
TokenizeStateSawCaret,
- TokenizeStateSawPipe,
+ TokenizeStateSawBar,
+ TokenizeStateSawBarBar,
TokenizeStateLineComment,
TokenizeStateLineString,
TokenizeStateLineStringEnd,
@@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) {
break;
case '|':
begin_token(&t, TokenIdBinOr);
- t.state = TokenizeStateSawPipe;
+ t.state = TokenizeStateSawBar;
break;
case '=':
begin_token(&t, TokenIdEq);
@@ -888,20 +889,37 @@ void tokenize(Buf *buf, Tokenization *out) {
continue;
}
break;
- case TokenizeStateSawPipe:
+ case TokenizeStateSawBar:
switch (c) {
case '=':
set_token_id(&t, t.cur_tok, TokenIdBitOrEq);
end_token(&t);
t.state = TokenizeStateStart;
break;
+ case '|':
+ set_token_id(&t, t.cur_tok, TokenIdBarBar);
+ t.state = TokenizeStateSawBarBar;
+ break;
+ default:
+ t.pos -= 1;
+ end_token(&t);
+ t.state = TokenizeStateStart;
+ continue;
+ }
+ break;
+ case TokenizeStateSawBarBar:
+ switch (c) {
+ case '=':
+ set_token_id(&t, t.cur_tok, TokenIdBarBarEq);
+ end_token(&t);
+ t.state = TokenizeStateStart;
+ break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
- break;
case TokenizeStateSawSlash:
switch (c) {
case '/':
@@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawDash:
case TokenizeStateSawAmpersand:
case TokenizeStateSawCaret:
- case TokenizeStateSawPipe:
+ case TokenizeStateSawBar:
case TokenizeStateSawEq:
case TokenizeStateSawBang:
case TokenizeStateSawLessThan:
@@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawMinusPercent:
case TokenizeStateLineString:
case TokenizeStateLineStringEnd:
+ case TokenizeStateSawBarBar:
end_token(&t);
break;
case TokenizeStateSawDotDot:
@@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) {
case TokenIdArrow: return "->";
case TokenIdAtSign: return "@";
case TokenIdBang: return "!";
+ case TokenIdBarBar: return "||";
case TokenIdBinOr: return "|";
case TokenIdBinXor: return "^";
case TokenIdBitAndEq: return "&=";
@@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) {
case TokenIdTimesEq: return "*=";
case TokenIdTimesPercent: return "*%";
case TokenIdTimesPercentEq: return "*%=";
+ case TokenIdBarBarEq: return "||=";
}
return "(invalid token)";
}
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index 749f72f419..92a3b8de0d 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -17,6 +17,8 @@ enum TokenId {
TokenIdArrow,
TokenIdAtSign,
TokenIdBang,
+ TokenIdBarBar,
+ TokenIdBarBarEq,
TokenIdBinOr,
TokenIdBinXor,
TokenIdBitAndEq,
diff --git a/src/util.hpp b/src/util.hpp
index ce6cc09a59..ae33cb84af 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -91,20 +91,23 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {
#endif
}
+template
+static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
+ T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T)));
+ if (!ptr)
+ zig_panic("allocation failed");
+ if (new_count > old_count) {
+ memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
+ }
+ return ptr;
+}
+
template
static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
-#ifdef NDEBUG
T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T)));
if (!ptr)
zig_panic("allocation failed");
return ptr;
-#else
- // manually assign every element to trigger compile error for non-copyable structs
- T *ptr = allocate_nonzero(new_count);
- safe_memcpy(ptr, old, old_count);
- free(old);
- return ptr;
-#endif
}
template
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index a33303a9ea..e92cfcd44b 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -437,6 +437,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {
return dwarf::DW_TAG_structure_type;
}
+unsigned ZigLLVMTag_DW_enumeration_type(void) {
+ return dwarf::DW_TAG_enumeration_type;
+}
+
unsigned ZigLLVMTag_DW_union_type(void) {
return dwarf::DW_TAG_union_type;
}
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index 9cdb28dc58..ac2fe01bfc 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -133,6 +133,7 @@ ZIG_EXTERN_C unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);
ZIG_EXTERN_C unsigned ZigLLVMLang_DW_LANG_C99(void);
ZIG_EXTERN_C unsigned ZigLLVMTag_DW_variable(void);
ZIG_EXTERN_C unsigned ZigLLVMTag_DW_structure_type(void);
+ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
diff --git a/std/array_list.zig b/std/array_list.zig
index bc4d3c1d81..2a44b66518 100644
--- a/std/array_list.zig
+++ b/std/array_list.zig
@@ -40,6 +40,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
return l.items[0..l.len];
}
+ pub fn at(l: &const Self, n: usize) T {
+ return l.toSliceConst()[n];
+ }
+
/// ArrayList takes ownership of the passed in slice. The slice must have been
/// allocated with `allocator`.
/// Deinitialize with `deinit` or use `toOwnedSlice`.
@@ -59,18 +63,34 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
return result;
}
- pub fn append(l: &Self, item: &const T) %void {
+ pub fn insert(l: &Self, n: usize, item: &const T) !void {
+ try l.ensureCapacity(l.len + 1);
+ l.len += 1;
+
+ mem.copy(T, l.items[n+1..l.len], l.items[n..l.len-1]);
+ l.items[n] = *item;
+ }
+
+ pub fn insertSlice(l: &Self, n: usize, items: []align(A) const T) !void {
+ try l.ensureCapacity(l.len + items.len);
+ l.len += items.len;
+
+ mem.copy(T, l.items[n+items.len..l.len], l.items[n..l.len-items.len]);
+ mem.copy(T, l.items[n..n+items.len], items);
+ }
+
+ pub fn append(l: &Self, item: &const T) !void {
const new_item_ptr = try l.addOne();
*new_item_ptr = *item;
}
- pub fn appendSlice(l: &Self, items: []align(A) const T) %void {
+ pub fn appendSlice(l: &Self, items: []align(A) const T) !void {
try l.ensureCapacity(l.len + items.len);
mem.copy(T, l.items[l.len..], items);
l.len += items.len;
}
- pub fn resize(l: &Self, new_len: usize) %void {
+ pub fn resize(l: &Self, new_len: usize) !void {
try l.ensureCapacity(new_len);
l.len = new_len;
}
@@ -80,7 +100,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
l.len = new_len;
}
- pub fn ensureCapacity(l: &Self, new_capacity: usize) %void {
+ pub fn ensureCapacity(l: &Self, new_capacity: usize) !void {
var better_capacity = l.items.len;
if (better_capacity >= new_capacity) return;
while (true) {
@@ -90,7 +110,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity);
}
- pub fn addOne(l: &Self) %&T {
+ pub fn addOne(l: &Self) !&T {
const new_length = l.len + 1;
try l.ensureCapacity(new_length);
const result = &l.items[l.len];
@@ -136,3 +156,22 @@ test "basic ArrayList test" {
list.appendSlice([]const i32 {}) catch unreachable;
assert(list.len == 9);
}
+
+test "insert ArrayList test" {
+ var list = ArrayList(i32).init(debug.global_allocator);
+ defer list.deinit();
+
+ try list.append(1);
+ try list.insert(0, 5);
+ assert(list.items[0] == 5);
+ assert(list.items[1] == 1);
+
+ try list.insertSlice(1, []const i32 { 9, 8 });
+ assert(list.items[0] == 5);
+ assert(list.items[1] == 9);
+ assert(list.items[2] == 8);
+
+ const items = []const i32 { 1 };
+ try list.insertSlice(0, items[0..0]);
+ assert(list.items[0] == 5);
+}
diff --git a/std/base64.zig b/std/base64.zig
index 8cd89b67b5..d9e1d2f908 100644
--- a/std/base64.zig
+++ b/std/base64.zig
@@ -79,8 +79,6 @@ pub const Base64Encoder = struct {
};
pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
-error InvalidPadding;
-error InvalidCharacter;
pub const Base64Decoder = struct {
/// e.g. 'A' => 0.
@@ -111,7 +109,7 @@ pub const Base64Decoder = struct {
}
/// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
- pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) %usize {
+ pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) !usize {
if (source.len % 4 != 0) return error.InvalidPadding;
return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
}
@@ -119,7 +117,7 @@ pub const Base64Decoder = struct {
/// dest.len must be what you get from ::calcSize.
/// invalid characters result in error.InvalidCharacter.
/// invalid padding results in error.InvalidPadding.
- pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) %void {
+ pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) !void {
assert(dest.len == (decoder.calcSize(source) catch unreachable));
assert(source.len % 4 == 0);
@@ -163,8 +161,6 @@ pub const Base64Decoder = struct {
}
};
-error OutputTooSmall;
-
pub const Base64DecoderWithIgnore = struct {
decoder: Base64Decoder,
char_is_ignored: [256]bool,
@@ -185,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct {
}
/// If no characters end up being ignored or padding, this will be the exact decoded size.
- pub fn calcSizeUpperBound(encoded_len: usize) %usize {
+ pub fn calcSizeUpperBound(encoded_len: usize) usize {
return @divTrunc(encoded_len, 4) * 3;
}
@@ -193,7 +189,7 @@ pub const Base64DecoderWithIgnore = struct {
/// Invalid padding results in error.InvalidPadding.
/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
/// Returns the number of bytes writen to dest.
- pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) %usize {
+ pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize {
const decoder = &decoder_with_ignore.decoder;
var src_cursor: usize = 0;
@@ -378,7 +374,7 @@ test "base64" {
comptime (testBase64() catch unreachable);
}
-fn testBase64() %void {
+fn testBase64() !void {
try testAllApis("", "");
try testAllApis("f", "Zg==");
try testAllApis("fo", "Zm8=");
@@ -412,7 +408,7 @@ fn testBase64() %void {
try testOutputTooSmallError("AAAAAA==");
}
-fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void {
+fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void {
// Base64Encoder
{
var buffer: [0x100]u8 = undefined;
@@ -434,7 +430,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void
const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, "");
var buffer: [0x100]u8 = undefined;
- var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
+ var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
assert(written <= decoded.len);
assert(mem.eql(u8, decoded[0..written], expected_decoded));
@@ -449,17 +445,16 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void
}
}
-fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) %void {
+fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, " ");
var buffer: [0x100]u8 = undefined;
- var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
+ var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
var written = try standard_decoder_ignore_space.decode(decoded, encoded);
assert(mem.eql(u8, decoded[0..written], expected_decoded));
}
-error ExpectedError;
-fn testError(encoded: []const u8, expected_err: error) %void {
+fn testError(encoded: []const u8, expected_err: error) !void {
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, " ");
var buffer: [0x100]u8 = undefined;
@@ -475,7 +470,7 @@ fn testError(encoded: []const u8, expected_err: error) %void {
} else |err| if (err != expected_err) return err;
}
-fn testOutputTooSmallError(encoded: []const u8) %void {
+fn testOutputTooSmallError(encoded: []const u8) !void {
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, " ");
var buffer: [0x100]u8 = undefined;
diff --git a/std/buf_map.zig b/std/buf_map.zig
index 15ffe785e6..d7f81cf2cc 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -27,7 +27,7 @@ pub const BufMap = struct {
self.hash_map.deinit();
}
- pub fn set(self: &BufMap, key: []const u8, value: []const u8) %void {
+ pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
if (self.hash_map.get(key)) |entry| {
const value_copy = try self.copy(value);
errdefer self.free(value_copy);
@@ -67,7 +67,7 @@ pub const BufMap = struct {
self.hash_map.allocator.free(mut_value);
}
- fn copy(self: &BufMap, value: []const u8) %[]const u8 {
+ fn copy(self: &BufMap, value: []const u8) ![]const u8 {
const result = try self.hash_map.allocator.alloc(u8, value.len);
mem.copy(u8, result, value);
return result;
diff --git a/std/buf_set.zig b/std/buf_set.zig
index 2349c17433..4fa16762b6 100644
--- a/std/buf_set.zig
+++ b/std/buf_set.zig
@@ -24,7 +24,7 @@ pub const BufSet = struct {
self.hash_map.deinit();
}
- pub fn put(self: &BufSet, key: []const u8) %void {
+ pub fn put(self: &BufSet, key: []const u8) !void {
if (self.hash_map.get(key) == null) {
const key_copy = try self.copy(key);
errdefer self.free(key_copy);
@@ -55,7 +55,7 @@ pub const BufSet = struct {
self.hash_map.allocator.free(mut_value);
}
- fn copy(self: &BufSet, value: []const u8) %[]const u8 {
+ fn copy(self: &BufSet, value: []const u8) ![]const u8 {
const result = try self.hash_map.allocator.alloc(u8, value.len);
mem.copy(u8, result, value);
return result;
diff --git a/std/buffer.zig b/std/buffer.zig
index 34428aa8e4..e0892d5933 100644
--- a/std/buffer.zig
+++ b/std/buffer.zig
@@ -12,14 +12,14 @@ pub const Buffer = struct {
list: ArrayList(u8),
/// Must deinitialize with deinit.
- pub fn init(allocator: &Allocator, m: []const u8) %Buffer {
+ pub fn init(allocator: &Allocator, m: []const u8) !Buffer {
var self = try initSize(allocator, m.len);
mem.copy(u8, self.list.items, m);
return self;
}
/// Must deinitialize with deinit.
- pub fn initSize(allocator: &Allocator, size: usize) %Buffer {
+ pub fn initSize(allocator: &Allocator, size: usize) !Buffer {
var self = initNull(allocator);
try self.resize(size);
return self;
@@ -37,7 +37,7 @@ pub const Buffer = struct {
}
/// Must deinitialize with deinit.
- pub fn initFromBuffer(buffer: &const Buffer) %Buffer {
+ pub fn initFromBuffer(buffer: &const Buffer) !Buffer {
return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
}
@@ -80,7 +80,7 @@ pub const Buffer = struct {
self.list.items[self.len()] = 0;
}
- pub fn resize(self: &Buffer, new_len: usize) %void {
+ pub fn resize(self: &Buffer, new_len: usize) !void {
try self.list.resize(new_len + 1);
self.list.items[self.len()] = 0;
}
@@ -93,24 +93,24 @@ pub const Buffer = struct {
return self.list.len - 1;
}
- pub fn append(self: &Buffer, m: []const u8) %void {
+ pub fn append(self: &Buffer, m: []const u8) !void {
const old_len = self.len();
try self.resize(old_len + m.len);
mem.copy(u8, self.list.toSlice()[old_len..], m);
}
// TODO: remove, use OutStream for this
- pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) %void {
+ pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
return fmt.format(self, append, format, args);
}
// TODO: remove, use OutStream for this
- pub fn appendByte(self: &Buffer, byte: u8) %void {
+ pub fn appendByte(self: &Buffer, byte: u8) !void {
return self.appendByteNTimes(byte, 1);
}
// TODO: remove, use OutStream for this
- pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) %void {
+ pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
var prev_size: usize = self.len();
const new_size = prev_size + count;
try self.resize(new_size);
@@ -137,7 +137,7 @@ pub const Buffer = struct {
return mem.eql(u8, self.list.items[start..l], m);
}
- pub fn replaceContents(self: &const Buffer, m: []const u8) %void {
+ pub fn replaceContents(self: &const Buffer, m: []const u8) !void {
try self.resize(m.len);
mem.copy(u8, self.list.toSlice(), m);
}
diff --git a/std/build.zig b/std/build.zig
index 6c56988896..e6b6676261 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -15,13 +15,6 @@ const BufSet = std.BufSet;
const BufMap = std.BufMap;
const fmt_lib = std.fmt;
-error ExtraArg;
-error UncleanExit;
-error InvalidStepName;
-error DependencyLoopDetected;
-error NoCompilerFound;
-error NeedAnObject;
-
pub const Builder = struct {
uninstall_tls: TopLevelStep,
install_tls: TopLevelStep,
@@ -242,7 +235,7 @@ pub const Builder = struct {
self.lib_paths.append(path) catch unreachable;
}
- pub fn make(self: &Builder, step_names: []const []const u8) %void {
+ pub fn make(self: &Builder, step_names: []const []const u8) !void {
var wanted_steps = ArrayList(&Step).init(self.allocator);
defer wanted_steps.deinit();
@@ -278,7 +271,7 @@ pub const Builder = struct {
return &self.uninstall_tls.step;
}
- fn makeUninstall(uninstall_step: &Step) %void {
+ fn makeUninstall(uninstall_step: &Step) error!void {
const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
@@ -292,7 +285,7 @@ pub const Builder = struct {
// TODO remove empty directories
}
- fn makeOneStep(self: &Builder, s: &Step) %void {
+ fn makeOneStep(self: &Builder, s: &Step) error!void {
if (s.loop_flag) {
warn("Dependency loop detected:\n {}\n", s.name);
return error.DependencyLoopDetected;
@@ -313,7 +306,7 @@ pub const Builder = struct {
try s.make();
}
- fn getTopLevelStepByName(self: &Builder, name: []const u8) %&Step {
+ fn getTopLevelStepByName(self: &Builder, name: []const u8) !&Step {
for (self.top_level_steps.toSliceConst()) |top_level_step| {
if (mem.eql(u8, top_level_step.step.name, name)) {
return &top_level_step.step;
@@ -548,7 +541,7 @@ pub const Builder = struct {
return self.invalid_user_input;
}
- fn spawnChild(self: &Builder, argv: []const []const u8) %void {
+ fn spawnChild(self: &Builder, argv: []const []const u8) !void {
return self.spawnChildEnvMap(null, &self.env_map, argv);
}
@@ -561,7 +554,7 @@ pub const Builder = struct {
}
fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
- argv: []const []const u8) %void
+ argv: []const []const u8) !void
{
if (self.verbose) {
printCmd(cwd, argv);
@@ -595,7 +588,7 @@ pub const Builder = struct {
}
}
- pub fn makePath(self: &Builder, path: []const u8) %void {
+ pub fn makePath(self: &Builder, path: []const u8) !void {
os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| {
warn("Unable to create path {}: {}\n", path, @errorName(err));
return err;
@@ -630,11 +623,11 @@ pub const Builder = struct {
self.installed_files.append(full_path) catch unreachable;
}
- fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) %void {
- return self.copyFileMode(source_path, dest_path, 0o666);
+ fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void {
+ return self.copyFileMode(source_path, dest_path, os.default_file_mode);
}
- fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) %void {
+ fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void {
if (self.verbose) {
warn("cp {} {}\n", source_path, dest_path);
}
@@ -672,7 +665,7 @@ pub const Builder = struct {
}
}
- pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) %[]const u8 {
+ pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
// TODO report error for ambiguous situations
const exe_extension = (Target { .Native = {}}).exeFileExt();
for (self.search_prefixes.toSliceConst()) |search_prefix| {
@@ -721,7 +714,7 @@ pub const Builder = struct {
return error.FileNotFound;
}
- pub fn exec(self: &Builder, argv: []const []const u8) %[]u8 {
+ pub fn exec(self: &Builder, argv: []const []const u8) ![]u8 {
const max_output_size = 100 * 1024;
const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size);
switch (result.term) {
@@ -1180,12 +1173,12 @@ pub const LibExeObjStep = struct {
self.disable_libc = disable;
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(LibExeObjStep, "step", step);
return if (self.is_zig) self.makeZig() else self.makeC();
}
- fn makeZig(self: &LibExeObjStep) %void {
+ fn makeZig(self: &LibExeObjStep) !void {
const builder = self.builder;
assert(self.is_zig);
@@ -1396,7 +1389,7 @@ pub const LibExeObjStep = struct {
}
}
- fn makeC(self: &LibExeObjStep) %void {
+ fn makeC(self: &LibExeObjStep) !void {
const builder = self.builder;
const cc = builder.getCCExe();
@@ -1687,7 +1680,7 @@ pub const TestStep = struct {
self.exec_cmd_args = args;
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(TestStep, "step", step);
const builder = self.builder;
@@ -1796,7 +1789,7 @@ pub const CommandStep = struct {
return self;
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(CommandStep, "step", step);
const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
@@ -1836,14 +1829,17 @@ const InstallArtifactStep = struct {
return self;
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(Self, "step", step);
const builder = self.builder;
- const mode = switch (self.artifact.kind) {
- LibExeObjStep.Kind.Obj => unreachable,
- LibExeObjStep.Kind.Exe => usize(0o755),
- LibExeObjStep.Kind.Lib => if (self.artifact.static) usize(0o666) else usize(0o755),
+ const mode = switch (builtin.os) {
+ builtin.Os.windows => {},
+ else => switch (self.artifact.kind) {
+ LibExeObjStep.Kind.Obj => unreachable,
+ LibExeObjStep.Kind.Exe => u32(0o755),
+ LibExeObjStep.Kind.Lib => if (self.artifact.static) u32(0o666) else u32(0o755),
+ },
};
try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode);
if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
@@ -1868,7 +1864,7 @@ pub const InstallFileStep = struct {
};
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(InstallFileStep, "step", step);
try self.builder.copyFile(self.src_path, self.dest_path);
}
@@ -1889,7 +1885,7 @@ pub const WriteFileStep = struct {
};
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(WriteFileStep, "step", step);
const full_path = self.builder.pathFromRoot(self.file_path);
const full_path_dir = os.path.dirname(full_path);
@@ -1897,7 +1893,7 @@ pub const WriteFileStep = struct {
warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
return err;
};
- io.writeFile(full_path, self.data, self.builder.allocator) catch |err| {
+ io.writeFile(self.builder.allocator, full_path, self.data) catch |err| {
warn("unable to write {}: {}\n", full_path, @errorName(err));
return err;
};
@@ -1917,7 +1913,7 @@ pub const LogStep = struct {
};
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) error!void {
const self = @fieldParentPtr(LogStep, "step", step);
warn("{}", self.data);
}
@@ -1936,7 +1932,7 @@ pub const RemoveDirStep = struct {
};
}
- fn make(step: &Step) %void {
+ fn make(step: &Step) !void {
const self = @fieldParentPtr(RemoveDirStep, "step", step);
const full_path = self.builder.pathFromRoot(self.dir_path);
@@ -1949,12 +1945,12 @@ pub const RemoveDirStep = struct {
pub const Step = struct {
name: []const u8,
- makeFn: fn(self: &Step) %void,
+ makeFn: fn(self: &Step) error!void,
dependencies: ArrayList(&Step),
loop_flag: bool,
done_flag: bool,
- pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)%void) Step {
+ pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)error!void) Step {
return Step {
.name = name,
.makeFn = makeFn,
@@ -1967,7 +1963,7 @@ pub const Step = struct {
return init(name, allocator, makeNoOp);
}
- pub fn make(self: &Step) %void {
+ pub fn make(self: &Step) !void {
if (self.done_flag)
return;
@@ -1979,11 +1975,11 @@ pub const Step = struct {
self.dependencies.append(other) catch unreachable;
}
- fn makeNoOp(self: &Step) %void {}
+ fn makeNoOp(self: &Step) error!void {}
};
fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8,
- filename_name_only: []const u8) %void
+ filename_name_only: []const u8) !void
{
const out_dir = os.path.dirname(output_path);
const out_basename = os.path.basename(output_path);
diff --git a/std/c/index.zig b/std/c/index.zig
index 7b34ccea82..24e24dc3d3 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -20,7 +20,7 @@ pub extern "c" fn open(path: &const u8, oflag: c_int, ...) c_int;
pub extern "c" fn raise(sig: c_int) c_int;
pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize;
pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int;
-pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) c_int;
+pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) isize;
pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int,
fd: c_int, offset: isize) ?&c_void;
pub extern "c" fn munmap(addr: &c_void, len: usize) c_int;
diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig
index 1ebe64d5a4..60610411b5 100644
--- a/std/crypto/throughput_test.zig
+++ b/std/crypto/throughput_test.zig
@@ -18,7 +18,7 @@ const c = @cImport({
const Mb = 1024 * 1024;
-pub fn main() %void {
+pub fn main() !void {
var stdout_file = try std.io.getStdOut();
var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
const stdout = &stdout_out_stream.stream;
diff --git a/std/cstr.zig b/std/cstr.zig
index 987c6d3341..d396dcbce3 100644
--- a/std/cstr.zig
+++ b/std/cstr.zig
@@ -1,8 +1,15 @@
const std = @import("index.zig");
+const builtin = @import("builtin");
const debug = std.debug;
const mem = std.mem;
const assert = debug.assert;
+pub const line_sep = switch (builtin.os) {
+ builtin.Os.windows => "\r\n",
+ else => "\n",
+};
+
+
pub fn len(ptr: &const u8) usize {
var count: usize = 0;
while (ptr[count] != 0) : (count += 1) {}
@@ -39,10 +46,9 @@ fn testCStrFnsImpl() void {
assert(len(c"123456789") == 9);
}
-/// Returns a mutable slice with exactly the same size which is guaranteed to
-/// have a null byte after it.
+/// Returns a mutable slice with 1 more byte of length which is a null byte.
/// Caller owns the returned memory.
-pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) %[]u8 {
+pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 {
const result = try allocator.alloc(u8, slice.len + 1);
mem.copy(u8, result, slice);
result[slice.len] = 0;
@@ -56,7 +62,7 @@ pub const NullTerminated2DArray = struct {
/// Takes N lists of strings, concatenates the lists together, and adds a null terminator
/// Caller must deinit result
- pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) %NullTerminated2DArray {
+ pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray {
var new_len: usize = 1; // 1 for the list null
var byte_count: usize = 0;
for (slices) |slice| {
diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig
index cc5a8bc045..f876b7902d 100644
--- a/std/debug/failing_allocator.zig
+++ b/std/debug/failing_allocator.zig
@@ -28,7 +28,7 @@ pub const FailingAllocator = struct {
};
}
- fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) %[]u8 {
+ fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
if (self.index == self.fail_index) {
return error.OutOfMemory;
@@ -39,7 +39,7 @@ pub const FailingAllocator = struct {
return result;
}
- fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {
+ fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
if (new_size <= old_mem.len) {
self.freed_bytes += old_mem.len - new_size;
diff --git a/std/debug/index.zig b/std/debug/index.zig
index ccf5f6d413..5426a197f2 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -10,26 +10,17 @@ const builtin = @import("builtin");
pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
-error MissingDebugInfo;
-error InvalidDebugInfo;
-error UnsupportedDebugInfo;
-error UnknownObjectFormat;
-error TodoSupportCoffDebugInfo;
-error TodoSupportMachoDebugInfo;
-error TodoSupportCOFFDebugInfo;
-
-
/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
/// TODO atomic/multithread support
-var stderr_file: io.File = undefined;
+var stderr_file: os.File = undefined;
var stderr_file_out_stream: io.FileOutStream = undefined;
-var stderr_stream: ?&io.OutStream = null;
+var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null;
pub fn warn(comptime fmt: []const u8, args: ...) void {
const stderr = getStderrStream() catch return;
stderr.print(fmt, args) catch return;
}
-fn getStderrStream() %&io.OutStream {
+fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) {
if (stderr_stream) |st| {
return st;
} else {
@@ -42,7 +33,7 @@ fn getStderrStream() %&io.OutStream {
}
var self_debug_info: ?&ElfStackTrace = null;
-pub fn getSelfDebugInfo() %&ElfStackTrace {
+pub fn getSelfDebugInfo() !&ElfStackTrace {
if (self_debug_info) |info| {
return info;
} else {
@@ -149,11 +140,8 @@ const WHITE = "\x1b[37;1m";
const DIM = "\x1b[2m";
const RESET = "\x1b[0m";
-error PathNotFound;
-error InvalidDebugInfo;
-
-pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator,
- debug_info: &ElfStackTrace, tty_color: bool) %void
+pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator,
+ debug_info: &ElfStackTrace, tty_color: bool) !void
{
var frame_index: usize = undefined;
var frames_left: usize = undefined;
@@ -174,8 +162,8 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.O
}
}
-pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator,
- debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) %void
+pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
+ debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) !void
{
var ignored_count: usize = 0;
@@ -191,7 +179,7 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat
}
}
-fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) %void {
+fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
if (builtin.os == builtin.Os.windows) {
return error.UnsupportedDebugInfo;
}
@@ -221,7 +209,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
}
} else |err| switch (err) {
- error.EndOfFile, error.PathNotFound => {},
+ error.EndOfFile => {},
else => return err,
}
} else |err| switch (err) {
@@ -232,7 +220,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
}
}
-pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {
+pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
switch (builtin.object_format) {
builtin.ObjectFormat.elf => {
const st = try allocator.create(ElfStackTrace);
@@ -276,8 +264,8 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) %&ElfStackTrace {
}
}
-fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) %void {
- var f = try io.File.openRead(line_info.file_name, allocator);
+fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void {
+ var f = try os.File.openRead(allocator, line_info.file_name);
defer f.close();
// TODO fstat and make sure that the file has the correct size
@@ -310,7 +298,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
}
pub const ElfStackTrace = struct {
- self_exe_file: io.File,
+ self_exe_file: os.File,
elf: elf.Elf,
debug_info: &elf.SectionHeader,
debug_abbrev: &elf.SectionHeader,
@@ -324,7 +312,7 @@ pub const ElfStackTrace = struct {
return self.abbrev_table_list.allocator;
}
- pub fn readString(self: &ElfStackTrace) %[]u8 {
+ pub fn readString(self: &ElfStackTrace) ![]u8 {
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
const in_stream = &in_file_stream.stream;
return readStringRaw(self.allocator(), in_stream);
@@ -387,7 +375,7 @@ const Constant = struct {
payload: []u8,
signed: bool,
- fn asUnsignedLe(self: &const Constant) %u64 {
+ fn asUnsignedLe(self: &const Constant) !u64 {
if (self.payload.len > @sizeOf(u64))
return error.InvalidDebugInfo;
if (self.signed)
@@ -414,7 +402,7 @@ const Die = struct {
return null;
}
- fn getAttrAddr(self: &const Die, id: u64) %u64 {
+ fn getAttrAddr(self: &const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
FormValue.Address => |value| value,
@@ -422,7 +410,7 @@ const Die = struct {
};
}
- fn getAttrSecOffset(self: &const Die, id: u64) %u64 {
+ fn getAttrSecOffset(self: &const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
FormValue.Const => |value| value.asUnsignedLe(),
@@ -431,7 +419,7 @@ const Die = struct {
};
}
- fn getAttrUnsignedLe(self: &const Die, id: u64) %u64 {
+ fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
FormValue.Const => |value| value.asUnsignedLe(),
@@ -439,7 +427,7 @@ const Die = struct {
};
}
- fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) %[]u8 {
+ fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
FormValue.String => |value| value,
@@ -512,7 +500,7 @@ const LineNumberProgram = struct {
};
}
- pub fn checkLineMatch(self: &LineNumberProgram) %?LineInfo {
+ pub fn checkLineMatch(self: &LineNumberProgram) !?LineInfo {
if (self.target_address >= self.prev_address and self.target_address < self.address) {
const file_entry = if (self.prev_file == 0) {
return error.MissingDebugInfo;
@@ -544,7 +532,7 @@ const LineNumberProgram = struct {
}
};
-fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {
+fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 {
var buf = ArrayList(u8).init(allocator);
while (true) {
const byte = try in_stream.readByte();
@@ -555,58 +543,70 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) %[]u8 {
return buf.toSlice();
}
-fn getString(st: &ElfStackTrace, offset: u64) %[]u8 {
+fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
const pos = st.debug_str.offset + offset;
try st.self_exe_file.seekTo(pos);
return st.readString();
}
-fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %[]u8 {
+fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
const buf = try global_allocator.alloc(u8, size);
errdefer global_allocator.free(buf);
if ((try in_stream.read(buf)) < size) return error.EndOfFile;
return buf;
}
-fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {
+fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue { .Block = buf };
}
-fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {
+fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size);
return parseFormValueBlockLen(allocator, in_stream, block_len);
}
-fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) %FormValue {
+fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
return FormValue { .Const = Constant {
.signed = signed,
.payload = try readAllocBytes(allocator, in_stream, size),
}};
}
-fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) %u64 {
+fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
return if (is_64) try in_stream.readIntLe(u64)
else u64(try in_stream.readIntLe(u32)) ;
}
-fn parseFormValueTargetAddrSize(in_stream: &io.InStream) %u64 {
+fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32))
else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64)
else unreachable;
}
-fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) %FormValue {
+fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue { .Ref = buf };
}
-fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) %FormValue {
+fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue {
const block_len = try in_stream.readIntLe(T);
return parseFormValueRefLen(allocator, in_stream, block_len);
}
-fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) %FormValue {
+const ParseFormValueError = error {
+ EndOfStream,
+ Io,
+ BadFd,
+ Unexpected,
+ InvalidDebugInfo,
+ EndOfFile,
+ OutOfMemory,
+};
+
+fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool)
+ ParseFormValueError!FormValue
+{
return switch (form_id) {
DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) },
DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
@@ -656,7 +656,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
};
}
-fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {
+fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable {
const in_file = &st.self_exe_file;
var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream;
@@ -688,7 +688,7 @@ fn parseAbbrevTable(st: &ElfStackTrace) %AbbrevTable {
/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
/// seeks in the stream and parses it.
-fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) %&const AbbrevTable {
+fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable {
for (st.abbrev_table_list.toSlice()) |*header| {
if (header.offset == abbrev_offset) {
return &header.table;
@@ -710,7 +710,7 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&con
return null;
}
-fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) %Die {
+fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) !Die {
const in_file = &st.self_exe_file;
var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream;
@@ -732,7 +732,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) %
return result;
}
-fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) %LineInfo {
+fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) !LineInfo {
const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);
const in_file = &st.self_exe_file;
@@ -747,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
try in_file.seekTo(this_offset);
var is_64: bool = undefined;
- const unit_length = try readInitialLength(in_stream, &is_64);
+ const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
if (unit_length == 0)
return error.MissingDebugInfo;
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
return error.MissingDebugInfo;
}
-fn scanAllCompileUnits(st: &ElfStackTrace) %void {
+fn scanAllCompileUnits(st: &ElfStackTrace) !void {
const debug_info_end = st.debug_info.offset + st.debug_info.size;
var this_unit_offset = st.debug_info.offset;
var cu_index: usize = 0;
@@ -922,7 +922,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {
try st.self_exe_file.seekTo(this_unit_offset);
var is_64: bool = undefined;
- const unit_length = try readInitialLength(in_stream, &is_64);
+ const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
if (unit_length == 0)
return;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
@@ -986,7 +986,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) %void {
}
}
-fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit {
+fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit {
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
const in_stream = &in_file_stream.stream;
for (st.compile_unit_list.toSlice()) |*compile_unit| {
@@ -1022,7 +1022,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) %&const CompileUnit
return error.MissingDebugInfo;
}
-fn readInitialLength(in_stream: &io.InStream, is_64: &bool) %u64 {
+fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 {
const first_32_bits = try in_stream.readIntLe(u32);
*is_64 = (first_32_bits == 0xffffffff);
if (*is_64) {
@@ -1033,7 +1033,7 @@ fn readInitialLength(in_stream: &io.InStream, is_64: &bool) %u64 {
}
}
-fn readULeb128(in_stream: &io.InStream) %u64 {
+fn readULeb128(in_stream: var) !u64 {
var result: u64 = 0;
var shift: usize = 0;
@@ -1054,7 +1054,7 @@ fn readULeb128(in_stream: &io.InStream) %u64 {
}
}
-fn readILeb128(in_stream: &io.InStream) %i64 {
+fn readILeb128(in_stream: var) !i64 {
var result: i64 = 0;
var shift: usize = 0;
diff --git a/std/elf.zig b/std/elf.zig
index 59e2150c69..7e20fa000f 100644
--- a/std/elf.zig
+++ b/std/elf.zig
@@ -1,13 +1,12 @@
const builtin = @import("builtin");
const std = @import("index.zig");
const io = std.io;
+const os = std.os;
const math = std.math;
const mem = std.mem;
const debug = std.debug;
const InStream = std.stream.InStream;
-error InvalidFormat;
-
pub const SHT_NULL = 0;
pub const SHT_PROGBITS = 1;
pub const SHT_SYMTAB = 2;
@@ -65,7 +64,7 @@ pub const SectionHeader = struct {
};
pub const Elf = struct {
- in_file: &io.File,
+ in_file: &os.File,
auto_close_stream: bool,
is_64: bool,
endian: builtin.Endian,
@@ -78,17 +77,17 @@ pub const Elf = struct {
string_section: &SectionHeader,
section_headers: []SectionHeader,
allocator: &mem.Allocator,
- prealloc_file: io.File,
+ prealloc_file: os.File,
/// Call close when done.
- pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) %void {
+ pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void {
try elf.prealloc_file.open(path);
try elf.openFile(allocator, &elf.prealloc_file);
elf.auto_close_stream = true;
}
/// Call close when done.
- pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) %void {
+ pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &os.File) !void {
elf.allocator = allocator;
elf.in_file = file;
elf.auto_close_stream = false;
@@ -239,7 +238,7 @@ pub const Elf = struct {
elf.in_file.close();
}
- pub fn findSection(elf: &Elf, name: []const u8) %?&SectionHeader {
+ pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader {
var file_stream = io.FileInStream.init(elf.in_file);
const in = &file_stream.stream;
@@ -263,7 +262,7 @@ pub const Elf = struct {
return null;
}
- pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) %void {
+ pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void {
try elf.in_file.seekTo(elf_section.offset);
}
};
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index a32a6e0295..56b0add86d 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -24,8 +24,8 @@ const State = enum { // TODO put inside format function and make sure the name a
/// Renders fmt string with args, calling output with slices of bytes.
/// If `output` returns an error, the error is returned from `format` and
/// `output` is not called again.
-pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
- comptime fmt: []const u8, args: ...) %void
+pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
+ comptime fmt: []const u8, args: ...) Errors!void
{
comptime var start_index = 0;
comptime var state = State.Start;
@@ -58,7 +58,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
start_index = i;
},
'}' => {
- try formatValue(args[next_arg], context, output);
+ try formatValue(args[next_arg], context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -110,7 +110,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
},
State.Integer => switch (c) {
'}' => {
- try formatInt(args[next_arg], radix, uppercase, width, context, output);
+ try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -124,7 +124,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
State.IntegerWidth => switch (c) {
'}' => {
width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
- try formatInt(args[next_arg], radix, uppercase, width, context, output);
+ try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -134,7 +134,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
},
State.Float => switch (c) {
'}' => {
- try formatFloatDecimal(args[next_arg], 0, context, output);
+ try formatFloatDecimal(args[next_arg], 0, context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -148,7 +148,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
State.FloatWidth => switch (c) {
'}' => {
width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
- try formatFloatDecimal(args[next_arg], width, context, output);
+ try formatFloatDecimal(args[next_arg], width, context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -159,7 +159,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
State.BufWidth => switch (c) {
'}' => {
width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
- try formatBuf(args[next_arg], width, context, output);
+ try formatBuf(args[next_arg], width, context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -169,7 +169,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
},
State.Character => switch (c) {
'}' => {
- try formatAsciiChar(args[next_arg], context, output);
+ try formatAsciiChar(args[next_arg], context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -191,14 +191,14 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void,
}
}
-pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)%void) %void {
+pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
const T = @typeOf(value);
switch (@typeId(T)) {
builtin.TypeId.Int => {
- return formatInt(value, 10, false, 0, context, output);
+ return formatInt(value, 10, false, 0, context, Errors, output);
},
builtin.TypeId.Float => {
- return formatFloat(value, context, output);
+ return formatFloat(value, context, Errors, output);
},
builtin.TypeId.Void => {
return output(context, "void");
@@ -208,19 +208,19 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
},
builtin.TypeId.Nullable => {
if (value) |payload| {
- return formatValue(payload, context, output);
+ return formatValue(payload, context, Errors, output);
} else {
return output(context, "null");
}
},
builtin.TypeId.ErrorUnion => {
if (value) |payload| {
- return formatValue(payload, context, output);
+ return formatValue(payload, context, Errors, output);
} else |err| {
- return formatValue(err, context, output);
+ return formatValue(err, context, Errors, output);
}
},
- builtin.TypeId.Error => {
+ builtin.TypeId.ErrorSet => {
try output(context, "error.");
return output(context, @errorName(value));
},
@@ -228,7 +228,7 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
if (@typeId(T.Child) == builtin.TypeId.Array and T.Child.Child == u8) {
return output(context, (*value)[0..]);
} else {
- return format(context, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
+ return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
}
},
else => if (@canImplicitCast([]const u8, value)) {
@@ -240,12 +240,12 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
}
}
-pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)%void) %void {
+pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
return output(context, (&c)[0..1]);
}
pub fn formatBuf(buf: []const u8, width: usize,
- context: var, output: fn(@typeOf(context), []const u8)%void) %void
+ context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
{
try output(context, buf);
@@ -256,7 +256,7 @@ pub fn formatBuf(buf: []const u8, width: usize,
}
}
-pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)%void) %void {
+pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
var x = f64(value);
// Errol doesn't handle these special cases.
@@ -290,11 +290,11 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
if (float_decimal.exp != 1) {
try output(context, "e");
- try formatInt(float_decimal.exp - 1, 10, false, 0, context, output);
+ try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);
}
}
-pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn(@typeOf(context), []const u8)%void) %void {
+pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
var x = f64(value);
// Errol doesn't handle these special cases.
@@ -336,17 +336,17 @@ pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn
pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
- context: var, output: fn(@typeOf(context), []const u8)%void) %void
+ context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
{
if (@typeOf(value).is_signed) {
- return formatIntSigned(value, base, uppercase, width, context, output);
+ return formatIntSigned(value, base, uppercase, width, context, Errors, output);
} else {
- return formatIntUnsigned(value, base, uppercase, width, context, output);
+ return formatIntUnsigned(value, base, uppercase, width, context, Errors, output);
}
}
fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
- context: var, output: fn(@typeOf(context), []const u8)%void) %void
+ context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
{
const uint = @IntType(false, @typeOf(value).bit_count);
if (value < 0) {
@@ -354,20 +354,20 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
try output(context, (&minus_sign)[0..1]);
const new_value = uint(-(value + 1)) + 1;
const new_width = if (width == 0) 0 else (width - 1);
- return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
+ return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
} else if (width == 0) {
- return formatIntUnsigned(uint(value), base, uppercase, width, context, output);
+ return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output);
} else {
const plus_sign: u8 = '+';
try output(context, (&plus_sign)[0..1]);
const new_value = uint(value);
const new_width = if (width == 0) 0 else (width - 1);
- return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
+ return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
}
}
fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
- context: var, output: fn(@typeOf(context), []const u8)%void) %void
+ context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void
{
// max_int_digits accounts for the minus sign. when printing an unsigned
// 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:
.out_buf = out_buf,
.index = 0,
};
- formatInt(value, base, uppercase, width, &context, formatIntCallback) catch unreachable;
+ formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable;
return context.index;
}
const FormatIntBuf = struct {
out_buf: []u8,
index: usize,
};
-fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) %void {
+fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) {
mem.copy(u8, context.out_buf[context.index..], bytes);
context.index += bytes.len;
}
-pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T {
+pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {
if (!T.is_signed)
return parseUnsigned(T, buf, radix);
if (buf.len == 0)
@@ -439,14 +439,21 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T {
test "fmt.parseInt" {
assert((parseInt(i32, "-10", 10) catch unreachable) == -10);
assert((parseInt(i32, "+10", 10) catch unreachable) == 10);
- assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidChar);
- assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidChar);
- assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidChar);
+ assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
+ assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter);
+ assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter);
assert((parseInt(u8, "255", 10) catch unreachable) == 255);
assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
}
-pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T {
+const ParseUnsignedError = error {
+ /// The result cannot fit in the type specified
+ Overflow,
+ /// The input had a byte that was not a digit
+ InvalidCharacter,
+};
+
+pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T {
var x: T = 0;
for (buf) |c| {
@@ -458,17 +465,16 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T {
return x;
}
-error InvalidChar;
-fn charToDigit(c: u8, radix: u8) %u8 {
+fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
const value = switch (c) {
'0' ... '9' => c - '0',
'A' ... 'Z' => c - 'A' + 10,
'a' ... 'z' => c - 'a' + 10,
- else => return error.InvalidChar,
+ else => return error.InvalidCharacter,
};
if (value >= radix)
- return error.InvalidChar;
+ return error.InvalidCharacter;
return value;
}
@@ -485,28 +491,26 @@ const BufPrintContext = struct {
remaining: []u8,
};
-error BufferTooSmall;
-fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) %void {
+fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void {
if (context.remaining.len < bytes.len) return error.BufferTooSmall;
mem.copy(u8, context.remaining, bytes);
context.remaining = context.remaining[bytes.len..];
}
-pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) %[]u8 {
+pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
var context = BufPrintContext { .remaining = buf, };
- try format(&context, bufPrintWrite, fmt, args);
+ try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args);
return buf[0..buf.len - context.remaining.len];
}
-pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) %[]u8 {
+pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {
var size: usize = 0;
- // Cannot fail because `countSize` cannot fail.
- format(&size, countSize, fmt, args) catch unreachable;
+ format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
const buf = try allocator.alloc(u8, size);
return bufPrint(buf, fmt, args);
}
-fn countSize(size: &usize, bytes: []const u8) %void {
+fn countSize(size: &usize, bytes: []const u8) (error{}!void) {
*size += bytes.len;
}
@@ -534,7 +538,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u
test "parse u64 digit too big" {
_ = parseUnsigned(u64, "123a", 10) catch |err| {
- if (err == error.InvalidChar) return;
+ if (err == error.InvalidCharacter) return;
unreachable;
};
unreachable;
@@ -567,13 +571,13 @@ test "fmt.format" {
}
{
var buf1: [32]u8 = undefined;
- const value: %i32 = 1234;
+ const value: error!i32 = 1234;
const result = try bufPrint(buf1[0..], "error union: {}\n", value);
assert(mem.eql(u8, result, "error union: 1234\n"));
}
{
var buf1: [32]u8 = undefined;
- const value: %i32 = error.InvalidChar;
+ const value: error!i32 = error.InvalidChar;
const result = try bufPrint(buf1[0..], "error union: {}\n", value);
assert(mem.eql(u8, result, "error union: error.InvalidChar\n"));
}
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 96ec10b933..659783bc84 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -80,7 +80,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
/// Returns the value that was already there.
- pub fn put(hm: &Self, key: K, value: &const V) %?V {
+ pub fn put(hm: &Self, key: K, value: &const V) !?V {
if (hm.entries.len == 0) {
try hm.initCapacity(16);
}
@@ -151,7 +151,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
};
}
- fn initCapacity(hm: &Self, capacity: usize) %void {
+ fn initCapacity(hm: &Self, capacity: usize) !void {
hm.entries = try hm.allocator.alloc(Entry, capacity);
hm.size = 0;
hm.max_distance_from_start_index = 0;
diff --git a/std/heap.zig b/std/heap.zig
index f023e7376d..2ff0e665c9 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -9,8 +9,6 @@ const c = std.c;
const Allocator = mem.Allocator;
-error OutOfMemory;
-
pub const c_allocator = &c_allocator_state;
var c_allocator_state = Allocator {
.allocFn = cAlloc,
@@ -18,14 +16,14 @@ var c_allocator_state = Allocator {
.freeFn = cFree,
};
-fn cAlloc(self: &Allocator, n: usize, alignment: u29) %[]u8 {
+fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 {
return if (c.malloc(usize(n))) |buf|
@ptrCast(&u8, buf)[0..n]
else
error.OutOfMemory;
}
-fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {
+fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
if (c.realloc(old_ptr, new_size)) |buf| {
return @ptrCast(&u8, buf)[0..new_size];
@@ -47,7 +45,7 @@ pub const IncrementingAllocator = struct {
end_index: usize,
heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
- fn init(capacity: usize) %IncrementingAllocator {
+ fn init(capacity: usize) !IncrementingAllocator {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
const p = os.posix;
@@ -105,7 +103,7 @@ pub const IncrementingAllocator = struct {
return self.bytes.len - self.end_index;
}
- fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 {
+ fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
const addr = @ptrToInt(&self.bytes[self.end_index]);
const rem = @rem(addr, alignment);
@@ -120,7 +118,7 @@ pub const IncrementingAllocator = struct {
return result;
}
- fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {
+ fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
if (new_size <= old_mem.len) {
return old_mem[0..new_size];
} else {
diff --git a/std/index.zig b/std/index.zig
index b5a80cba23..8d292c2f5c 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -28,6 +28,7 @@ pub const os = @import("os/index.zig");
pub const rand = @import("rand.zig");
pub const sort = @import("sort.zig");
pub const unicode = @import("unicode.zig");
+pub const zig = @import("zig/index.zig");
test "std" {
// run tests from these
@@ -58,4 +59,5 @@ test "std" {
_ = @import("rand.zig");
_ = @import("sort.zig");
_ = @import("unicode.zig");
+ _ = @import("zig/index.zig");
}
diff --git a/std/io.zig b/std/io.zig
index 2fe57e4dfe..94685c4d03 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -1,12 +1,6 @@
const std = @import("index.zig");
const builtin = @import("builtin");
const Os = builtin.Os;
-const system = switch(builtin.os) {
- Os.linux => @import("os/linux/index.zig"),
- Os.macosx, Os.ios => @import("os/darwin.zig"),
- Os.windows => @import("os/windows/index.zig"),
- else => @compileError("Unsupported OS"),
-};
const c = std.c;
const math = std.math;
@@ -16,65 +10,38 @@ const os = std.os;
const mem = std.mem;
const Buffer = std.Buffer;
const fmt = std.fmt;
+const File = std.os.File;
const is_posix = builtin.os != builtin.Os.windows;
const is_windows = builtin.os == builtin.Os.windows;
-test "import io tests" {
- comptime {
- _ = @import("io_test.zig");
- }
-}
+const GetStdIoErrs = os.WindowsGetStdHandleErrs;
-/// The function received invalid input at runtime. An Invalid error means a
-/// bug in the program that called the function.
-error Invalid;
-
-error DiskQuota;
-error FileTooBig;
-error Io;
-error NoSpaceLeft;
-error BadPerm;
-error BrokenPipe;
-error BadFd;
-error IsDir;
-error NotDir;
-error SymLinkLoop;
-error ProcessFdQuotaExceeded;
-error SystemFdQuotaExceeded;
-error NameTooLong;
-error NoDevice;
-error PathNotFound;
-error OutOfMemory;
-error Unseekable;
-error EndOfFile;
-error FilePosLargerThanPointerRange;
-
-pub fn getStdErr() %File {
+pub fn getStdErr() GetStdIoErrs!File {
const handle = if (is_windows)
- try os.windowsGetStdHandle(system.STD_ERROR_HANDLE)
+ try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE)
else if (is_posix)
- system.STDERR_FILENO
+ os.posix.STDERR_FILENO
else
unreachable;
return File.openHandle(handle);
}
-pub fn getStdOut() %File {
+pub fn getStdOut() GetStdIoErrs!File {
const handle = if (is_windows)
- try os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)
+ try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE)
else if (is_posix)
- system.STDOUT_FILENO
+ os.posix.STDOUT_FILENO
else
unreachable;
return File.openHandle(handle);
}
-pub fn getStdIn() %File {
+pub fn getStdIn() GetStdIoErrs!File {
const handle = if (is_windows)
- try os.windowsGetStdHandle(system.STD_INPUT_HANDLE)
+ try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE)
else if (is_posix)
- system.STDIN_FILENO
+ os.posix.STDIN_FILENO
else
unreachable;
return File.openHandle(handle);
@@ -83,18 +50,21 @@ pub fn getStdIn() %File {
/// Implementation of InStream trait for File
pub const FileInStream = struct {
file: &File,
- stream: InStream,
+ stream: Stream,
+
+ pub const Error = @typeOf(File.read).ReturnType.ErrorSet;
+ pub const Stream = InStream(Error);
pub fn init(file: &File) FileInStream {
return FileInStream {
.file = file,
- .stream = InStream {
+ .stream = Stream {
.readFn = readFn,
},
};
}
- fn readFn(in_stream: &InStream, buffer: []u8) %usize {
+ fn readFn(in_stream: &Stream, buffer: []u8) Error!usize {
const self = @fieldParentPtr(FileInStream, "stream", in_stream);
return self.file.read(buffer);
}
@@ -103,453 +73,202 @@ pub const FileInStream = struct {
/// Implementation of OutStream trait for File
pub const FileOutStream = struct {
file: &File,
- stream: OutStream,
+ stream: Stream,
+
+ pub const Error = File.WriteError;
+ pub const Stream = OutStream(Error);
pub fn init(file: &File) FileOutStream {
return FileOutStream {
.file = file,
- .stream = OutStream {
+ .stream = Stream {
.writeFn = writeFn,
},
};
}
- fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {
+ fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
const self = @fieldParentPtr(FileOutStream, "stream", out_stream);
return self.file.write(bytes);
}
};
-pub const File = struct {
- /// The OS-specific file descriptor or file handle.
- handle: os.FileHandle,
+pub fn InStream(comptime ReadError: type) type {
+ return struct {
+ const Self = this;
+ pub const Error = ReadError;
- /// `path` may need to be copied in memory to add a null terminating byte. In this case
- /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
- /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
- /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
- /// Call close to clean up.
- pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) %File {
- if (is_posix) {
- const flags = system.O_LARGEFILE|system.O_RDONLY;
- const fd = try os.posixOpen(path, flags, 0, allocator);
- return openHandle(fd);
- } else if (is_windows) {
- const handle = try os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
- system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
- return openHandle(handle);
- } else {
- unreachable;
- }
- }
+ /// Return the number of bytes read. If the number read is smaller than buf.len, it
+ /// means the stream reached the end. Reaching the end of a stream is not an error
+ /// condition.
+ readFn: fn(self: &Self, buffer: []u8) Error!usize,
- /// Calls `openWriteMode` with 0o666 for the mode.
- pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) %File {
- return openWriteMode(path, 0o666, allocator);
+ /// Replaces `buffer` contents by reading from the stream until it is finished.
+ /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
+ /// the contents read from the stream are lost.
+ pub fn readAllBuffer(self: &Self, buffer: &Buffer, max_size: usize) !void {
+ try buffer.resize(0);
- }
+ var actual_buf_len: usize = 0;
+ while (true) {
+ const dest_slice = buffer.toSlice()[actual_buf_len..];
+ const bytes_read = try self.readFn(self, dest_slice);
+ actual_buf_len += bytes_read;
- /// `path` may need to be copied in memory to add a null terminating byte. In this case
- /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
- /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
- /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
- /// Call close to clean up.
- pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) %File {
- if (is_posix) {
- const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
- const fd = try os.posixOpen(path, flags, mode, allocator);
- return openHandle(fd);
- } else if (is_windows) {
- const handle = try os.windowsOpen(path, system.GENERIC_WRITE,
- system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE,
- system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL, allocator);
- return openHandle(handle);
- } else {
- unreachable;
- }
-
- }
-
- pub fn openHandle(handle: os.FileHandle) File {
- return File {
- .handle = handle,
- };
- }
-
-
- /// Upon success, the stream is in an uninitialized state. To continue using it,
- /// you must use the open() function.
- pub fn close(self: &File) void {
- os.close(self.handle);
- self.handle = undefined;
- }
-
- /// Calls `os.isTty` on `self.handle`.
- pub fn isTty(self: &File) bool {
- return os.isTty(self.handle);
- }
-
- pub fn seekForward(self: &File, amount: isize) %void {
- switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
- const result = system.lseek(self.handle, amount, system.SEEK_CUR);
- const err = system.getErrno(result);
- if (err > 0) {
- return switch (err) {
- system.EBADF => error.BadFd,
- system.EINVAL => error.Unseekable,
- system.EOVERFLOW => error.Unseekable,
- system.ESPIPE => error.Unseekable,
- system.ENXIO => error.Unseekable,
- else => os.unexpectedErrorPosix(err),
- };
- }
- },
- Os.windows => {
- if (system.SetFilePointerEx(self.handle, amount, null, system.FILE_CURRENT) == 0) {
- const err = system.GetLastError();
- return switch (err) {
- system.ERROR.INVALID_PARAMETER => error.BadFd,
- else => os.unexpectedErrorWindows(err),
- };
- }
- },
- else => @compileError("unsupported OS"),
- }
- }
-
- pub fn seekTo(self: &File, pos: usize) %void {
- switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
- const ipos = try math.cast(isize, pos);
- const result = system.lseek(self.handle, ipos, system.SEEK_SET);
- const err = system.getErrno(result);
- if (err > 0) {
- return switch (err) {
- system.EBADF => error.BadFd,
- system.EINVAL => error.Unseekable,
- system.EOVERFLOW => error.Unseekable,
- system.ESPIPE => error.Unseekable,
- system.ENXIO => error.Unseekable,
- else => os.unexpectedErrorPosix(err),
- };
- }
- },
- Os.windows => {
- const ipos = try math.cast(isize, pos);
- if (system.SetFilePointerEx(self.handle, ipos, null, system.FILE_BEGIN) == 0) {
- const err = system.GetLastError();
- return switch (err) {
- system.ERROR.INVALID_PARAMETER => error.BadFd,
- else => os.unexpectedErrorWindows(err),
- };
- }
- },
- else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
- }
- }
-
- pub fn getPos(self: &File) %usize {
- switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
- const result = system.lseek(self.handle, 0, system.SEEK_CUR);
- const err = system.getErrno(result);
- if (err > 0) {
- return switch (err) {
- system.EBADF => error.BadFd,
- system.EINVAL => error.Unseekable,
- system.EOVERFLOW => error.Unseekable,
- system.ESPIPE => error.Unseekable,
- system.ENXIO => error.Unseekable,
- else => os.unexpectedErrorPosix(err),
- };
- }
- return result;
- },
- Os.windows => {
- var pos : system.LARGE_INTEGER = undefined;
- if (system.SetFilePointerEx(self.handle, 0, &pos, system.FILE_CURRENT) == 0) {
- const err = system.GetLastError();
- return switch (err) {
- system.ERROR.INVALID_PARAMETER => error.BadFd,
- else => os.unexpectedErrorWindows(err),
- };
+ if (bytes_read != dest_slice.len) {
+ buffer.shrink(actual_buf_len);
+ return;
}
- assert(pos >= 0);
- if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) {
- if (pos > @maxValue(usize)) {
- return error.FilePosLargerThanPointerRange;
- }
- }
-
- return usize(pos);
- },
- else => @compileError("unsupported OS"),
- }
- }
-
- pub fn getEndPos(self: &File) %usize {
- if (is_posix) {
- var stat: system.Stat = undefined;
- const err = system.getErrno(system.fstat(self.handle, &stat));
- if (err > 0) {
- return switch (err) {
- system.EBADF => error.BadFd,
- system.ENOMEM => error.SystemResources,
- else => os.unexpectedErrorPosix(err),
- };
+ const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
+ if (new_buf_size == actual_buf_len)
+ return error.StreamTooLong;
+ try buffer.resize(new_buf_size);
}
-
- return usize(stat.size);
- } else if (is_windows) {
- var file_size: system.LARGE_INTEGER = undefined;
- if (system.GetFileSizeEx(self.handle, &file_size) == 0) {
- const err = system.GetLastError();
- return switch (err) {
- else => os.unexpectedErrorWindows(err),
- };
- }
- if (file_size < 0)
- return error.Overflow;
- return math.cast(usize, u64(file_size));
- } else {
- unreachable;
}
- }
- pub fn read(self: &File, buffer: []u8) %usize {
- if (is_posix) {
- var index: usize = 0;
- while (index < buffer.len) {
- const amt_read = system.read(self.handle, &buffer[index], buffer.len - index);
- const read_err = system.getErrno(amt_read);
- if (read_err > 0) {
- switch (read_err) {
- system.EINTR => continue,
- system.EINVAL => unreachable,
- system.EFAULT => unreachable,
- system.EBADF => return error.BadFd,
- system.EIO => return error.Io,
- else => return os.unexpectedErrorPosix(read_err),
- }
+ /// Allocates enough memory to hold all the contents of the stream. If the allocated
+ /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
+ /// Caller owns returned memory.
+ /// If this function returns an error, the contents from the stream read so far are lost.
+ pub fn readAllAlloc(self: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 {
+ var buf = Buffer.initNull(allocator);
+ defer buf.deinit();
+
+ try self.readAllBuffer(&buf, max_size);
+ return buf.toOwnedSlice();
+ }
+
+ /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
+ /// Does not include the delimiter in the result.
+ /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
+ /// read from the stream so far are lost.
+ pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
+ try buf.resize(0);
+
+ while (true) {
+ var byte: u8 = try self.readByte();
+
+ if (byte == delimiter) {
+ return;
}
- if (amt_read == 0) return index;
- index += amt_read;
- }
- return index;
- } else if (is_windows) {
- var index: usize = 0;
- while (index < buffer.len) {
- const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buffer.len - index));
- var amt_read: system.DWORD = undefined;
- if (system.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) {
- const err = system.GetLastError();
- return switch (err) {
- system.ERROR.OPERATION_ABORTED => continue,
- system.ERROR.BROKEN_PIPE => return index,
- else => os.unexpectedErrorWindows(err),
- };
+
+ if (buf.len() == max_size) {
+ return error.StreamTooLong;
}
- if (amt_read == 0) return index;
- index += amt_read;
+
+ try buf.appendByte(byte);
}
- return index;
- } else {
- unreachable;
}
- }
- fn write(self: &File, bytes: []const u8) %void {
- if (is_posix) {
- try os.posixWrite(self.handle, bytes);
- } else if (is_windows) {
- try os.windowsWrite(self.handle, bytes);
- } else {
- @compileError("Unsupported OS");
+ /// Allocates enough memory to read until `delimiter`. If the allocated
+ /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
+ /// Caller owns returned memory.
+ /// If this function returns an error, the contents from the stream read so far are lost.
+ pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator,
+ delimiter: u8, max_size: usize) ![]u8
+ {
+ var buf = Buffer.initNull(allocator);
+ defer buf.deinit();
+
+ try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
+ return buf.toOwnedSlice();
}
- }
-};
-error StreamTooLong;
-error EndOfStream;
+ /// Returns the number of bytes read. If the number read is smaller than buf.len, it
+ /// means the stream reached the end. Reaching the end of a stream is not an error
+ /// condition.
+ pub fn read(self: &Self, buffer: []u8) !usize {
+ return self.readFn(self, buffer);
+ }
-pub const InStream = struct {
- /// Return the number of bytes read. If the number read is smaller than buf.len, it
- /// means the stream reached the end. Reaching the end of a stream is not an error
- /// condition.
- readFn: fn(self: &InStream, buffer: []u8) %usize,
+ /// Same as `read` but end of stream returns `error.EndOfStream`.
+ pub fn readNoEof(self: &Self, buf: []u8) !void {
+ const amt_read = try self.read(buf);
+ if (amt_read < buf.len) return error.EndOfStream;
+ }
- /// Replaces `buffer` contents by reading from the stream until it is finished.
- /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and
- /// the contents read from the stream are lost.
- pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) %void {
- try buffer.resize(0);
+ /// Reads 1 byte from the stream or returns `error.EndOfStream`.
+ pub fn readByte(self: &Self) !u8 {
+ var result: [1]u8 = undefined;
+ try self.readNoEof(result[0..]);
+ return result[0];
+ }
- var actual_buf_len: usize = 0;
- while (true) {
- const dest_slice = buffer.toSlice()[actual_buf_len..];
- const bytes_read = try self.readFn(self, dest_slice);
- actual_buf_len += bytes_read;
+ /// Same as `readByte` except the returned byte is signed.
+ pub fn readByteSigned(self: &Self) !i8 {
+ return @bitCast(i8, try self.readByte());
+ }
- if (bytes_read != dest_slice.len) {
- buffer.shrink(actual_buf_len);
- return;
+ pub fn readIntLe(self: &Self, comptime T: type) !T {
+ return self.readInt(builtin.Endian.Little, T);
+ }
+
+ pub fn readIntBe(self: &Self, comptime T: type) !T {
+ return self.readInt(builtin.Endian.Big, T);
+ }
+
+ pub fn readInt(self: &Self, endian: builtin.Endian, comptime T: type) !T {
+ var bytes: [@sizeOf(T)]u8 = undefined;
+ try self.readNoEof(bytes[0..]);
+ return mem.readInt(bytes, T, endian);
+ }
+
+ pub fn readVarInt(self: &Self, endian: builtin.Endian, comptime T: type, size: usize) !T {
+ assert(size <= @sizeOf(T));
+ assert(size <= 8);
+ var input_buf: [8]u8 = undefined;
+ const input_slice = input_buf[0..size];
+ try self.readNoEof(input_slice);
+ return mem.readInt(input_slice, T, endian);
+ }
+ };
+}
+
+pub fn OutStream(comptime WriteError: type) type {
+ return struct {
+ const Self = this;
+ pub const Error = WriteError;
+
+ writeFn: fn(self: &Self, bytes: []const u8) Error!void,
+
+ pub fn print(self: &Self, comptime format: []const u8, args: ...) !void {
+ return std.fmt.format(self, Error, self.writeFn, format, args);
+ }
+
+ pub fn write(self: &Self, bytes: []const u8) !void {
+ return self.writeFn(self, bytes);
+ }
+
+ pub fn writeByte(self: &Self, byte: u8) !void {
+ const slice = (&byte)[0..1];
+ return self.writeFn(self, slice);
+ }
+
+ pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void {
+ const slice = (&byte)[0..1];
+ var i: usize = 0;
+ while (i < n) : (i += 1) {
+ try self.writeFn(self, slice);
}
-
- const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
- if (new_buf_size == actual_buf_len)
- return error.StreamTooLong;
- try buffer.resize(new_buf_size);
}
- }
+ };
+}
- /// Allocates enough memory to hold all the contents of the stream. If the allocated
- /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
- /// Caller owns returned memory.
- /// If this function returns an error, the contents from the stream read so far are lost.
- pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) %[]u8 {
- var buf = Buffer.initNull(allocator);
- defer buf.deinit();
-
- try self.readAllBuffer(&buf, max_size);
- return buf.toOwnedSlice();
- }
-
- /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
- /// Does not include the delimiter in the result.
- /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
- /// read from the stream so far are lost.
- pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) %void {
- try buf.resize(0);
-
- while (true) {
- var byte: u8 = try self.readByte();
-
- if (byte == delimiter) {
- return;
- }
-
- if (buf.len() == max_size) {
- return error.StreamTooLong;
- }
-
- try buf.appendByte(byte);
- }
- }
-
- /// Allocates enough memory to read until `delimiter`. If the allocated
- /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
- /// Caller owns returned memory.
- /// If this function returns an error, the contents from the stream read so far are lost.
- pub fn readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator,
- delimiter: u8, max_size: usize) %[]u8
- {
- var buf = Buffer.initNull(allocator);
- defer buf.deinit();
-
- try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
- return buf.toOwnedSlice();
- }
-
- /// Returns the number of bytes read. If the number read is smaller than buf.len, it
- /// means the stream reached the end. Reaching the end of a stream is not an error
- /// condition.
- pub fn read(self: &InStream, buffer: []u8) %usize {
- return self.readFn(self, buffer);
- }
-
- /// Same as `read` but end of stream returns `error.EndOfStream`.
- pub fn readNoEof(self: &InStream, buf: []u8) %void {
- const amt_read = try self.read(buf);
- if (amt_read < buf.len) return error.EndOfStream;
- }
-
- /// Reads 1 byte from the stream or returns `error.EndOfStream`.
- pub fn readByte(self: &InStream) %u8 {
- var result: [1]u8 = undefined;
- try self.readNoEof(result[0..]);
- return result[0];
- }
-
- /// Same as `readByte` except the returned byte is signed.
- pub fn readByteSigned(self: &InStream) %i8 {
- return @bitCast(i8, try self.readByte());
- }
-
- pub fn readIntLe(self: &InStream, comptime T: type) %T {
- return self.readInt(builtin.Endian.Little, T);
- }
-
- pub fn readIntBe(self: &InStream, comptime T: type) %T {
- return self.readInt(builtin.Endian.Big, T);
- }
-
- pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) %T {
- var bytes: [@sizeOf(T)]u8 = undefined;
- try self.readNoEof(bytes[0..]);
- return mem.readInt(bytes, T, endian);
- }
-
- pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) %T {
- assert(size <= @sizeOf(T));
- assert(size <= 8);
- var input_buf: [8]u8 = undefined;
- const input_slice = input_buf[0..size];
- try self.readNoEof(input_slice);
- return mem.readInt(input_slice, T, endian);
- }
-
-
-};
-
-pub const OutStream = struct {
- writeFn: fn(self: &OutStream, bytes: []const u8) %void,
-
- pub fn print(self: &OutStream, comptime format: []const u8, args: ...) %void {
- return std.fmt.format(self, self.writeFn, format, args);
- }
-
- pub fn write(self: &OutStream, bytes: []const u8) %void {
- return self.writeFn(self, bytes);
- }
-
- pub fn writeByte(self: &OutStream, byte: u8) %void {
- const slice = (&byte)[0..1];
- return self.writeFn(self, slice);
- }
-
- pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) %void {
- const slice = (&byte)[0..1];
- var i: usize = 0;
- while (i < n) : (i += 1) {
- try self.writeFn(self, slice);
- }
- }
-};
-
-/// `path` may need to be copied in memory to add a null terminating byte. In this case
-/// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed
-/// size buffer is too small, and the provided allocator is null, `error.NameTooLong` is returned.
-/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
-pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) %void {
- var file = try File.openWrite(path, allocator);
+/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
+pub fn writeFile(allocator: &mem.Allocator, path: []const u8, data: []const u8) !void {
+ var file = try File.openWrite(allocator, path);
defer file.close();
try file.write(data);
}
/// On success, caller owns returned buffer.
-pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) %[]u8 {
- return readFileAllocExtra(path, allocator, 0);
-}
-/// On success, caller owns returned buffer.
-/// Allocates extra_len extra bytes at the end of the file buffer, which are uninitialized.
-pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len: usize) %[]u8 {
- var file = try File.openRead(path, allocator);
+pub fn readFileAlloc(allocator: &mem.Allocator, path: []const u8) ![]u8 {
+ var file = try File.openRead(allocator, path);
defer file.close();
const size = try file.getEndPos();
- const buf = try allocator.alloc(u8, size + extra_len);
+ const buf = try allocator.alloc(u8, size);
errdefer allocator.free(buf);
var adapter = FileInStream.init(&file);
@@ -557,21 +276,24 @@ pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len
return buf;
}
-pub const BufferedInStream = BufferedInStreamCustom(os.page_size);
+pub fn BufferedInStream(comptime Error: type) type {
+ return BufferedInStreamCustom(os.page_size, Error);
+}
-pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
+pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type {
return struct {
const Self = this;
+ const Stream = InStream(Error);
- pub stream: InStream,
+ pub stream: Stream,
- unbuffered_in_stream: &InStream,
+ unbuffered_in_stream: &Stream,
buffer: [buffer_size]u8,
start_index: usize,
end_index: usize,
- pub fn init(unbuffered_in_stream: &InStream) Self {
+ pub fn init(unbuffered_in_stream: &Stream) Self {
return Self {
.unbuffered_in_stream = unbuffered_in_stream,
.buffer = undefined,
@@ -583,13 +305,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
.start_index = buffer_size,
.end_index = buffer_size,
- .stream = InStream {
+ .stream = Stream {
.readFn = readFn,
},
};
}
- fn readFn(in_stream: &InStream, dest: []u8) %usize {
+ fn readFn(in_stream: &Stream, dest: []u8) !usize {
const self = @fieldParentPtr(Self, "stream", in_stream);
var dest_index: usize = 0;
@@ -628,39 +350,40 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type {
};
}
-pub const BufferedOutStream = BufferedOutStreamCustom(os.page_size);
+pub fn BufferedOutStream(comptime Error: type) type {
+ return BufferedOutStreamCustom(os.page_size, Error);
+}
-pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type {
+pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type {
return struct {
const Self = this;
+ pub const Stream = OutStream(Error);
+ pub const Error = OutStreamError;
- pub stream: OutStream,
+ pub stream: Stream,
- unbuffered_out_stream: &OutStream,
+ unbuffered_out_stream: &Stream,
buffer: [buffer_size]u8,
index: usize,
- pub fn init(unbuffered_out_stream: &OutStream) Self {
+ pub fn init(unbuffered_out_stream: &Stream) Self {
return Self {
.unbuffered_out_stream = unbuffered_out_stream,
.buffer = undefined,
.index = 0,
- .stream = OutStream {
+ .stream = Stream {
.writeFn = writeFn,
},
};
}
- pub fn flush(self: &Self) %void {
- if (self.index == 0)
- return;
-
+ pub fn flush(self: &Self) !void {
try self.unbuffered_out_stream.write(self.buffer[0..self.index]);
self.index = 0;
}
- fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {
+ fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
const self = @fieldParentPtr(Self, "stream", out_stream);
if (bytes.len >= self.buffer.len) {
@@ -687,20 +410,71 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type {
/// Implementation of OutStream trait for Buffer
pub const BufferOutStream = struct {
buffer: &Buffer,
- stream: OutStream,
+ stream: Stream,
+
+ pub const Error = error{OutOfMemory};
+ pub const Stream = OutStream(Error);
pub fn init(buffer: &Buffer) BufferOutStream {
return BufferOutStream {
.buffer = buffer,
- .stream = OutStream {
+ .stream = Stream {
.writeFn = writeFn,
},
};
}
- fn writeFn(out_stream: &OutStream, bytes: []const u8) %void {
+ fn writeFn(out_stream: &Stream, bytes: []const u8) !void {
const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
return self.buffer.append(bytes);
}
};
+
+pub const BufferedAtomicFile = struct {
+ atomic_file: os.AtomicFile,
+ file_stream: FileOutStream,
+ buffered_stream: BufferedOutStream(FileOutStream.Error),
+
+ pub fn create(allocator: &mem.Allocator, dest_path: []const u8) !&BufferedAtomicFile {
+ // TODO with well defined copy elision we don't need this allocation
+ var self = try allocator.create(BufferedAtomicFile);
+ errdefer allocator.destroy(self);
+
+ *self = BufferedAtomicFile {
+ .atomic_file = undefined,
+ .file_stream = undefined,
+ .buffered_stream = undefined,
+ };
+
+ self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode);
+ errdefer self.atomic_file.deinit();
+
+ self.file_stream = FileOutStream.init(&self.atomic_file.file);
+ self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream);
+ return self;
+ }
+
+ /// always call destroy, even after successful finish()
+ pub fn destroy(self: &BufferedAtomicFile) void {
+ const allocator = self.atomic_file.allocator;
+ self.atomic_file.deinit();
+ allocator.destroy(self);
+ }
+
+ pub fn finish(self: &BufferedAtomicFile) !void {
+ try self.buffered_stream.flush();
+ try self.atomic_file.finish();
+ }
+
+ pub fn stream(self: &BufferedAtomicFile) &OutStream(FileOutStream.Error) {
+ return &self.buffered_stream.stream;
+ }
+};
+
+test "import io tests" {
+ comptime {
+ _ = @import("io_test.zig");
+ }
+}
+
diff --git a/std/io_test.zig b/std/io_test.zig
index 1767a546ea..993ec84d20 100644
--- a/std/io_test.zig
+++ b/std/io_test.zig
@@ -13,11 +13,11 @@ test "write a file, read it, then delete it" {
rng.fillBytes(data[0..]);
const tmp_file_name = "temp_test_file.txt";
{
- var file = try io.File.openWrite(tmp_file_name, allocator);
+ var file = try os.File.openWrite(allocator, tmp_file_name);
defer file.close();
var file_out_stream = io.FileOutStream.init(&file);
- var buf_stream = io.BufferedOutStream.init(&file_out_stream.stream);
+ var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream);
const st = &buf_stream.stream;
try st.print("begin");
try st.write(data[0..]);
@@ -25,7 +25,7 @@ test "write a file, read it, then delete it" {
try buf_stream.flush();
}
{
- var file = try io.File.openRead(tmp_file_name, allocator);
+ var file = try os.File.openRead(allocator, tmp_file_name);
defer file.close();
const file_size = try file.getEndPos();
@@ -33,7 +33,7 @@ test "write a file, read it, then delete it" {
assert(file_size == expected_file_size);
var file_in_stream = io.FileInStream.init(&file);
- var buf_stream = io.BufferedInStream.init(&file_in_stream.stream);
+ var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream);
const st = &buf_stream.stream;
const contents = try st.readAllAlloc(allocator, 2 * 1024);
defer allocator.free(contents);
diff --git a/std/linked_list.zig b/std/linked_list.zig
index ffd68d5147..a6ab093341 100644
--- a/std/linked_list.zig
+++ b/std/linked_list.zig
@@ -190,7 +190,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
///
/// Returns:
/// A pointer to the new node.
- pub fn allocateNode(list: &Self, allocator: &Allocator) %&Node {
+ pub fn allocateNode(list: &Self, allocator: &Allocator) !&Node {
comptime assert(!isIntrusive());
return allocator.create(Node);
}
@@ -213,7 +213,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
///
/// Returns:
/// A pointer to the new node.
- pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) %&Node {
+ pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) !&Node {
comptime assert(!isIntrusive());
var node = try list.allocateNode(allocator);
*node = Node.init(data);
diff --git a/std/math/index.zig b/std/math/index.zig
index 64d24a4dfd..f8668cc00d 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -191,30 +191,26 @@ test "math.max" {
assert(max(i32(-1), i32(2)) == 2);
}
-error Overflow;
-pub fn mul(comptime T: type, a: T, b: T) %T {
+pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
var answer: T = undefined;
return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
-error Overflow;
-pub fn add(comptime T: type, a: T, b: T) %T {
+pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
var answer: T = undefined;
return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
-error Overflow;
-pub fn sub(comptime T: type, a: T, b: T) %T {
+pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
var answer: T = undefined;
return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
-pub fn negate(x: var) %@typeOf(x) {
+pub fn negate(x: var) !@typeOf(x) {
return sub(@typeOf(x), 0, x);
}
-error Overflow;
-pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) %T {
+pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
var answer: T = undefined;
return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;
}
@@ -323,8 +319,7 @@ fn testOverflow() void {
}
-error Overflow;
-pub fn absInt(x: var) %@typeOf(x) {
+pub fn absInt(x: var) !@typeOf(x) {
const T = @typeOf(x);
comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
comptime assert(T.is_signed); // must pass a signed integer to absInt
@@ -347,9 +342,7 @@ fn testAbsInt() void {
pub const absFloat = @import("fabs.zig").fabs;
-error DivisionByZero;
-error Overflow;
-pub fn divTrunc(comptime T: type, numerator: T, denominator: T) %T {
+pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false);
if (denominator == 0)
return error.DivisionByZero;
@@ -372,9 +365,7 @@ fn testDivTrunc() void {
assert((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0);
}
-error DivisionByZero;
-error Overflow;
-pub fn divFloor(comptime T: type, numerator: T, denominator: T) %T {
+pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false);
if (denominator == 0)
return error.DivisionByZero;
@@ -397,10 +388,7 @@ fn testDivFloor() void {
assert((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0);
}
-error DivisionByZero;
-error Overflow;
-error UnexpectedRemainder;
-pub fn divExact(comptime T: type, numerator: T, denominator: T) %T {
+pub fn divExact(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false);
if (denominator == 0)
return error.DivisionByZero;
@@ -428,9 +416,7 @@ fn testDivExact() void {
if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder);
}
-error DivisionByZero;
-error NegativeDenominator;
-pub fn mod(comptime T: type, numerator: T, denominator: T) %T {
+pub fn mod(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false);
if (denominator == 0)
return error.DivisionByZero;
@@ -455,9 +441,7 @@ fn testMod() void {
if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
}
-error DivisionByZero;
-error NegativeDenominator;
-pub fn rem(comptime T: type, numerator: T, denominator: T) %T {
+pub fn rem(comptime T: type, numerator: T, denominator: T) !T {
@setRuntimeSafety(false);
if (denominator == 0)
return error.DivisionByZero;
@@ -505,8 +489,7 @@ test "math.absCast" {
/// Returns the negation of the integer parameter.
/// Result is a signed integer.
-error Overflow;
-pub fn negateCast(x: var) %@IntType(true, @typeOf(x).bit_count) {
+pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) {
if (@typeOf(x).is_signed)
return negate(x);
@@ -532,8 +515,7 @@ test "math.negateCast" {
/// Cast an integer to a different integer type. If the value doesn't fit,
/// return an error.
-error Overflow;
-pub fn cast(comptime T: type, x: var) %T {
+pub fn cast(comptime T: type, x: var) !T {
comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer
if (x > @maxValue(T)) {
return error.Overflow;
diff --git a/std/mem.zig b/std/mem.zig
index 86bf5e2f3d..25c0648888 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -4,13 +4,13 @@ const assert = debug.assert;
const math = std.math;
const builtin = @import("builtin");
-error OutOfMemory;
-
pub const Allocator = struct {
+ const Error = error {OutOfMemory};
+
/// Allocate byte_count bytes and return them in a slice, with the
/// slice's pointer aligned at least to alignment bytes.
/// The returned newly allocated memory is undefined.
- allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) %[]u8,
+ allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
/// If `new_byte_count > old_mem.len`:
/// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
@@ -21,12 +21,12 @@ pub const Allocator = struct {
/// * alignment <= alignment of old_mem.ptr
///
/// The returned newly allocated memory is undefined.
- reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) %[]u8,
+ reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
freeFn: fn (self: &Allocator, old_mem: []u8) void,
- fn create(self: &Allocator, comptime T: type) %&T {
+ fn create(self: &Allocator, comptime T: type) !&T {
const slice = try self.alloc(T, 1);
return &slice[0];
}
@@ -35,14 +35,14 @@ pub const Allocator = struct {
self.free(ptr[0..1]);
}
- fn alloc(self: &Allocator, comptime T: type, n: usize) %[]T {
+ fn alloc(self: &Allocator, comptime T: type, n: usize) ![]T {
return self.alignedAlloc(T, @alignOf(T), n);
}
fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
- n: usize) %[]align(alignment) T
+ n: usize) ![]align(alignment) T
{
- const byte_count = try math.mul(usize, @sizeOf(T), n);
+ const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
const byte_slice = try self.allocFn(self, byte_count, alignment);
// This loop should get optimized out in ReleaseFast mode
for (byte_slice) |*byte| {
@@ -51,19 +51,19 @@ pub const Allocator = struct {
return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
}
- fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) %[]T {
+ fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) ![]T {
return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
}
fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,
- old_mem: []align(alignment) T, n: usize) %[]align(alignment) T
+ old_mem: []align(alignment) T, n: usize) ![]align(alignment) T
{
if (old_mem.len == 0) {
return self.alloc(T, n);
}
const old_byte_slice = ([]u8)(old_mem);
- const byte_count = try math.mul(usize, @sizeOf(T), n);
+ const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
// This loop should get optimized out in ReleaseFast mode
for (byte_slice[old_byte_slice.len..]) |*byte| {
@@ -123,7 +123,7 @@ pub const FixedBufferAllocator = struct {
};
}
- fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 {
+ fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
const addr = @ptrToInt(&self.buffer[self.end_index]);
const rem = @rem(addr, alignment);
@@ -138,7 +138,7 @@ pub const FixedBufferAllocator = struct {
return result;
}
- fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 {
+ fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
if (new_size <= old_mem.len) {
return old_mem[0..new_size];
} else {
@@ -197,7 +197,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
}
/// Copies ::m to newly allocated memory. Caller is responsible to free it.
-pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) %[]T {
+pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
const new_buf = try allocator.alloc(T, m.len);
copy(T, new_buf, m);
return new_buf;
@@ -428,7 +428,7 @@ const SplitIterator = struct {
/// Naively combines a series of strings with a separator.
/// Allocates memory for the result, which must be freed by the caller.
-pub fn join(allocator: &Allocator, sep: u8, strings: ...) %[]u8 {
+pub fn join(allocator: &Allocator, sep: u8, strings: ...) ![]u8 {
comptime assert(strings.len >= 1);
var total_strings_len: usize = strings.len; // 1 sep per string
{
diff --git a/std/net.zig b/std/net.zig
index 4fbfd9b9aa..1140b6449b 100644
--- a/std/net.zig
+++ b/std/net.zig
@@ -5,19 +5,10 @@ const endian = std.endian;
// TODO don't trust this file, it bit rotted. start over
-error SigInterrupt;
-error Io;
-error TimedOut;
-error ConnectionReset;
-error ConnectionRefused;
-error OutOfMemory;
-error NotSocket;
-error BadFd;
-
const Connection = struct {
socket_fd: i32,
- pub fn send(c: Connection, buf: []const u8) %usize {
+ pub fn send(c: Connection, buf: []const u8) !usize {
const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
const send_err = linux.getErrno(send_ret);
switch (send_err) {
@@ -31,7 +22,7 @@ const Connection = struct {
}
}
- pub fn recv(c: Connection, buf: []u8) %[]u8 {
+ pub fn recv(c: Connection, buf: []u8) ![]u8 {
const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
const recv_err = linux.getErrno(recv_ret);
switch (recv_err) {
@@ -48,7 +39,7 @@ const Connection = struct {
}
}
- pub fn close(c: Connection) %void {
+ pub fn close(c: Connection) !void {
switch (linux.getErrno(linux.close(c.socket_fd))) {
0 => return,
linux.EBADF => unreachable,
@@ -66,7 +57,7 @@ const Address = struct {
sort_key: i32,
};
-pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {
+pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
if (hostname.len == 0) {
unreachable; // TODO
@@ -75,7 +66,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address {
unreachable; // TODO
}
-pub fn connectAddr(addr: &Address, port: u16) %Connection {
+pub fn connectAddr(addr: &Address, port: u16) !Connection {
const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
const socket_err = linux.getErrno(socket_ret);
if (socket_err > 0) {
@@ -118,7 +109,7 @@ pub fn connectAddr(addr: &Address, port: u16) %Connection {
};
}
-pub fn connect(hostname: []const u8, port: u16) %Connection {
+pub fn connect(hostname: []const u8, port: u16) !Connection {
var addrs_buf: [1]Address = undefined;
const addrs_slice = try lookup(hostname, addrs_buf[0..]);
const main_addr = &addrs_slice[0];
@@ -126,9 +117,7 @@ pub fn connect(hostname: []const u8, port: u16) %Connection {
return connectAddr(main_addr, port);
}
-error InvalidIpLiteral;
-
-pub fn parseIpLiteral(buf: []const u8) %Address {
+pub fn parseIpLiteral(buf: []const u8) !Address {
return error.InvalidIpLiteral;
}
@@ -146,12 +135,7 @@ fn hexDigit(c: u8) u8 {
}
}
-error InvalidChar;
-error Overflow;
-error JunkAtEnd;
-error Incomplete;
-
-fn parseIp6(buf: []const u8) %Address {
+fn parseIp6(buf: []const u8) !Address {
var result: Address = undefined;
result.family = linux.AF_INET6;
result.scope_id = 0;
@@ -232,7 +216,7 @@ fn parseIp6(buf: []const u8) %Address {
return error.Incomplete;
}
-fn parseIp4(buf: []const u8) %u32 {
+fn parseIp4(buf: []const u8) !u32 {
var result: u32 = undefined;
const out_ptr = ([]u8)((&result)[0..1]);
diff --git a/std/os/child_process.zig b/std/os/child_process.zig
index f4709ce75a..c85202c9ed 100644
--- a/std/os/child_process.zig
+++ b/std/os/child_process.zig
@@ -13,10 +13,6 @@ const builtin = @import("builtin");
const Os = builtin.Os;
const LinkedList = std.LinkedList;
-error PermissionDenied;
-error ProcessNotFound;
-error InvalidName;
-
var children_nodes = LinkedList(&ChildProcess).init();
const is_windows = builtin.os == Os.windows;
@@ -28,11 +24,11 @@ pub const ChildProcess = struct {
pub allocator: &mem.Allocator,
- pub stdin: ?io.File,
- pub stdout: ?io.File,
- pub stderr: ?io.File,
+ pub stdin: ?os.File,
+ pub stdout: ?os.File,
+ pub stderr: ?os.File,
- pub term: ?%Term,
+ pub term: ?(SpawnError!Term),
pub argv: []const []const u8,
@@ -58,6 +54,25 @@ pub const ChildProcess = struct {
err_pipe: if (is_windows) void else [2]i32,
llnode: if (is_windows) void else LinkedList(&ChildProcess).Node,
+ pub const SpawnError = error {
+ ProcessFdQuotaExceeded,
+ Unexpected,
+ NotDir,
+ SystemResources,
+ FileNotFound,
+ NameTooLong,
+ SymLinkLoop,
+ FileSystem,
+ OutOfMemory,
+ AccessDenied,
+ PermissionDenied,
+ InvalidUserId,
+ ResourceLimitReached,
+ InvalidExe,
+ IsDir,
+ FileBusy,
+ };
+
pub const Term = union(enum) {
Exited: i32,
Signal: i32,
@@ -74,7 +89,7 @@ pub const ChildProcess = struct {
/// First argument in argv is the executable.
/// On success must call deinit.
- pub fn init(argv: []const []const u8, allocator: &mem.Allocator) %&ChildProcess {
+ pub fn init(argv: []const []const u8, allocator: &mem.Allocator) !&ChildProcess {
const child = try allocator.create(ChildProcess);
errdefer allocator.destroy(child);
@@ -103,7 +118,7 @@ pub const ChildProcess = struct {
return child;
}
- pub fn setUserName(self: &ChildProcess, name: []const u8) %void {
+ pub fn setUserName(self: &ChildProcess, name: []const u8) !void {
const user_info = try os.getUserInfo(name);
self.uid = user_info.uid;
self.gid = user_info.gid;
@@ -111,7 +126,7 @@ pub const ChildProcess = struct {
/// onTerm can be called before `spawn` returns.
/// On success must call `kill` or `wait`.
- pub fn spawn(self: &ChildProcess) %void {
+ pub fn spawn(self: &ChildProcess) !void {
if (is_windows) {
return self.spawnWindows();
} else {
@@ -119,13 +134,13 @@ pub const ChildProcess = struct {
}
}
- pub fn spawnAndWait(self: &ChildProcess) %Term {
+ pub fn spawnAndWait(self: &ChildProcess) !Term {
try self.spawn();
return self.wait();
}
/// Forcibly terminates child process and then cleans up all resources.
- pub fn kill(self: &ChildProcess) %Term {
+ pub fn kill(self: &ChildProcess) !Term {
if (is_windows) {
return self.killWindows(1);
} else {
@@ -133,7 +148,7 @@ pub const ChildProcess = struct {
}
}
- pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) %Term {
+ pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) !Term {
if (self.term) |term| {
self.cleanupStreams();
return term;
@@ -145,11 +160,11 @@ pub const ChildProcess = struct {
else => os.unexpectedErrorWindows(err),
};
}
- self.waitUnwrappedWindows();
+ try self.waitUnwrappedWindows();
return ??self.term;
}
- pub fn killPosix(self: &ChildProcess) %Term {
+ pub fn killPosix(self: &ChildProcess) !Term {
block_SIGCHLD();
defer restore_SIGCHLD();
@@ -172,7 +187,7 @@ pub const ChildProcess = struct {
}
/// Blocks until child process terminates and then cleans up all resources.
- pub fn wait(self: &ChildProcess) %Term {
+ pub fn wait(self: &ChildProcess) !Term {
if (is_windows) {
return self.waitWindows();
} else {
@@ -189,7 +204,7 @@ pub const ChildProcess = struct {
/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
/// If it succeeds, the caller owns result.stdout and result.stderr memory.
pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8,
- env_map: ?&const BufMap, max_output_size: usize) %ExecResult
+ env_map: ?&const BufMap, max_output_size: usize) !ExecResult
{
const child = try ChildProcess.init(argv, allocator);
defer child.deinit();
@@ -220,7 +235,7 @@ pub const ChildProcess = struct {
};
}
- fn waitWindows(self: &ChildProcess) %Term {
+ fn waitWindows(self: &ChildProcess) !Term {
if (self.term) |term| {
self.cleanupStreams();
return term;
@@ -230,7 +245,7 @@ pub const ChildProcess = struct {
return ??self.term;
}
- fn waitPosix(self: &ChildProcess) %Term {
+ fn waitPosix(self: &ChildProcess) !Term {
block_SIGCHLD();
defer restore_SIGCHLD();
@@ -247,10 +262,10 @@ pub const ChildProcess = struct {
self.allocator.destroy(self);
}
- fn waitUnwrappedWindows(self: &ChildProcess) %void {
+ fn waitUnwrappedWindows(self: &ChildProcess) !void {
const result = os.windowsWaitSingle(self.handle, windows.INFINITE);
- self.term = (%Term)(x: {
+ self.term = (SpawnError!Term)(x: {
var exit_code: windows.DWORD = undefined;
if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {
break :x Term { .Unknown = 0 };
@@ -295,7 +310,7 @@ pub const ChildProcess = struct {
if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; }
}
- fn cleanupAfterWait(self: &ChildProcess, status: i32) %Term {
+ fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term {
children_nodes.remove(&self.llnode);
defer {
@@ -313,7 +328,7 @@ pub const ChildProcess = struct {
// Here we potentially return the fork child's error
// from the parent pid.
if (err_int != @maxValue(ErrInt)) {
- return error(err_int);
+ return SpawnError(err_int);
}
return statusToTerm(status);
@@ -331,7 +346,7 @@ pub const ChildProcess = struct {
;
}
- fn spawnPosix(self: &ChildProcess) %void {
+ fn spawnPosix(self: &ChildProcess) !void {
// TODO atomically set a flag saying that we already did this
install_SIGCHLD_handler();
@@ -345,11 +360,14 @@ pub const ChildProcess = struct {
errdefer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
- const dev_null_fd = if (any_ignore)
- try os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
- else
- undefined
- ;
+ const dev_null_fd = if (any_ignore) blk: {
+ const dev_null_path = "/dev/null";
+ var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined;
+ var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0);
+ } else blk: {
+ break :blk undefined;
+ };
defer { if (any_ignore) os.close(dev_null_fd); }
var env_map_owned: BufMap = undefined;
@@ -410,17 +428,17 @@ pub const ChildProcess = struct {
// we are the parent
const pid = i32(pid_result);
if (self.stdin_behavior == StdIo.Pipe) {
- self.stdin = io.File.openHandle(stdin_pipe[1]);
+ self.stdin = os.File.openHandle(stdin_pipe[1]);
} else {
self.stdin = null;
}
if (self.stdout_behavior == StdIo.Pipe) {
- self.stdout = io.File.openHandle(stdout_pipe[0]);
+ self.stdout = os.File.openHandle(stdout_pipe[0]);
} else {
self.stdout = null;
}
if (self.stderr_behavior == StdIo.Pipe) {
- self.stderr = io.File.openHandle(stderr_pipe[0]);
+ self.stderr = os.File.openHandle(stderr_pipe[0]);
} else {
self.stderr = null;
}
@@ -440,7 +458,7 @@ pub const ChildProcess = struct {
if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
}
- fn spawnWindows(self: &ChildProcess) %void {
+ fn spawnWindows(self: &ChildProcess) !void {
const saAttr = windows.SECURITY_ATTRIBUTES {
.nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
.bInheritHandle = windows.TRUE,
@@ -451,12 +469,15 @@ pub const ChildProcess = struct {
self.stdout_behavior == StdIo.Ignore or
self.stderr_behavior == StdIo.Ignore);
- const nul_handle = if (any_ignore)
- try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
- windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null)
- else
- undefined
- ;
+ const nul_handle = if (any_ignore) blk: {
+ const nul_file_path = "NUL";
+ var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined;
+ var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
+ windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
+ } else blk: {
+ break :blk undefined;
+ };
defer { if (any_ignore) os.close(nul_handle); }
if (any_ignore) {
try windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);
@@ -599,17 +620,17 @@ pub const ChildProcess = struct {
};
if (g_hChildStd_IN_Wr) |h| {
- self.stdin = io.File.openHandle(h);
+ self.stdin = os.File.openHandle(h);
} else {
self.stdin = null;
}
if (g_hChildStd_OUT_Rd) |h| {
- self.stdout = io.File.openHandle(h);
+ self.stdout = os.File.openHandle(h);
} else {
self.stdout = null;
}
if (g_hChildStd_ERR_Rd) |h| {
- self.stderr = io.File.openHandle(h);
+ self.stderr = os.File.openHandle(h);
} else {
self.stderr = null;
}
@@ -623,7 +644,7 @@ pub const ChildProcess = struct {
if (self.stdout_behavior == StdIo.Pipe) { os.close(??g_hChildStd_OUT_Wr); }
}
- fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) %void {
+ fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
switch (stdio) {
StdIo.Pipe => try os.posixDup2(pipe_fd, std_fileno),
StdIo.Close => os.close(std_fileno),
@@ -635,7 +656,7 @@ pub const ChildProcess = struct {
};
fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8,
- lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) %void
+ lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void
{
if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0,
@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: ?
/// Caller must dealloc.
/// Guarantees a null byte at result[result.len].
-fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) %[]u8 {
+fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) ![]u8 {
var buf = try Buffer.initSize(allocator, 0);
defer buf.deinit();
@@ -700,7 +721,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
// a namespace field lookup
const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
-fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void {
+fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void {
if (windows.CreatePipe(rd, wr, sattr, 0) == 0) {
const err = windows.GetLastError();
return switch (err) {
@@ -709,7 +730,7 @@ fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECUR
}
}
-fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) %void {
+fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) !void {
if (windows.SetHandleInformation(h, mask, flags) == 0) {
const err = windows.GetLastError();
return switch (err) {
@@ -718,7 +739,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D
}
}
-fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void {
+fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void {
var rd_h: windows.HANDLE = undefined;
var wr_h: windows.HANDLE = undefined;
try windowsMakePipe(&rd_h, &wr_h, sattr);
@@ -728,7 +749,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S
*wr = wr_h;
}
-fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void {
+fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void {
var rd_h: windows.HANDLE = undefined;
var wr_h: windows.HANDLE = undefined;
try windowsMakePipe(&rd_h, &wr_h, sattr);
@@ -738,7 +759,7 @@ fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const
*wr = wr_h;
}
-fn makePipe() %[2]i32 {
+fn makePipe() ![2]i32 {
var fds: [2]i32 = undefined;
const err = posix.getErrno(posix.pipe(&fds));
if (err > 0) {
@@ -757,20 +778,20 @@ fn destroyPipe(pipe: &const [2]i32) void {
// Child of fork calls this to report an error to the fork parent.
// Then the child exits.
-fn forkChildErrReport(fd: i32, err: error) noreturn {
+fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
_ = writeIntFd(fd, ErrInt(err));
posix.exit(1);
}
const ErrInt = @IntType(false, @sizeOf(error) * 8);
-fn writeIntFd(fd: i32, value: ErrInt) %void {
+fn writeIntFd(fd: i32, value: ErrInt) !void {
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
mem.writeInt(bytes[0..], value, builtin.endian);
os.posixWrite(fd, bytes[0..]) catch return error.SystemResources;
}
-fn readIntFd(fd: i32) %ErrInt {
+fn readIntFd(fd: i32) !ErrInt {
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
os.posixRead(fd, bytes[0..]) catch return error.SystemResources;
return mem.readInt(bytes[0..], ErrInt, builtin.endian);
diff --git a/std/os/file.zig b/std/os/file.zig
new file mode 100644
index 0000000000..772fbf7c73
--- /dev/null
+++ b/std/os/file.zig
@@ -0,0 +1,311 @@
+const std = @import("../index.zig");
+const builtin = @import("builtin");
+const os = std.os;
+const mem = std.mem;
+const math = std.math;
+const assert = std.debug.assert;
+const posix = os.posix;
+const windows = os.windows;
+const Os = builtin.Os;
+
+const is_posix = builtin.os != builtin.Os.windows;
+const is_windows = builtin.os == builtin.Os.windows;
+
+pub const File = struct {
+ /// The OS-specific file descriptor or file handle.
+ handle: os.FileHandle,
+
+ const OpenError = os.WindowsOpenError || os.PosixOpenError;
+
+ /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
+ /// Call close to clean up.
+ pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File {
+ if (is_posix) {
+ const flags = posix.O_LARGEFILE|posix.O_RDONLY;
+ const fd = try os.posixOpen(allocator, path, flags, 0);
+ return openHandle(fd);
+ } else if (is_windows) {
+ const handle = try os.windowsOpen(allocator, path, windows.GENERIC_READ, windows.FILE_SHARE_READ,
+ windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
+ return openHandle(handle);
+ } else {
+ @compileError("TODO implement openRead for this OS");
+ }
+ }
+
+ /// Calls `openWriteMode` with os.default_file_mode for the mode.
+ pub fn openWrite(allocator: &mem.Allocator, path: []const u8) OpenError!File {
+ return openWriteMode(allocator, path, os.default_file_mode);
+
+ }
+
+ /// If the path does not exist it will be created.
+ /// If a file already exists in the destination it will be truncated.
+ /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
+ /// Call close to clean up.
+ pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
+ if (is_posix) {
+ const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_TRUNC;
+ const fd = try os.posixOpen(allocator, path, flags, file_mode);
+ return openHandle(fd);
+ } else if (is_windows) {
+ const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE,
+ windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE,
+ windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL);
+ return openHandle(handle);
+ } else {
+ @compileError("TODO implement openWriteMode for this OS");
+ }
+
+ }
+
+ /// If the path does not exist it will be created.
+ /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
+ /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
+ /// Call close to clean up.
+ pub fn openWriteNoClobber(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
+ if (is_posix) {
+ const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_EXCL;
+ const fd = try os.posixOpen(allocator, path, flags, file_mode);
+ return openHandle(fd);
+ } else if (is_windows) {
+ const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE,
+ windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE,
+ windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL);
+ return openHandle(handle);
+ } else {
+ @compileError("TODO implement openWriteMode for this OS");
+ }
+
+ }
+
+ pub fn openHandle(handle: os.FileHandle) File {
+ return File {
+ .handle = handle,
+ };
+ }
+
+
+ /// Upon success, the stream is in an uninitialized state. To continue using it,
+ /// you must use the open() function.
+ pub fn close(self: &File) void {
+ os.close(self.handle);
+ self.handle = undefined;
+ }
+
+ /// Calls `os.isTty` on `self.handle`.
+ pub fn isTty(self: &File) bool {
+ return os.isTty(self.handle);
+ }
+
+ pub fn seekForward(self: &File, amount: isize) !void {
+ switch (builtin.os) {
+ Os.linux, Os.macosx, Os.ios => {
+ const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
+ const err = posix.getErrno(result);
+ if (err > 0) {
+ return switch (err) {
+ posix.EBADF => error.BadFd,
+ posix.EINVAL => error.Unseekable,
+ posix.EOVERFLOW => error.Unseekable,
+ posix.ESPIPE => error.Unseekable,
+ posix.ENXIO => error.Unseekable,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+ },
+ Os.windows => {
+ if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
+ const err = windows.GetLastError();
+ return switch (err) {
+ windows.ERROR.INVALID_PARAMETER => error.BadFd,
+ else => os.unexpectedErrorWindows(err),
+ };
+ }
+ },
+ else => @compileError("unsupported OS"),
+ }
+ }
+
+ pub fn seekTo(self: &File, pos: usize) !void {
+ switch (builtin.os) {
+ Os.linux, Os.macosx, Os.ios => {
+ const ipos = try math.cast(isize, pos);
+ const result = posix.lseek(self.handle, ipos, posix.SEEK_SET);
+ const err = posix.getErrno(result);
+ if (err > 0) {
+ return switch (err) {
+ posix.EBADF => error.BadFd,
+ posix.EINVAL => error.Unseekable,
+ posix.EOVERFLOW => error.Unseekable,
+ posix.ESPIPE => error.Unseekable,
+ posix.ENXIO => error.Unseekable,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+ },
+ Os.windows => {
+ const ipos = try math.cast(isize, pos);
+ if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
+ const err = windows.GetLastError();
+ return switch (err) {
+ windows.ERROR.INVALID_PARAMETER => error.BadFd,
+ else => os.unexpectedErrorWindows(err),
+ };
+ }
+ },
+ else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
+ }
+ }
+
+ pub fn getPos(self: &File) !usize {
+ switch (builtin.os) {
+ Os.linux, Os.macosx, Os.ios => {
+ const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
+ const err = posix.getErrno(result);
+ if (err > 0) {
+ return switch (err) {
+ posix.EBADF => error.BadFd,
+ posix.EINVAL => error.Unseekable,
+ posix.EOVERFLOW => error.Unseekable,
+ posix.ESPIPE => error.Unseekable,
+ posix.ENXIO => error.Unseekable,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+ return result;
+ },
+ Os.windows => {
+ var pos : windows.LARGE_INTEGER = undefined;
+ if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
+ const err = windows.GetLastError();
+ return switch (err) {
+ windows.ERROR.INVALID_PARAMETER => error.BadFd,
+ else => os.unexpectedErrorWindows(err),
+ };
+ }
+
+ assert(pos >= 0);
+ if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) {
+ if (pos > @maxValue(usize)) {
+ return error.FilePosLargerThanPointerRange;
+ }
+ }
+
+ return usize(pos);
+ },
+ else => @compileError("unsupported OS"),
+ }
+ }
+
+ pub fn getEndPos(self: &File) !usize {
+ if (is_posix) {
+ var stat: posix.Stat = undefined;
+ const err = posix.getErrno(posix.fstat(self.handle, &stat));
+ if (err > 0) {
+ return switch (err) {
+ posix.EBADF => error.BadFd,
+ posix.ENOMEM => error.SystemResources,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+
+ return usize(stat.size);
+ } else if (is_windows) {
+ var file_size: windows.LARGE_INTEGER = undefined;
+ if (windows.GetFileSizeEx(self.handle, &file_size) == 0) {
+ const err = windows.GetLastError();
+ return switch (err) {
+ else => os.unexpectedErrorWindows(err),
+ };
+ }
+ if (file_size < 0)
+ return error.Overflow;
+ return math.cast(usize, u64(file_size));
+ } else {
+ @compileError("TODO support getEndPos on this OS");
+ }
+ }
+
+ pub const ModeError = error {
+ BadFd,
+ SystemResources,
+ Unexpected,
+ };
+
+ fn mode(self: &File) ModeError!FileMode {
+ if (is_posix) {
+ var stat: posix.Stat = undefined;
+ const err = posix.getErrno(posix.fstat(self.handle, &stat));
+ if (err > 0) {
+ return switch (err) {
+ posix.EBADF => error.BadFd,
+ posix.ENOMEM => error.SystemResources,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+
+ return stat.mode;
+ } else if (is_windows) {
+ return {};
+ } else {
+ @compileError("TODO support file mode on this OS");
+ }
+ }
+
+ pub const ReadError = error {};
+
+ pub fn read(self: &File, buffer: []u8) !usize {
+ if (is_posix) {
+ var index: usize = 0;
+ while (index < buffer.len) {
+ const amt_read = posix.read(self.handle, &buffer[index], buffer.len - index);
+ const read_err = posix.getErrno(amt_read);
+ if (read_err > 0) {
+ switch (read_err) {
+ posix.EINTR => continue,
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EBADF => return error.BadFd,
+ posix.EIO => return error.Io,
+ else => return os.unexpectedErrorPosix(read_err),
+ }
+ }
+ if (amt_read == 0) return index;
+ index += amt_read;
+ }
+ return index;
+ } else if (is_windows) {
+ var index: usize = 0;
+ while (index < buffer.len) {
+ const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
+ var amt_read: windows.DWORD = undefined;
+ if (windows.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) {
+ const err = windows.GetLastError();
+ return switch (err) {
+ windows.ERROR.OPERATION_ABORTED => continue,
+ windows.ERROR.BROKEN_PIPE => return index,
+ else => os.unexpectedErrorWindows(err),
+ };
+ }
+ if (amt_read == 0) return index;
+ index += amt_read;
+ }
+ return index;
+ } else {
+ unreachable;
+ }
+ }
+
+ pub const WriteError = os.WindowsWriteError || os.PosixWriteError;
+
+ fn write(self: &File, bytes: []const u8) WriteError!void {
+ if (is_posix) {
+ try os.posixWrite(self.handle, bytes);
+ } else if (is_windows) {
+ try os.windowsWrite(self.handle, bytes);
+ } else {
+ @compileError("Unsupported OS");
+ }
+ }
+};
diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig
index 68cb268169..11410ffa64 100644
--- a/std/os/get_user_id.zig
+++ b/std/os/get_user_id.zig
@@ -9,7 +9,7 @@ pub const UserInfo = struct {
};
/// POSIX function which gets a uid from username.
-pub fn getUserInfo(name: []const u8) %UserInfo {
+pub fn getUserInfo(name: []const u8) !UserInfo {
return switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => posixGetUserInfo(name),
else => @compileError("Unsupported OS"),
@@ -24,13 +24,10 @@ const State = enum {
ReadGroupId,
};
-error UserNotFound;
-error CorruptPasswordFile;
-
// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
-pub fn posixGetUserInfo(name: []const u8) %UserInfo {
+pub fn posixGetUserInfo(name: []const u8) !UserInfo {
var in_stream = try io.InStream.open("/etc/passwd", null);
defer in_stream.close();
diff --git a/std/os/index.zig b/std/os/index.zig
index a543f27be4..2131e72760 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -15,13 +15,18 @@ pub const posix = switch(builtin.os) {
else => @compileError("Unsupported OS"),
};
-pub const max_noalloc_path_len = 1024;
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const path = @import("path.zig");
+pub const File = @import("file.zig").File;
-pub const line_sep = switch (builtin.os) {
- Os.windows => "\r\n",
- else => "\n",
+pub const FileMode = switch (builtin.os) {
+ Os.windows => void,
+ else => u32,
+};
+
+pub const default_file_mode = switch (builtin.os) {
+ Os.windows => {},
+ else => 0o666,
};
pub const page_size = 4 * 1024;
@@ -38,6 +43,10 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;
pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
+pub const WindowsWaitError = windows_util.WaitError;
+pub const WindowsOpenError = windows_util.OpenError;
+pub const WindowsWriteError = windows_util.WriteError;
+
pub const FileHandle = if (is_windows) windows.HANDLE else i32;
const debug = std.debug;
@@ -57,25 +66,10 @@ const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const math = std.math;
-error SystemResources;
-error AccessDenied;
-error InvalidExe;
-error FileSystem;
-error IsDir;
-error FileNotFound;
-error FileBusy;
-error PathAlreadyExists;
-error SymLinkLoop;
-error ReadOnlyFileSystem;
-error LinkQuotaExceeded;
-error RenameAcrossMountPoints;
-error DirNotEmpty;
-error WouldBlock;
-
/// Fills `buf` with random bytes. If linking against libc, this calls the
/// appropriate OS-specific library call. Otherwise it uses the zig standard
/// library implementation.
-pub fn getRandomBytes(buf: []u8) %void {
+pub fn getRandomBytes(buf: []u8) !void {
switch (builtin.os) {
Os.linux => while (true) {
// TODO check libc version and potentially call c.getrandom.
@@ -188,7 +182,7 @@ pub fn close(handle: FileHandle) void {
}
/// Calls POSIX read, and keeps trying if it gets interrupted.
-pub fn posixRead(fd: i32, buf: []u8) %void {
+pub fn posixRead(fd: i32, buf: []u8) !void {
// Linux can return EINVAL when read amount is > 0x7ffff000
// See https://github.com/zig-lang/zig/pull/743#issuecomment-363158274
const max_buf_len = 0x7ffff000;
@@ -214,17 +208,21 @@ pub fn posixRead(fd: i32, buf: []u8) %void {
}
}
-error WouldBlock;
-error FileClosed;
-error DestinationAddressRequired;
-error DiskQuota;
-error FileTooBig;
-error InputOutput;
-error NoSpaceLeft;
-error BrokenPipe;
+pub const PosixWriteError = error {
+ WouldBlock,
+ FileClosed,
+ DestinationAddressRequired,
+ DiskQuota,
+ FileTooBig,
+ InputOutput,
+ NoSpaceLeft,
+ AccessDenied,
+ BrokenPipe,
+ Unexpected,
+};
/// Calls POSIX write, and keeps trying if it gets interrupted.
-pub fn posixWrite(fd: i32, bytes: []const u8) %void {
+pub fn posixWrite(fd: i32, bytes: []const u8) !void {
// Linux can return EINVAL when write amount is > 0x7ffff000
// See https://github.com/zig-lang/zig/pull/743#issuecomment-363165856
const max_bytes_len = 0x7ffff000;
@@ -238,15 +236,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {
return switch (write_err) {
posix.EINTR => continue,
posix.EINVAL, posix.EFAULT => unreachable,
- posix.EAGAIN => error.WouldBlock,
- posix.EBADF => error.FileClosed,
- posix.EDESTADDRREQ => error.DestinationAddressRequired,
- posix.EDQUOT => error.DiskQuota,
- posix.EFBIG => error.FileTooBig,
- posix.EIO => error.InputOutput,
- posix.ENOSPC => error.NoSpaceLeft,
- posix.EPERM => error.AccessDenied,
- posix.EPIPE => error.BrokenPipe,
+ posix.EAGAIN => PosixWriteError.WouldBlock,
+ posix.EBADF => PosixWriteError.FileClosed,
+ posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
+ posix.EDQUOT => PosixWriteError.DiskQuota,
+ posix.EFBIG => PosixWriteError.FileTooBig,
+ posix.EIO => PosixWriteError.InputOutput,
+ posix.ENOSPC => PosixWriteError.NoSpaceLeft,
+ posix.EPERM => PosixWriteError.AccessDenied,
+ posix.EPIPE => PosixWriteError.BrokenPipe,
else => unexpectedErrorPosix(write_err),
};
}
@@ -254,66 +252,66 @@ pub fn posixWrite(fd: i32, bytes: []const u8) %void {
}
}
-/// ::file_path may need to be copied in memory to add a null terminating byte. In this case
-/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
-/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
-/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
+pub const PosixOpenError = error {
+ OutOfMemory,
+ AccessDenied,
+ FileTooBig,
+ IsDir,
+ SymLinkLoop,
+ ProcessFdQuotaExceeded,
+ NameTooLong,
+ SystemFdQuotaExceeded,
+ NoDevice,
+ PathNotFound,
+ SystemResources,
+ NoSpaceLeft,
+ NotDir,
+ PathAlreadyExists,
+ Unexpected,
+};
+
+/// ::file_path needs to be copied in memory to add a null terminating byte.
/// Calls POSIX open, keeps trying if it gets interrupted, and translates
/// the return value into zig errors.
-pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) %i32 {
- var stack_buf: [max_noalloc_path_len]u8 = undefined;
- var path0: []u8 = undefined;
- var need_free = false;
+pub fn posixOpen(allocator: &Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
+ const path_with_null = try cstr.addNullByte(allocator, file_path);
+ defer allocator.free(path_with_null);
- if (file_path.len < stack_buf.len) {
- path0 = stack_buf[0..file_path.len + 1];
- } else if (allocator) |a| {
- path0 = try a.alloc(u8, file_path.len + 1);
- need_free = true;
- } else {
- return error.NameTooLong;
- }
- defer if (need_free) {
- (??allocator).free(path0);
- };
- mem.copy(u8, path0, file_path);
- path0[file_path.len] = 0;
-
- return posixOpenC(path0.ptr, flags, perm);
+ return posixOpenC(path_with_null.ptr, flags, perm);
}
-pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) %i32 {
+pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
while (true) {
const result = posix.open(file_path, flags, perm);
const err = posix.getErrno(result);
if (err > 0) {
- return switch (err) {
+ switch (err) {
posix.EINTR => continue,
posix.EFAULT => unreachable,
posix.EINVAL => unreachable,
- posix.EACCES => error.AccessDenied,
- posix.EFBIG, posix.EOVERFLOW => error.FileTooBig,
- posix.EISDIR => error.IsDir,
- posix.ELOOP => error.SymLinkLoop,
- posix.EMFILE => error.ProcessFdQuotaExceeded,
- posix.ENAMETOOLONG => error.NameTooLong,
- posix.ENFILE => error.SystemFdQuotaExceeded,
- posix.ENODEV => error.NoDevice,
- posix.ENOENT => error.PathNotFound,
- posix.ENOMEM => error.SystemResources,
- posix.ENOSPC => error.NoSpaceLeft,
- posix.ENOTDIR => error.NotDir,
- posix.EPERM => error.AccessDenied,
- posix.EEXIST => error.PathAlreadyExists,
- else => unexpectedErrorPosix(err),
- };
+ posix.EACCES => return PosixOpenError.AccessDenied,
+ posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,
+ posix.EISDIR => return PosixOpenError.IsDir,
+ posix.ELOOP => return PosixOpenError.SymLinkLoop,
+ posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
+ posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
+ posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
+ posix.ENODEV => return PosixOpenError.NoDevice,
+ posix.ENOENT => return PosixOpenError.PathNotFound,
+ posix.ENOMEM => return PosixOpenError.SystemResources,
+ posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
+ posix.ENOTDIR => return PosixOpenError.NotDir,
+ posix.EPERM => return PosixOpenError.AccessDenied,
+ posix.EEXIST => return PosixOpenError.PathAlreadyExists,
+ else => return unexpectedErrorPosix(err),
+ }
}
return i32(result);
}
}
-pub fn posixDup2(old_fd: i32, new_fd: i32) %void {
+pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
while (true) {
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
if (err > 0) {
@@ -328,7 +326,7 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) %void {
}
}
-pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) %[]?&u8 {
+pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) ![]?&u8 {
const envp_count = env_map.count();
const envp_buf = try allocator.alloc(?&u8, envp_count + 1);
mem.set(?&u8, envp_buf, null);
@@ -365,7 +363,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
/// `argv[0]` is the executable path.
/// This function also uses the PATH environment variable to get the full path to the executable.
pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
- allocator: &Allocator) %void
+ allocator: &Allocator) !void
{
const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
mem.set(?&u8, argv_buf, null);
@@ -421,7 +419,19 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
return posixExecveErrnoToErr(err);
}
-fn posixExecveErrnoToErr(err: usize) error {
+pub const PosixExecveError = error {
+ SystemResources,
+ AccessDenied,
+ InvalidExe,
+ FileSystem,
+ IsDir,
+ FileNotFound,
+ NotDir,
+ FileBusy,
+ Unexpected,
+};
+
+fn posixExecveErrnoToErr(err: usize) PosixExecveError {
assert(err > 0);
return switch (err) {
posix.EFAULT => unreachable,
@@ -440,7 +450,7 @@ fn posixExecveErrnoToErr(err: usize) error {
pub var posix_environ_raw: []&u8 = undefined;
/// Caller must free result when done.
-pub fn getEnvMap(allocator: &Allocator) %BufMap {
+pub fn getEnvMap(allocator: &Allocator) !BufMap {
var result = BufMap.init(allocator);
errdefer result.deinit();
@@ -501,10 +511,8 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
return null;
}
-error EnvironmentVariableNotFound;
-
/// Caller must free returned memory.
-pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) %[]u8 {
+pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) ![]u8 {
if (is_windows) {
const key_with_null = try cstr.addNullByte(allocator, key);
defer allocator.free(key_with_null);
@@ -538,7 +546,7 @@ pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) %[]u8 {
}
/// Caller must free the returned memory.
-pub fn getCwd(allocator: &Allocator) %[]u8 {
+pub fn getCwd(allocator: &Allocator) ![]u8 {
switch (builtin.os) {
Os.windows => {
var buf = try allocator.alloc(u8, 256);
@@ -585,7 +593,9 @@ test "os.getCwd" {
_ = getCwd(debug.global_allocator);
}
-pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {
+pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
+
+pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
if (is_windows) {
return symLinkWindows(allocator, existing_path, new_path);
} else {
@@ -593,7 +603,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con
}
}
-pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {
+pub const WindowsSymLinkError = error {
+ OutOfMemory,
+ Unexpected,
+};
+
+pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
const existing_with_null = try cstr.addNullByte(allocator, existing_path);
defer allocator.free(existing_with_null);
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
}
}
-pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {
+pub const PosixSymLinkError = error {
+ OutOfMemory,
+ AccessDenied,
+ DiskQuota,
+ PathAlreadyExists,
+ FileSystem,
+ SymLinkLoop,
+ NameTooLong,
+ FileNotFound,
+ SystemResources,
+ NoSpaceLeft,
+ ReadOnlyFileSystem,
+ NotDir,
+ Unexpected,
+};
+
+pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
defer allocator.free(full_buf);
@@ -644,36 +675,36 @@ const b64_fs_encoder = base64.Base64Encoder.init(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
base64.standard_pad_char);
-pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) %void {
+pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
if (symLink(allocator, existing_path, new_path)) {
return;
- } else |err| {
- if (err != error.PathAlreadyExists) {
- return err;
- }
+ } else |err| switch (err) {
+ error.PathAlreadyExists => {},
+ else => return err, // TODO zig should know this set does not include PathAlreadyExists
}
+ const dirname = os.path.dirname(new_path);
+
var rand_buf: [12]u8 = undefined;
- const tmp_path = try allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
+ const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
defer allocator.free(tmp_path);
- mem.copy(u8, tmp_path[0..], new_path);
+ mem.copy(u8, tmp_path[0..], dirname);
+ tmp_path[dirname.len] = os.path.sep;
while (true) {
try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf);
+ b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+
if (symLink(allocator, existing_path, tmp_path)) {
return rename(allocator, tmp_path, new_path);
- } else |err| {
- if (err == error.PathAlreadyExists) {
- continue;
- } else {
- return err;
- }
+ } else |err| switch (err) {
+ error.PathAlreadyExists => continue,
+ else => return err, // TODO zig should know this set does not include PathAlreadyExists
}
}
}
-pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {
+pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
if (builtin.os == Os.windows) {
return deleteFileWindows(allocator, file_path);
} else {
@@ -681,10 +712,7 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) %void {
}
}
-error FileNotFound;
-error AccessDenied;
-
-pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {
+pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
const buf = try allocator.alloc(u8, file_path.len + 1);
defer allocator.free(buf);
@@ -702,7 +730,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) %void {
}
}
-pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {
+pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
const buf = try allocator.alloc(u8, file_path.len + 1);
defer allocator.free(buf);
@@ -728,38 +756,109 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) %void {
}
}
-/// Calls ::copyFileMode with 0o666 for the mode.
-pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) %void {
- return copyFileMode(allocator, source_path, dest_path, 0o666);
-}
-
-// TODO instead of accepting a mode argument, use the mode from fstat'ing the source path once open
-/// Guaranteed to be atomic.
-pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) %void {
- var rand_buf: [12]u8 = undefined;
- const tmp_path = try allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
- defer allocator.free(tmp_path);
- mem.copy(u8, tmp_path[0..], dest_path);
- try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf);
-
- var out_file = try io.File.openWriteMode(tmp_path, mode, allocator);
- defer out_file.close();
- errdefer _ = deleteFile(allocator, tmp_path);
-
- var in_file = try io.File.openRead(source_path, allocator);
+/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
+/// merged and readily available,
+/// there is a possibility of power loss or application termination leaving temporary files present
+/// in the same directory as dest_path.
+/// Destination file will have the same mode as the source file.
+pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) !void {
+ var in_file = try os.File.openRead(allocator, source_path);
defer in_file.close();
+ const mode = try in_file.mode();
+
+ var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
+ defer atomic_file.deinit();
+
var buf: [page_size]u8 = undefined;
while (true) {
const amt = try in_file.read(buf[0..]);
- try out_file.write(buf[0..amt]);
- if (amt != buf.len)
- return rename(allocator, tmp_path, dest_path);
+ try atomic_file.file.write(buf[0..amt]);
+ if (amt != buf.len) {
+ return atomic_file.finish();
+ }
}
}
-pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) %void {
+/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
+/// merged and readily available,
+/// there is a possibility of power loss or application termination leaving temporary files present
+pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void {
+ var in_file = try os.File.openRead(allocator, source_path);
+ defer in_file.close();
+
+ var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
+ defer atomic_file.deinit();
+
+ var buf: [page_size]u8 = undefined;
+ while (true) {
+ const amt = try in_file.read(buf[0..]);
+ try atomic_file.file.write(buf[0..amt]);
+ if (amt != buf.len) {
+ return atomic_file.finish();
+ }
+ }
+}
+
+pub const AtomicFile = struct {
+ allocator: &Allocator,
+ file: os.File,
+ tmp_path: []u8,
+ dest_path: []const u8,
+ finished: bool,
+
+ /// dest_path must remain valid for the lifetime of AtomicFile
+ /// call finish to atomically replace dest_path with contents
+ pub fn init(allocator: &Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile {
+ const dirname = os.path.dirname(dest_path);
+
+ var rand_buf: [12]u8 = undefined;
+ const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
+ errdefer allocator.free(tmp_path);
+ mem.copy(u8, tmp_path[0..], dirname);
+ tmp_path[dirname.len] = os.path.sep;
+
+ while (true) {
+ try getRandomBytes(rand_buf[0..]);
+ b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+
+ const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
+ error.PathAlreadyExists => continue,
+ // TODO zig should figure out that this error set does not include PathAlreadyExists since
+ // it is handled in the above switch
+ else => return err,
+ };
+
+ return AtomicFile {
+ .allocator = allocator,
+ .file = file,
+ .tmp_path = tmp_path,
+ .dest_path = dest_path,
+ .finished = false,
+ };
+ }
+ }
+
+ /// always call deinit, even after successful finish()
+ pub fn deinit(self: &AtomicFile) void {
+ if (!self.finished) {
+ self.file.close();
+ deleteFile(self.allocator, self.tmp_path) catch {};
+ self.allocator.free(self.tmp_path);
+ self.finished = true;
+ }
+ }
+
+ pub fn finish(self: &AtomicFile) !void {
+ assert(!self.finished);
+ self.file.close();
+ try rename(self.allocator, self.tmp_path, self.dest_path);
+ self.allocator.free(self.tmp_path);
+ self.finished = true;
+ }
+};
+
+pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) !void {
const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);
defer allocator.free(full_buf);
@@ -804,7 +903,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
}
}
-pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {
+pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void {
if (is_windows) {
return makeDirWindows(allocator, dir_path);
} else {
@@ -812,7 +911,7 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) %void {
}
}
-pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {
+pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void {
const path_buf = try cstr.addNullByte(allocator, dir_path);
defer allocator.free(path_buf);
@@ -826,7 +925,7 @@ pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) %void {
}
}
-pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {
+pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
const path_buf = try cstr.addNullByte(allocator, dir_path);
defer allocator.free(path_buf);
@@ -852,7 +951,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) %void {
/// Calls makeDir recursively to make an entire path. Returns success if the path
/// already exists and is a directory.
-pub fn makePath(allocator: &Allocator, full_path: []const u8) %void {
+pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
const resolved_path = try path.resolve(allocator, full_path);
defer allocator.free(resolved_path);
@@ -890,7 +989,7 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) %void {
/// Returns ::error.DirNotEmpty if the directory is not empty.
/// To delete a directory recursively, see ::deleteTree
-pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) %void {
+pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
const path_buf = try allocator.alloc(u8, dir_path.len + 1);
defer allocator.free(path_buf);
@@ -919,24 +1018,68 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) %void {
/// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again.
// TODO non-recursive implementation
-pub fn deleteTree(allocator: &Allocator, full_path: []const u8) %void {
+const DeleteTreeError = error {
+ OutOfMemory,
+ AccessDenied,
+ FileTooBig,
+ IsDir,
+ SymLinkLoop,
+ ProcessFdQuotaExceeded,
+ NameTooLong,
+ SystemFdQuotaExceeded,
+ NoDevice,
+ PathNotFound,
+ SystemResources,
+ NoSpaceLeft,
+ PathAlreadyExists,
+ ReadOnlyFileSystem,
+ NotDir,
+ FileNotFound,
+ FileSystem,
+ FileBusy,
+ DirNotEmpty,
+ Unexpected,
+};
+pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {
start_over: while (true) {
// First, try deleting the item as a file. This way we don't follow sym links.
if (deleteFile(allocator, full_path)) {
return;
- } else |err| {
- if (err == error.FileNotFound)
- return;
- if (err != error.IsDir)
- return err;
+ } else |err| switch (err) {
+ error.FileNotFound => return,
+ error.IsDir => {},
+
+ error.OutOfMemory,
+ error.AccessDenied,
+ error.SymLinkLoop,
+ error.NameTooLong,
+ error.SystemResources,
+ error.ReadOnlyFileSystem,
+ error.NotDir,
+ error.FileSystem,
+ error.FileBusy,
+ error.Unexpected
+ => return err,
}
{
- var dir = Dir.open(allocator, full_path) catch |err| {
- if (err == error.FileNotFound)
- return;
- if (err == error.NotDir)
- continue :start_over;
- return err;
+ var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
+ error.NotDir => continue :start_over,
+
+ error.OutOfMemory,
+ error.AccessDenied,
+ error.FileTooBig,
+ error.IsDir,
+ error.SymLinkLoop,
+ error.ProcessFdQuotaExceeded,
+ error.NameTooLong,
+ error.SystemFdQuotaExceeded,
+ error.NoDevice,
+ error.PathNotFound,
+ error.SystemResources,
+ error.NoSpaceLeft,
+ error.PathAlreadyExists,
+ error.Unexpected
+ => return err,
};
defer dir.close();
@@ -988,8 +1131,8 @@ pub const Dir = struct {
};
};
- pub fn open(allocator: &Allocator, dir_path: []const u8) %Dir {
- const fd = try posixOpen(dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0, allocator);
+ pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
+ const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0);
return Dir {
.allocator = allocator,
.fd = fd,
@@ -1006,7 +1149,7 @@ pub const Dir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
- pub fn next(self: &Dir) %?Entry {
+ pub fn next(self: &Dir) !?Entry {
start_over: while (true) {
if (self.index >= self.end_index) {
if (self.buf.len == 0) {
@@ -1063,7 +1206,7 @@ pub const Dir = struct {
}
};
-pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {
+pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void {
const path_buf = try allocator.alloc(u8, dir_path.len + 1);
defer allocator.free(path_buf);
@@ -1087,7 +1230,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) %void {
}
/// Read value of a symbolic link.
-pub fn readLink(allocator: &Allocator, pathname: []const u8) %[]u8 {
+pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
const path_buf = try allocator.alloc(u8, pathname.len + 1);
defer allocator.free(path_buf);
@@ -1164,11 +1307,7 @@ test "os.sleep" {
sleep(0, 1);
}
-error ResourceLimitReached;
-error InvalidUserId;
-error PermissionDenied;
-
-pub fn posix_setuid(uid: u32) %void {
+pub fn posix_setuid(uid: u32) !void {
const err = posix.getErrno(posix.setuid(uid));
if (err == 0) return;
return switch (err) {
@@ -1179,7 +1318,7 @@ pub fn posix_setuid(uid: u32) %void {
};
}
-pub fn posix_setreuid(ruid: u32, euid: u32) %void {
+pub fn posix_setreuid(ruid: u32, euid: u32) !void {
const err = posix.getErrno(posix.setreuid(ruid, euid));
if (err == 0) return;
return switch (err) {
@@ -1190,7 +1329,7 @@ pub fn posix_setreuid(ruid: u32, euid: u32) %void {
};
}
-pub fn posix_setgid(gid: u32) %void {
+pub fn posix_setgid(gid: u32) !void {
const err = posix.getErrno(posix.setgid(gid));
if (err == 0) return;
return switch (err) {
@@ -1201,7 +1340,7 @@ pub fn posix_setgid(gid: u32) %void {
};
}
-pub fn posix_setregid(rgid: u32, egid: u32) %void {
+pub fn posix_setregid(rgid: u32, egid: u32) !void {
const err = posix.getErrno(posix.setregid(rgid, egid));
if (err == 0) return;
return switch (err) {
@@ -1212,8 +1351,12 @@ pub fn posix_setregid(rgid: u32, egid: u32) %void {
};
}
-error NoStdHandles;
-pub fn windowsGetStdHandle(handle_id: windows.DWORD) %windows.HANDLE {
+pub const WindowsGetStdHandleErrs = error {
+ NoStdHandles,
+ Unexpected,
+};
+
+pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE {
if (windows.GetStdHandle(handle_id)) |handle| {
if (handle == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError();
@@ -1267,6 +1410,8 @@ pub const ArgIteratorWindows = struct {
quote_count: usize,
seen_quote_count: usize,
+ pub const NextError = error{OutOfMemory};
+
pub fn init() ArgIteratorWindows {
return initWithCmdLine(windows.GetCommandLineA());
}
@@ -1282,7 +1427,7 @@ pub const ArgIteratorWindows = struct {
}
/// You must free the returned memory when done.
- pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?%[]u8 {
+ pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(NextError![]u8) {
// march forward over whitespace
while (true) : (self.index += 1) {
const byte = self.cmd_line[self.index];
@@ -1335,7 +1480,7 @@ pub const ArgIteratorWindows = struct {
}
}
- fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) %[]u8 {
+ fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) NextError![]u8 {
var buf = try Buffer.initSize(allocator, 0);
defer buf.deinit();
@@ -1379,7 +1524,7 @@ pub const ArgIteratorWindows = struct {
}
}
- fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) %void {
+ fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) !void {
var i: usize = 0;
while (i < emit_count) : (i += 1) {
try buf.appendByte('\\');
@@ -1409,16 +1554,20 @@ pub const ArgIteratorWindows = struct {
};
pub const ArgIterator = struct {
- inner: if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix,
+ const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix;
+
+ inner: InnerType,
pub fn init() ArgIterator {
return ArgIterator {
- .inner = if (builtin.os == Os.windows) ArgIteratorWindows.init() else ArgIteratorPosix.init(),
+ .inner = InnerType.init(),
};
}
+
+ pub const NextError = ArgIteratorWindows.NextError;
/// You must free the returned memory when done.
- pub fn next(self: &ArgIterator, allocator: &Allocator) ?%[]u8 {
+ pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
if (builtin.os == Os.windows) {
return self.inner.next(allocator);
} else {
@@ -1443,7 +1592,7 @@ pub fn args() ArgIterator {
}
/// Caller must call freeArgs on result.
-pub fn argsAlloc(allocator: &mem.Allocator) %[]const []u8 {
+pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 {
// TODO refactor to only make 1 allocation.
var it = args();
var contents = try Buffer.initSize(allocator, 0);
@@ -1525,14 +1674,12 @@ test "std.os" {
}
-error Unexpected;
-
// TODO make this a build variable that you can set
const unexpected_error_tracing = false;
/// Call this when you made a syscall or something that sets errno
/// and you get an unexpected error.
-pub fn unexpectedErrorPosix(errno: usize) error {
+pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
if (unexpected_error_tracing) {
debug.warn("unexpected errno: {}\n", errno);
debug.dumpStackTrace();
@@ -1542,7 +1689,7 @@ pub fn unexpectedErrorPosix(errno: usize) error {
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
-pub fn unexpectedErrorWindows(err: windows.DWORD) error {
+pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
if (unexpected_error_tracing) {
debug.warn("unexpected GetLastError(): {}\n", err);
debug.dumpStackTrace();
@@ -1550,25 +1697,38 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) error {
return error.Unexpected;
}
-pub fn openSelfExe() %io.File {
+pub fn openSelfExe() !os.File {
switch (builtin.os) {
Os.linux => {
- return io.File.openRead("/proc/self/exe", null);
+ const proc_file_path = "/proc/self/exe";
+ var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
+ var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
},
Os.macosx, Os.ios => {
- @panic("TODO: openSelfExe on Darwin");
+ var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
+ var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ const self_exe_path = try selfExePath(&fixed_allocator.allocator);
+ return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
},
else => @compileError("Unsupported OS"),
}
}
+test "openSelfExe" {
+ switch (builtin.os) {
+ Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
+ else => return, // Unsupported OS.
+ }
+}
+
/// Get the path to the current executable.
/// If you only need the directory, use selfExeDirPath.
/// If you only want an open file handle, use openSelfExe.
/// This function may return an error if the current executable
/// was deleted after spawning.
/// Caller owns returned memory.
-pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {
+pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
switch (builtin.os) {
Os.linux => {
// If the currently executing binary has been deleted,
@@ -1611,7 +1771,7 @@ pub fn selfExePath(allocator: &mem.Allocator) %[]u8 {
/// Get the directory path that contains the current executable.
/// Caller owns returned memory.
-pub fn selfExeDirPath(allocator: &mem.Allocator) %[]u8 {
+pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
switch (builtin.os) {
Os.linux => {
// If the currently executing binary has been deleted,
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 43d175b74d..113f2ef454 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -720,7 +720,7 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
// error SystemResources;
// error Io;
//
-// pub fn if_nametoindex(name: []u8) %u32 {
+// pub fn if_nametoindex(name: []u8) !u32 {
// var ifr: ifreq = undefined;
//
// if (name.len >= ifr.ifr_name.len) {
diff --git a/std/os/path.zig b/std/os/path.zig
index eb95f83f45..0ea5d5a753 100644
--- a/std/os/path.zig
+++ b/std/os/path.zig
@@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool {
/// Naively combines a series of paths with the native path seperator.
/// Allocates memory for the result, which must be freed by the caller.
-pub fn join(allocator: &Allocator, paths: ...) %[]u8 {
+pub fn join(allocator: &Allocator, paths: ...) ![]u8 {
if (is_windows) {
return joinWindows(allocator, paths);
} else {
@@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) %[]u8 {
}
}
-pub fn joinWindows(allocator: &Allocator, paths: ...) %[]u8 {
+pub fn joinWindows(allocator: &Allocator, paths: ...) ![]u8 {
return mem.join(allocator, sep_windows, paths);
}
-pub fn joinPosix(allocator: &Allocator, paths: ...) %[]u8 {
+pub fn joinPosix(allocator: &Allocator, paths: ...) ![]u8 {
return mem.join(allocator, sep_posix, paths);
}
@@ -313,7 +313,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
}
/// Converts the command line arguments into a slice and calls `resolveSlice`.
-pub fn resolve(allocator: &Allocator, args: ...) %[]u8 {
+pub fn resolve(allocator: &Allocator, args: ...) ![]u8 {
var paths: [args.len][]const u8 = undefined;
comptime var arg_i = 0;
inline while (arg_i < args.len) : (arg_i += 1) {
@@ -323,7 +323,7 @@ pub fn resolve(allocator: &Allocator, args: ...) %[]u8 {
}
/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
-pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 {
+pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 {
if (is_windows) {
return resolveWindows(allocator, paths);
} else {
@@ -337,7 +337,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 {
/// If all paths are relative it uses the current working directory as a starting point.
/// Each drive has its own current working directory.
/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
-pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 {
+pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 {
if (paths.len == 0) {
assert(is_windows); // resolveWindows called on non windows can't use getCwd
return os.getCwd(allocator);
@@ -520,7 +520,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 {
/// It resolves "." and "..".
/// The result does not have a trailing path separator.
/// If all paths are relative it uses the current working directory as a starting point.
-pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) %[]u8 {
+pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) ![]u8 {
if (paths.len == 0) {
assert(!is_windows); // resolvePosix called on windows can't use getCwd
return os.getCwd(allocator);
@@ -890,7 +890,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void {
/// resolve to the same path (after calling `resolve` on each), a zero-length
/// string is returned.
/// On Windows this canonicalizes the drive to a capital letter and paths to `\\`.
-pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {
+pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 {
if (is_windows) {
return relativeWindows(allocator, from, to);
} else {
@@ -898,7 +898,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {
}
}
-pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {
+pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 {
const resolved_from = try resolveWindows(allocator, [][]const u8{from});
defer allocator.free(resolved_from);
@@ -971,7 +971,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8)
return []u8{};
}
-pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 {
+pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 {
const resolved_from = try resolvePosix(allocator, [][]const u8{from});
defer allocator.free(resolved_from);
@@ -1066,18 +1066,11 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
assert(mem.eql(u8, result, expected_output));
}
-error AccessDenied;
-error FileNotFound;
-error NotSupported;
-error NotDir;
-error NameTooLong;
-error SymLinkLoop;
-error InputOutput;
/// Return the canonicalized absolute pathname.
/// Expands all symbolic links and resolves references to `.`, `..`, and
/// extra `/` characters in ::pathname.
/// Caller must deallocate result.
-pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {
+pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 {
switch (builtin.os) {
Os.windows => {
const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
@@ -1168,7 +1161,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 {
return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
},
Os.linux => {
- const fd = try os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator);
+ const fd = try os.posixOpen(allocator, pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0);
defer os.close(fd);
var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig
index a8a2e3fcd5..5af318b7b0 100644
--- a/std/os/windows/util.zig
+++ b/std/os/windows/util.zig
@@ -1,4 +1,5 @@
const std = @import("../../index.zig");
+const builtin = @import("builtin");
const os = std.os;
const windows = std.os.windows;
const assert = std.debug.assert;
@@ -6,11 +7,13 @@ const mem = std.mem;
const BufMap = std.BufMap;
const cstr = std.cstr;
-error WaitAbandoned;
-error WaitTimeOut;
-error Unexpected;
+pub const WaitError = error {
+ WaitAbandoned,
+ WaitTimeOut,
+ Unexpected,
+};
-pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) %void {
+pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void {
const result = windows.WaitForSingleObject(handle, milliseconds);
return switch (result) {
windows.WAIT_ABANDONED => error.WaitAbandoned,
@@ -30,21 +33,24 @@ pub fn windowsClose(handle: windows.HANDLE) void {
assert(windows.CloseHandle(handle) != 0);
}
-error SystemResources;
-error OperationAborted;
-error IoPending;
-error BrokenPipe;
+pub const WriteError = error {
+ SystemResources,
+ OperationAborted,
+ IoPending,
+ BrokenPipe,
+ Unexpected,
+};
-pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) %void {
+pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {
const err = windows.GetLastError();
return switch (err) {
- windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,
- windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,
- windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
- windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,
- windows.ERROR.IO_PENDING => error.IoPending,
- windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
+ windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
+ windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
+ windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
+ windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
+ windows.ERROR.IO_PENDING => WriteError.IoPending,
+ windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
else => os.unexpectedErrorWindows(err),
};
}
@@ -75,43 +81,35 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {
mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null;
}
-error SharingViolation;
-error PipeBusy;
+pub const OpenError = error {
+ SharingViolation,
+ PathAlreadyExists,
+ FileNotFound,
+ AccessDenied,
+ PipeBusy,
+ Unexpected,
+ OutOfMemory,
+};
-/// `file_path` may need to be copied in memory to add a null terminating byte. In this case
-/// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed
-/// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned.
-/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
-pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD,
- creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) %windows.HANDLE
+/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
+pub fn windowsOpen(allocator: &mem.Allocator, file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD,
+ creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD)
+ OpenError!windows.HANDLE
{
- var stack_buf: [os.max_noalloc_path_len]u8 = undefined;
- var path0: []u8 = undefined;
- var need_free = false;
- defer if (need_free) (??allocator).free(path0);
+ const path_with_null = try cstr.addNullByte(allocator, file_path);
+ defer allocator.free(path_with_null);
- if (file_path.len < stack_buf.len) {
- path0 = stack_buf[0..file_path.len + 1];
- } else if (allocator) |a| {
- path0 = try a.alloc(u8, file_path.len + 1);
- need_free = true;
- } else {
- return error.NameTooLong;
- }
- mem.copy(u8, path0, file_path);
- path0[file_path.len] = 0;
-
- const result = windows.CreateFileA(path0.ptr, desired_access, share_mode, null, creation_disposition,
+ const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition,
flags_and_attrs, null);
if (result == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError();
return switch (err) {
- windows.ERROR.SHARING_VIOLATION => error.SharingViolation,
- windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists,
- windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
- windows.ERROR.ACCESS_DENIED => error.AccessDenied,
- windows.ERROR.PIPE_BUSY => error.PipeBusy,
+ windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
+ windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
+ windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
+ windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
+ windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
else => os.unexpectedErrorWindows(err),
};
}
@@ -120,7 +118,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
}
/// Caller must free result.
-pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) %[]u8 {
+pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) ![]u8 {
// count bytes needed
const bytes_needed = x: {
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)
return result;
}
-error DllNotFound;
-pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) %windows.HMODULE {
+pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows.HMODULE {
const padded_buff = try cstr.addNullByte(allocator, dll_path);
defer allocator.free(padded_buff);
return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
@@ -164,6 +161,8 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
test "InvalidDll" {
+ if (builtin.os != builtin.Os.windows) return;
+
const DllName = "asdf.dll";
const allocator = std.debug.global_allocator;
const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index bcb3456353..f5754638b0 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -77,7 +77,7 @@ fn callMain() u8 {
},
builtin.TypeId.Int => {
if (@typeOf(root.main).ReturnType.bit_count != 8) {
- @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+ @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
}
return root.main();
},
@@ -91,6 +91,6 @@ fn callMain() u8 {
};
return 0;
},
- else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),
+ else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
}
}
diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig
index 282759bedb..2edfdadf50 100644
--- a/std/special/build_file_template.zig
+++ b/std/special/build_file_template.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) !void {
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
exe.setBuildMode(mode);
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index e1648276aa..4805240db7 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -1,5 +1,6 @@
const root = @import("@build");
const std = @import("std");
+const builtin = @import("builtin");
const io = std.io;
const fmt = std.fmt;
const os = std.os;
@@ -8,9 +9,7 @@ const mem = std.mem;
const ArrayList = std.ArrayList;
const warn = std.debug.warn;
-error InvalidArgs;
-
-pub fn main() %void {
+pub fn main() !void {
var arg_it = os.args();
// TODO use a more general purpose allocator here
@@ -45,14 +44,14 @@ pub fn main() %void {
var stderr_file = io.getStdErr();
var stderr_file_stream: io.FileOutStream = undefined;
- var stderr_stream: %&io.OutStream = if (stderr_file) |*f| x: {
+ var stderr_stream = if (stderr_file) |*f| x: {
stderr_file_stream = io.FileOutStream.init(f);
break :x &stderr_file_stream.stream;
} else |err| err;
var stdout_file = io.getStdOut();
var stdout_file_stream: io.FileOutStream = undefined;
- var stdout_stream: %&io.OutStream = if (stdout_file) |*f| x: {
+ var stdout_stream = if (stdout_file) |*f| x: {
stdout_file_stream = io.FileOutStream.init(f);
break :x &stdout_file_stream.stream;
} else |err| err;
@@ -112,7 +111,7 @@ pub fn main() %void {
}
builder.setInstallPrefix(prefix);
- try root.build(&builder);
+ try runBuild(&builder);
if (builder.validateUserInputDidItFail())
return usageAndErr(&builder, true, try stderr_stream);
@@ -125,11 +124,19 @@ pub fn main() %void {
};
}
-fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) %void {
+fn runBuild(builder: &Builder) error!void {
+ switch (@typeId(@typeOf(root.build).ReturnType)) {
+ builtin.TypeId.Void => root.build(builder),
+ builtin.TypeId.ErrorUnion => try root.build(builder),
+ else => @compileError("expected return type of build to be 'void' or '!void'"),
+ }
+}
+
+fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void {
// run the build script to collect the options
if (!already_ran_build) {
builder.setInstallPrefix(null);
- try root.build(builder);
+ try runBuild(builder);
}
// 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)
\\
\\General Options:
\\ --help Print this help and exit
+ \\ --init Generate a build.zig template
\\ --verbose Print commands before executing them
\\ --prefix [path] Override default install prefix
\\ --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)
);
}
-fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) error {
+fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: var) error {
usage(builder, already_ran_build, out_stream) catch {};
return error.InvalidArgs;
}
-fn unwrapArg(arg: %[]u8) %[]u8 {
+const UnwrapArgError = error {OutOfMemory};
+
+fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
return arg catch |err| {
warn("Unable to parse command line: {}\n", err);
return err;
diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig
index 3284f740b0..76a54a5018 100644
--- a/std/special/test_runner.zig
+++ b/std/special/test_runner.zig
@@ -4,7 +4,7 @@ const builtin = @import("builtin");
const test_fn_list = builtin.__zig_test_fn_slice;
const warn = std.debug.warn;
-pub fn main() %void {
+pub fn main() !void {
for (test_fn_list) |test_fn, i| {
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
diff --git a/std/unicode.zig b/std/unicode.zig
index 235ac4ceac..df62e9162f 100644
--- a/std/unicode.zig
+++ b/std/unicode.zig
@@ -1,11 +1,9 @@
const std = @import("./index.zig");
-error Utf8InvalidStartByte;
-
/// Given the first byte of a UTF-8 codepoint,
/// returns a number 1-4 indicating the total length of the codepoint in bytes.
/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.
-pub fn utf8ByteSequenceLength(first_byte: u8) %u3 {
+pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
if (first_byte < 0b10000000) return u3(1);
if (first_byte & 0b11100000 == 0b11000000) return u3(2);
if (first_byte & 0b11110000 == 0b11100000) return u3(3);
@@ -13,16 +11,11 @@ pub fn utf8ByteSequenceLength(first_byte: u8) %u3 {
return error.Utf8InvalidStartByte;
}
-error Utf8OverlongEncoding;
-error Utf8ExpectedContinuation;
-error Utf8EncodesSurrogateHalf;
-error Utf8CodepointTooLarge;
-
/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
/// If you already know the length at comptime, you can call one of
/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
-pub fn utf8Decode(bytes: []const u8) %u32 {
+pub fn utf8Decode(bytes: []const u8) !u32 {
return switch (bytes.len) {
1 => u32(bytes[0]),
2 => utf8Decode2(bytes),
@@ -31,7 +24,7 @@ pub fn utf8Decode(bytes: []const u8) %u32 {
else => unreachable,
};
}
-pub fn utf8Decode2(bytes: []const u8) %u32 {
+pub fn utf8Decode2(bytes: []const u8) !u32 {
std.debug.assert(bytes.len == 2);
std.debug.assert(bytes[0] & 0b11100000 == 0b11000000);
var value: u32 = bytes[0] & 0b00011111;
@@ -44,7 +37,7 @@ pub fn utf8Decode2(bytes: []const u8) %u32 {
return value;
}
-pub fn utf8Decode3(bytes: []const u8) %u32 {
+pub fn utf8Decode3(bytes: []const u8) !u32 {
std.debug.assert(bytes.len == 3);
std.debug.assert(bytes[0] & 0b11110000 == 0b11100000);
var value: u32 = bytes[0] & 0b00001111;
@@ -62,7 +55,7 @@ pub fn utf8Decode3(bytes: []const u8) %u32 {
return value;
}
-pub fn utf8Decode4(bytes: []const u8) %u32 {
+pub fn utf8Decode4(bytes: []const u8) !u32 {
std.debug.assert(bytes.len == 4);
std.debug.assert(bytes[0] & 0b11111000 == 0b11110000);
var value: u32 = bytes[0] & 0b00000111;
@@ -85,7 +78,6 @@ pub fn utf8Decode4(bytes: []const u8) %u32 {
return value;
}
-error UnexpectedEof;
test "valid utf8" {
testValid("\x00", 0x0);
testValid("\x20", 0x20);
@@ -161,7 +153,7 @@ fn testValid(bytes: []const u8, expected_codepoint: u32) void {
std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint);
}
-fn testDecode(bytes: []const u8) %u32 {
+fn testDecode(bytes: []const u8) !u32 {
const length = try utf8ByteSequenceLength(bytes[0]);
if (bytes.len < length) return error.UnexpectedEof;
std.debug.assert(bytes.len == length);
diff --git a/src-self-hosted/ast.zig b/std/zig/ast.zig
similarity index 98%
rename from src-self-hosted/ast.zig
rename to std/zig/ast.zig
index b63c0a347e..a966c0316e 100644
--- a/src-self-hosted/ast.zig
+++ b/std/zig/ast.zig
@@ -1,7 +1,7 @@
-const std = @import("std");
+const std = @import("../index.zig");
const assert = std.debug.assert;
const ArrayList = std.ArrayList;
-const Token = @import("tokenizer.zig").Token;
+const Token = std.zig.Token;
const mem = std.mem;
pub const Node = struct {
diff --git a/std/zig/index.zig b/std/zig/index.zig
new file mode 100644
index 0000000000..32699935d9
--- /dev/null
+++ b/std/zig/index.zig
@@ -0,0 +1,11 @@
+const tokenizer = @import("tokenizer.zig");
+pub const Token = tokenizer.Token;
+pub const Tokenizer = tokenizer.Tokenizer;
+pub const Parser = @import("parser.zig").Parser;
+pub const ast = @import("ast.zig");
+
+test "std.zig tests" {
+ _ = @import("tokenizer.zig");
+ _ = @import("parser.zig");
+ _ = @import("ast.zig");
+}
diff --git a/src-self-hosted/parser.zig b/std/zig/parser.zig
similarity index 95%
rename from src-self-hosted/parser.zig
rename to std/zig/parser.zig
index 550d29b2c4..601e91fe7f 100644
--- a/src-self-hosted/parser.zig
+++ b/std/zig/parser.zig
@@ -1,10 +1,10 @@
-const std = @import("std");
+const std = @import("../index.zig");
const assert = std.debug.assert;
const ArrayList = std.ArrayList;
const mem = std.mem;
-const ast = @import("ast.zig");
-const Tokenizer = @import("tokenizer.zig").Tokenizer;
-const Token = @import("tokenizer.zig").Token;
+const ast = std.zig.ast;
+const Tokenizer = std.zig.Tokenizer;
+const Token = std.zig.Token;
const builtin = @import("builtin");
const io = std.io;
@@ -12,8 +12,6 @@ const io = std.io;
// get rid of this
const warn = std.debug.warn;
-error ParseError;
-
pub const Parser = struct {
allocator: &mem.Allocator,
tokenizer: &Tokenizer,
@@ -63,7 +61,7 @@ pub const Parser = struct {
NullableField: &?&ast.Node,
List: &ArrayList(&ast.Node),
- pub fn store(self: &const DestPtr, value: &ast.Node) %void {
+ pub fn store(self: &const DestPtr, value: &ast.Node) !void {
switch (*self) {
DestPtr.Field => |ptr| *ptr = value,
DestPtr.NullableField => |ptr| *ptr = value,
@@ -99,7 +97,7 @@ pub const Parser = struct {
/// Returns an AST tree, allocated with the parser's allocator.
/// Result should be freed with `freeAst` when done.
- pub fn parse(self: &Parser) %Tree {
+ pub fn parse(self: &Parser) !Tree {
var stack = self.initUtilityArrayList(State);
defer self.deinitUtilityArrayList(stack);
@@ -135,7 +133,6 @@ pub const Parser = struct {
Token.Id.Eof => return Tree {.root_node = root_node},
else => {
self.putBackToken(token);
- // TODO shouldn't need this cast
stack.append(State { .TopLevelExtern = null }) catch unreachable;
continue;
},
@@ -544,7 +541,7 @@ pub const Parser = struct {
}
}
- fn createRoot(self: &Parser) %&ast.NodeRoot {
+ fn createRoot(self: &Parser) !&ast.NodeRoot {
const node = try self.allocator.create(ast.NodeRoot);
*node = ast.NodeRoot {
@@ -555,7 +552,7 @@ pub const Parser = struct {
}
fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
- extern_token: &const ?Token) %&ast.NodeVarDecl
+ extern_token: &const ?Token) !&ast.NodeVarDecl
{
const node = try self.allocator.create(ast.NodeVarDecl);
@@ -577,7 +574,7 @@ pub const Parser = struct {
}
fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
- cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) %&ast.NodeFnProto
+ cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
{
const node = try self.allocator.create(ast.NodeFnProto);
@@ -599,7 +596,7 @@ pub const Parser = struct {
return node;
}
- fn createParamDecl(self: &Parser) %&ast.NodeParamDecl {
+ fn createParamDecl(self: &Parser) !&ast.NodeParamDecl {
const node = try self.allocator.create(ast.NodeParamDecl);
*node = ast.NodeParamDecl {
@@ -613,7 +610,7 @@ pub const Parser = struct {
return node;
}
- fn createBlock(self: &Parser, begin_token: &const Token) %&ast.NodeBlock {
+ fn createBlock(self: &Parser, begin_token: &const Token) !&ast.NodeBlock {
const node = try self.allocator.create(ast.NodeBlock);
*node = ast.NodeBlock {
@@ -625,7 +622,7 @@ pub const Parser = struct {
return node;
}
- fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) %&ast.NodeInfixOp {
+ fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
const node = try self.allocator.create(ast.NodeInfixOp);
*node = ast.NodeInfixOp {
@@ -638,7 +635,7 @@ pub const Parser = struct {
return node;
}
- fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) %&ast.NodePrefixOp {
+ fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
const node = try self.allocator.create(ast.NodePrefixOp);
*node = ast.NodePrefixOp {
@@ -650,7 +647,7 @@ pub const Parser = struct {
return node;
}
- fn createIdentifier(self: &Parser, name_token: &const Token) %&ast.NodeIdentifier {
+ fn createIdentifier(self: &Parser, name_token: &const Token) !&ast.NodeIdentifier {
const node = try self.allocator.create(ast.NodeIdentifier);
*node = ast.NodeIdentifier {
@@ -660,7 +657,7 @@ pub const Parser = struct {
return node;
}
- fn createIntegerLiteral(self: &Parser, token: &const Token) %&ast.NodeIntegerLiteral {
+ fn createIntegerLiteral(self: &Parser, token: &const Token) !&ast.NodeIntegerLiteral {
const node = try self.allocator.create(ast.NodeIntegerLiteral);
*node = ast.NodeIntegerLiteral {
@@ -670,7 +667,7 @@ pub const Parser = struct {
return node;
}
- fn createFloatLiteral(self: &Parser, token: &const Token) %&ast.NodeFloatLiteral {
+ fn createFloatLiteral(self: &Parser, token: &const Token) !&ast.NodeFloatLiteral {
const node = try self.allocator.create(ast.NodeFloatLiteral);
*node = ast.NodeFloatLiteral {
@@ -680,13 +677,13 @@ pub const Parser = struct {
return node;
}
- fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) %&ast.NodeIdentifier {
+ fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
const node = try self.createIdentifier(name_token);
try dest_ptr.store(&node.base);
return node;
}
- fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) %&ast.NodeParamDecl {
+ fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
const node = try self.createParamDecl();
try list.append(&node.base);
return node;
@@ -694,7 +691,7 @@ pub const Parser = struct {
fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
- inline_token: &const ?Token) %&ast.NodeFnProto
+ inline_token: &const ?Token) !&ast.NodeFnProto
{
const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
try list.append(&node.base);
@@ -702,14 +699,14 @@ pub const Parser = struct {
}
fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
- mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) %&ast.NodeVarDecl
+ mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
{
const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
try list.append(&node.base);
return node;
}
- fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) error {
+ fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
const loc = self.tokenizer.getTokenLocation(token);
warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
@@ -730,13 +727,13 @@ pub const Parser = struct {
return error.ParseError;
}
- fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) %void {
+ fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
if (token.id != id) {
return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
}
}
- fn eatToken(self: &Parser, id: @TagType(Token.Id)) %Token {
+ fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
const token = self.getNextToken();
try self.expectToken(token, id);
return token;
@@ -763,7 +760,7 @@ pub const Parser = struct {
indent: usize,
};
- pub fn renderAst(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) %void {
+ pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
var stack = self.initUtilityArrayList(RenderAstFrame);
defer self.deinitUtilityArrayList(stack);
@@ -802,7 +799,7 @@ pub const Parser = struct {
Indent: usize,
};
- pub fn renderSource(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) %void {
+ pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
var stack = self.initUtilityArrayList(RenderState);
defer self.deinitUtilityArrayList(stack);
@@ -1038,14 +1035,11 @@ pub const Parser = struct {
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
-fn testParse(source: []const u8, allocator: &mem.Allocator) %[]u8 {
+fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
var padded_source: [0x100]u8 = undefined;
std.mem.copy(u8, padded_source[0..source.len], source);
- padded_source[source.len + 0] = '\n';
- padded_source[source.len + 1] = '\n';
- padded_source[source.len + 2] = '\n';
- var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]);
+ var tokenizer = Tokenizer.init(padded_source[0..source.len]);
var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
defer parser.deinit();
@@ -1058,13 +1052,9 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) %[]u8 {
return buffer.toOwnedSlice();
}
-error TestFailed;
-error NondeterministicMemoryUsage;
-error MemoryLeakDetected;
-
// TODO test for memory leaks
// TODO test for valid frees
-fn testCanonical(source: []const u8) %void {
+fn testCanonical(source: []const u8) !void {
const needed_alloc_count = x: {
// Try it once with unlimited memory, make sure it works
var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
@@ -1088,16 +1078,18 @@ fn testCanonical(source: []const u8) %void {
var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
if (testParse(source, &failing_allocator.allocator)) |_| {
return error.NondeterministicMemoryUsage;
- } else |err| {
- assert(err == error.OutOfMemory);
- // TODO make this pass
- //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
- // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
- // fail_index, needed_alloc_count,
- // failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
- // failing_allocator.index, failing_allocator.deallocations);
- // return error.MemoryLeakDetected;
- //}
+ } else |err| switch (err) {
+ error.OutOfMemory => {
+ // TODO make this pass
+ //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
+ // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
+ // fail_index, needed_alloc_count,
+ // failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
+ // failing_allocator.index, failing_allocator.deallocations);
+ // return error.MemoryLeakDetected;
+ //}
+ },
+ error.ParseError => @panic("test failed"),
}
}
}
diff --git a/src-self-hosted/tokenizer.zig b/std/zig/tokenizer.zig
similarity index 97%
rename from src-self-hosted/tokenizer.zig
rename to std/zig/tokenizer.zig
index f1f87abfc9..546356caa3 100644
--- a/src-self-hosted/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -1,4 +1,4 @@
-const std = @import("std");
+const std = @import("../index.zig");
const mem = std.mem;
pub const Token = struct {
@@ -175,12 +175,7 @@ pub const Tokenizer = struct {
std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
}
- /// buffer must end with "\n\n\n". This is so that attempting to decode
- /// a the 3 trailing bytes of a 4-byte utf8 sequence is never a buffer overflow.
pub fn init(buffer: []const u8) Tokenizer {
- std.debug.assert(buffer[buffer.len - 1] == '\n');
- std.debug.assert(buffer[buffer.len - 2] == '\n');
- std.debug.assert(buffer[buffer.len - 3] == '\n');
return Tokenizer {
.buffer = buffer,
.index = 0,
@@ -556,8 +551,9 @@ pub const Tokenizer = struct {
} else {
// check utf8-encoded character.
const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1;
- // the last 3 bytes in the buffer are guaranteed to be '\n',
- // which means we don't need to do any bounds checking here.
+ if (self.index + length >= self.buffer.len) {
+ return u3(self.buffer.len - self.index);
+ }
const bytes = self.buffer[self.index..self.index + length];
switch (length) {
2 => {
diff --git a/test/cases/cast.zig b/test/cases/cast.zig
index 2455179c89..6ffb558174 100644
--- a/test/cases/cast.zig
+++ b/test/cases/cast.zig
@@ -32,7 +32,6 @@ fn funcWithConstPtrPtr(x: &const &i32) void {
**x += 1;
}
-error ItBroke;
test "explicit cast from integer to error type" {
testCastIntToErr(error.ItBroke);
comptime testCastIntToErr(error.ItBroke);
@@ -75,7 +74,7 @@ test "string literal to &const []const u8" {
assert(mem.eql(u8, *x, "hello"));
}
-test "implicitly cast from T to %?T" {
+test "implicitly cast from T to error!?T" {
castToMaybeTypeError(1);
comptime castToMaybeTypeError(1);
}
@@ -84,37 +83,37 @@ const A = struct {
};
fn castToMaybeTypeError(z: i32) void {
const x = i32(1);
- const y: %?i32 = x;
+ const y: error!?i32 = x;
assert(??(try y) == 1);
const f = z;
- const g: %?i32 = f;
+ const g: error!?i32 = f;
const a = A{ .a = z };
- const b: %?A = a;
+ const b: error!?A = a;
assert((??(b catch unreachable)).a == 1);
}
-test "implicitly cast from int to %?T" {
+test "implicitly cast from int to error!?T" {
implicitIntLitToMaybe();
comptime implicitIntLitToMaybe();
}
fn implicitIntLitToMaybe() void {
const f: ?i32 = 1;
- const g: %?i32 = 1;
+ const g: error!?i32 = 1;
}
-test "return null from fn() %?&T" {
+test "return null from fn() error!?&T" {
const a = returnNullFromMaybeTypeErrorRef();
const b = returnNullLitFromMaybeTypeErrorRef();
assert((try a) == null and (try b) == null);
}
-fn returnNullFromMaybeTypeErrorRef() %?&A {
+fn returnNullFromMaybeTypeErrorRef() error!?&A {
const a: ?&A = null;
return a;
}
-fn returnNullLitFromMaybeTypeErrorRef() %?&A {
+fn returnNullLitFromMaybeTypeErrorRef() error!?&A {
return null;
}
@@ -161,7 +160,7 @@ fn castToMaybeSlice() ?[]const u8 {
}
-test "implicitly cast from [0]T to %[]T" {
+test "implicitly cast from [0]T to error![]T" {
testCastZeroArrayToErrSliceMut();
comptime testCastZeroArrayToErrSliceMut();
}
@@ -170,11 +169,11 @@ fn testCastZeroArrayToErrSliceMut() void {
assert((gimmeErrOrSlice() catch unreachable).len == 0);
}
-fn gimmeErrOrSlice() %[]u8 {
+fn gimmeErrOrSlice() error![]u8 {
return []u8{};
}
-test "peer type resolution: [0]u8, []const u8, and %[]u8" {
+test "peer type resolution: [0]u8, []const u8, and error![]u8" {
{
var data = "hi";
const slice = data[0..];
@@ -188,7 +187,7 @@ test "peer type resolution: [0]u8, []const u8, and %[]u8" {
assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
}
}
-fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) %[]u8 {
+fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) error![]u8 {
if (a) {
return []u8{};
}
@@ -230,7 +229,7 @@ fn foo(args: ...) void {
test "peer type resolution: error and [N]T" {
- // TODO: implicit %T to %U where T can implicitly cast to U
+ // TODO: implicit error!T to error!U where T can implicitly cast to U
//assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
//comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
@@ -238,14 +237,13 @@ test "peer type resolution: error and [N]T" {
comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK"));
}
-error BadValue;
-//fn testPeerErrorAndArray(x: u8) %[]const u8 {
+//fn testPeerErrorAndArray(x: u8) error![]const u8 {
// return switch (x) {
// 0x00 => "OK",
// else => error.BadValue,
// };
//}
-fn testPeerErrorAndArray2(x: u8) %[]const u8 {
+fn testPeerErrorAndArray2(x: u8) error![]const u8 {
return switch (x) {
0x00 => "OK",
0x01 => "OKK",
diff --git a/test/cases/defer.zig b/test/cases/defer.zig
index 6490ec0acd..a989af18c2 100644
--- a/test/cases/defer.zig
+++ b/test/cases/defer.zig
@@ -3,9 +3,7 @@ const assert = @import("std").debug.assert;
var result: [3]u8 = undefined;
var index: usize = undefined;
-error FalseNotAllowed;
-
-fn runSomeErrorDefers(x: bool) %bool {
+fn runSomeErrorDefers(x: bool) !bool {
index = 0;
defer {result[index] = 'a'; index += 1;}
errdefer {result[index] = 'b'; index += 1;}
diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig
index 9abc15d129..0c2ae1c383 100644
--- a/test/cases/enum_with_members.zig
+++ b/test/cases/enum_with_members.zig
@@ -6,7 +6,7 @@ const ET = union(enum) {
SINT: i32,
UINT: u32,
- pub fn print(a: &const ET, buf: []u8) %usize {
+ pub fn print(a: &const ET, buf: []u8) error!usize {
return switch (*a) {
ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
diff --git a/test/cases/error.zig b/test/cases/error.zig
index 47e5f25be9..e64bf02c91 100644
--- a/test/cases/error.zig
+++ b/test/cases/error.zig
@@ -1,16 +1,18 @@
-const assert = @import("std").debug.assert;
-const mem = @import("std").mem;
+const std = @import("std");
+const assert = std.debug.assert;
+const mem = std.mem;
+const builtin = @import("builtin");
-pub fn foo() %i32 {
+pub fn foo() error!i32 {
const x = try bar();
return x + 1;
}
-pub fn bar() %i32 {
+pub fn bar() error!i32 {
return 13;
}
-pub fn baz() %i32 {
+pub fn baz() error!i32 {
const y = foo() catch 1234;
return y + 1;
}
@@ -19,7 +21,6 @@ test "error wrapping" {
assert((baz() catch unreachable) == 15);
}
-error ItBroke;
fn gimmeItBroke() []const u8 {
return @errorName(error.ItBroke);
}
@@ -28,8 +29,6 @@ test "@errorName" {
assert(mem.eql(u8, @errorName(error.AnError), "AnError"));
assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
}
-error AnError;
-error ALongerErrorName;
test "error values" {
@@ -37,16 +36,11 @@ test "error values" {
const b = i32(error.err2);
assert(a != b);
}
-error err1;
-error err2;
test "redefinition of error values allowed" {
shouldBeNotEqual(error.AnError, error.SecondError);
}
-error AnError;
-error AnError;
-error SecondError;
fn shouldBeNotEqual(a: error, b: error) void {
if (a == b) unreachable;
}
@@ -58,8 +52,7 @@ test "error binary operator" {
assert(a == 3);
assert(b == 10);
}
-error ItBroke;
-fn errBinaryOperatorG(x: bool) %isize {
+fn errBinaryOperatorG(x: bool) error!isize {
return if (x) error.ItBroke else isize(10);
}
@@ -68,18 +61,117 @@ test "unwrap simple value from error" {
const i = unwrapSimpleValueFromErrorDo() catch unreachable;
assert(i == 13);
}
-fn unwrapSimpleValueFromErrorDo() %isize { return 13; }
+fn unwrapSimpleValueFromErrorDo() error!isize { return 13; }
test "error return in assignment" {
doErrReturnInAssignment() catch unreachable;
}
-fn doErrReturnInAssignment() %void {
+fn doErrReturnInAssignment() error!void {
var x : i32 = undefined;
x = try makeANonErr();
}
-fn makeANonErr() %i32 {
+fn makeANonErr() error!i32 {
return 1;
}
+
+test "error union type " {
+ testErrorUnionType();
+ comptime testErrorUnionType();
+}
+
+fn testErrorUnionType() void {
+ const x: error!i32 = 1234;
+ if (x) |value| assert(value == 1234) else |_| unreachable;
+ assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion);
+ assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet);
+ assert(@typeOf(x).ErrorSet == error);
+}
+
+test "error set type " {
+ testErrorSetType();
+ comptime testErrorSetType();
+}
+
+const MyErrSet = error {OutOfMemory, FileNotFound};
+
+fn testErrorSetType() void {
+ assert(@memberCount(MyErrSet) == 2);
+
+ const a: MyErrSet!i32 = 5678;
+ const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
+
+ if (a) |value| assert(value == 5678) else |err| switch (err) {
+ error.OutOfMemory => unreachable,
+ error.FileNotFound => unreachable,
+ }
+}
+
+
+test "explicit error set cast" {
+ testExplicitErrorSetCast(Set1.A);
+ comptime testExplicitErrorSetCast(Set1.A);
+}
+
+const Set1 = error{A, B};
+const Set2 = error{A, C};
+
+fn testExplicitErrorSetCast(set1: Set1) void {
+ var x = Set2(set1);
+ var y = Set1(x);
+ assert(y == error.A);
+}
+
+test "comptime test error for empty error set" {
+ testComptimeTestErrorEmptySet(1234);
+ comptime testComptimeTestErrorEmptySet(1234);
+}
+
+const EmptyErrorSet = error {};
+
+fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void {
+ if (x) |v| assert(v == 1234) else |err| @compileError("bad");
+}
+
+test "syntax: nullable operator in front of error union operator" {
+ comptime {
+ assert(?error!i32 == ?(error!i32));
+ }
+}
+
+test "comptime err to int of error set with only 1 possible value" {
+ testErrToIntWithOnePossibleValue(error.A, u32(error.A));
+ comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A));
+}
+fn testErrToIntWithOnePossibleValue(x: error{A}, comptime value: u32) void {
+ if (u32(x) != value) {
+ @compileError("bad");
+ }
+}
+
+test "error union peer type resolution" {
+ testErrorUnionPeerTypeResolution(1);
+ comptime testErrorUnionPeerTypeResolution(1);
+}
+
+fn testErrorUnionPeerTypeResolution(x: i32) void {
+ const y = switch (x) {
+ 1 => bar_1(),
+ 2 => baz_1(),
+ else => quux_1(),
+ };
+}
+
+fn bar_1() error {
+ return error.A;
+}
+
+fn baz_1() !i32 {
+ return error.B;
+}
+
+fn quux_1() !i32 {
+ return error.C;
+}
diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig
index 44dfa330a1..202df19f62 100644
--- a/test/cases/ir_block_deps.zig
+++ b/test/cases/ir_block_deps.zig
@@ -1,6 +1,6 @@
const assert = @import("std").debug.assert;
-fn foo(id: u64) %i32 {
+fn foo(id: u64) !i32 {
return switch (id) {
1 => getErrInt(),
2 => {
@@ -11,9 +11,7 @@ fn foo(id: u64) %i32 {
};
}
-fn getErrInt() %i32 { return 0; }
-
-error ItBroke;
+fn getErrInt() error!i32 { return 0; }
test "ir block deps" {
assert((foo(1) catch unreachable) == 0);
diff --git a/test/cases/misc.zig b/test/cases/misc.zig
index 85757efbb8..964c5babc1 100644
--- a/test/cases/misc.zig
+++ b/test/cases/misc.zig
@@ -262,7 +262,7 @@ test "generic malloc free" {
memFree(u8, a);
}
const some_mem : [100]u8 = undefined;
-fn memAlloc(comptime T: type, n: usize) %[]T {
+fn memAlloc(comptime T: type, n: usize) error![]T {
return @ptrCast(&T, &some_mem[0])[0..n];
}
fn memFree(comptime T: type, memory: []T) void { }
@@ -419,7 +419,7 @@ test "cast slice to u8 slice" {
test "pointer to void return type" {
testPointerToVoidReturnType() catch unreachable;
}
-fn testPointerToVoidReturnType() %void {
+fn testPointerToVoidReturnType() error!void {
const a = testPointerToVoidReturnType2();
return *a;
}
@@ -475,8 +475,8 @@ test "@typeId" {
assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral);
assert(@typeId(@typeOf(null)) == Tid.NullLiteral);
assert(@typeId(?i32) == Tid.Nullable);
- assert(@typeId(%i32) == Tid.ErrorUnion);
- assert(@typeId(error) == Tid.Error);
+ assert(@typeId(error!i32) == Tid.ErrorUnion);
+ assert(@typeId(error) == Tid.ErrorSet);
assert(@typeId(AnEnum) == Tid.Enum);
assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum);
assert(@typeId(AUnionEnum) == Tid.Union);
diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig
index 8e103a3bc7..18a766d9fc 100644
--- a/test/cases/reflection.zig
+++ b/test/cases/reflection.zig
@@ -5,7 +5,7 @@ test "reflection: array, pointer, nullable, error union type child" {
comptime {
assert(([10]u8).Child == u8);
assert((&u8).Child == u8);
- assert((%u8).Child == u8);
+ assert((error!u8).Payload == u8);
assert((?u8).Child == u8);
}
}
diff --git a/test/cases/switch.zig b/test/cases/switch.zig
index f742057e68..a0ac646160 100644
--- a/test/cases/switch.zig
+++ b/test/cases/switch.zig
@@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 {
return 10;
}
-fn return_a_number() %i32 {
+fn return_a_number() error!i32 {
return 1;
}
diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig
index 2da5b5f09a..136e8834e6 100644
--- a/test/cases/switch_prong_err_enum.zig
+++ b/test/cases/switch_prong_err_enum.zig
@@ -2,19 +2,17 @@ const assert = @import("std").debug.assert;
var read_count: u64 = 0;
-fn readOnce() %u64 {
+fn readOnce() error!u64 {
read_count += 1;
return read_count;
}
-error InvalidDebugInfo;
-
const FormValue = union(enum) {
Address: u64,
Other: bool,
};
-fn doThing(form_id: u64) %FormValue {
+fn doThing(form_id: u64) error!FormValue {
return switch (form_id) {
17 => FormValue { .Address = try readOnce() },
else => error.InvalidDebugInfo,
diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig
index 300b8c21a1..335feeef43 100644
--- a/test/cases/switch_prong_implicit_cast.zig
+++ b/test/cases/switch_prong_implicit_cast.zig
@@ -5,9 +5,7 @@ const FormValue = union(enum) {
Two: bool,
};
-error Whatever;
-
-fn foo(id: u64) %FormValue {
+fn foo(id: u64) !FormValue {
return switch (id) {
2 => FormValue { .Two = true },
1 => FormValue { .One = {} },
diff --git a/test/cases/try.zig b/test/cases/try.zig
index 00259721b1..4a0425e22e 100644
--- a/test/cases/try.zig
+++ b/test/cases/try.zig
@@ -17,10 +17,7 @@ fn tryOnErrorUnionImpl() void {
assert(x == 11);
}
-error ItBroke;
-error NoMem;
-error CrappedOut;
-fn returnsTen() %i32 {
+fn returnsTen() error!i32 {
return 10;
}
@@ -32,7 +29,7 @@ test "try without vars" {
assert(result2 == 1);
}
-fn failIfTrue(ok: bool) %void {
+fn failIfTrue(ok: bool) error!void {
if (ok) {
return error.ItBroke;
} else {
diff --git a/test/cases/union.zig b/test/cases/union.zig
index ea8a9da188..dc2a7c3414 100644
--- a/test/cases/union.zig
+++ b/test/cases/union.zig
@@ -13,7 +13,7 @@ const Agg = struct {
const v1 = Value { .Int = 1234 };
const v2 = Value { .Array = []u8{3} ** 9 };
-const err = (%Agg)(Agg {
+const err = (error!Agg)(Agg {
.val1 = v1,
.val2 = v2,
});
diff --git a/test/cases/while.zig b/test/cases/while.zig
index 5970e742ea..33d5a5623a 100644
--- a/test/cases/while.zig
+++ b/test/cases/while.zig
@@ -50,7 +50,7 @@ fn runContinueAndBreakTest() void {
test "return with implicit cast from while loop" {
returnWithImplicitCastFromWhileLoopTest() catch unreachable;
}
-fn returnWithImplicitCastFromWhileLoopTest() %void {
+fn returnWithImplicitCastFromWhileLoopTest() error!void {
while (true) {
return;
}
@@ -116,8 +116,7 @@ test "while with error union condition" {
}
var numbers_left: i32 = undefined;
-error OutOfNumbers;
-fn getNumberOrErr() %i32 {
+fn getNumberOrErr() error!i32 {
return if (numbers_left == 0)
error.OutOfNumbers
else x: {
@@ -205,8 +204,7 @@ fn testContinueOuter() void {
fn returnNull() ?i32 { return null; }
fn returnMaybe(x: i32) ?i32 { return x; }
-error YouWantedAnError;
-fn returnError() %i32 { return error.YouWantedAnError; }
-fn returnSuccess(x: i32) %i32 { return x; }
+fn returnError() error!i32 { return error.YouWantedAnError; }
+fn returnSuccess(x: i32) error!i32 { return x; }
fn returnFalse() bool { return false; }
fn returnTrue() bool { return true; }
diff --git a/test/compare_output.zig b/test/compare_output.zig
index 6e379c8d1e..2ca2ba262e 100644
--- a/test/compare_output.zig
+++ b/test/compare_output.zig
@@ -1,4 +1,6 @@
-const os = @import("std").os;
+const builtin = @import("builtin");
+const std = @import("std");
+const os = std.os;
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompareOutputContext) void {
@@ -8,14 +10,14 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ _ = c.puts(c"Hello, world!");
\\ return 0;
\\}
- , "Hello, world!" ++ os.line_sep);
+ , "Hello, world!" ++ std.cstr.line_sep);
cases.addCase(x: {
var tc = cases.create("multiple files with private function",
\\use @import("std").io;
\\use @import("foo.zig");
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ privateFunction();
\\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream);
\\ stdout.print("OK 2\n") catch unreachable;
@@ -49,7 +51,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\use @import("foo.zig");
\\use @import("bar.zig");
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ foo_function();
\\ bar_function();
\\}
@@ -89,7 +91,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
var tc = cases.create("two files use import each other",
\\use @import("a.zig");
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ ok();
\\}
, "OK\n");
@@ -118,7 +120,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
cases.add("hello world without libc",
\\const io = @import("std").io;
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
\\}
@@ -268,7 +270,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\const z = io.stdin_fileno;
\\const x : @typeOf(y) = 1234;
\\const y : u16 = 5678;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ var x_local : i32 = print_ok(x);
\\}
\\fn print_ok(val: @typeOf(x)) @typeOf(foo) {
@@ -351,7 +353,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ fn method(b: &const Bar) bool { return true; }
\\};
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const bar = Bar {.field2 = 13,};
\\ const foo = Foo {.field1 = bar,};
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
@@ -367,7 +369,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
cases.add("defer with only fallthrough",
\\const io = @import("std").io;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
@@ -380,7 +382,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
cases.add("defer with return",
\\const io = @import("std").io;
\\const os = @import("std").os;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
@@ -394,10 +396,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
cases.add("errdefer and it fails",
\\const io = @import("std").io;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ do_test() catch return;
\\}
- \\fn do_test() %void {
+ \\fn do_test() !void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
@@ -406,18 +408,17 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ defer stdout.print("defer3\n") catch unreachable;
\\ stdout.print("after\n") catch unreachable;
\\}
- \\error IToldYouItWouldFail;
- \\fn its_gonna_fail() %void {
+ \\fn its_gonna_fail() !void {
\\ return error.IToldYouItWouldFail;
\\}
, "before\ndeferErr\ndefer1\n");
cases.add("errdefer and it passes",
\\const io = @import("std").io;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ do_test() catch return;
\\}
- \\fn do_test() %void {
+ \\fn do_test() !void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ stdout.print("before\n") catch unreachable;
\\ defer stdout.print("defer1\n") catch unreachable;
@@ -426,7 +427,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ defer stdout.print("defer3\n") catch unreachable;
\\ stdout.print("after\n") catch unreachable;
\\}
- \\fn its_gonna_pass() %void { }
+ \\fn its_gonna_pass() error!void { }
, "before\nafter\ndefer3\ndefer1\n");
cases.addCase(x: {
@@ -434,7 +435,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\const foo_txt = @embedFile("foo.txt");
\\const io = @import("std").io;
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream);
\\ stdout.print(foo_txt) catch unreachable;
\\}
@@ -452,7 +453,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\const os = std.os;
\\const allocator = std.debug.global_allocator;
\\
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ var args_it = os.args();
\\ var stdout_file = try io.getStdOut();
\\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
@@ -493,7 +494,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\const os = std.os;
\\const allocator = std.debug.global_allocator;
\\
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ var args_it = os.args();
\\ var stdout_file = try io.getStdOut();
\\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 92066d7e0e..f60705aa31 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,220 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) void {
+ cases.add("no else prong on switch on global error set",
+ \\export fn entry() void {
+ \\ foo(error.A);
+ \\}
+ \\fn foo(a: error) void {
+ \\ switch (a) {
+ \\ error.A => {},
+ \\ }
+ \\}
+ ,
+ ".tmp_source.zig:5:5: error: else prong required when switching on type 'error'");
+
+ cases.add("inferred error set with no returned error",
+ \\export fn entry() void {
+ \\ foo() catch unreachable;
+ \\}
+ \\fn foo() !void {
+ \\}
+ ,
+ ".tmp_source.zig:4:11: error: function with inferred error set must return at least one possible error");
+
+ cases.add("error not handled in switch",
+ \\export fn entry() void {
+ \\ foo(452) catch |err| switch (err) {
+ \\ error.Foo => {},
+ \\ };
+ \\}
+ \\fn foo(x: i32) !void {
+ \\ switch (x) {
+ \\ 0 ... 10 => return error.Foo,
+ \\ 11 ... 20 => return error.Bar,
+ \\ 21 ... 30 => return error.Baz,
+ \\ else => {},
+ \\ }
+ \\}
+ ,
+ ".tmp_source.zig:2:26: error: error.Baz not handled in switch",
+ ".tmp_source.zig:2:26: error: error.Bar not handled in switch");
+
+ cases.add("duplicate error in switch",
+ \\export fn entry() void {
+ \\ foo(452) catch |err| switch (err) {
+ \\ error.Foo => {},
+ \\ error.Bar => {},
+ \\ error.Foo => {},
+ \\ else => {},
+ \\ };
+ \\}
+ \\fn foo(x: i32) !void {
+ \\ switch (x) {
+ \\ 0 ... 10 => return error.Foo,
+ \\ 11 ... 20 => return error.Bar,
+ \\ else => {},
+ \\ }
+ \\}
+ ,
+ ".tmp_source.zig:5:14: error: duplicate switch value: '@typeOf(foo).ReturnType.ErrorSet.Foo'",
+ ".tmp_source.zig:3:14: note: other value is here");
+
+ cases.add("range operator in switch used on error set",
+ \\export fn entry() void {
+ \\ try foo(452) catch |err| switch (err) {
+ \\ error.A ... error.B => {},
+ \\ else => {},
+ \\ };
+ \\}
+ \\fn foo(x: i32) !void {
+ \\ switch (x) {
+ \\ 0 ... 10 => return error.Foo,
+ \\ 11 ... 20 => return error.Bar,
+ \\ else => {},
+ \\ }
+ \\}
+ ,
+ ".tmp_source.zig:3:17: error: operator not allowed for errors");
+
+ cases.add("inferring error set of function pointer",
+ \\comptime {
+ \\ const z: ?fn()!void = null;
+ \\}
+ ,
+ ".tmp_source.zig:2:15: error: inferring error set of return type valid only for function definitions");
+
+ cases.add("access non-existent member of error set",
+ \\const Foo = error{A};
+ \\comptime {
+ \\ const z = Foo.Bar;
+ \\}
+ ,
+ ".tmp_source.zig:3:18: error: no error named 'Bar' in 'Foo'");
+
+ cases.add("error union operator with non error set LHS",
+ \\comptime {
+ \\ const z = i32!i32;
+ \\}
+ ,
+ ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'");
+
+ cases.add("error equality but sets have no common members",
+ \\const Set1 = error{A, C};
+ \\const Set2 = error{B, D};
+ \\export fn entry() void {
+ \\ foo(Set1.A);
+ \\}
+ \\fn foo(x: Set1) void {
+ \\ if (x == Set2.B) {
+ \\
+ \\ }
+ \\}
+ ,
+ ".tmp_source.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors");
+
+ cases.add("only equality binary operator allowed for error sets",
+ \\comptime {
+ \\ const z = error.A > error.B;
+ \\}
+ ,
+ ".tmp_source.zig:2:23: error: operator not allowed for errors");
+
+ cases.add("explicit error set cast known at comptime violates error sets",
+ \\const Set1 = error {A, B};
+ \\const Set2 = error {A, C};
+ \\comptime {
+ \\ var x = Set1.B;
+ \\ var y = Set2(x);
+ \\}
+ ,
+ ".tmp_source.zig:5:17: error: error.B not a member of error set 'Set2'");
+
+ cases.add("cast error union of global error set to error union of smaller error set",
+ \\const SmallErrorSet = error{A};
+ \\export fn entry() void {
+ \\ var x: SmallErrorSet!i32 = foo();
+ \\}
+ \\fn foo() error!i32 {
+ \\ return error.B;
+ \\}
+ ,
+ ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32', found 'error!i32'",
+ ".tmp_source.zig:3:35: note: unable to cast global error set into smaller set");
+
+ cases.add("cast global error set to error set",
+ \\const SmallErrorSet = error{A};
+ \\export fn entry() void {
+ \\ var x: SmallErrorSet = foo();
+ \\}
+ \\fn foo() error {
+ \\ return error.B;
+ \\}
+ ,
+ ".tmp_source.zig:3:31: error: expected 'SmallErrorSet', found 'error'",
+ ".tmp_source.zig:3:31: note: unable to cast global error set into smaller set");
+
+ cases.add("recursive inferred error set",
+ \\export fn entry() void {
+ \\ foo() catch unreachable;
+ \\}
+ \\fn foo() !void {
+ \\ try foo();
+ \\}
+ ,
+ ".tmp_source.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet");
+
+ cases.add("implicit cast of error set not a subset",
+ \\const Set1 = error{A, B};
+ \\const Set2 = error{A, C};
+ \\export fn entry() void {
+ \\ foo(Set1.B);
+ \\}
+ \\fn foo(set1: Set1) void {
+ \\ var x: Set2 = set1;
+ \\}
+ ,
+ ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'",
+ ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set");
+
+ cases.add("int to err global invalid number",
+ \\const Set1 = error{A, B};
+ \\comptime {
+ \\ var x: usize = 3;
+ \\ var y = error(x);
+ \\}
+ ,
+ ".tmp_source.zig:4:18: error: integer value 3 represents no error");
+
+ cases.add("int to err non global invalid number",
+ \\const Set1 = error{A, B};
+ \\const Set2 = error{A, C};
+ \\comptime {
+ \\ var x = usize(Set1.B);
+ \\ var y = Set2(x);
+ \\}
+ ,
+ ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'");
+
+ cases.add("@memberCount of error",
+ \\comptime {
+ \\ _ = @memberCount(error);
+ \\}
+ ,
+ ".tmp_source.zig:2:9: error: global error set member count not available at comptime");
+
+ cases.add("duplicate error value in error set",
+ \\const Foo = error {
+ \\ Bar,
+ \\ Bar,
+ \\};
+ \\export fn entry() void {
+ \\ const a: Foo = undefined;
+ \\}
+ ,
+ ".tmp_source.zig:3:5: error: duplicate error: 'Bar'",
+ ".tmp_source.zig:2:5: note: other error here");
+
cases.add("cast negative integer literal to usize",
\\export fn entry() void {
\\ const x = usize(-10);
@@ -112,12 +326,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
cases.add("wrong return type for main",
\\pub fn main() f32 { }
- , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+ , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
cases.add("double ?? on main return value",
\\pub fn main() ??void {
\\}
- , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+ , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
cases.add("bad identifier in function with struct defined inside function which references local const",
\\export fn entry() void {
@@ -1173,7 +1387,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\export fn f() void {
\\ try something();
\\}
- \\fn something() %void { }
+ \\fn something() error!void { }
,
".tmp_source.zig:2:5: error: expected type 'void', found 'error'");
@@ -1264,7 +1478,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
, ".tmp_source.zig:3:11: error: cannot assign to constant");
cases.add("main function with bogus args type",
- \\pub fn main(args: [][]bogus) %void {}
+ \\pub fn main(args: [][]bogus) !void {}
, ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'");
cases.add("for loop missing element param",
@@ -1396,7 +1610,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
, ".tmp_source.zig:6:13: error: cannot assign to constant");
cases.add("return from defer expression",
- \\pub fn testTrickyDefer() %void {
+ \\pub fn testTrickyDefer() !void {
\\ defer canFail() catch {};
\\
\\ defer try canFail();
@@ -1404,7 +1618,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\ const a = maybeInt() ?? return;
\\}
\\
- \\fn canFail() %void { }
+ \\fn canFail() error!void { }
\\
\\pub fn maybeInt() ?i32 {
\\ return 0;
@@ -1534,7 +1748,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\export fn foo() void {
\\ bar() catch unreachable;
\\}
- \\fn bar() %i32 { return 0; }
+ \\fn bar() error!i32 { return 0; }
, ".tmp_source.zig:2:11: error: expression value is ignored");
cases.add("ignored statement value",
@@ -1565,7 +1779,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\export fn foo() void {
\\ defer bar();
\\}
- \\fn bar() %i32 { return 0; }
+ \\fn bar() error!i32 { return 0; }
, ".tmp_source.zig:2:14: error: expression value is ignored");
cases.add("dereference an array",
@@ -1632,13 +1846,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
, ".tmp_source.zig:2:21: error: expected pointer, found 'usize'");
cases.add("too many error values to cast to small integer",
- \\error A; error B; error C; error D; error E; error F; error G; error H;
- \\const u2 = @IntType(false, 2);
- \\fn foo(e: error) u2 {
+ \\const Error = error { A, B, C, D, E, F, G, H };
+ \\fn foo(e: Error) u2 {
\\ return u2(e);
\\}
\\export fn entry() usize { return @sizeOf(@typeOf(foo)); }
- , ".tmp_source.zig:4:14: error: too many error values to fit in 'u2'");
+ , ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'");
cases.add("asm at compile time",
\\comptime {
@@ -1821,9 +2034,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\export fn foo() void {
\\ while (bar()) {}
\\}
- \\fn bar() %i32 { return 1; }
+ \\fn bar() error!i32 { return 1; }
,
- ".tmp_source.zig:2:15: error: expected type 'bool', found '%i32'");
+ ".tmp_source.zig:2:15: error: expected type 'bool', found 'error!i32'");
cases.add("while expected nullable, got bool",
\\export fn foo() void {
@@ -1837,9 +2050,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\export fn foo() void {
\\ while (bar()) |x| {}
\\}
- \\fn bar() %i32 { return 1; }
+ \\fn bar() error!i32 { return 1; }
,
- ".tmp_source.zig:2:15: error: expected nullable type, found '%i32'");
+ ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'");
cases.add("while expected error union, got bool",
\\export fn foo() void {
@@ -1983,7 +2196,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\fn foo1(args: ...) void {}
\\fn foo2(args: ...) void {}
\\
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ foos[0]();
\\}
,
@@ -1995,7 +2208,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\fn foo1(arg: var) void {}
\\fn foo2(arg: var) void {}
\\
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ foos[0](true);
\\}
,
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index 3fb2a91544..8b8f612056 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -5,7 +5,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ @panic("oh no");
\\}
);
@@ -14,7 +14,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const a = []i32{1, 2, 3, 4};
\\ baz(bar(a));
\\}
@@ -28,8 +28,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = add(65530, 10);
\\ if (x == 0) return error.Whatever;
\\}
@@ -42,8 +41,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = sub(10, 20);
\\ if (x == 0) return error.Whatever;
\\}
@@ -56,8 +54,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = mul(300, 6000);
\\ if (x == 0) return error.Whatever;
\\}
@@ -70,8 +67,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = neg(-32768);
\\ if (x == 32767) return error.Whatever;
\\}
@@ -84,8 +80,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = div(-32768, -1);
\\ if (x == 32767) return error.Whatever;
\\}
@@ -98,8 +93,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = shl(-16385, 1);
\\ if (x == 0) return error.Whatever;
\\}
@@ -112,8 +106,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = shl(0b0010111111111111, 3);
\\ if (x == 0) return error.Whatever;
\\}
@@ -126,8 +119,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = shr(-16385, 1);
\\ if (x == 0) return error.Whatever;
\\}
@@ -140,8 +132,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = shr(0b0010111111111111, 3);
\\ if (x == 0) return error.Whatever;
\\}
@@ -154,8 +145,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ const x = div0(999, 0);
\\}
\\fn div0(a: i32, b: i32) i32 {
@@ -167,8 +157,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = divExact(10, 3);
\\ if (x == 0) return error.Whatever;
\\}
@@ -181,8 +170,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = widenSlice([]u8{1, 2, 3, 4, 5});
\\ if (x.len == 0) return error.Whatever;
\\}
@@ -195,8 +183,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = shorten_cast(200);
\\ if (x == 0) return error.Whatever;
\\}
@@ -209,8 +196,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ const x = unsigned_cast(-10);
\\ if (x == 0) return error.Whatever;
\\}
@@ -226,20 +212,19 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ }
\\ @import("std").os.exit(0); // test failed
\\}
- \\error Whatever;
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ bar() catch unreachable;
\\}
- \\fn bar() %void {
+ \\fn bar() !void {
\\ return error.Whatever;
\\}
);
- cases.addRuntimeSafety("cast integer to error and no code matches",
+ cases.addRuntimeSafety("cast integer to global error and no code matches",
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ _ = bar(9999);
\\}
\\fn bar(x: u32) error {
@@ -247,12 +232,25 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\}
);
+ cases.addRuntimeSafety("cast integer to non-global error set and no match",
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\const Set1 = error{A, B};
+ \\const Set2 = error{A, C};
+ \\pub fn main() void {
+ \\ _ = foo(Set1.B);
+ \\}
+ \\fn foo(set1: Set1) Set2 {
+ \\ return Set2(set1);
+ \\}
+ );
+
cases.addRuntimeSafety("@alignCast misaligned",
\\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
\\}
- \\error Wrong;
- \\pub fn main() %void {
+ \\pub fn main() !void {
\\ var array align(4) = []u32{0x11111111, 0x11111111};
\\ const bytes = ([]u8)(array[0..]);
\\ if (foo(bytes) != 0x11111111) return error.Wrong;
@@ -274,7 +272,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ int: u32,
\\};
\\
- \\pub fn main() %void {
+ \\pub fn main() void {
\\ var f = Foo { .int = 42 };
\\ bar(&f);
\\}
diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig
index af3160a8c6..7752f599df 100644
--- a/test/standalone/brace_expansion/build.zig
+++ b/test/standalone/brace_expansion/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const main = b.addTest("main.zig");
main.setBuildMode(b.standardReleaseOptions());
diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig
index 995da8bb56..b8eab68a3e 100644
--- a/test/standalone/brace_expansion/main.zig
+++ b/test/standalone/brace_expansion/main.zig
@@ -6,9 +6,6 @@ const assert = debug.assert;
const Buffer = std.Buffer;
const ArrayList = std.ArrayList;
-error InvalidInput;
-error OutOfMem;
-
const Token = union(enum) {
Word: []const u8,
OpenBrace,
@@ -19,7 +16,7 @@ const Token = union(enum) {
var global_allocator: &mem.Allocator = undefined;
-fn tokenize(input:[] const u8) %ArrayList(Token) {
+fn tokenize(input:[] const u8) !ArrayList(Token) {
const State = enum {
Start,
Word,
@@ -71,7 +68,12 @@ const Node = union(enum) {
Combine: []Node,
};
-fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node {
+const ParseError = error {
+ InvalidInput,
+ OutOfMemory,
+};
+
+fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node {
const first_token = tokens.items[*token_index];
*token_index += 1;
@@ -107,7 +109,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node {
}
}
-fn expandString(input: []const u8, output: &Buffer) %void {
+fn expandString(input: []const u8, output: &Buffer) !void {
const tokens = try tokenize(input);
if (tokens.len == 1) {
return output.resize(0);
@@ -135,7 +137,11 @@ fn expandString(input: []const u8, output: &Buffer) %void {
}
}
-fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void {
+const ExpandNodeError = error {
+ OutOfMemory,
+};
+
+fn expandNode(node: &const Node, output: &ArrayList(Buffer)) ExpandNodeError!void {
assert(output.len == 0);
switch (*node) {
Node.Scalar => |scalar| {
@@ -172,7 +178,7 @@ fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void {
}
}
-pub fn main() %void {
+pub fn main() !void {
var stdin_file = try io.getStdIn();
var stdout_file = try io.getStdOut();
diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig
index 8cf4cfae77..f3ab327006 100644
--- a/test/standalone/issue_339/build.zig
+++ b/test/standalone/issue_339/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const obj = b.addObject("test", "test.zig");
const test_step = b.step("test", "Test the program");
diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig
index 1c9480a0a6..f65b9f734e 100644
--- a/test/standalone/issue_339/test.zig
+++ b/test/standalone/issue_339/test.zig
@@ -1,7 +1,7 @@
const StackTrace = @import("builtin").StackTrace;
pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} }
-fn bar() %void {}
+fn bar() error!void {}
export fn foo() void {
bar() catch unreachable;
diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig
index 2caea10bfe..bb9416d3c4 100644
--- a/test/standalone/pkg_import/build.zig
+++ b/test/standalone/pkg_import/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
const exe = b.addExecutable("test", "test.zig");
exe.addPackagePath("my_pkg", "pkg.zig");
diff --git a/test/standalone/pkg_import/test.zig b/test/standalone/pkg_import/test.zig
index 9575671f2a..ffd2080022 100644
--- a/test/standalone/pkg_import/test.zig
+++ b/test/standalone/pkg_import/test.zig
@@ -1,6 +1,6 @@
const my_pkg = @import("my_pkg");
const assert = @import("std").debug.assert;
-pub fn main() %void {
+pub fn main() void {
assert(my_pkg.add(10, 20) == 30);
}
diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig
index 912058d5a9..ecbba297d8 100644
--- a/test/standalone/use_alias/build.zig
+++ b/test/standalone/use_alias/build.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) %void {
+pub fn build(b: &Builder) void {
b.addCIncludePath(".");
const main = b.addTest("main.zig");
diff --git a/test/tests.zig b/test/tests.zig
index 554bb6a2bc..19a4f82b74 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -45,9 +45,6 @@ const test_targets = []TestTarget {
},
};
-error TestFailed;
-error CompilationIncorrectlySucceeded;
-
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step {
@@ -248,7 +245,7 @@ pub const CompareOutputContext = struct {
return ptr;
}
- fn make(step: &build.Step) %void {
+ fn make(step: &build.Step) !void {
const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
const b = self.context.b;
@@ -337,7 +334,7 @@ pub const CompareOutputContext = struct {
return ptr;
}
- fn make(step: &build.Step) %void {
+ fn make(step: &build.Step) !void {
const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step);
const b = self.context.b;
@@ -563,7 +560,7 @@ pub const CompileErrorContext = struct {
return ptr;
}
- fn make(step: &build.Step) %void {
+ fn make(step: &build.Step) !void {
const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
const b = self.context.b;
@@ -847,7 +844,7 @@ pub const TranslateCContext = struct {
return ptr;
}
- fn make(step: &build.Step) %void {
+ fn make(step: &build.Step) !void {
const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
const b = self.context.b;
@@ -1045,14 +1042,14 @@ pub const GenHContext = struct {
return ptr;
}
- fn make(step: &build.Step) %void {
+ fn make(step: &build.Step) !void {
const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
const b = self.context.b;
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
const full_h_path = b.pathFromRoot(self.h_path);
- const actual_h = try io.readFileAlloc(full_h_path, b.allocator);
+ const actual_h = try io.readFileAlloc(b.allocator, full_h_path);
for (self.case.expected_lines.toSliceConst()) |expected_line| {
if (mem.indexOf(u8, actual_h, expected_line) == null) {