Commit 06f2f4d64b

Andrew Kelley <superjoe30@gmail.com>
2016-09-13 22:46:27
change `unreachable{}` to `@unreachable()`
instead of a container init expression, it's a builtin function call.
1 parent ea2f659
src/all_types.hpp
@@ -1174,6 +1174,7 @@ enum BuiltinFnId {
     BuiltinFnIdDivExact,
     BuiltinFnIdTruncate,
     BuiltinFnIdIntType,
+    BuiltinFnIdUnreachable,
 };
 
 struct BuiltinFnEntry {
src/analyze.cpp
@@ -2677,13 +2677,6 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry
         } else {
             return resolve_expr_const_val_as_void(g, node);
         }
-    } else if (container_type->id == TypeTableEntryIdUnreachable) {
-        if (container_init_expr->entries.length != 0) {
-            add_node_error(g, node, buf_sprintf("unreachable expression expects no arguments"));
-            return g->builtin_types.entry_invalid;
-        } else {
-            return container_type;
-        }
     } else {
         add_node_error(g, node,
             buf_sprintf("type '%s' does not support %s initialization syntax",
@@ -5435,6 +5428,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
             return analyze_compile_err(g, import, context, node);
         case BuiltinFnIdIntType:
             return analyze_int_type(g, import, context, node);
+        case BuiltinFnIdUnreachable:
+            return g->builtin_types.entry_unreachable;
     }
     zig_unreachable();
 }
src/codegen.cpp
@@ -497,6 +497,20 @@ static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
     return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
 }
 
+static LLVMValueRef gen_unreachable(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeFnCallExpr);
+
+    set_debug_source_node(g, node);
+
+    if (want_debug_safety(g, node) || g->is_test_build) {
+        gen_debug_safety_crash(g);
+    } else {
+        LLVMBuildUnreachable(g->builder);
+    }
+
+    return nullptr;
+}
+
 static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
 
@@ -689,6 +703,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
             return gen_div_exact(g, node);
         case BuiltinFnIdTruncate:
             return gen_truncate(g, node);
+        case BuiltinFnIdUnreachable:
+            return gen_unreachable(g, node);
     }
     zig_unreachable();
 }
@@ -2949,15 +2965,6 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
         }
 
         return tmp_struct_ptr;
-    } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-        assert(node->data.container_init_expr.entries.length == 0);
-        set_debug_source_node(g, node);
-        if (want_debug_safety(g, node) || g->is_test_build) {
-            gen_debug_safety_crash(g);
-        } else {
-            LLVMBuildUnreachable(g->builder);
-        }
-        return nullptr;
     } else if (type_entry->id == TypeTableEntryIdVoid) {
         assert(node->data.container_init_expr.entries.length == 0);
         return nullptr;
@@ -4859,6 +4866,7 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn_with_arg_count(g, BuiltinFnIdTruncate, "truncate", 2);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileErr, "compileError", 1);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdIntType, "intType", 2);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdUnreachable, "unreachable", 0);
 }
 
 static void init(CodeGen *g, Buf *source_path) {
src/eval.cpp
@@ -491,13 +491,6 @@ static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue *
         }
     } else if (container_type->id == TypeTableEntryIdVoid) {
         return false;
-    } else if (container_type->id == TypeTableEntryIdUnreachable) {
-        ef->root->abort = true;
-        ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
-                buf_sprintf("function evaluation reached unreachable expression"));
-        add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
-        add_error_note(ef->root->codegen, msg, node, buf_sprintf("unreachable expression here"));
-        return true;
     } else if (container_type->id == TypeTableEntryIdStruct &&
                container_type->data.structure.is_slice &&
                kind == ContainerInitKindArray)
@@ -791,6 +784,15 @@ static bool eval_div_exact(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
     return false;
 }
 
+static bool eval_unreachable(EvalFn *ef, AstNode *node, ConstExprValue *out_val) {
+    ef->root->abort = true;
+    ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
+            buf_sprintf("function evaluation reached unreachable expression"));
+    add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
+    add_error_note(ef->root->codegen, msg, node, buf_sprintf("unreachable expression here"));
+    return true;
+}
+
 static bool eval_fn_with_overflow(EvalFn *ef, AstNode *node, ConstExprValue *out_val,
     bool (*bignum_fn)(BigNum *dest, BigNum *op1, BigNum *op2))
 {
@@ -851,6 +853,8 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
             return false;
         case BuiltinFnIdDivExact:
             return eval_div_exact(ef, node, out_val);
+        case BuiltinFnIdUnreachable:
+            return eval_unreachable(ef, node, out_val);
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
         case BuiltinFnIdSizeof:
std/compiler_rt.zig
@@ -266,5 +266,5 @@ fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_
 }
 
 fn assert(b: bool) {
-    if (!b) unreachable{};
+    if (!b) @unreachable();
 }
