Commit 0d5ff6f462

Andrew Kelley <superjoe30@gmail.com>
2018-02-08 08:08:45
error sets - most tests passing
1 parent 68238d5
doc/docgen.zig
@@ -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);
@@ -596,8 +594,6 @@ 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);
@@ -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) {
@@ -974,9 +968,6 @@ 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 {
     const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
     switch (result.term) {
example/cat/main.zig
@@ -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;
example/mix_o_files/build.zig
@@ -1,6 +1,6 @@
 const Builder = @import("std").build.Builder;
 
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
     const obj = b.addObject("base64", "base64.zig");
 
     const exe = b.addCExecutable("test");
example/shared_library/build.zig
@@ -1,6 +1,6 @@
 const Builder = @import("std").build.Builder;
 
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
     const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
 
     const exe = b.addCExecutable("test");
src/ir.cpp
@@ -5442,6 +5442,10 @@ static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors,
     buf_resize(&err_set_type->name, 0);
     buf_appendf(&err_set_type->name, "error{");
 
+    for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) {
+        assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]);
+    }
+
     uint32_t count = set1->data.error_set.err_count;
     for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
         ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
@@ -5523,6 +5527,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
         err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
     }
 
+    ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
+
     for (uint32_t i = 0; i < err_count; i += 1) {
         AstNode *symbol_node = node->data.err_set_decl.decls.at(i);
         assert(symbol_node->type == NodeTypeSymbol);
@@ -5543,7 +5549,16 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
                 buf_ptr(err_name), error_value_count));
         }
         err_set_type->data.error_set.errors[i] = err;
+
+        ErrorTableEntry *prev_err = errors[err->value];
+        if (prev_err != nullptr) {
+            ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
+            add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here"));
+            return irb->codegen->invalid_instruction;
+        }
+        errors[err->value] = err;
     }
+    free(errors);
     return ir_build_const_type(irb, parent_scope, node, err_set_type);
 }
 
@@ -6512,6 +6527,7 @@ static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry
     ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
     for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
         ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
+        assert(errors[error_entry->value] == nullptr);
         errors[error_entry->value] = error_entry;
     }
     ZigList<ErrorTableEntry *> intersection_list = {};
@@ -6653,6 +6669,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
         ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length);
         for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
             ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
+            assert(errors[error_entry->value] == nullptr);
             errors[error_entry->value] = error_entry;
         }
         for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) {
@@ -6767,6 +6784,12 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
                 buf_sprintf("unable to cast global error set into smaller set"));
             return ImplicitCastMatchResultReportedError;
         }
+    } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) {
+        ErrorMsg *msg = ir_add_error(ira, value,
+            buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));
+        add_error_note(ira->codegen, msg, value->source_node,
+            buf_sprintf("unable to cast global error set into smaller set"));
+        return ImplicitCastMatchResultReportedError;
     }
     if (missing_errors != nullptr) {
         ErrorMsg *msg = ir_add_error(ira, value,
@@ -6995,6 +7018,12 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
     return ImplicitCastMatchResultNo;
 }
 
+static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) {
+    size_t old_errors_count = *errors_count;
+    *errors_count = g->errors_by_index.length;
+    *errors = reallocate(*errors, old_errors_count, *errors_count);
+}
+
 static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
     assert(instruction_count >= 1);
     IrInstruction *prev_inst = instructions[0];
@@ -7002,6 +7031,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
         return ira->codegen->builtin_types.entry_invalid;
     }
     ErrorTableEntry **errors = nullptr;
+    size_t errors_count = 0;
     TypeTableEntry *err_set_type = nullptr;
     if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
         if (type_is_global_error_set(prev_inst->value.type)) {
@@ -7011,9 +7041,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
             if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) {
                 return ira->codegen->builtin_types.entry_invalid;
             }
-            errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
+            update_errors_helper(ira->codegen, &errors, &errors_count);
+
             for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
                 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+                assert(errors[error_entry->value] == nullptr);
                 errors[error_entry->value] = error_entry;
             }
         }
