From 76239f2089bfb03b24dac0dcad21c9c430ad076d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 03:02:41 -0500 Subject: [PATCH] error sets - update langref. all tests passing --- doc/langref.html.in | 103 +++++++++++++++++----------------------- test/runtime_safety.zig | 28 +++-------- 2 files changed, 49 insertions(+), 82 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 24c4b324ba..9e224b2e7d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -108,7 +108,7 @@ {#code_begin|exe|hello#} const std = @import("std"); -pub fn main() %void { +pub fn main() !void { // If this program is run without stdout attached, exit with an error. var stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit @@ -129,8 +129,8 @@ pub fn main() void { } {#code_end#}

- Note that we also left off the % from the return type. - In Zig, if your main function cannot fail, you may use the void return type. + Note that we also left off the ! from the return type. + In Zig, if your main function cannot fail, you must use the void return type.

{#see_also|Values|@import|Errors|Root Source File#} {#header_close#} @@ -151,10 +151,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); @@ -183,7 +180,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); @@ -691,7 +688,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)); @@ -1046,7 +1043,7 @@ a catch |err| b err is the error and is in scope of the expression b. -
const value: %u32 = null;
+            
const value: error!u32 = error.Broken;
 const unwrapped = value catch 1234;
 unwrapped == 1234
@@ -1279,7 +1276,8 @@ const ptr = &x; {#header_close#} {#header_open|Precedence#}
x() x[] x.y
-!x -x -%x ~x *x &x ?x %x ??x
+a!b
+!x -x -%x ~x *x &x ?x ??x
 x{}
 ! * / % ** *%
 + - ++ +% -%
@@ -2278,8 +2276,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;
@@ -2408,7 +2406,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;
 
@@ -2469,20 +2467,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| {
@@ -2500,7 +2496,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| {
@@ -2568,8 +2564,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
@@ -2678,7 +2673,7 @@ test "foo" {
     assert(value == 1234);
 }
 
-fn bar() %u32 {
+fn bar() error!u32 {
     return 1234;
 }
 
@@ -2791,13 +2786,8 @@ test "implicitly cast to const pointer" {
       One of the distinguishing features of Zig is its exception handling strategy.
       

- Among the top level declarations available is the error value declaration: + TODO rewrite the errors section to take into account error sets

- {#code_begin|syntax#} -error FileNotFound; -error OutOfMemory; -error UnexpectedToken; - {#code_end#}

These error values are assigned an unsigned integer value greater than 0 at compile time. You are allowed to declare the same error value more than once, @@ -2809,26 +2799,23 @@ error UnexpectedToken;

Each error value across the entire compilation unit gets a unique integer, - and this determines the size of the pure error type. + and this determines the size of the error set type.

- The pure error type is one of the error values, and in the same way that pointers - cannot be null, a pure error is always an error. + The error set type is one of the error values, and in the same way that pointers + cannot be null, a error set instance is always an error.

{#code_begin|syntax#}const pure_error = error.FileNotFound;{#code_end#}

- Most of the time you will not find yourself using a pure error type. Instead, - likely you will be using the error union type. This is when you take a normal type, - and prefix it with the % operator. + Most of the time you will not find yourself using an error set type. Instead, + likely you will be using the error union type. This is when you take an error set + and a normal type, and create an error union with the ! binary operator.

Here is a function to parse a string into a 64-bit integer:

{#code_begin|test#} -error InvalidChar; -error Overflow; - -pub fn parseU64(buf: []const u8, radix: u8) %u64 { +pub fn parseU64(buf: []const u8, radix: u8) !u64 { var x: u64 = 0; for (buf) |c| { @@ -2867,13 +2854,14 @@ test "parse u64" { } {#code_end#}

- Notice the return type is %u64. This means that the function - either returns an unsigned 64 bit integer, or an error. + Notice the return type is !u64. This means that the function + either returns an unsigned 64 bit integer, or an error. We left off the error set + to the left of the !, so the error set is inferred.

Within the function definition, you can see some return statements that return - a pure error, and at the bottom a return statement that returns a u64. - Both types implicitly cast to %u64. + an error, and at the bottom a return statement that returns a u64. + Both types implicitly cast to error!u64.

What it looks like to use this function varies depending on what you're @@ -2900,7 +2888,7 @@ fn doAThing(str: []u8) void {

Let's say you wanted to return the error if you got one, otherwise continue with the function logic:

{#code_begin|syntax#} -fn doAThing(str: []u8) %void { +fn doAThing(str: []u8) !void { const number = parseU64(str, 10) catch |err| return err; // ... } @@ -2909,7 +2897,7 @@ fn doAThing(str: []u8) %void { There is a shortcut for this. The try expression:

{#code_begin|syntax#} -fn doAThing(str: []u8) %void { +fn doAThing(str: []u8) !void { const number = try parseU64(str, 10); // ... } @@ -2959,7 +2947,7 @@ fn doAThing(str: []u8) void { Example:

{#code_begin|syntax#} -fn createFoo(param: i32) %Foo { +fn createFoo(param: i32) !Foo { const foo = try tryToAllocateFoo(); // now we have allocated foo. we need to free it if the function fails. // but we want to return it if the function succeeds. @@ -3567,7 +3555,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, @@ -3639,7 +3627,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) %void { and emits a function that actually looks like this:

{#code_begin|syntax#} -pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void { +pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void { try self.write("here is a string: '"); try self.printValue(arg0); try self.write("' here is a number: "); @@ -3653,7 +3641,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) %void { on the type:

{#code_begin|syntax#} -pub fn printValue(self: &OutStream, value: var) %void { +pub fn printValue(self: &OutStream, value: var) !void { const T = @typeOf(value); if (@isInteger(T)) { return self.printInt(T, value); @@ -4582,7 +4570,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); @@ -4724,7 +4712,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| { @@ -4752,7 +4740,7 @@ pub fn main() %void {

{#code_begin|exe#} const warn = @import("std").debug.warn; -pub fn main() %void { +pub fn main() void { var byte: u8 = 255; var result: u8 = undefined; @@ -4861,14 +4849,12 @@ pub fn main() void { {#header_close#} {#header_open|Attempt to Unwrap Error#}

At compile-time:

- {#code_begin|test_err|unable to unwrap error 'UnableToReturnNumber'#} + {#code_begin|test_err|caught unexpected error 'UnableToReturnNumber'#} comptime { const number = getNumberOrFail() catch unreachable; } -error UnableToReturnNumber; - -fn getNumberOrFail() %i32 { +fn getNumberOrFail() !i32 { return error.UnableToReturnNumber; } {#code_end#} @@ -4888,9 +4874,7 @@ pub fn main() void { } } -error UnableToReturnNumber; - -fn getNumberOrFail() %i32 { +fn getNumberOrFail() !i32 { return error.UnableToReturnNumber; } {#code_end#} @@ -4898,7 +4882,6 @@ fn getNumberOrFail() %i32 { {#header_open|Invalid Error Code#}

At compile-time:

{#code_begin|test_err|integer value 11 represents no error#} -error AnError; comptime { const err = error.AnError; const number = u32(err) + 10; @@ -5298,7 +5281,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"); diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 20b905b59b..def6430961 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -5,7 +5,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} - \\pub fn main() !void { + \\pub fn main() void { \\ @panic("oh no"); \\} ); @@ -14,7 +14,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} - \\pub fn main() !void { + \\pub fn main() void { \\ const a = []i32{1, 2, 3, 4}; \\ baz(bar(a)); \\} @@ -28,7 +28,6 @@ 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 { \\ const x = add(65530, 10); \\ if (x == 0) return error.Whatever; @@ -42,7 +41,6 @@ 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 { \\ const x = sub(10, 20); \\ if (x == 0) return error.Whatever; @@ -56,7 +54,6 @@ 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 { \\ const x = mul(300, 6000); \\ if (x == 0) return error.Whatever; @@ -70,7 +67,6 @@ 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 { \\ const x = neg(-32768); \\ if (x == 32767) return error.Whatever; @@ -84,7 +80,6 @@ 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 { \\ const x = div(-32768, -1); \\ if (x == 32767) return error.Whatever; @@ -98,7 +93,6 @@ 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 { \\ const x = shl(-16385, 1); \\ if (x == 0) return error.Whatever; @@ -112,7 +106,6 @@ 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 { \\ const x = shl(0b0010111111111111, 3); \\ if (x == 0) return error.Whatever; @@ -126,7 +119,6 @@ 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 { \\ const x = shr(-16385, 1); \\ if (x == 0) return error.Whatever; @@ -140,7 +132,6 @@ 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 { \\ 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,7 +157,6 @@ 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 { \\ const x = divExact(10, 3); \\ if (x == 0) return error.Whatever; @@ -181,7 +170,6 @@ 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 { \\ const x = widenSlice([]u8{1, 2, 3, 4, 5}); \\ if (x.len == 0) return error.Whatever; @@ -195,7 +183,6 @@ 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 { \\ const x = shorten_cast(200); \\ if (x == 0) return error.Whatever; @@ -209,7 +196,6 @@ 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 { \\ const x = unsigned_cast(-10); \\ if (x == 0) return error.Whatever; @@ -226,8 +212,7 @@ 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 { @@ -239,7 +224,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 { \\ _ = bar(9999); \\} \\fn bar(x: u32) error { @@ -251,7 +236,6 @@ 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 Wrong; \\pub fn main() !void { \\ var array align(4) = []u32{0x11111111, 0x11111111}; \\ const bytes = ([]u8)(array[0..]); @@ -274,7 +258,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); \\}