diff --git a/CMakeLists.txt b/CMakeLists.txt index c3f0721bb..867f2684d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -556,7 +556,7 @@ set(ZIG_STD_FILES "net.zig" "os/child_process.zig" "os/darwin.zig" - "os/darwin_errno.zig" + "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" "os/get_app_data_dir.zig" diff --git a/README.md b/README.md index f4daee9e5..6e582a27e 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ that counts as "freestanding" for the purposes of this table. ## Community - * IRC: `#zig` on Freenode. + * IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)). * Reddit: [/r/zig](https://www.reddit.com/r/zig) * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index d624fe4da..c34f06753 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -2172,8 +2172,7 @@ const Analyze = struct { break :fits true; } if (dest_type.cast(Type.Int)) |int| { - break :fits (from_int.positive or from_int.eqZero() or int.key.is_signed) and - int.key.bit_count >= from_int.bitcount(); + break :fits from_int.fitsInTwosComp(int.key.is_signed, int.key.bit_count); } break :cast; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 6e121be27..7420da979 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) { return new_package("", ""); } +static const char *symbols_that_llvm_depends_on[] = { + "memcpy", + "memset", + "sqrt", + "powi", + "sin", + "cos", + "pow", + "exp", + "exp2", + "log", + "log10", + "log2", + "fma", + "fabs", + "minnum", + "maxnum", + "copysign", + "floor", + "ceil", + "trunc", + "rint", + "nearbyint", + "round", + // TODO probably all of compiler-rt needs to go here +}; + CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir) { @@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); + for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { + g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); + } + if (root_src_path) { Buf *src_basename = buf_alloc(); Buf *src_dir = buf_alloc(); diff --git a/src/ir.cpp b/src/ir.cpp index 96ade9d39..fe5fb7708 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2961,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; - while (inner_scope != outer_scope) { - assert(inner_scope); - if (inner_scope->id == ScopeIdDefer) { - AstNode *defer_node = inner_scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - results[defer_kind] += 1; + Scope *scope = inner_scope; + while (scope != outer_scope) { + assert(scope); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + results[defer_kind] += 1; + scope = scope->parent; + continue; + } + case ScopeIdDecls: + case ScopeIdFnDef: + return; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); } - inner_scope = inner_scope->parent; } } @@ -2986,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o if (!scope) return is_noreturn; - if (scope->id == ScopeIdDefer) { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - if (defer_kind == ReturnKindUnconditional || - (gen_error_defers && defer_kind == ReturnKindError)) - { - AstNode *defer_expr_node = defer_node->data.defer.expr; - Scope *defer_expr_scope = defer_node->data.defer.expr_scope; - IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); - if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { - is_noreturn = true; - } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + if (defer_kind == ReturnKindUnconditional || + (gen_error_defers && defer_kind == ReturnKindError)) + { + AstNode *defer_expr_node = defer_node->data.defer.expr; + Scope *defer_expr_scope = defer_node->data.defer.expr_scope; + IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); + if (defer_expr_value != irb->codegen->invalid_instruction) { + if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { + is_noreturn = true; + } else { + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + } } } + scope = scope->parent; + continue; } - + case ScopeIdDecls: + case ScopeIdFnDef: + return is_noreturn; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); } - scope = scope->parent; } return is_noreturn; } diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 4189dfead..1bd1d6c4c 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -30,7 +30,7 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; -pub use @import("../os/darwin_errno.zig"); +pub use @import("../os/darwin/errno.zig"); pub const _errno = __error; diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 5151ecf93..416a8c07d 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -125,8 +125,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File test "listen on a port, send bytes, receive bytes" { if (builtin.os != builtin.Os.linux) { // TODO build abstractions for other operating systems - return; + return error.SkipZigTest; } + const MyServer = struct { tcp_server: Server, diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 9af033bd0..41e1503d4 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -116,13 +116,63 @@ pub const Int = struct { return !r.isOdd(); } - fn bitcount(self: Int) usize { - const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); - return @boolToInt(!self.positive) + u_bit_count; + // Returns the number of bits required to represent the absolute value of self. + fn bitCountAbs(self: Int) usize { + return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); } + // Returns the number of bits required to represent the integer in twos-complement form. + // + // If the integer is negative the value returned is the number of bits needed by a signed + // integer to represent the value. If positive the value is the number of bits for an + // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount + // one greater than the returned value. + // + // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. + fn bitCountTwosComp(self: Int) usize { + var bits = self.bitCountAbs(); + + // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos + // complement requires one less bit. + if (!self.positive) block: { + bits += 1; + + if (@popCount(self.limbs[self.len - 1]) == 1) { + for (self.limbs[0 .. self.len - 1]) |limb| { + if (@popCount(limb) != 0) { + break :block; + } + } + + bits -= 1; + } + } + + return bits; + } + + pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool { + if (self.eqZero()) { + return true; + } + if (!is_signed and !self.positive) { + return false; + } + + const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed); + return bit_count >= req_bits; + } + + pub fn fits(self: Int, comptime T: type) bool { + return self.fitsInTwosComp(T.is_signed, T.bit_count); + } + + // Returns the approximate size of the integer in the given base. Negative values accomodate for + // the minus sign. This is used for determining the number of characters needed to print the + // value. It is inexact and will exceed the given value by 1-2 digits. pub fn sizeInBase(self: Int, base: usize) usize { - return (self.bitcount() / math.log2(base)) + 1; + const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs(); + return (bit_count / math.log2(base)) + 1; } pub fn set(self: *Int, value: var) Allocator.Error!void { @@ -190,9 +240,9 @@ pub const Int = struct { pub fn to(self: Int, comptime T: type) ConvertError!T { switch (@typeId(T)) { TypeId.Int => { - const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; + const UT = @IntType(false, T.bit_count); - if (self.bitcount() > 8 * @sizeOf(UT)) { + if (self.bitCountTwosComp() > T.bit_count) { return error.TargetTooSmall; } @@ -209,9 +259,17 @@ pub const Int = struct { } if (!T.is_signed) { - return if (self.positive) r else error.NegativeIntoUnsigned; + return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned; } else { - return if (self.positive) @intCast(T, r) else -@intCast(T, r); + if (self.positive) { + return @intCast(T, r); + } else { + if (math.cast(T, r)) |ok| { + return -ok; + } else |_| { + return @minValue(T); + } + } } }, else => { @@ -1137,24 +1195,88 @@ test "big.int bitcount + sizeInBase" { var a = try Int.init(al); try a.set(0b100); - debug.assert(a.bitcount() == 3); + debug.assert(a.bitCountAbs() == 3); debug.assert(a.sizeInBase(2) >= 3); debug.assert(a.sizeInBase(10) >= 1); + a.negate(); + debug.assert(a.bitCountAbs() == 3); + debug.assert(a.sizeInBase(2) >= 4); + debug.assert(a.sizeInBase(10) >= 2); + try a.set(0xffffffff); - debug.assert(a.bitcount() == 32); + debug.assert(a.bitCountAbs() == 32); debug.assert(a.sizeInBase(2) >= 32); debug.assert(a.sizeInBase(10) >= 10); try a.shiftLeft(a, 5000); - debug.assert(a.bitcount() == 5032); + debug.assert(a.bitCountAbs() == 5032); debug.assert(a.sizeInBase(2) >= 5032); a.positive = false; - debug.assert(a.bitcount() == 5033); + debug.assert(a.bitCountAbs() == 5032); debug.assert(a.sizeInBase(2) >= 5033); } +test "big.int bitcount/to" { + var a = try Int.init(al); + + try a.set(0); + debug.assert(a.bitCountTwosComp() == 0); + + // TODO: stack smashing + // debug.assert((try a.to(u0)) == 0); + // TODO: sigsegv + // debug.assert((try a.to(i0)) == 0); + + try a.set(-1); + debug.assert(a.bitCountTwosComp() == 1); + debug.assert((try a.to(i1)) == -1); + + try a.set(-8); + debug.assert(a.bitCountTwosComp() == 4); + debug.assert((try a.to(i4)) == -8); + + try a.set(127); + debug.assert(a.bitCountTwosComp() == 7); + debug.assert((try a.to(u7)) == 127); + + try a.set(-128); + debug.assert(a.bitCountTwosComp() == 8); + debug.assert((try a.to(i8)) == -128); + + try a.set(-129); + debug.assert(a.bitCountTwosComp() == 9); + debug.assert((try a.to(i9)) == -129); +} + +test "big.int fits" { + var a = try Int.init(al); + + try a.set(0); + debug.assert(a.fits(u0)); + debug.assert(a.fits(i0)); + + try a.set(255); + debug.assert(!a.fits(u0)); + debug.assert(!a.fits(u1)); + debug.assert(!a.fits(i8)); + debug.assert(a.fits(u8)); + debug.assert(a.fits(u9)); + debug.assert(a.fits(i9)); + + try a.set(-128); + debug.assert(!a.fits(i7)); + debug.assert(a.fits(i8)); + debug.assert(a.fits(i9)); + debug.assert(!a.fits(u9)); + + try a.set(0x1ffffffffeeeeeeee); + debug.assert(!a.fits(u32)); + debug.assert(!a.fits(u64)); + debug.assert(a.fits(u65)); +} + test "big.int string set" { var a = try Int.init(al); try a.setString(10, "120317241209124781241290847124"); diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 4134e382f..cf67b01d5 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -2,7 +2,7 @@ const std = @import("../index.zig"); const c = std.c; const assert = std.debug.assert; -pub use @import("darwin_errno.zig"); +pub use @import("darwin/errno.zig"); pub const PATH_MAX = 1024; @@ -482,6 +482,92 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080; /// data is mach absolute time units pub const NOTE_MACHTIME = 0x00000100; +pub const AF_UNSPEC: c_int = 0; +pub const AF_LOCAL: c_int = 1; +pub const AF_UNIX: c_int = AF_LOCAL; +pub const AF_INET: c_int = 2; +pub const AF_SYS_CONTROL: c_int = 2; +pub const AF_IMPLINK: c_int = 3; +pub const AF_PUP: c_int = 4; +pub const AF_CHAOS: c_int = 5; +pub const AF_NS: c_int = 6; +pub const AF_ISO: c_int = 7; +pub const AF_OSI: c_int = AF_ISO; +pub const AF_ECMA: c_int = 8; +pub const AF_DATAKIT: c_int = 9; +pub const AF_CCITT: c_int = 10; +pub const AF_SNA: c_int = 11; +pub const AF_DECnet: c_int = 12; +pub const AF_DLI: c_int = 13; +pub const AF_LAT: c_int = 14; +pub const AF_HYLINK: c_int = 15; +pub const AF_APPLETALK: c_int = 16; +pub const AF_ROUTE: c_int = 17; +pub const AF_LINK: c_int = 18; +pub const AF_XTP: c_int = 19; +pub const AF_COIP: c_int = 20; +pub const AF_CNT: c_int = 21; +pub const AF_RTIP: c_int = 22; +pub const AF_IPX: c_int = 23; +pub const AF_SIP: c_int = 24; +pub const AF_PIP: c_int = 25; +pub const AF_ISDN: c_int = 28; +pub const AF_E164: c_int = AF_ISDN; +pub const AF_KEY: c_int = 29; +pub const AF_INET6: c_int = 30; +pub const AF_NATM: c_int = 31; +pub const AF_SYSTEM: c_int = 32; +pub const AF_NETBIOS: c_int = 33; +pub const AF_PPP: c_int = 34; +pub const AF_MAX: c_int = 40; + +pub const PF_UNSPEC: c_int = AF_UNSPEC; +pub const PF_LOCAL: c_int = AF_LOCAL; +pub const PF_UNIX: c_int = PF_LOCAL; +pub const PF_INET: c_int = AF_INET; +pub const PF_IMPLINK: c_int = AF_IMPLINK; +pub const PF_PUP: c_int = AF_PUP; +pub const PF_CHAOS: c_int = AF_CHAOS; +pub const PF_NS: c_int = AF_NS; +pub const PF_ISO: c_int = AF_ISO; +pub const PF_OSI: c_int = AF_ISO; +pub const PF_ECMA: c_int = AF_ECMA; +pub const PF_DATAKIT: c_int = AF_DATAKIT; +pub const PF_CCITT: c_int = AF_CCITT; +pub const PF_SNA: c_int = AF_SNA; +pub const PF_DECnet: c_int = AF_DECnet; +pub const PF_DLI: c_int = AF_DLI; +pub const PF_LAT: c_int = AF_LAT; +pub const PF_HYLINK: c_int = AF_HYLINK; +pub const PF_APPLETALK: c_int = AF_APPLETALK; +pub const PF_ROUTE: c_int = AF_ROUTE; +pub const PF_LINK: c_int = AF_LINK; +pub const PF_XTP: c_int = AF_XTP; +pub const PF_COIP: c_int = AF_COIP; +pub const PF_CNT: c_int = AF_CNT; +pub const PF_SIP: c_int = AF_SIP; +pub const PF_IPX: c_int = AF_IPX; +pub const PF_RTIP: c_int = AF_RTIP; +pub const PF_PIP: c_int = AF_PIP; +pub const PF_ISDN: c_int = AF_ISDN; +pub const PF_KEY: c_int = AF_KEY; +pub const PF_INET6: c_int = AF_INET6; +pub const PF_NATM: c_int = AF_NATM; +pub const PF_SYSTEM: c_int = AF_SYSTEM; +pub const PF_NETBIOS: c_int = AF_NETBIOS; +pub const PF_PPP: c_int = AF_PPP; +pub const PF_MAX: c_int = AF_MAX; + +pub const SYSPROTO_EVENT: c_int = 1; +pub const SYSPROTO_CONTROL: c_int = 2; + +pub const SOCK_STREAM: c_int = 1; +pub const SOCK_DGRAM: c_int = 2; +pub const SOCK_RAW: c_int = 3; +pub const SOCK_RDM: c_int = 4; +pub const SOCK_SEQPACKET: c_int = 5; +pub const SOCK_MAXADDRLEN: c_int = 255; + fn wstatus(x: i32) i32 { return x & 0o177; } diff --git a/std/os/darwin_errno.zig b/std/os/darwin/errno.zig similarity index 100% rename from std/os/darwin_errno.zig rename to std/os/darwin/errno.zig diff --git a/std/os/file.zig b/std/os/file.zig index f468615ec..52bc590f7 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -15,7 +15,7 @@ pub const File = struct { /// The OS-specific file descriptor or file handle. handle: os.FileHandle, - const OpenError = os.WindowsOpenError || os.PosixOpenError; + pub const OpenError = os.WindowsOpenError || os.PosixOpenError; /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. @@ -239,7 +239,7 @@ pub const File = struct { }, Os.windows => { var pos: windows.LARGE_INTEGER = undefined; - if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) { + if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_PARAMETER => error.BadFd, @@ -248,13 +248,7 @@ pub const File = struct { } assert(pos >= 0); - if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) { - if (pos > @maxValue(usize)) { - return error.FilePosLargerThanPointerRange; - } - } - - return usize(pos); + return math.cast(usize, pos) catch error.FilePosLargerThanPointerRange; }, else => @compileError("unsupported OS"), } @@ -286,7 +280,7 @@ pub const File = struct { Unexpected, }; - fn mode(self: *File) ModeError!os.FileMode { + pub fn mode(self: *File) ModeError!os.FileMode { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -361,7 +355,7 @@ pub const File = struct { pub const WriteError = os.WindowsWriteError || os.PosixWriteError; - fn write(self: *File, bytes: []const u8) WriteError!void { + pub fn write(self: *File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { diff --git a/std/os/index.zig b/std/os/index.zig index 87053fd8d..77fd2a78a 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -11,7 +11,7 @@ const os = this; test "std.os" { _ = @import("child_process.zig"); _ = @import("darwin.zig"); - _ = @import("darwin_errno.zig"); + _ = @import("darwin/errno.zig"); _ = @import("get_user_id.zig"); _ = @import("linux/index.zig"); _ = @import("path.zig"); diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig index 76a54a501..857739e82 100644 --- a/std/special/test_runner.zig +++ b/std/special/test_runner.zig @@ -5,11 +5,25 @@ const test_fn_list = builtin.__zig_test_fn_slice; const warn = std.debug.warn; pub fn main() !void { + var ok_count: usize = 0; + var skip_count: usize = 0; for (test_fn_list) |test_fn, i| { warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name); - try test_fn.func(); - - warn("OK\n"); + if (test_fn.func()) |_| { + ok_count += 1; + warn("OK\n"); + } else |err| switch (err) { + error.SkipZigTest => { + skip_count += 1; + warn("SKIP\n"); + }, + else => return err, + } + } + if (ok_count == test_fn_list.len) { + warn("All tests passed.\n"); + } else { + warn("{} passed; {} skipped.\n", ok_count, skip_count); } } diff --git a/test/cases/defer.zig b/test/cases/defer.zig index d2b00d1f9..7d4d1bc3d 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -61,3 +61,18 @@ test "defer and labeled break" { assert(i == 1); } + +test "errdefer does not apply to fn inside fn" { + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); +} + +fn testNestedFnErrDefer() error!void { + var a: i32 = 0; + errdefer a += 1; + const S = struct { + fn baz() error { + return error.Bad; + } + }; + return S.baz(); +}