@@ -7064,6 +7096,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     continue;
                 }
 
+                // number of declared errors might have increased now
+                update_errors_helper(ira->codegen, &errors, &errors_count);
+
                 // if err_set_type is a superset of cur_type, keep err_set_type.
                 // if cur_type is a superset of err_set_type, switch err_set_type to cur_type
                 bool prev_is_superset = true;
@@ -7084,8 +7119,12 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
                     errors[error_entry->value] = nullptr;
                 }
+                for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
+                    assert(errors[i] == nullptr);
+                }
                 for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
                     ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i];
+                    assert(errors[error_entry->value] == nullptr);
                     errors[error_entry->value] = error_entry;
                 }
                 bool cur_is_superset = true;
@@ -7122,14 +7161,21 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     prev_inst = cur_inst;
                     continue;
                 }
+
+                update_errors_helper(ira->codegen, &errors, &errors_count);
+
                 // test if err_set_type is a subset of cur_type's error set
                 // unset everything in errors
                 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
                     ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
                     errors[error_entry->value] = nullptr;
                 }
+                for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
+                    assert(errors[i] == nullptr);
+                }
                 for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
                     ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
+                    assert(errors[error_entry->value] == nullptr);
                     errors[error_entry->value] = error_entry;
                 }
                 bool cur_is_superset = true;
@@ -7173,15 +7219,18 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
             if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
                 return ira->codegen->builtin_types.entry_invalid;
             }
+
+            update_errors_helper(ira->codegen, &errors, &errors_count);
+
             if (err_set_type == nullptr) {
                 if (prev_type->id == TypeTableEntryIdErrorUnion) {
                     err_set_type = prev_type->data.error_union.err_set_type;
                 } else {
                     err_set_type = cur_type;
                 }
-                errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
                 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
                     ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
+                    assert(errors[error_entry->value] == nullptr);
                     errors[error_entry->value] = error_entry;
                 }
                 if (err_set_type == cur_type) {
@@ -7237,11 +7286,13 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     continue;
                 }
 
+                update_errors_helper(ira->codegen, &errors, &errors_count);
+
                 if (err_set_type == nullptr) {
                     err_set_type = prev_err_set_type;
-                    errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
                     for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
                         ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i];
+                        assert(errors[error_entry->value] == nullptr);
                         errors[error_entry->value] = error_entry;
                     }
                 }
@@ -7262,8 +7313,12 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
                     errors[error_entry->value] = nullptr;
                 }
+                for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
+                    assert(errors[i] == nullptr);
+                }
                 for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
                     ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
+                    assert(errors[error_entry->value] == nullptr);
                     errors[error_entry->value] = error_entry;
                 }
                 bool cur_is_superset = true;
@@ -7331,6 +7386,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     continue;
                 }
 
+                update_errors_helper(ira->codegen, &errors, &errors_count);
+
                 err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type);
             }
             prev_inst = cur_inst;
@@ -8000,6 +8057,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou
         ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
         for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
             ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
+            assert(errors[error_entry->value] == nullptr);
             errors[error_entry->value] = error_entry;
         }
         ErrorMsg *err_msg = nullptr;
@@ -10212,8 +10270,9 @@ static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstruction
     }
 
     ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
-    for (uint32_t i = 0; i < op1_type->data.error_set.err_count; i += 1) {
+    for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
         ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
+        assert(errors[error_entry->value] == nullptr);
         errors[error_entry->value] = error_entry;
     }
     TypeTableEntry *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type);