std/debug.zig
@@ -9,7 +9,7 @@ pub error InvalidDebugInfo;
 pub error UnsupportedDebugInfo;
 
 pub fn assert(b: bool) {
-    if (!b) unreachable{}
+    if (!b) @unreachable()
 }
 
 pub fn printStackTrace() -> %void {
std/hash_map.zig
@@ -55,7 +55,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
                     return entry;
                 }
             }
-            unreachable{} // no next item
+            @unreachable() // no next item
         }
     }
     
@@ -137,9 +137,9 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
                 entry.distance_from_start_index -= 1;
                 entry = next_entry;
             }
-            unreachable{} // shifting everything in the table
+            @unreachable() // shifting everything in the table
         }}
-        unreachable{} // key not found
+        @unreachable() // key not found
     }
 
     pub fn entryIterator(hm: &Self) -> Iterator {
@@ -210,7 +210,7 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b
             };
             return;
         }
-        unreachable{} // put into a full map
+        @unreachable() // put into a full map
     }
 
     fn internalGet(hm: &Self, key: K) -> ?&Entry {
std/io.zig
@@ -116,7 +116,7 @@ pub struct OutStream {
                 return switch (write_err) {
                     errno.EINTR  => continue,
 
-                    errno.EINVAL => unreachable{},
+                    errno.EINVAL => @unreachable(),
                     errno.EDQUOT => error.DiskQuota,
                     errno.EFBIG  => error.FileTooBig,
                     errno.EIO    => error.Io,
@@ -165,8 +165,8 @@ pub struct InStream {
                         return switch (err) {
                             errno.EINTR => continue,
 
-                            errno.EFAULT => unreachable{},
-                            errno.EINVAL => unreachable{},
+                            errno.EFAULT => @unreachable(),
+                            errno.EINVAL => @unreachable(),
                             errno.EACCES => error.BadPerm,
                             errno.EFBIG, errno.EOVERFLOW => error.FileTooBig,
                             errno.EISDIR => error.IsDir,
@@ -228,8 +228,8 @@ pub struct InStream {
                         switch (read_err) {
                             errno.EINTR  => continue,
 
-                            errno.EINVAL => unreachable{},
-                            errno.EFAULT => unreachable{},
+                            errno.EINVAL => @unreachable(),
+                            errno.EFAULT => @unreachable(),
                             errno.EBADF  => return error.BadFd,
                             errno.EIO    => return error.Io,
                             else         => return error.Unexpected,
@@ -426,9 +426,9 @@ fn bufPrintUnsigned(inline T: type, out_buf: []u8, x: T) -> usize {
 fn parseU64DigitTooBig() {
     parseUnsigned(u64, "123a", 10) %% |err| {
         if (err == error.InvalidChar) return;
-        unreachable{};
+        @unreachable();
     };
-    unreachable{};
+    @unreachable();
 }
 
 pub fn openSelfExe(stream: &InStream) -> %void {
std/linux.zig
@@ -299,7 +299,7 @@ pub fn lseek(fd: i32, offset: usize, ref_pos: usize) -> usize {
 
 pub fn exit(status: i32) -> unreachable {
     arch.syscall1(arch.SYS_exit, usize(status));
-    unreachable{}
+    @unreachable()
 }
 
 pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize {
std/net.zig
@@ -21,8 +21,8 @@ struct Connection {
         const send_err = linux.getErrno(send_ret);
         switch (send_err) {
             0 => return send_ret,
-            errno.EINVAL => unreachable{},
-            errno.EFAULT => unreachable{},
+            errno.EINVAL => @unreachable(),
+            errno.EFAULT => @unreachable(),
             errno.ECONNRESET => return error.ConnectionReset,
             errno.EINTR => return error.SigInterrupt,
             // TODO there are more possible errors
@@ -35,8 +35,8 @@ struct Connection {
         const recv_err = linux.getErrno(recv_ret);
         switch (recv_err) {
             0 => return buf[0...recv_ret],
-            errno.EINVAL => unreachable{},
-            errno.EFAULT => unreachable{},
+            errno.EINVAL => @unreachable(),
+            errno.EFAULT => @unreachable(),
             errno.ENOTSOCK => return error.NotSocket,
             errno.EINTR => return error.SigInterrupt,
             errno.ENOMEM => return error.NoMem,
@@ -50,7 +50,7 @@ struct Connection {
     pub fn close(c: Connection) -> %void {
         switch (linux.getErrno(linux.close(c.socket_fd))) {
             0 => return,
-            errno.EBADF => unreachable{},
+            errno.EBADF => @unreachable(),
             errno.EINTR => return error.SigInterrupt,
             errno.EIO => return error.Io,
             else => return error.Unexpected,
@@ -74,7 +74,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
 //		if (family != AF_INET)
 //			buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
 //
-        unreachable{} // TODO
+        @unreachable() // TODO
     }
 
     switch (parseIpLiteral(hostname)) {
@@ -85,7 +85,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
         else => {},
     };
 
-    unreachable{} // TODO
+    @unreachable() // TODO
 }
 
 pub fn connectAddr(addr: &Address, port: u16) -> %Connection {
@@ -113,7 +113,7 @@ pub fn connectAddr(addr: &Address, port: u16) -> %Connection {
         @memcpy(&os_addr.addr[0], &addr.addr[0], 16);
         linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6))
     } else {
-        unreachable{}
+        @unreachable()
     };
     const connect_err = linux.getErrno(connect_ret);
     if (connect_err > 0) {
@@ -321,11 +321,11 @@ fn parseIp4(buf: []const u8) -> %u32 {
 #attribute("test")
 fn testParseIp4() {
     assert(%%parseIp4("127.0.0.1") == endian.swapIfLe(u32, 0x7f000001));
-    switch (parseIp4("256.0.0.1")) { Overflow => {}, else => unreachable {}, }
-    switch (parseIp4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, }
-    switch (parseIp4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, }
-    switch (parseIp4("127.0.0.")) { Incomplete => {}, else => unreachable {}, }
-    switch (parseIp4("100..0.1")) { InvalidChar => {}, else => unreachable {}, }
+    switch (parseIp4("256.0.0.1")) { Overflow => {}, else => @unreachable(), }
+    switch (parseIp4("x.0.0.1")) { InvalidChar => {}, else => @unreachable(), }
+    switch (parseIp4("127.0.0.1.1")) { JunkAtEnd => {}, else => @unreachable(), }
+    switch (parseIp4("127.0.0.")) { Incomplete => {}, else => @unreachable(), }
+    switch (parseIp4("100..0.1")) { InvalidChar => {}, else => @unreachable(), }
 }
 
 #attribute("test")
std/os.zig
@@ -11,8 +11,8 @@ pub fn getRandomBytes(buf: []u8) -> %void {
             const err = linux.getErrno(ret);
             if (err > 0) {
                 return switch (err) {
-                    errno.EINVAL => unreachable{},
-                    errno.EFAULT => unreachable{},
+                    errno.EINVAL => @unreachable(),
+                    errno.EFAULT => @unreachable(),
                     errno.EINTR  => error.SigInterrupt,
                     else         => error.Unexpected,
                 }
test/run_tests.cpp
@@ -485,8 +485,8 @@ pub fn main(args: [][]u8) -> %void {
 const c = @cImport(@cInclude("stdlib.h"));
 
 export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int {
-    const a_int = (&i32)(a ?? unreachable{});
-    const b_int = (&i32)(b ?? unreachable{});
+    const a_int = (&i32)(a ?? @unreachable());
+    const b_int = (&i32)(b ?? @unreachable());
     if (*a_int < *b_int) {
         -1
     } else if (*a_int > *b_int) {
test/self_hosted.zig
@@ -25,20 +25,20 @@ fn ifStatements() {
 }
 fn shouldBeEqual(a: i32, b: i32) {
     if (a != b) {
-        unreachable{};
+        @unreachable();
     } else {
         return;
     }
 }
 fn firstEqlThird(a: i32, b: i32, c: i32) {
     if (a == b) {
-        unreachable{};
+        @unreachable();
     } else if (b == c) {
-        unreachable{};
+        @unreachable();
     } else if (a == c) {
         return;
     } else {
-        unreachable{};
+        @unreachable();
     }
 }
 
@@ -58,7 +58,7 @@ fn localVariables() {
 }
 fn testLocVars(b: i32) {
     const a: i32 = 1;
-    if (a + b != 3) unreachable{};
+    if (a + b != 3) @unreachable();
 }
 
 #attribute("test")
@@ -145,7 +145,7 @@ fn shortCircuit() {
 
 #static_eval_enable(false)
 fn assertRuntime(b: bool) {
-    if (!b) unreachable{}
+    if (!b) @unreachable()
 }
 
 #attribute("test")
@@ -328,10 +328,10 @@ fn maybeType() {
         if (y) {
             // OK
         } else {
-            unreachable{};
+            @unreachable();
         }
     } else {
-        unreachable{};
+        @unreachable();
     }
 
     const next_x : ?i32 = null;
@@ -342,7 +342,7 @@ fn maybeType() {
 
     const final_x : ?i32 = 13;
 
-    const num = final_x ?? unreachable{};
+    const num = final_x ?? @unreachable();
 
     assert(num == 13);
 }
@@ -360,7 +360,7 @@ fn enumType() {
     const expected_foo_size = switch (@compileVar("arch")) {
         i386 => 20,
         x86_64 => 24,
-        else => unreachable{},
+        else => @unreachable(),
     };
     assert(@sizeOf(EnumTypeFoo) == expected_foo_size);
     assert(@sizeOf(EnumTypeBar) == 1);
@@ -437,7 +437,7 @@ error AnError;
 error AnError;
 error SecondError;
 fn shouldBeNotEqual(a: error, b: error) {
-    if (a == b) unreachable{}
+    if (a == b) @unreachable()
 }
 
 
@@ -454,13 +454,13 @@ fn constantEnumWithPayload() {
 fn shouldBeEmpty(x: AnEnumWithPayload) {
     switch (x) {
         Empty => {},
-        else => unreachable{},
+        else => @unreachable(),
     }
 }
 
 fn shouldBeNotEmpty(x: AnEnumWithPayload) {
     switch (x) {
-        Empty => unreachable{},
+        Empty => @unreachable(),
         else => {},
     }
 }
@@ -482,7 +482,7 @@ fn continueInForLoop() {
         }
         break;
     }
-    if (sum != 6) unreachable{}
+    if (sum != 6) @unreachable()
 }
 
 
@@ -514,9 +514,9 @@ enum Fruit {
 #static_eval_enable(false)
 fn nonConstSwitchOnEnum(fruit: Fruit) {
     switch (fruit) {
-        Apple => unreachable{},
+        Apple => @unreachable(),
         Orange => {},
-        Banana => unreachable{},
+        Banana => @unreachable(),
     }
 }
 
@@ -532,7 +532,7 @@ fn nonConstSwitch(foo: SwitchStatmentFoo) {
         C => 3,
         D => 4,
     };
-    if (val != 3) unreachable{};
+    if (val != 3) @unreachable();
 }
 enum SwitchStatmentFoo {
     A,
@@ -557,10 +557,10 @@ enum SwitchProngWithVarEnum {
 fn switchProngWithVarFn(a: SwitchProngWithVarEnum) {
     switch(a) {
         One => |x| {
-            if (x != 13) unreachable{};
+            if (x != 13) @unreachable();
         },
         Two => |x| {
-            if (x != 13.0) unreachable{};
+            if (x != 13.0) @unreachable();
         },
         Meh => |x| {
             const v: void = x;
@@ -601,7 +601,7 @@ fn implicitCastFnUnreachableReturn() {
 fn wantsFnWithVoid(f: fn()) { }
 
 fn fnWithUnreachable() -> unreachable {
-    unreachable {}
+    @unreachable()
 }
 
 
@@ -644,13 +644,13 @@ fn slicing() {
 
     var slice = array[5...10];
 
-    if (slice.len != 5) unreachable{};
+    if (slice.len != 5) @unreachable();
 
     const ptr = &slice[0];
-    if (ptr[0] != 1234) unreachable{};
+    if (ptr[0] != 1234) @unreachable();
 
     var slice_rest = array[10...];
-    if (slice_rest.len != 10) unreachable{};
+    if (slice_rest.len != 10) @unreachable();
 }
 
 
@@ -662,7 +662,7 @@ fn memcpyAndMemsetIntrinsics() {
     @memset(&foo[0], 'A', foo.len);
     @memcpy(&bar[0], &foo[0], bar.len);
 
-    if (bar[11] != 'A') unreachable{};
+    if (bar[11] != 'A') @unreachable();
 }
 
 
@@ -807,7 +807,7 @@ exit:
     if (it_worked) {
         return;
     }
-    unreachable{};
+    @unreachable();
 entry:
     defer it_worked = true;
     if (b) goto exit;
@@ -1221,7 +1221,7 @@ fn test3_1(f: Test3Foo) {
             assert(pt.x == 3);
             assert(pt.y == 4);
         },
-        else => unreachable{},
+        else => @unreachable(),
     }
 }
 #static_eval_enable(false)
@@ -1230,7 +1230,7 @@ fn test3_2(f: Test3Foo) {
         Two => |x| {
             assert(x == 13);
         },
-        else => unreachable{},
+        else => @unreachable(),
     }
 }