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

This commit is contained in:
Andrew Kelley 2018-02-11 23:49:20 -05:00
commit ef6260b3a7
91 changed files with 4059 additions and 2135 deletions

View File

@ -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

View File

@ -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"});

View File

@ -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"

View File

@ -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, "A<span class=\"t32\">green</span>B"));
}
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("<pre><code class=\"zig\">{}</code></pre>", 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| {

View File

@ -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#}
<p>
Note that we also left off the <code class="zig">%</code> from the return type.
In Zig, if your main function cannot fail, you may use the <code class="zig">void</code> return type.
Note that we also left off the <code class="zig">!</code> from the return type.
In Zig, if your main function cannot fail, you must use the <code class="zig">void</code> return type.
</p>
{#see_also|Values|@import|Errors|Root Source File#}
{#header_close#}
@ -141,10 +141,7 @@ const warn = std.debug.warn;
const os = std.os;
const assert = std.debug.assert;
// error declaration, makes `error.ArgNotFound` available
error ArgNotFound;
pub fn main() %void {
pub fn main() void {
// integers
const one_plus_one: i32 = 1 + 1;
warn("1 + 1 = {}\n", one_plus_one);
@ -173,7 +170,7 @@ pub fn main() %void {
@typeName(@typeOf(nullable_value)), nullable_value);
// error union
var number_or_error: %i32 = error.ArgNotFound;
var number_or_error: error!i32 = error.ArgNotFound;
warn("\nerror union 1\ntype: {}\nvalue: {}\n",
@typeName(@typeOf(number_or_error)), number_or_error);
@ -681,7 +678,7 @@ const warn = @import("std").debug.warn;
extern fn foo_strict(x: f64) f64;
extern fn foo_optimized(x: f64) f64;
pub fn main() %void {
pub fn main() void {
const x = 0.001;
warn("optimized = {}\n", foo_optimized(x));
warn("strict = {}\n", foo_strict(x));
@ -1036,7 +1033,7 @@ a catch |err| b</code></pre></td>
<code>err</code> is the <code>error</code> and is in scope of the expression <code>b</code>.
</td>
<td>
<pre><code class="zig">const value: %u32 = null;
<pre><code class="zig">const value: error!u32 = error.Broken;
const unwrapped = value catch 1234;
unwrapped == 1234</code></pre>
</td>
@ -1269,9 +1266,10 @@ const ptr = &amp;x;
{#header_close#}
{#header_open|Precedence#}
<pre><code>x() x[] x.y
!x -x -%x ~x *x &amp;x ?x %x ??x
a!b
!x -x -%x ~x *x &amp;x ?x ??x
x{}
* / % ** *%
! * / % ** *%
+ - ++ +% -%
&lt;&lt; &gt;&gt;
&amp;
@ -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.
</p>
<p>
Among the top level declarations available is the error value declaration:
TODO rewrite the errors section to take into account error sets
</p>
{#code_begin|syntax#}
error FileNotFound;
error OutOfMemory;
error UnexpectedToken;
{#code_end#}
<p>
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;
</p>
<p>
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.
</p>
<p>
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.
</p>
{#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}
<p>
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 <code>%</code> 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 <code>!</code> binary operator.
</p>
<p>
Here is a function to parse a string into a 64-bit integer:
</p>
{#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#}
<p>
Notice the return type is <code>%u64</code>. This means that the function
either returns an unsigned 64 bit integer, or an error.
Notice the return type is <code>!u64</code>. 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 <code>!</code>, so the error set is inferred.
</p>
<p>
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 <code>u64</code>.
Both types implicitly cast to <code>%u64</code>.
an error, and at the bottom a return statement that returns a <code>u64</code>.
Both types implicitly cast to <code>error!u64</code>.
</p>
<p>
What it looks like to use this function varies depending on what you're
@ -2900,7 +2888,7 @@ fn doAThing(str: []u8) void {
<p>Let's say you wanted to return the error if you got one, otherwise continue with the
function logic:</p>
{#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 <code>try</code> expression:
</p>
{#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:
</p>
{#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 {
</ul>
{#see_also|defer|if|switch#}
{#header_open|Error Union Type#}
<p>An error union is created by putting a <code>%</code> in front of a type.
<p>An error union is created with the <code>!</code> binary operator.
You can use compile-time reflection to access the child type of an error union:</p>
{#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:
</p>
{#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:
</p>
{#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 {
</p>
{#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#}
<p>At compile-time:</p>
{#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#}
<p>At compile-time:</p>
{#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#}
<pre><code class="nohighlight">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 = "!" | "-" | "~" | "*" | ("&amp;" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "??" | "-%" | "try"
PrefixOp = "!" | "-" | "~" | "*" | ("&amp;" 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) "}"</code></pre>

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -4,6 +4,8 @@
#include <assert.h>
#include <string.h>
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;
}

View File

@ -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");

View File

@ -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");
}

View File

@ -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;

View File

@ -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<AstNode *> 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<AstNode *> error_decls;
TypeTableEntry *err_tag_type;
ZigList<ZigLLVMDIEnumerator *> err_enumerators;
ZigList<ErrorTableEntry *> 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<ZigLLVMDIType **> error_di_types;
ZigList<Buf *> 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;

View File

@ -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<LLVMTypeRef>(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<ErrorTableEntry>(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<ErrorTableEntry *>(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;
}

View File

@ -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

View File

@ -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:

View File

@ -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<LLVMValueRef>(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<ConstGlobalRefs>(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<LLVMValueRef>(g->error_decls.length);
LLVMValueRef *values = allocate<LLVMValueRef>(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;
}

View File

@ -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);

1670
src/ir.cpp

File diff suppressed because it is too large Load Diff

View File

@ -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");
}

View File

@ -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<const char *> llvm_argv = {0};
ZigList<const char *> lib_dirs = {0};
ZigList<const char *> link_libs = {0};
ZigList<const char *> forbidden_link_libs = {0};
ZigList<const char *> 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));
}

View File

@ -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 = "!" | "-" | "~" | "*" | ("&amp;" 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;
}
}

View File

@ -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)";
}

View File

@ -17,6 +17,8 @@ enum TokenId {
TokenIdArrow,
TokenIdAtSign,
TokenIdBang,
TokenIdBarBar,
TokenIdBarBarEq,
TokenIdBinOr,
TokenIdBinXor,
TokenIdBitAndEq,

View File

@ -91,20 +91,23 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {
#endif
}
template<typename T>
static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
T *ptr = reinterpret_cast<T*>(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<typename T>
static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
#ifdef NDEBUG
T *ptr = reinterpret_cast<T*>(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<T>(new_count);
safe_memcpy(ptr, old, old_count);
free(old);
return ptr;
#endif
}
template <typename T, size_t n>

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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| {

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
};

View File

@ -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"));
}

View File

@ -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;

View File

@ -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 {

View File

@ -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");
}

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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
{

View File

@ -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]);

View File

@ -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);

311
std/os/file.zig Normal file
View File

@ -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");
}
}
};

View File

@ -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();

View File

@ -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,

View File

@ -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) {

View File

@ -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;

View File

@ -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| {

View File

@ -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'"),
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 {

11
std/zig/index.zig Normal file
View File

@ -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");
}

View File

@ -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"),
}
}
}

View File

@ -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 => {

View File

@ -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",

View File

@ -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;}

View File

@ -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),

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 {
return 10;
}
fn return_a_number() %i32 {
fn return_a_number() error!i32 {
return 1;
}

View File

@ -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,

View File

@ -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 = {} },

View File

@ -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 {

View File

@ -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,
});

View File

@ -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; }

View File

@ -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);

View File

@ -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);
\\}
,

View File

@ -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);
\\}

View File

@ -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());

View File

@ -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();

View File

@ -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");

View File

@ -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;

View File

@ -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");

View File

@ -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);
}

View File

@ -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");

View File

@ -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) {