@@ -14987,6 +15046,15 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
         result = container_type->data.structure.src_field_count;
     } else if (container_type->id == TypeTableEntryIdUnion) {
         result = container_type->data.unionation.src_field_count;
+    } else if (container_type->id == TypeTableEntryIdErrorSet) {
+        if (!resolve_inferred_error_set(ira, container_type, instruction->base.source_node)) {
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        if (type_is_global_error_set(container_type)) {
+            ir_add_error(ira, &instruction->base, buf_sprintf("global error set member count not available at comptime"));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+        result = container_type->data.error_set.err_count;
     } else {
         ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));
         return ira->codegen->builtin_types.entry_invalid;
src/util.hpp
@@ -92,19 +92,22 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {
 }
 
 template<typename T>
-static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
-#ifdef NDEBUG
+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;
-#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);
+}
+
+template<typename T>
+static inline T *reallocate_nonzero(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");
     return ptr;
-#endif
 }
 
 template <typename T, size_t n>
src-self-hosted/main.zig
@@ -14,10 +14,6 @@ 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 {
@@ -472,7 +468,7 @@ pub fn main2() !void {
     }
 }
 
-fn printUsage(stream: &io.OutStream) !void {
+fn printUsage(stream: var) !void {
     try stream.write(
         \\Usage: zig [command] [options]
         \\
src-self-hosted/module.zig
@@ -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();
@@ -265,6 +265,7 @@ pub const Module = struct {
 
     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 {
src-self-hosted/parser.zig
@@ -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,
@@ -555,7 +553,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 +575,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);
 
@@ -694,7 +692,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,7 +700,7 @@ 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);
@@ -763,7 +761,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 +800,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);
 
@@ -1058,10 +1056,6 @@ 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 {
std/fmt/index.zig
@@ -510,7 +510,7 @@ pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...
     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;
 }
 
std/os/windows/util.zig
@@ -30,7 +30,6 @@ pub fn windowsClose(handle: windows.HANDLE) void {
 pub const WriteError = error {
     SystemResources,
     OperationAborted,
-    SystemResources,
     IoPending,
     BrokenPipe,
     Unexpected,
@@ -83,6 +82,8 @@ pub const OpenError = error {
     AccessDenied,
     PipeBusy,
     Unexpected,
+    OutOfMemory,
+    NameTooLong,
 };
 
 /// `file_path` may need to be copied in memory to add a null terminating byte. In this case
std/os/child_process.zig
@@ -55,7 +55,22 @@ pub const ChildProcess = struct {
     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) {
@@ -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);
@@ -757,7 +772,7 @@ 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);
 }
std/os/index.zig
@@ -243,7 +243,6 @@ pub const PosixOpenError = error {
     SystemResources,
     NoSpaceLeft,
     NotDir,
-    AccessDenied,
     PathAlreadyExists,
     Unexpected,
 };
@@ -411,7 +410,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,
@@ -904,24 +915,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();
 
@@ -1252,6 +1307,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());
     }
@@ -1267,7 +1324,7 @@ pub const ArgIteratorWindows = struct {
     }
 
     /// You must free the returned memory when done.
-    pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(@typeOf(internalNext).ReturnType.ErrorSet![]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];
@@ -1320,7 +1377,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();
 
@@ -1394,16 +1451,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 {
std/special/bootstrap.zig
@@ -77,7 +77,7 @@ fn callMain() u8 {
         },
         builtin.TypeId.Int => {
             if (@typeOf(root.main).ReturnType.bit_count != 8) {
-                @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'");
+                @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'");
             }
             return root.main();
         },
@@ -91,6 +91,6 @@ fn callMain() u8 {
             };
             return 0;
         },
-        else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"),
+        else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
     }
 }
std/special/build_runner.zig
@@ -1,5 +1,6 @@
 const root = @import("@build");
 const std = @import("std");
+const builtin = @import("builtin");
 const io = std.io;
 const fmt = std.fmt;
 const os = std.os;
@@ -43,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;
@@ -110,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);
@@ -123,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
@@ -181,12 +190,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;
std/build.zig
@@ -271,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);
 
@@ -285,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;
@@ -1910,7 +1910,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);
     }
@@ -1972,7 +1972,7 @@ pub const Step = struct {
         self.dependencies.append(other) catch unreachable;
     }
 
-    fn makeNoOp(self: &Step) (error{}!void) {}
+    fn makeNoOp(self: &Step) error!void {}
 };
 
 fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8,
