diff --git a/src/all_types.hpp b/src/all_types.hpp index d5b24b121d..71bc596f81 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1174,6 +1174,7 @@ enum BuiltinFnId { BuiltinFnIdDivExact, BuiltinFnIdTruncate, BuiltinFnIdIntType, + BuiltinFnIdUnreachable, }; struct BuiltinFnEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index 3ed246d1b5..b39bc584ac 100644 --- a/src/analyze.cpp +++ b/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(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 41a1a394cd..ddf06b7eef 100644 --- a/src/codegen.cpp +++ b/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) { diff --git a/src/eval.cpp b/src/eval.cpp index 1e71fd262b..79d3079adc 100644 --- a/src/eval.cpp +++ b/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: diff --git a/std/compiler_rt.zig b/std/compiler_rt.zig index f63d559563..0c8a04fb0c 100644 --- a/std/compiler_rt.zig +++ b/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(); } diff --git a/std/debug.zig b/std/debug.zig index a2f40cd730..dd864f4f0a 100644 --- a/std/debug.zig +++ b/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 { diff --git a/std/hash_map.zig b/std/hash_map.zig index e9bb533677..92c2742d0b 100644 --- a/std/hash_map.zig +++ b/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 { diff --git a/std/io.zig b/std/io.zig index e060251b0a..762d65cc64 100644 --- a/std/io.zig +++ b/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 { diff --git a/std/linux.zig b/std/linux.zig index cf7d0768b1..8ec9128532 100644 --- a/std/linux.zig +++ b/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 { diff --git a/std/net.zig b/std/net.zig index 38a28ea986..3c943091b2 100644 --- a/std/net.zig +++ b/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") diff --git a/std/os.zig b/std/os.zig index 13cfe7257f..bfdc97220a 100644 --- a/std/os.zig +++ b/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, } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 10bca96abe..725c391ebb 100644 --- a/test/run_tests.cpp +++ b/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) { diff --git a/test/self_hosted.zig b/test/self_hosted.zig index b23dd955fb..bc69642b7f 100644 --- a/test/self_hosted.zig +++ b/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(), } }