std/io.zig
@@ -694,13 +694,13 @@ pub const BufferOutStream = struct {
     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);
     }
test/cases/error.zig
@@ -1,5 +1,7 @@
-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() error!i32 {
     const x = try bar();
@@ -74,3 +76,35 @@ fn doErrReturnInAssignment() error!void {
 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/standalone/brace_expansion/build.zig
@@ -1,6 +1,6 @@
 const Builder = @import("std").build.Builder;
 
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
     const main = b.addTest("main.zig");
     main.setBuildMode(b.standardReleaseOptions());
 
test/standalone/brace_expansion/main.zig
@@ -68,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;
 
@@ -132,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| {
test/standalone/issue_339/build.zig
@@ -1,6 +1,6 @@
 const Builder = @import("std").build.Builder;
 
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
     const obj = b.addObject("test", "test.zig");
 
     const test_step = b.step("test", "Test the program");
test/standalone/issue_339/test.zig
@@ -1,7 +1,7 @@
 const StackTrace = @import("builtin").StackTrace;
 pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} }
 
-fn bar() %void {}
+fn bar() error!void {}
 
 export fn foo() void {
     bar() catch unreachable;
test/standalone/pkg_import/build.zig
@@ -1,6 +1,6 @@
 const Builder = @import("std").build.Builder;
 
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
     const exe = b.addExecutable("test", "test.zig");
     exe.addPackagePath("my_pkg", "pkg.zig");
 
test/standalone/pkg_import/test.zig
@@ -1,6 +1,6 @@
 const my_pkg = @import("my_pkg");
 const assert = @import("std").debug.assert;
 
-pub fn main() !void {
+pub fn main() void {
     assert(my_pkg.add(10, 20) == 30);
 }
test/standalone/use_alias/build.zig
@@ -1,6 +1,6 @@
 const Builder = @import("std").build.Builder;
 
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
     b.addCIncludePath(".");
 
     const main = b.addTest("main.zig");
test/compare_output.zig
@@ -15,7 +15,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
             \\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 +49,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 +89,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 +118,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 +268,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 +351,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 +367,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 +380,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,7 +394,7 @@ 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 {
@@ -406,7 +406,6 @@ 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 {
         \\    return error.IToldYouItWouldFail;
         \\}
@@ -414,7 +413,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
 
     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 {
@@ -426,7 +425,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 +433,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;
             \\}
test/compile_errors.zig
@@ -1,6 +1,25 @@
 const tests = @import("tests.zig");
 
 pub fn addCases(cases: &tests.CompileErrorContext) void {
+    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("duplicate struct field",
         \\const Foo = struct {
         \\    Bar: i32,
@@ -99,12 +118,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 {
@@ -1160,7 +1179,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'");
 
@@ -1251,7 +1270,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",
@@ -1391,7 +1410,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;
@@ -1521,7 +1540,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",
@@ -1552,7 +1571,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",
@@ -1619,13 +1638,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 {
@@ -1808,9 +1826,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 {
@@ -1824,9 +1842,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 {
TODO
@@ -1,5 +1,6 @@
 sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find . -name "*.zig")
 
+
 the literal translation of `%T` to this new code is `error!T`.
 however this would not take advantage of error sets. It's
 recommended to generally have all your functions which return possible
@@ -11,6 +12,11 @@ fn foo() !void {
 
 then you can return void, or any error, and the error set is inferred.
 
+
+you can get the compiler to tell you the possible errors for an inferred error set like this:
+
+foo() catch |err| switch (err) {};
+
 // TODO this is an explicit cast and should actually coerce the type
    erorr set casting
 
@@ -27,3 +33,8 @@ comptime test for err
 undefined in infer error 
 
 syntax -  ?a!b  should be ?(a!b) but it's (?a)!b
+
+syntax - (error{}!void) as the return type
+
+
+passing a fn()error{}!T to a fn()error!T should be a compile error, they're not compatible