From patchwork Mon Feb 28 21:25:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Iain Buclaw X-Patchwork-Id: 1599324 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=ixrCtu7f; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4K7GRD5tZZz9sDX for ; Tue, 1 Mar 2022 23:15:20 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 19C1B3858C2C for ; Mon, 28 Feb 2022 21:26:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 19C1B3858C2C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1646083608; bh=uk6AwHjcj2mKO94EcdnqxIG7rwfxhr3Rq5La55d7VHc=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=ixrCtu7fed15MfsCTLEk4d/gmx90A8y5vjnQ4UCpiOf74TbQXojzcANDmJaq4pour 1s8Z2YIBhXsFa0dk8QqEEw6StADBS1TGHhCBuMSl8iEumjltm4JxbSQlyJIAuRU+dZ 7EZtb6lbNiaw2wLMe+KpSYy2o8av7ZV4yTBEyst8= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [IPv6:2001:67c:2050::465:102]) by sourceware.org (Postfix) with ESMTPS id A7D8C3858D20 for ; Mon, 28 Feb 2022 21:25:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org A7D8C3858D20 Received: from smtp1.mailbox.org (smtp1.mailbox.org [80.241.60.240]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4K6thg228yz9sRJ; Mon, 28 Feb 2022 22:25:39 +0100 (CET) To: gcc-patches@gcc.gnu.org Subject: [committed] d: Merge upstream dmd cf63dd8e5, druntime caf14b0f, phobos 41aaf8c26. Date: Mon, 28 Feb 2022 22:25:23 +0100 Message-Id: <20220228212523.3878326-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 X-Spam-Status: No, score=-15.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Iain Buclaw via Gcc-patches From: Iain Buclaw Reply-To: Iain Buclaw Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Hi, This patch merges the D front-end implementation with upstream dmd cf63dd8e5, as well as the D runtime libraries with druntime caf14b0f, and phobos 41aaf8c26, synchronizing with the release of 2.099.0-rc1. D front-end changes: - Import dmd v2.099.0-rc.1. - The `main' can now return type `noreturn' and supports return inference. D Runtime changes: - Import druntime v2.099.0-rc.1. - C bindings for stat_t on powerpc-linux has been fixed. Phobos changes: - Import phobos v2.099.0-rc.1. Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, and powerpc-linux-gnu. Committed to mainline. Regards, Iain. --- gcc/d/ChangeLog: * d-target.cc (Target::_init): Initialize C type size fields. * dmd/MERGE: Merge upstream dmd cf63dd8e5. * dmd/VERSION: Update version to v2.099.0-rc.1. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime caf14b0f. * src/MERGE: Merge upstream phobos 41aaf8c26. gcc/testsuite/ChangeLog: * gdc.dg/torture/simd7413a.d: Update. * gdc.dg/ubsan/pr88957.d: Update. * gdc.dg/simd18489.d: New test. * gdc.dg/torture/simd21727.d: New test. --- gcc/d/d-target.cc | 9 +- gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/VERSION | 2 +- gcc/d/dmd/common/outbuffer.d | 32 +- gcc/d/dmd/cparse.d | 66 +- gcc/d/dmd/cppmangle.d | 44 +- gcc/d/dmd/dmangle.d | 626 +++++++++--------- gcc/d/dmd/dmodule.d | 8 + gcc/d/dmd/dsymbolsem.d | 3 +- gcc/d/dmd/expressionsem.d | 6 +- gcc/d/dmd/file_manager.d | 6 +- gcc/d/dmd/func.d | 19 +- gcc/d/dmd/lexer.d | 12 +- gcc/d/dmd/mtype.d | 8 + gcc/d/dmd/root/file.d | 40 +- gcc/d/dmd/root/speller.d | 23 +- gcc/d/dmd/root/string.d | 11 +- gcc/d/dmd/semantic3.d | 22 +- gcc/d/dmd/target.d | 4 + gcc/d/dmd/target.h | 4 + gcc/d/dmd/tokens.h | 20 +- gcc/d/dmd/traits.d | 7 +- gcc/d/dmd/typesem.d | 13 +- gcc/testsuite/gdc.dg/simd18489.d | 8 + .../ice21727.d => gdc.dg/torture/simd21727.d} | 11 +- gcc/testsuite/gdc.dg/torture/simd7413a.d | 1 - gcc/testsuite/gdc.dg/ubsan/pr88957.d | 3 +- gcc/testsuite/gdc.test/compilable/b18489.d | 8 - .../gdc.test/compilable/issue21390.d | 3 + .../gdc.test/fail_compilation/fail17927.d | 2 +- .../gdc.test/fail_compilation/fix17751.d | 22 - .../gdc.test/fail_compilation/issue22826.d | 7 + .../gdc.test/fail_compilation/test21546.d | 59 ++ .../gdc.test/fail_compilation/test22023.d | 26 + .../gdc.test/fail_compilation/test22818.d | 21 + gcc/testsuite/gdc.test/runnable/nan.d | 17 +- gcc/testsuite/gdc.test/runnable/previewin.d | 6 +- gcc/testsuite/gdc.test/runnable/sroa13220.d | 103 --- gcc/testsuite/gdc.test/runnable/test15.d | 2 +- gcc/testsuite/gdc.test/runnable/testconst.d | 16 +- gcc/testsuite/gdc.test/runnable/testscope2.d | 2 +- .../runnable/traits_getPointerBitmap.d | 2 +- libphobos/libdruntime/MERGE | 2 +- libphobos/libdruntime/core/gc/gcinterface.d | 4 +- libphobos/libdruntime/core/internal/gc/bits.d | 12 +- .../core/internal/gc/impl/conservative/gc.d | 257 ++++--- .../libdruntime/core/internal/gc/pooltable.d | 29 +- .../libdruntime/core/internal/gc/proxy.d | 4 +- libphobos/libdruntime/core/memory.d | 4 +- libphobos/libdruntime/core/stdcpp/string.d | 8 +- .../libdruntime/core/sys/posix/sys/stat.d | 85 ++- libphobos/libdruntime/core/time.d | 158 +++-- libphobos/libdruntime/object.d | 13 +- libphobos/src/MERGE | 2 +- libphobos/src/std/file.d | 4 +- libphobos/src/std/getopt.d | 8 +- libphobos/src/std/range/primitives.d | 11 +- libphobos/src/std/sumtype.d | 108 ++- 58 files changed, 1164 insertions(+), 851 deletions(-) create mode 100644 gcc/testsuite/gdc.dg/simd18489.d rename gcc/testsuite/{gdc.test/runnable/ice21727.d => gdc.dg/torture/simd21727.d} (71%) delete mode 100644 gcc/testsuite/gdc.test/compilable/b18489.d create mode 100644 gcc/testsuite/gdc.test/compilable/issue21390.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fix17751.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/issue22826.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test21546.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test22023.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test22818.d delete mode 100644 gcc/testsuite/gdc.test/runnable/sroa13220.d diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc index 02f7b742455..610be74ad48 100644 --- a/gcc/d/d-target.cc +++ b/gcc/d/d-target.cc @@ -158,9 +158,14 @@ Target::_init (const Param &) Type::thash_t = Type::tsize_t; /* Set-up target C ABI. */ - this->c.longsize = int_size_in_bytes (long_integer_type_node); - this->c.long_doublesize = int_size_in_bytes (long_double_type_node); + this->c.boolsize = (BOOL_TYPE_SIZE / BITS_PER_UNIT); + this->c.shortsize = (SHORT_TYPE_SIZE / BITS_PER_UNIT); + this->c.intsize = (INT_TYPE_SIZE / BITS_PER_UNIT); + this->c.longsize = (LONG_TYPE_SIZE / BITS_PER_UNIT); + this->c.long_longsize = (LONG_LONG_TYPE_SIZE / BITS_PER_UNIT); + this->c.long_doublesize = (LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT); this->c.wchar_tsize = (WCHAR_TYPE_SIZE / BITS_PER_UNIT); + this->c.bitFieldStyle = targetm.ms_bitfield_layout_p (unknown_type_node) ? TargetC::BitFieldStyle::MS : TargetC::BitFieldStyle::Gcc_Clang; diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index b92f3760e88..f08d53aa3cd 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -cb49e99f80e8111c71035b88fe47fe7d855c300f +cf63dd8e5a77ecb68cf5e7c43bf7b6c4c1154bbe The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index 12042fff7ec..4bb69df8e2c 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.099.0-beta.1 +v2.099.0-rc.1 diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index e5cc43b9d12..fafe90e5357 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -82,18 +82,17 @@ struct OutBuffer /** Frees resources associated. */ - extern (C++) void dtor() nothrow @trusted + extern (C++) void dtor() pure nothrow @trusted { if (fileMapping) { if (fileMapping.active) fileMapping.close(); - fileMapping = null; } else { debug (stomp) memset(data.ptr, 0xFF, data.length); - free(data.ptr); + pureFree(data.ptr); } } @@ -102,17 +101,7 @@ struct OutBuffer */ extern (C++) ~this() pure nothrow @trusted { - if (fileMapping) - { - if (fileMapping.active) - fileMapping.close(); - fileMapping = null; - } - else - { - debug (stomp) memset(data.ptr, 0xFF, data.length); - pureFree(data.ptr); - } + dtor(); } /// For porting with ease from dmd.backend.outbuf.Outbuffer @@ -150,17 +139,10 @@ struct OutBuffer */ extern (C++) void destroy() pure nothrow @trusted { - if (fileMapping && fileMapping.active) - { - fileMapping.close(); - data = null; - offset = 0; - } - else - { - debug (stomp) memset(data.ptr, 0xFF, data.length); - pureFree(extractData()); - } + dtor(); + fileMapping = null; + data = null; + offset = 0; } /** diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index 0fe645906b7..3ded10a7346 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -48,8 +48,12 @@ final class CParser(AST) : Parser!AST linkage = LINK.c; Ccompile = true; - // Configure sizes for C `long`, `long double`, `wchar_t` + // Configure sizes for C `long`, `long double`, `wchar_t`, ... + this.boolsize = target.boolsize; + this.shortsize = target.shortsize; + this.intsize = target.intsize; this.longsize = target.longsize; + this.long_longsize = target.long_longsize; this.long_doublesize = target.long_doublesize; this.wchar_tsize = target.wchar_tsize; @@ -2271,36 +2275,36 @@ final class CParser(AST) : Parser!AST case TKW.xshort: case TKW.xsigned | TKW.xshort: case TKW.xsigned | TKW.xshort | TKW.xint: - case TKW.xshort | TKW.xint: t = AST.Type.tint16; break; + case TKW.xshort | TKW.xint: t = integerTypeForSize(shortsize); break; case TKW.xunsigned | TKW.xshort | TKW.xint: - case TKW.xunsigned | TKW.xshort: t = AST.Type.tuns16; break; + case TKW.xunsigned | TKW.xshort: t = unsignedTypeForSize(shortsize); break; case TKW.xint: case TKW.xsigned: - case TKW.xsigned | TKW.xint: t = AST.Type.tint32; break; + case TKW.xsigned | TKW.xint: t = integerTypeForSize(intsize); break; case TKW.xunsigned: - case TKW.xunsigned | TKW.xint: t = AST.Type.tuns32; break; + case TKW.xunsigned | TKW.xint: t = unsignedTypeForSize(intsize); break; case TKW.xlong: case TKW.xsigned | TKW.xlong: case TKW.xsigned | TKW.xlong | TKW.xint: - case TKW.xlong | TKW.xint: t = longsize == 4 ? AST.Type.tint32 : AST.Type.tint64; break; + case TKW.xlong | TKW.xint: t = integerTypeForSize(longsize); break; case TKW.xunsigned | TKW.xlong | TKW.xint: - case TKW.xunsigned | TKW.xlong: t = longsize == 4 ? AST.Type.tuns32 : AST.Type.tuns64; break; + case TKW.xunsigned | TKW.xlong: t = unsignedTypeForSize(longsize); break; case TKW.xllong: case TKW.xsigned | TKW.xllong: case TKW.xsigned | TKW.xllong | TKW.xint: - case TKW.xllong | TKW.xint: t = AST.Type.tint64; break; + case TKW.xllong | TKW.xint: t = integerTypeForSize(long_longsize); break; case TKW.xunsigned | TKW.xllong | TKW.xint: - case TKW.xunsigned | TKW.xllong: t = AST.Type.tuns64; break; + case TKW.xunsigned | TKW.xllong: t = unsignedTypeForSize(long_longsize); break; case TKW.xvoid: t = AST.Type.tvoid; break; - case TKW.xbool: t = AST.Type.tbool; break; + case TKW.xbool: t = boolsize == 1 ? AST.Type.tbool : integerTypeForSize(boolsize); break; case TKW.xfloat: t = AST.Type.tfloat32; break; case TKW.xdouble: t = AST.Type.tfloat64; break; @@ -4378,6 +4382,48 @@ final class CParser(AST) : Parser!AST return stc; } + /*********************** + * Return suitable signed integer type for the given size + * Params: + * size = size of type + * Returns: + * corresponding signed D integer type + */ + private AST.Type integerTypeForSize(ubyte size) + { + if (size <= 1) + return AST.Type.tint8; + if (size <= 2) + return AST.Type.tint16; + if (size <= 4) + return AST.Type.tint32; + if (size <= 8) + return AST.Type.tint64; + error("unsupported integer type"); + return AST.Type.terror; + } + + /*********************** + * Return suitable unsigned integer type for the given size + * Params: + * size = size of type + * Returns: + * corresponding unsigned D integer type + */ + private AST.Type unsignedTypeForSize(ubyte size) + { + if (size <= 1) + return AST.Type.tuns8; + if (size <= 2) + return AST.Type.tuns16; + if (size <= 4) + return AST.Type.tuns32; + if (size <= 8) + return AST.Type.tuns64; + error("unsupported integer type"); + return AST.Type.terror; + } + /*********************** * Return suitable D float type for C `long double` * Params: diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d index 986b53ff74a..9564b03f753 100644 --- a/gcc/d/dmd/cppmangle.d +++ b/gcc/d/dmd/cppmangle.d @@ -1713,6 +1713,38 @@ extern(C++): * Ds char16_t * u # vendor extended type */ + if (t.isimaginary() || t.iscomplex()) + { + // https://issues.dlang.org/show_bug.cgi?id=22806 + // Complex and imaginary types are represented in the same way as + // arrays or vectors in C++. First substitute the outer type, then + // write out the mangle string of the underlying type. + if (substitute(t)) + return; + append(t); + CV_qualifiers(t); + + if (t.isimaginary()) + buf.writeByte('G'); // 'G' means imaginary + else + buf.writeByte('C'); // 'C' means complex + + switch (t.ty) + { + case Timaginary32: + case Tcomplex32: + return Type.tfloat32.accept(this); + case Timaginary64: + case Tcomplex64: + return Type.tfloat64.accept(this); + case Timaginary80: + case Tcomplex80: + return Type.tfloat80.accept(this); + default: + assert(0); + } + } + char c; char p = 0; switch (t.ty) @@ -1739,12 +1771,6 @@ extern(C++): case Tchar: c = 'c'; break; case Twchar: p = 'D'; c = 's'; break; // since C++11 case Tdchar: p = 'D'; c = 'i'; break; // since C++11 - case Timaginary32: p = 'G'; c = 'f'; break; // 'G' means imaginary - case Timaginary64: p = 'G'; c = 'd'; break; - case Timaginary80: p = 'G'; c = 'e'; break; - case Tcomplex32: p = 'C'; c = 'f'; break; // 'C' means complex - case Tcomplex64: p = 'C'; c = 'd'; break; - case Tcomplex80: p = 'C'; c = 'e'; break; default: return error(t); @@ -1889,11 +1915,11 @@ extern(C++): else if (id == Id.__c_ulonglong) return writeBasicType(t, 0, 'y'); else if (id == Id.__c_complex_float) - return writeBasicType(t, 'C', 'f'); + return Type.tcomplex32.accept(this); else if (id == Id.__c_complex_double) - return writeBasicType(t, 'C', 'd'); + return Type.tcomplex64.accept(this); else if (id == Id.__c_complex_real) - return writeBasicType(t, 'C', 'e'); + return Type.tcomplex80.accept(this); doSymbol(t); } diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d index ad305f966d4..1e6799f63a1 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/dmangle.d @@ -231,168 +231,19 @@ unittest } } -/*********************** - * Mangle basic type ty to buf. - */ - -private void tyToDecoBuffer(OutBuffer* buf, int ty) -{ - const c = mangleChar[ty]; - buf.writeByte(c); - if (c == 'z') - buf.writeByte(ty == Tint128 ? 'i' : 'k'); -} - -/********************************* - * Mangling for mod. - */ -private void MODtoDecoBuffer(OutBuffer* buf, MOD mod) -{ - switch (mod) - { - case 0: - break; - case MODFlags.const_: - buf.writeByte('x'); - break; - case MODFlags.immutable_: - buf.writeByte('y'); - break; - case MODFlags.shared_: - buf.writeByte('O'); - break; - case MODFlags.shared_ | MODFlags.const_: - buf.writestring("Ox"); - break; - case MODFlags.wild: - buf.writestring("Ng"); - break; - case MODFlags.wildconst: - buf.writestring("Ngx"); - break; - case MODFlags.shared_ | MODFlags.wild: - buf.writestring("ONg"); - break; - case MODFlags.shared_ | MODFlags.wildconst: - buf.writestring("ONgx"); - break; - default: - assert(0); - } -} - private extern (C++) final class Mangler : Visitor { alias visit = Visitor.visit; public: static assert(Key.sizeof == size_t.sizeof); - AssocArray!(Type, size_t) types; // Type => (offset+1) in buf - AssocArray!(Identifier, size_t) idents; // Identifier => (offset+1) in buf + OutBuffer* buf; - Type rootType; + Backref backref; extern (D) this(OutBuffer* buf, Type rootType = null) { this.buf = buf; - this.rootType = rootType; - } - - /** - * writes a back reference with the relative position encoded with base 26 - * using upper case letters for all digits but the last digit which uses - * a lower case letter. - * The decoder has to look up the referenced position to determine - * whether the back reference is an identifier (starts with a digit) - * or a type (starts with a letter). - * - * Params: - * pos = relative position to encode - */ - void writeBackRef(size_t pos) - { - buf.writeByte('Q'); - enum base = 26; - size_t mul = 1; - while (pos >= mul * base) - mul *= base; - while (mul >= base) - { - auto dig = cast(ubyte)(pos / mul); - buf.writeByte('A' + dig); - pos -= dig * mul; - mul /= base; - } - buf.writeByte('a' + cast(ubyte)pos); - } - - /** - * Back references a non-basic type - * - * The encoded mangling is - * 'Q' - * - * Params: - * t = the type to encode via back referencing - * - * Returns: - * true if the type was found. A back reference has been encoded. - * false if the type was not found. The current position is saved for later back references. - */ - bool backrefType(Type t) - { - if (t.isTypeBasic()) - return false; - - /** - * https://issues.dlang.org/show_bug.cgi?id=21591 - * - * Special case for unmerged TypeFunctions: use the generic merged - * function type as backref cache key to avoid missed backrefs. - * - * Merging is based on mangling, so we need to avoid an infinite - * recursion by excluding the case where `t` is the root type passed to - * `mangleToBuffer()`. - */ - if (t != rootType) - { - if (t.isFunction_Delegate_PtrToFunction()) - { - t = t.merge2(); - } - } - - return backrefImpl(types, t); - } - - /** - * Back references a single identifier - * - * The encoded mangling is - * 'Q' - * - * Params: - * id = the identifier to encode via back referencing - * - * Returns: - * true if the identifier was found. A back reference has been encoded. - * false if the identifier was not found. The current position is saved for later back references. - */ - bool backrefIdentifier(Identifier id) - { - return backrefImpl(idents, id); - } - - private extern(D) bool backrefImpl(T)(ref AssocArray!(T, size_t) aa, T key) - { - auto p = aa.getLvalue(key); - if (*p) - { - const offset = *p - 1; - writeBackRef(buf.length - offset); - return true; - } - *p = buf.length + 1; - return false; + this.backref = Backref(rootType); } void mangleSymbol(Dsymbol s) @@ -402,14 +253,14 @@ public: void mangleType(Type t) { - if (!backrefType(t)) + if (!backref.addRefToType(buf, t)) t.accept(this); } void mangleIdentifier(Identifier id, Dsymbol s) { - if (!backrefIdentifier(id)) - toBuffer(id.toString(), s); + if (!backref.addRefToIdentifier(buf, id)) + toBuffer(buf, id.toString(), s); } //////////////////////////////////////////////////////////////////////////// @@ -541,7 +392,7 @@ public: // Write argument types foreach (idx, param; t.parameterList) - param.accept(this); + mangleParameter(param); //if (buf.data[buf.length - 1] == '@') assert(0); buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list if (tret !is null) @@ -582,7 +433,7 @@ public: //printf("TypeTuple.toDecoBuffer() t = %p, %s\n", t, t.toChars()); visit(cast(Type)t); Parameter._foreach(t.arguments, (idx, param) { - param.accept(this); + mangleParameter(param); return 0; }); buf.writeByte('Z'); @@ -643,24 +494,8 @@ public: else buf.writeByte('0'); - /* There can be multiple different declarations in the same - * function that have the same mangled name. - * This results in localNum having a non-zero number, which - * is used to add a fake parent of the form `__Sddd` to make - * the mangled names unique. - * https://issues.dlang.org/show_bug.cgi?id=20565 - */ if (localNum) - { - uint ndigits = 1; - auto n = localNum; - while (n >= 10) - { - n /= 10; - ++ndigits; - } - buf.printf("%u__S%u", ndigits + 3, localNum); - } + writeLocalParent(buf, localNum); } } @@ -692,67 +527,6 @@ public: } } - /************************************************************ - * Write length prefixed string to buf. - */ - extern (D) void toBuffer(const(char)[] id, Dsymbol s) - { - const len = id.length; - if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone - s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len)); - else - { - buf.print(len); - buf.writestring(id); - } - } - - /************************************************************ - * Try to obtain an externally mangled identifier from a declaration. - * If the declaration is at global scope or mixed in at global scope, - * the user might want to call it externally, so an externally mangled - * name is returned. Member functions or nested functions can't be called - * externally in C, so in that case null is returned. C++ does support - * namespaces, so extern(C++) always gives a C++ mangled name. - * - * See also: https://issues.dlang.org/show_bug.cgi?id=20012 - * - * Params: - * d = declaration to mangle - * - * Returns: - * an externally mangled name or null if the declaration cannot be called externally - */ - extern (D) static const(char)[] externallyMangledIdentifier(Declaration d) - { - const par = d.toParent(); //toParent() skips over mixin templates - if (!par || par.isModule() || d.linkage == LINK.cpp || - (d.linkage == LINK.c && d.isCsymbol() && d.isFuncDeclaration())) - { - if (d.linkage != LINK.d && d.localNum) - d.error("the same declaration cannot be in multiple scopes with non-D linkage"); - final switch (d.linkage) - { - case LINK.d: - break; - case LINK.c: - case LINK.windows: - case LINK.objc: - return d.ident.toString(); - case LINK.cpp: - { - const p = target.cpp.toMangle(d); - return p.toDString(); - } - case LINK.default_: - case LINK.system: - d.error("forward declaration"); - return d.ident.toString(); - } - } - return null; - } - override void visit(Declaration d) { //printf("Declaration.mangle(this = %p, '%s', parent = '%s', linkage = %d)\n", @@ -1009,13 +783,13 @@ public: if (d.mangleOverride) { buf.writeByte('X'); - toBuffer(d.mangleOverride, d); + toBuffer(buf, d.mangleOverride, d); continue; } if (const id = externallyMangledIdentifier(d)) { buf.writeByte('X'); - toBuffer(id, d); + toBuffer(buf, id, d); continue; } if (!d.type || !d.type.deco) @@ -1052,7 +826,7 @@ public: if (s.ident) mangleIdentifier(s.ident, s); else - toBuffer(s.toString(), s); + toBuffer(buf, s.toString(), s); //printf("Dsymbol.mangle() %s = %s\n", s.toChars(), id); } @@ -1080,68 +854,15 @@ public: override void visit(RealExp e) { buf.writeByte('e'); - realToMangleBuffer(e.value); - } - - void realToMangleBuffer(real_t value) - { - /* Rely on %A to get portable mangling. - * Must munge result to get only identifier characters. - * - * Possible values from %A => mangled result - * NAN => NAN - * -INF => NINF - * INF => INF - * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79 - * 0X1.9P+2 => 19P2 - */ - if (CTFloat.isNaN(value)) - { - buf.writestring("NAN"); // no -NAN bugs - return; - } - - if (value < CTFloat.zero) - { - buf.writeByte('N'); - value = -value; - } - - if (CTFloat.isInfinity(value)) - { - buf.writestring("INF"); - return; - } - - char[36] buffer = void; - // 'A' format yields [-]0xh.hhhhp+-d - const n = CTFloat.sprint(buffer.ptr, 'A', value); - assert(n < buffer.length); - foreach (const c; buffer[2 .. n]) - { - switch (c) - { - case '-': - buf.writeByte('N'); - break; - - case '+': - case '.': - break; - - default: - buf.writeByte(c); - break; - } - } + realToMangleBuffer(buf, e.value); } override void visit(ComplexExp e) { buf.writeByte('c'); - realToMangleBuffer(e.toReal()); + realToMangleBuffer(buf, e.toReal()); buf.writeByte('c'); // separate the two - realToMangleBuffer(e.toImaginary()); + realToMangleBuffer(buf, e.toImaginary()); } override void visit(NullExp e) @@ -1258,7 +979,7 @@ public: //////////////////////////////////////////////////////////////////////////// - override void visit(Parameter p) + void mangleParameter(Parameter p) { // https://dlang.org/spec/abi.html#Parameter @@ -1331,3 +1052,318 @@ public: visitWithMask(p.type, (stc & STC.in_) ? MODFlags.const_ : 0); } } + +/*************************************** + * Manage back reference mangling + */ +private struct Backref +{ + /** + * Back references a non-basic type + * + * The encoded mangling is + * 'Q' + * + * Params: + * t = the type to encode via back referencing + * + * Returns: + * true if the type was found. A back reference has been encoded. + * false if the type was not found. The current position is saved for later back references. + */ + bool addRefToType(OutBuffer* buf, Type t) + { + if (t.isTypeBasic()) + return false; + + /** + * https://issues.dlang.org/show_bug.cgi?id=21591 + * + * Special case for unmerged TypeFunctions: use the generic merged + * function type as backref cache key to avoid missed backrefs. + * + * Merging is based on mangling, so we need to avoid an infinite + * recursion by excluding the case where `t` is the root type passed to + * `mangleToBuffer()`. + */ + if (t != rootType) + { + if (t.isFunction_Delegate_PtrToFunction()) + { + t = t.merge2(); + } + } + + return backrefImpl(buf, types, t); + } + + /** + * Back references a single identifier + * + * The encoded mangling is + * 'Q' + * + * Params: + * id = the identifier to encode via back referencing + * + * Returns: + * true if the identifier was found. A back reference has been encoded. + * false if the identifier was not found. The current position is saved for later back references. + */ + bool addRefToIdentifier(OutBuffer* buf, Identifier id) + { + return backrefImpl(buf, idents, id); + } + + private: + + extern(D) bool backrefImpl(T)(OutBuffer* buf, ref AssocArray!(T, size_t) aa, T key) + { + auto p = aa.getLvalue(key); + if (*p) + { + const offset = *p - 1; + writeBackRef(buf, buf.length - offset); + return true; + } + *p = buf.length + 1; + return false; + } + + Type rootType; /// avoid infinite recursion + AssocArray!(Type, size_t) types; /// Type => (offset+1) in buf + AssocArray!(Identifier, size_t) idents; /// Identifier => (offset+1) in buf +} + + +/*********************** + * Mangle basic type ty to buf. + */ + +private void tyToDecoBuffer(OutBuffer* buf, int ty) +{ + const c = mangleChar[ty]; + buf.writeByte(c); + if (c == 'z') + buf.writeByte(ty == Tint128 ? 'i' : 'k'); +} + +/********************************* + * Mangling for mod. + */ +private void MODtoDecoBuffer(OutBuffer* buf, MOD mod) +{ + switch (mod) + { + case 0: + break; + case MODFlags.const_: + buf.writeByte('x'); + break; + case MODFlags.immutable_: + buf.writeByte('y'); + break; + case MODFlags.shared_: + buf.writeByte('O'); + break; + case MODFlags.shared_ | MODFlags.const_: + buf.writestring("Ox"); + break; + case MODFlags.wild: + buf.writestring("Ng"); + break; + case MODFlags.wildconst: + buf.writestring("Ngx"); + break; + case MODFlags.shared_ | MODFlags.wild: + buf.writestring("ONg"); + break; + case MODFlags.shared_ | MODFlags.wildconst: + buf.writestring("ONgx"); + break; + default: + assert(0); + } +} + + +/** + * writes a back reference with the relative position encoded with base 26 + * using upper case letters for all digits but the last digit which uses + * a lower case letter. + * The decoder has to look up the referenced position to determine + * whether the back reference is an identifier (starts with a digit) + * or a type (starts with a letter). + * + * Params: + * buf = buffer to write to + * pos = relative position to encode + */ +private +void writeBackRef(OutBuffer* buf, size_t pos) +{ + buf.writeByte('Q'); + enum base = 26; + size_t mul = 1; + while (pos >= mul * base) + mul *= base; + while (mul >= base) + { + auto dig = cast(ubyte)(pos / mul); + buf.writeByte('A' + dig); + pos -= dig * mul; + mul /= base; + } + buf.writeByte('a' + cast(ubyte)pos); +} + + +/************************************************************ + * Write length prefixed string to buf. + */ +private +extern (D) void toBuffer(OutBuffer* buf, const(char)[] id, Dsymbol s) +{ + const len = id.length; + if (buf.length + len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone + s.error("excessive length %llu for symbol, possible recursive expansion?", cast(ulong)(buf.length + len)); + else + { + buf.print(len); + buf.writestring(id); + } +} + + +/***** + * There can be multiple different declarations in the same + * function that have the same mangled name. + * This results in localNum having a non-zero number, which + * is used to add a fake parent of the form `__Sddd` to make + * the mangled names unique. + * https://issues.dlang.org/show_bug.cgi?id=20565 + * Params: + * buf = buffer to write to + * localNum = local symbol number + */ +private +void writeLocalParent(OutBuffer* buf, uint localNum) +{ + uint ndigits = 1; + auto n = localNum; + while (n >= 10) + { + n /= 10; + ++ndigits; + } + buf.printf("%u__S%u", ndigits + 3, localNum); +} + +/************************* + * Write real to buffer. + * Params: + * buf = buffer to write to + * value = real to write + */ +private +void realToMangleBuffer(OutBuffer* buf, real_t value) +{ + /* Rely on %A to get portable mangling. + * Must munge result to get only identifier characters. + * + * Possible values from %A => mangled result + * NAN => NAN + * -INF => NINF + * INF => INF + * -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79 + * 0X1.9P+2 => 19P2 + */ + if (CTFloat.isNaN(value)) + { + buf.writestring("NAN"); // no -NAN bugs + return; + } + + if (value < CTFloat.zero) + { + buf.writeByte('N'); + value = -value; + } + + if (CTFloat.isInfinity(value)) + { + buf.writestring("INF"); + return; + } + + char[36] buffer = void; + // 'A' format yields [-]0xh.hhhhp+-d + const n = CTFloat.sprint(buffer.ptr, 'A', value); + assert(n < buffer.length); + foreach (const c; buffer[2 .. n]) + { + switch (c) + { + case '-': + buf.writeByte('N'); + break; + + case '+': + case '.': + break; + + default: + buf.writeByte(c); + break; + } + } +} + +/************************************************************ + * Try to obtain an externally mangled identifier from a declaration. + * If the declaration is at global scope or mixed in at global scope, + * the user might want to call it externally, so an externally mangled + * name is returned. Member functions or nested functions can't be called + * externally in C, so in that case null is returned. C++ does support + * namespaces, so extern(C++) always gives a C++ mangled name. + * + * See also: https://issues.dlang.org/show_bug.cgi?id=20012 + * + * Params: + * d = declaration to mangle + * + * Returns: + * an externally mangled name or null if the declaration cannot be called externally + */ +private +extern (D) const(char)[] externallyMangledIdentifier(Declaration d) +{ + const par = d.toParent(); //toParent() skips over mixin templates + if (!par || par.isModule() || d.linkage == LINK.cpp || + (d.linkage == LINK.c && d.isCsymbol() && d.isFuncDeclaration())) + { + if (d.linkage != LINK.d && d.localNum) + d.error("the same declaration cannot be in multiple scopes with non-D linkage"); + final switch (d.linkage) + { + case LINK.d: + break; + case LINK.c: + case LINK.windows: + case LINK.objc: + return d.ident.toString(); + case LINK.cpp: + { + const p = target.cpp.toMangle(d); + return p.toDString(); + } + case LINK.default_: + case LINK.system: + d.error("forward declaration"); + return d.ident.toString(); + } + } + return null; +} + + diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 84e29fe1023..6568442c17a 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -615,6 +615,14 @@ extern (C++) final class Module : Package const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found"; errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr); } + else if (FileName.ext(this.arg) || !loc.isValid()) + { + // Modules whose original argument name has an extension, or do not + // have a valid location come from the command-line. + // Error that their file cannot be found and return early. + .error(loc, "cannot find input file `%s`", srcfile.toChars()); + return false; + } else { // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 8ad0178f814..ef25717e79f 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -464,8 +464,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor //printf("sc.stc = %x\n", sc.stc); //printf("storage_class = x%x\n", storage_class); - if (global.params.vcomplex) - dsym.type.checkComplexTransition(dsym.loc, sc); + dsym.type.checkComplexTransition(dsym.loc, sc); // Calculate type size + safety checks if (sc.func && !sc.intypeof) diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 0320f662bdf..6692fb9717e 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -3285,8 +3285,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else assert(0); - if (global.params.vcomplex) - exp.type.checkComplexTransition(exp.loc, sc); + exp.type.checkComplexTransition(exp.loc, sc); result = e; } @@ -5375,8 +5374,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if (global.params.vcomplex) - ta.checkComplexTransition(exp.loc, sc); + ta.checkComplexTransition(exp.loc, sc); Expression e; auto tb = ta.toBasetype(); diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d index 0ca756eb58a..7e0f404dd73 100644 --- a/gcc/d/dmd/file_manager.d +++ b/gcc/d/dmd/file_manager.d @@ -185,11 +185,7 @@ nothrow: if (res == 1) return readToFileBuffer(name); - const fullName = lookForSourceFile(name, global.path ? (*global.path)[] : null); - if (!fullName) - return null; - - return readToFileBuffer(fullName); + return null; } extern(C++) FileBuffer* lookup(const(char)* filename) diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 39cb8456fda..afc0ebbee16 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -549,9 +549,22 @@ extern (C++) class FuncDeclaration : Declaration if (thandle.ty == Tstruct) { vthis.storage_class |= STC.ref_; - // if member function is marked 'inout', then 'this' is 'return ref' - if (type.ty == Tfunction && (cast(TypeFunction)type).isInOutQual()) - vthis.storage_class |= STC.return_; + + /* if member function is marked 'inout', then 'this' is 'return ref' + * The same thing is done for `ref inout` parameters in TypeFunction's semantic routine. + */ + if (auto tf = type.isTypeFunction()) + { + /* This feature was a mistake, but existing code relies on it. + * So only disable it in @safe code and DIP1000 code + */ + if (!(global.params.useDIP1000 == FeatureState.enabled && + tf.trust == TRUST.safe)) + { + if (tf.isInOutQual()) + vthis.storage_class |= STC.return_; + } + } } } diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index 7c8b504f419..6377e9c7dea 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -60,7 +60,11 @@ class Lexer bool Ccompile; /// true if compiling ImportC // The following are valid only if (Ccompile == true) + ubyte boolsize; /// size of a C _Bool, default 1 + ubyte shortsize; /// size of a C short, default 2 + ubyte intsize; /// size of a C int, default 4 ubyte longsize; /// size of C long, 4 or 8 + ubyte long_longsize; /// size of a C long long, default 8 ubyte long_doublesize; /// size of C long double, 8 or D real.sizeof ubyte wchar_tsize; /// size of C wchar_t, 2 or 4 @@ -2312,7 +2316,7 @@ class Lexer case FLAGS.decimal | FLAGS.long_: /* First that fits: long, long long */ - if (longsize == 4) + if (longsize == 4 || long_longsize == 4) { if (n & 0xFFFFFFFF_80000000L) result = TOK.int64Literal; @@ -2329,7 +2333,7 @@ class Lexer /* First that fits: long, unsigned long, long long, * unsigned long long */ - if (longsize == 4) + if (longsize == 4 || long_longsize == 4) { if (n & 0x8000000000000000L) result = TOK.uns64Literal; @@ -2353,7 +2357,7 @@ class Lexer case FLAGS.decimal | FLAGS.unsigned | FLAGS.long_: /* First that fits: unsigned long, unsigned long long */ - if (longsize == 4) + if (longsize == 4 || long_longsize == 4) { if (n & 0xFFFFFFFF00000000L) result = TOK.uns64Literal; @@ -2710,6 +2714,8 @@ class Lexer case '2': case '3': case '4': + if (!linemarker) + goto Lerr; flags = true; // linemarker flags seen ++p; if ('0' <= *p && *p <= '9') diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 28978776e03..9297ad9dd03 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -648,7 +648,15 @@ extern (C++) abstract class Type : ASTNode goto Lcovariant; } else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n)) + { + if (t1.isref && t2.isref) + { + // Treat like pointers to t1n and t2n + if (t1n.constConv(t2n) < MATCH.constant) + goto Lnotcovariant; + } goto Lcovariant; + } else if (t1n.ty == Tnull) { // NULL is covariant with any pointer type, but not with any diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d index 6331a6297f1..1f33c184800 100644 --- a/gcc/d/dmd/root/file.d +++ b/gcc/d/dmd/root/file.d @@ -97,13 +97,13 @@ nothrow: int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY)); if (fd == -1) { - //printf("\topen error, errno = %d\n",errno); + //perror("\topen error"); return result; } //printf("\tfile opened\n"); if (fstat(fd, &buf)) { - perror("\tfstat error"); + //perror("\tfstat error"); close(fd); return result; } @@ -112,12 +112,12 @@ nothrow: numread = .read(fd, buffer, size); if (numread != size) { - perror("\tread error"); + //perror("\tread error"); goto err2; } if (close(fd) == -1) { - perror("\tclose error"); + //perror("\tclose error"); goto err; } // Always store a wchar ^Z past end of buffer so scanner has a @@ -289,3 +289,35 @@ nothrow: } } +private +{ + version (linux) version (PPC) + { + // https://issues.dlang.org/show_bug.cgi?id=22823 + // Define our own version of stat_t, as older versions of the compiler + // had the st_size field at the wrong offset on PPC. + alias stat_t_imported = core.sys.posix.sys.stat.stat_t; + static if (stat_t_imported.st_size.offsetof != 48) + { + extern (C) nothrow @nogc: + struct stat_t + { + ulong[6] __pad1; + ulong st_size; + ulong[6] __pad2; + } + version (CRuntime_Glibc) + { + int fstat64(int, stat_t*) @trusted; + alias fstat = fstat64; + int stat64(const scope char*, stat_t*) @system; + alias stat = stat64; + } + else + { + int fstat(int, stat_t*) @trusted; + int stat(const scope char*, stat_t*) @system; + } + } + } +} diff --git a/gcc/d/dmd/root/speller.d b/gcc/d/dmd/root/speller.d index b3e59f5182e..9b9460d3269 100644 --- a/gcc/d/dmd/root/speller.d +++ b/gcc/d/dmd/root/speller.d @@ -42,6 +42,7 @@ private: import core.stdc.stdlib; import core.stdc.string; +import dmd.common.string : SmallBuffer; enum isSearchFunction(alias fun) = is(searchFunctionType!fun); alias searchFunctionType(alias fun) = typeof(() {int x; return fun("", x);}()); @@ -63,15 +64,8 @@ auto spellerX(alias dg)(const(char)[] seed, bool flag) /* Need buffer to store trial strings in */ char[30] tmp = void; - char[] buf; - if (seed.length <= tmp.sizeof - 1) - buf = tmp; - else - { - buf = (cast(char*)alloca(seed.length + 1))[0 .. seed.length + 1]; // leave space for extra char - if (!buf.ptr) - return null; // no matches - } + auto sb = SmallBuffer!char(seed.length + 1, tmp[]); + char[] buf = sb[]; int cost = int.max; searchFunctionType!dg p = null; @@ -164,15 +158,8 @@ auto spellerY(alias dg)(const(char)[] seed, size_t index, out int cost) * space for an extra char for insertions */ char[30] tmp = void; // stack allocations are fastest - char[] buf; - if (seed.length <= tmp.sizeof - 1) - buf = tmp; - else - { - buf = (cast(char*)alloca(seed.length + 1))[0 .. seed.length + 1]; // leave space for extra char - if (!buf.ptr) - return null; // no matches - } + auto sb = SmallBuffer!char(seed.length + 1, tmp[]); + char[] buf = sb[]; buf[0 .. index] = seed[0 .. index]; cost = int.max; // start with worst possible match diff --git a/gcc/d/dmd/root/string.d b/gcc/d/dmd/root/string.d index 0c7cad0b390..ec62292d7df 100644 --- a/gcc/d/dmd/root/string.d +++ b/gcc/d/dmd/root/string.d @@ -69,17 +69,12 @@ The return value of `T` auto toCStringThen(alias dg)(const(char)[] src) nothrow { import dmd.root.rmem : mem; + import dmd.common.string : SmallBuffer; const len = src.length + 1; char[512] small = void; - scope ptr = (src.length < (small.length - 1)) - ? small[0 .. len] - : (cast(char*)mem.xmalloc(len))[0 .. len]; - scope (exit) - { - if (&ptr[0] != &small[0]) - mem.xfree(&ptr[0]); - } + auto sb = SmallBuffer!char(len, small[]); + scope ptr = sb[]; ptr[0 .. src.length] = src[]; ptr[src.length] = '\0'; return dg(ptr); diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 3f019669400..b706777a1ed 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -468,7 +468,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { stc |= STC.variadic; auto vtypeb = vtype.toBasetype(); - if (vtypeb.ty == Tarray) + if (vtypeb.ty == Tarray || vtypeb.ty == Tclass) { /* Since it'll be pointing into the stack for the array * contents, it needs to be `scope` @@ -620,7 +620,7 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.checkDmain(); // Check main() parameters and return type } - if (global.params.vcomplex && f.next !is null) + if (f.next !is null) f.next.checkComplexTransition(funcdecl.loc, sc); if (funcdecl.returns && !funcdecl.fbody.isErrorStatement()) @@ -1292,17 +1292,13 @@ private extern(C++) final class Semantic3Visitor : Visitor // Eliminate maybescope's { // Create and fill array[] with maybe candidates from the `this` and the parameters - VarDeclaration[] array = void; - VarDeclaration[10] tmp = void; size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.dim : 0); - if (dim <= tmp.length) - array = tmp[0 .. dim]; - else - { - auto ptr = cast(VarDeclaration*)mem.xmalloc(dim * VarDeclaration.sizeof); - array = ptr[0 .. dim]; - } + + import dmd.common.string : SmallBuffer; + auto sb = SmallBuffer!VarDeclaration(dim, tmp[]); + VarDeclaration[] array = sb[]; + size_t n = 0; if (funcdecl.vthis) array[n++] = funcdecl.vthis; @@ -1313,11 +1309,7 @@ private extern(C++) final class Semantic3Visitor : Visitor array[n++] = v; } } - eliminateMaybeScopes(array[0 .. n]); - - if (dim > tmp.length) - mem.xfree(array.ptr); } // Infer STC.scope_ diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index e95462568af..7b9c454b33a 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -331,7 +331,11 @@ struct TargetC Gcc_Clang, /// gcc and clang } bool crtDestructorsSupported = true; /// Not all platforms support crt_destructor + ubyte boolsize; /// size of a C `_Bool` type + ubyte shortsize; /// size of a C `short` or `unsigned short` type + ubyte intsize; /// size of a C `int` or `unsigned int` type ubyte longsize; /// size of a C `long` or `unsigned long` type + ubyte long_longsize; /// size of a C `long long` or `unsigned long long` type ubyte long_doublesize; /// size of a C `long double` ubyte wchar_tsize; /// size of a C `wchar_t` type Runtime runtime; /// vendor of the C runtime to link against diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index fdae14c9dea..f3d3859e224 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -70,7 +70,11 @@ struct TargetC }; uint8_t crtDestructorsSupported; // Not all platforms support crt_destructor + uint8_t boolsize; // size of a C '_Bool' type + uint8_t shortsize; // size of a C 'short' or 'unsigned short' type + uint8_t intsize; // size of a C 'int' or 'unsigned int' type uint8_t longsize; // size of a C 'long' or 'unsigned long' type + uint8_t long_longsize; // size of a C 'long long' or 'unsigned long long' type uint8_t long_doublesize; // size of a C 'long double' uint8_t wchar_tsize; // size of a C 'wchar_t' type Runtime runtime; diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index a9f5028038e..c23e0fb4a01 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -67,7 +67,7 @@ enum class TOK : unsigned char comment, // Operators - lessThan, // 54 + lessThan, greaterThan, lessOrEqual, greaterOrEqual, @@ -77,7 +77,7 @@ enum class TOK : unsigned char notIdentity, is_, - leftShift, // 64 + leftShift, rightShift, leftShiftAssign, rightShiftAssign, @@ -112,7 +112,7 @@ enum class TOK : unsigned char orOr, // Numeric literals - int32Literal, // 104, + int32Literal, uns32Literal, int64Literal, uns64Literal, @@ -126,12 +126,12 @@ enum class TOK : unsigned char imaginary80Literal, // Char constants - charLiteral, // 116, + charLiteral, wcharLiteral, dcharLiteral, // Leaf operators - identifier, // 119, + identifier, string_, hexadecimalString, this_, @@ -139,7 +139,7 @@ enum class TOK : unsigned char error, // Basic types - void_, // 127 + void_, int8, uns8, int16, @@ -165,7 +165,7 @@ enum class TOK : unsigned char bool_, // Aggregates - struct_, // 151 + struct_, class_, interface_, union_, @@ -197,7 +197,7 @@ enum class TOK : unsigned char immutable_, // Statements - if_, // 181 + if_, else_, while_, for_, @@ -223,7 +223,7 @@ enum class TOK : unsigned char onScopeSuccess, // Contracts - invariant_, // 205 + invariant_, // Testing unittest_, @@ -233,7 +233,7 @@ enum class TOK : unsigned char ref_, macro_, - parameters, // 210 + parameters, traits, pure_, nothrow_, diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index ec86bc576bd..61602518163 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -568,11 +568,8 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.isDeprecated) { - if (global.params.vcomplex) - { - if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true)) - return True(); - } + if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true)) + return True(); return isDsymX(t => t.isDeprecated()); } if (e.ident == Id.isFuture) diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 637b32ecdad..1f038363dfe 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -1151,7 +1151,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) return mtype; } //printf("TypeFunction::semantic() this = %p\n", this); - //printf("TypeFunction::semantic() %s, sc.stc = %llx, fargs = %p\n", toChars(), sc.stc, fargs); + //printf("TypeFunction::semantic() %s, sc.stc = %llx\n", mtype.toChars(), sc.stc); bool errors = false; @@ -1458,6 +1458,17 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) fparam.storageClass &= ~STC.return_; // https://issues.dlang.org/show_bug.cgi?id=18963 } } + + if (i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe && + (t.isTypeDArray() || t.isTypeClass())) + { + /* This is because they can be constructed on the stack + * https://dlang.org/spec/function.html#typesafe_variadic_functions + */ + .error(loc, "typesafe variadic function parameter `%s` of type `%s` cannot be marked `return`", + fparam.ident ? fparam.ident.toChars() : "", t.toChars()); + errors = true; + } } if (fparam.storageClass & STC.out_) diff --git a/gcc/testsuite/gdc.dg/simd18489.d b/gcc/testsuite/gdc.dg/simd18489.d new file mode 100644 index 00000000000..4591f687c07 --- /dev/null +++ b/gcc/testsuite/gdc.dg/simd18489.d @@ -0,0 +1,8 @@ +// { dg-additional-options "-mavx" { target avx_runtime } } +// { dg-do compile { target { avx_runtime || vect_sizes_16B_8B } } } +// { dg-skip-if "needs gcc/config.d" { ! d_runtime } } +import core.simd; + +double dot (double2 a) { + return a.ptr[0] * a.ptr[1]; +} diff --git a/gcc/testsuite/gdc.test/runnable/ice21727.d b/gcc/testsuite/gdc.dg/torture/simd21727.d similarity index 71% rename from gcc/testsuite/gdc.test/runnable/ice21727.d rename to gcc/testsuite/gdc.dg/torture/simd21727.d index 5b5745f9df0..d277f5366da 100644 --- a/gcc/testsuite/gdc.test/runnable/ice21727.d +++ b/gcc/testsuite/gdc.dg/torture/simd21727.d @@ -1,7 +1,7 @@ -// REQUIRED_ARGS: -m64 -O -inline -// DISABLED: win32 linux32 freebsd32 osx32 netbsd32 dragonflybsd32 // https://issues.dlang.org/show_bug.cgi?id=21727 - +// { dg-additional-options "-mavx" { target avx_runtime } } +// { dg-do run { target { avx_runtime || vect_sizes_16B_8B } } } +// { dg-skip-if "needs gcc/config.d" { ! d_runtime } } import core.simd; @nogc nothrow pure @safe: @@ -25,10 +25,7 @@ pragma(inline, false) Float4 identity(Float4 a) pragma(inline, true) Float4 twoTimes(const ref Float4 a) { - version (D_SIMD) - return Float4(cast(float4) __simd(XMM.ADDPS, a.mVector, a.mVector)); - else // Allow non-DMD compilers to compile this test. - return Float4(a.mVector + a.mVector); + return Float4(a.mVector + a.mVector); } pragma(inline, false) Float4 fourTimes(const Float4 a) diff --git a/gcc/testsuite/gdc.dg/torture/simd7413a.d b/gcc/testsuite/gdc.dg/torture/simd7413a.d index 13bd69a122f..38c9924f63e 100644 --- a/gcc/testsuite/gdc.dg/torture/simd7413a.d +++ b/gcc/testsuite/gdc.dg/torture/simd7413a.d @@ -2,7 +2,6 @@ // { dg-additional-options "-mavx" { target avx_runtime } } // { dg-do run { target { avx_runtime || vect_sizes_16B_8B } } } // { dg-skip-if "needs gcc/config.d" { ! d_runtime } } -// { dg-skip-if "needs gcc/config.d" { ! d_runtime } } import core.simd; void main() diff --git a/gcc/testsuite/gdc.dg/ubsan/pr88957.d b/gcc/testsuite/gdc.dg/ubsan/pr88957.d index e6366d463b2..23433d5861f 100644 --- a/gcc/testsuite/gdc.dg/ubsan/pr88957.d +++ b/gcc/testsuite/gdc.dg/ubsan/pr88957.d @@ -1,5 +1,6 @@ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88957 -// { dg-do compile } +// { dg-additional-options "-mavx" { target avx_runtime } } +// { dg-do compile { target { avx_runtime || vect_sizes_16B_8B } } } // { dg-additional-options "-fsanitize=undefined" } alias int4 = __vector(int[4]); diff --git a/gcc/testsuite/gdc.test/compilable/b18489.d b/gcc/testsuite/gdc.test/compilable/b18489.d deleted file mode 100644 index 2cc386f307a..00000000000 --- a/gcc/testsuite/gdc.test/compilable/b18489.d +++ /dev/null @@ -1,8 +0,0 @@ -// REQUIRED_ARGS: -O -m64 -import core.simd; - -double dot (double2 a) { - return a.ptr[0] * a.ptr[1]; -} - -void main () { } diff --git a/gcc/testsuite/gdc.test/compilable/issue21390.d b/gcc/testsuite/gdc.test/compilable/issue21390.d new file mode 100644 index 00000000000..a5536325ade --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/issue21390.d @@ -0,0 +1,3 @@ +struct S { @disable this(); } +// Does not compile: "default construction is disabled for type `S`" +extern __gshared S gVariable1; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17927.d b/gcc/testsuite/gdc.test/fail_compilation/fail17927.d index 348d473ec27..5f371da46c8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail17927.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail17927.d @@ -2,11 +2,11 @@ * TEST_OUTPUT: --- fail_compilation/fail17927.d(13): Error: scope variable `this` may not be returned +fail_compilation/fail17927.d(15): Error: scope variable `this` may not be returned fail_compilation/fail17927.d(21): Error: scope variable `ptr` may not be returned fail_compilation/fail17927.d(23): Error: scope variable `ptr` may not be returned --- */ - // https://issues.dlang.org/show_bug.cgi?id=17927 struct String { diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix17751.d b/gcc/testsuite/gdc.test/fail_compilation/fix17751.d deleted file mode 100644 index 11b9c548993..00000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fix17751.d +++ /dev/null @@ -1,22 +0,0 @@ -/* REQUIRED_ARGS: -m64 - * TEST_OUTPUT: ---- -fail_compilation/fix17751.d(15): Error: last parameter to `__simd()` must be a constant ---- - */ - -// https://issues.dlang.org/show_bug.cgi?id=17751 - -import core.simd; - -pure @safe V1 simd(XMM opcode, V1, V2)(V1 op1, V2 op2, ubyte imm8) - if (is(V1 == __vector) && is(V2 == __vector)) -{ - return cast(V1)__simd(opcode, op1, op2, imm8); -} - -void main() -{ - float4 a, b; - a = simd!(XMM.CMPPD)(a, b, 0x7A); -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue22826.d b/gcc/testsuite/gdc.test/fail_compilation/issue22826.d new file mode 100644 index 00000000000..ee1802af6c7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/issue22826.d @@ -0,0 +1,7 @@ +/* TEST_OUTPUT: +--- +fail_compilation/issue22826.d(7): Error: #line integer ["filespec"]\n expected +fail_compilation/issue22826.d(7): Error: declaration expected, not `3` +--- +*/ +#line 12 "issue22826.d" 3 diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21546.d b/gcc/testsuite/gdc.test/fail_compilation/test21546.d new file mode 100644 index 00000000000..22565e4a8a2 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21546.d @@ -0,0 +1,59 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test21546.d(113): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `int* delegate() return` +fail_compilation/test21546.d(114): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `immutable(int)* delegate() return` +fail_compilation/test21546.d(115): Error: cannot implicitly convert expression `pi` of type `immutable(int)* delegate() return` to `int* delegate() return` +fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `int delegate() ref return` +fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `immutable(int) delegate() ref return` +fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() ref return` to `int delegate() ref return` +fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() ref return[]` to `int delegate() ref return[]` +--- + */ +// https://issues.dlang.org/show_bug.cgi?id=21546 + +#line 100 + +alias Pm = int* delegate() return; +alias Pc = const(int)* delegate() return; +alias Pi = immutable(int)* delegate() return; + +void f() +{ + Pm pm; + Pc pc; + Pi pi; + pc = pm; + pc = pi; + + pm = pc; + pi = pc; + pm = pi; +} + +#line 200 + +alias DGm = ref int delegate() return; +alias DGc = ref const(int) delegate() return; +alias DGi = ref immutable(int) delegate() return; + +void g() +{ + DGm dm; + DGc dc; + DGi di; + dc = dm; + dc = di; + + dm = dc; + di = dc; + dm = di; +} + +#line 300 + +void h() +{ + immutable int i = 0; + DGi dgi = ref() => i; + DGm[] dgms = [ dgi ]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22023.d b/gcc/testsuite/gdc.test/fail_compilation/test22023.d new file mode 100644 index 00000000000..a0f553ba5bf --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test22023.d @@ -0,0 +1,26 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test22023.d(102): Error: typesafe variadic function parameter `a` of type `int[]` cannot be marked `return` +fail_compilation/test22023.d(107): Error: typesafe variadic function parameter `c` of type `test22023.C` cannot be marked `return` +--- +*/ + +// issues.dlang.org/show_bug.cgi?id=22023 + +#line 100 + +@safe: +ref int f(return int[] a ...) +{ + return a[2]; +} + +ref int g(return C c ...) +{ + return c.x; +} + +class C +{ + int x; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22818.d b/gcc/testsuite/gdc.test/fail_compilation/test22818.d new file mode 100644 index 00000000000..ae96b3bc109 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test22818.d @@ -0,0 +1,21 @@ +/* REQUIRED_ARGS: -preview=dip1000 + * TEST_OUTPUT: +--- +fail_compilation/test22818.d(104): Error: scope variable `c` may not be returned +--- +*/ + +// issues.dlang.org/show_bug.cgi?id=22818 + +#line 100 + +@safe: +ref int g(C c ...) +{ + return c.x; +} + +class C +{ + int x; +} diff --git a/gcc/testsuite/gdc.test/runnable/nan.d b/gcc/testsuite/gdc.test/runnable/nan.d index d4e4ae4ebdc..9b97699b8ba 100644 --- a/gcc/testsuite/gdc.test/runnable/nan.d +++ b/gcc/testsuite/gdc.test/runnable/nan.d @@ -45,15 +45,26 @@ void test2(T)() assert(a is c); static if (T.mant_dig == 64 && T.max_exp == 16384) + { enum size = 10; // x87, exclude padding + enum mant_dig = T.mant_dig; + } + else static if (T.mant_dig == 106) + { + enum size = 8; // IBM, only look at first index + enum mant_dig = 53; + } else + { enum size = T.sizeof; + enum mant_dig = T.mant_dig; + } const pa = (cast(ubyte*) &a)[0 .. size]; // the highest 2 bits of the mantissa should be set, everything else zero - assert(bittst(pa, T.mant_dig - 1)); - assert(bittst(pa, T.mant_dig - 2)); - foreach(p; 0..T.mant_dig - 2) + assert(bittst(pa, mant_dig - 1)); + assert(bittst(pa, mant_dig - 2)); + foreach(p; 0..mant_dig - 2) assert(!bittst(pa, p)); } diff --git a/gcc/testsuite/gdc.test/runnable/previewin.d b/gcc/testsuite/gdc.test/runnable/previewin.d index 12a0551f7e5..117070dfe5e 100644 --- a/gcc/testsuite/gdc.test/runnable/previewin.d +++ b/gcc/testsuite/gdc.test/runnable/previewin.d @@ -157,10 +157,10 @@ struct WithDtor void testin1(in uint p) { static assert(!__traits(isRef, p)); } // By ref because of size void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); } -// By value or ref depending on size -void testin3(in ValueT p) { static assert(!__traits(isRef, p)); } +// By value or ref depending on size (or structs always passed by reference) +void testin3(in ValueT p) { static assert(!__traits(isRef, p) || true); } void testin3(in RefT p) { static assert(__traits(isRef, p)); } -// By ref because of size +// By ref because of size (or arrays always passed by reference) void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); } void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); } diff --git a/gcc/testsuite/gdc.test/runnable/sroa13220.d b/gcc/testsuite/gdc.test/runnable/sroa13220.d deleted file mode 100644 index 2cec6665a33..00000000000 --- a/gcc/testsuite/gdc.test/runnable/sroa13220.d +++ /dev/null @@ -1,103 +0,0 @@ -/* REQUIRED_ARGS: -O -inline -noboundscheck - */ -// https://github.com/dlang/pull/13220 - -version (D_SIMD) -{ - -mixin template VectorOps(VectorType, ArrayType: BaseType[N], BaseType, size_t N) -{ - enum Count = N; - alias Base = BaseType; - - BaseType* ptr() return pure nothrow @nogc - { - return array.ptr; - } - - // Unary operators - VectorType opUnary(string op)() pure nothrow @safe @nogc - { - VectorType res = void; - mixin("res.array[] = " ~ op ~ "array[];"); - return res; - } - - // Binary operators - VectorType opBinary(string op)(VectorType other) pure const nothrow @safe @nogc - { - VectorType res = void; - mixin("res.array[] = array[] " ~ op ~ " other.array[];"); - return res; - } - - // Assigning a BaseType value - void opAssign(BaseType e) pure nothrow @safe @nogc - { - array[] = e; - } - - // Assigning a static array - void opAssign(ArrayType v) pure nothrow @safe @nogc - { - array[] = v[]; - } - - void opOpAssign(string op)(VectorType other) pure nothrow @safe @nogc - { - mixin("array[] " ~ op ~ "= other.array[];"); - } - - // Assigning a dyn array - this(ArrayType v) pure nothrow @safe @nogc - { - array[] = v[]; - } - - // Broadcast constructor - this(BaseType x) pure nothrow @safe @nogc - { - array[] = x; - } - - ref inout(BaseType) opIndex(size_t i) inout pure nothrow @safe @nogc - { - return array[i]; - } -} - -// Note: can't be @safe with this signature -Vec loadUnaligned(Vec)(const(BaseType!Vec)* pvec) @trusted -{ - // Since this vector is emulated, it doesn't have alignement constraints - // and as such we can just cast it. - return *cast(Vec*)(pvec); -} - -private template BaseType(V) -{ - alias typeof( ( { V v; return v; }()).array[0]) BaseType; -} - -struct int4 -{ - int[4] array; - mixin VectorOps!(int4, int[4]); -} - -alias __m128i = int4; -} - -int main() -{ - version (D_SIMD) - { - int4 A = [1, 2, 3, 4]; - int4 ia = A; - ia.ptr[2] = 5; - int4 C = ia; - int[4] result = [1, 2, 5, 4]; - assert(C.array == result); - } - return 0; -} diff --git a/gcc/testsuite/gdc.test/runnable/test15.d b/gcc/testsuite/gdc.test/runnable/test15.d index 70e3a8255e5..b4acc235289 100644 --- a/gcc/testsuite/gdc.test/runnable/test15.d +++ b/gcc/testsuite/gdc.test/runnable/test15.d @@ -1425,7 +1425,7 @@ void test19758() int[2] array = [16, 678]; union U { int i; bool b; } U u; - u.i = 0xDEADBEEF; + u.i = 0xBFBFBFBF; assert(array[u.b] == 678); } diff --git a/gcc/testsuite/gdc.test/runnable/testconst.d b/gcc/testsuite/gdc.test/runnable/testconst.d index 502dca03c92..191ddadc9f2 100644 --- a/gcc/testsuite/gdc.test/runnable/testconst.d +++ b/gcc/testsuite/gdc.test/runnable/testconst.d @@ -1623,7 +1623,7 @@ struct S3748 const int z = 6; C3748 c; - inout(int)* getX() inout + inout(int)* getX() inout return { static assert(!__traits(compiles, { x = 4; @@ -3348,9 +3348,9 @@ struct S10758 { int x; inout(int) screwUpVal(ref inout(int) _) inout { return x; } - ref inout(int) screwUpRef(ref inout(int) _) inout { return x; } - inout(int)* screwUpPtr(ref inout(int) _) inout { return &x; } - inout(int)[] screwUpArr(ref inout(int) _) inout { return (&x)[0 .. 1]; } + ref inout(int) screwUpRef(ref inout(int) _) inout return { return x; } + inout(int)* screwUpPtr(ref inout(int) _) inout return { return &x; } + inout(int)[] screwUpArr(ref inout(int) _) inout return { return (&x)[0 .. 1]; } } void test10758(ref inout(int) wx, inout(int)* wp, inout(int)[] wa, inout(S10758) ws) @@ -3497,14 +3497,14 @@ inout(int)* delegate(inout(int)*) nest10761(inout(int)* x) struct S10761 { int x; - inout(int)* screwUp() inout { return &x; } + inout(int)* screwUp() inout return { return &x; } } -inout(int)* delegate() inout memfn10761(inout(int)* x) +inout(int)* delegate() inout return memfn10761(inout(int)* x) { auto s = new inout S10761(1); auto dg = &s.screwUp; - static assert(is(typeof(dg) == inout(int)* delegate() inout)); + static assert(is(typeof(dg) == inout(int)* delegate() inout return)); return dg; } @@ -3542,7 +3542,7 @@ void test10761() auto dg_m = memfn10761(&mx); auto dg_c = memfn10761(&cx); auto dg_i = memfn10761(&ix); - alias DG = const(int)* delegate() const; + alias DG = const(int)* delegate() return const; static assert(is(typeof(dg_m) == DG)); static assert(is(typeof(dg_c) == DG)); static assert(is(typeof(dg_i) == DG)); diff --git a/gcc/testsuite/gdc.test/runnable/testscope2.d b/gcc/testsuite/gdc.test/runnable/testscope2.d index 1b8cf296d3b..4de1eba7132 100644 --- a/gcc/testsuite/gdc.test/runnable/testscope2.d +++ b/gcc/testsuite/gdc.test/runnable/testscope2.d @@ -178,7 +178,7 @@ struct S10 { int x; - ref inout(int) foo() inout + ref inout(int) foo() inout return { return x; } diff --git a/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d b/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d index 3c5a4bd9786..8996c9ebb8f 100644 --- a/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d +++ b/gcc/testsuite/gdc.test/runnable/traits_getPointerBitmap.d @@ -213,7 +213,7 @@ void testRTInfo() testType!(fn) ([ 0b0 ]); testType!(S!fn) ([ 0b100 ]); testType!(NullType) ([ 0b0 ]); - version(D_LP64) + static if (__traits(compiles, __vector(float[4]))) testType!(__vector(float[4])) ([ 0b00 ]); testType!(Object[int]) ([ 0b1 ]); diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 49f6ae282e0..7c0bb573e2b 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -55528bd1e963d858eaa63901fc818b957c349fbc +caf14b0f4ebbae4157aac89368d6278332ee2aa1 The first line of this file holds the git revision number of the last merge done from the dlang/druntime repository. diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d index e8cdf1109ad..5560c6229ca 100644 --- a/libphobos/libdruntime/core/gc/gcinterface.d +++ b/libphobos/libdruntime/core/gc/gcinterface.d @@ -141,13 +141,13 @@ interface GC * Retrieve statistics about garbage collection. * Useful for debugging and tuning. */ - core.memory.GC.Stats stats() nothrow; + core.memory.GC.Stats stats() @safe nothrow @nogc; /** * Retrieve profile statistics about garbage collection. * Useful for debugging and tuning. */ - core.memory.GC.ProfileStats profileStats() nothrow @safe; + core.memory.GC.ProfileStats profileStats() @safe nothrow @nogc; /** * add p to list of roots diff --git a/libphobos/libdruntime/core/internal/gc/bits.d b/libphobos/libdruntime/core/internal/gc/bits.d index d50c38f0d21..3c1bb543460 100644 --- a/libphobos/libdruntime/core/internal/gc/bits.d +++ b/libphobos/libdruntime/core/internal/gc/bits.d @@ -60,7 +60,7 @@ struct GCBits onOutOfMemoryError(); } - wordtype test(size_t i) const nothrow + wordtype test(size_t i) const scope @trusted pure nothrow @nogc in { assert(i < nbits); @@ -70,7 +70,7 @@ struct GCBits return core.bitop.bt(data, i); } - int set(size_t i) nothrow + int set(size_t i) scope @trusted pure nothrow @nogc in { assert(i < nbits); @@ -80,7 +80,7 @@ struct GCBits return core.bitop.bts(data, i); } - int clear(size_t i) nothrow + int clear(size_t i) scope @trusted pure nothrow @nogc in { assert(i <= nbits); @@ -91,7 +91,7 @@ struct GCBits } // return non-zero if bit already set - size_t setLocked(size_t i) nothrow + size_t setLocked(size_t i) scope @trusted pure nothrow @nogc { version (GNU) { @@ -112,7 +112,7 @@ struct GCBits } else version (D_InlineAsm_X86) { - asm @nogc nothrow { + asm pure @nogc nothrow { mov EAX, this; mov ECX, data[EAX]; mov EDX, i; @@ -123,7 +123,7 @@ struct GCBits } else version (D_InlineAsm_X86_64) { - asm @nogc nothrow { + asm pure @nogc nothrow { mov RAX, this; mov RAX, data[RAX]; mov RDX, i; diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index 87c45fb2872..aa51867fc2c 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -38,6 +38,8 @@ import core.gc.config; import core.gc.gcinterface; import core.internal.container.treap; +import core.internal.spinlock; +import core.internal.gc.pooltable; import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; import core.stdc.string : memcpy, memset, memmove; @@ -145,7 +147,6 @@ class ConservativeGC : GC Gcx *gcx; // implementation - import core.internal.spinlock; static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy); static bool _inFinalizer; __gshared bool isPrecise = false; @@ -155,7 +156,7 @@ class ConservativeGC : GC * * Throws: InvalidMemoryOperationError on recursive locking during finalization. */ - static void lockNR() @nogc nothrow + static void lockNR() @safe @nogc nothrow { if (_inFinalizer) onInvalidMemoryOperationError(); @@ -685,7 +686,7 @@ class ConservativeGC : GC else if (pagenum + newsz <= pool.npages) { // Attempt to expand in place (TODO: merge with extend) - if (lpool.pagetable[pagenum + psz] != B_FREE) + if (lpool.pagetable[pagenum + psz] != Bins.B_FREE) return doMalloc(); auto newPages = newsz - psz; @@ -695,7 +696,7 @@ class ConservativeGC : GC debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize); debug (PRINTF) printFreeInfo(pool); - memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newPages); + memset(&lpool.pagetable[pagenum + psz], Bins.B_PAGEPLUS, newPages); lpool.bPageOffsets[pagenum] = cast(uint) newsz; for (auto offset = psz; offset < newsz; offset++) lpool.bPageOffsets[pagenum + offset] = cast(uint) offset; @@ -766,7 +767,7 @@ class ConservativeGC : GC auto lpool = cast(LargeObjectPool*) pool; size_t pagenum = lpool.pagenumOf(p); - if (lpool.pagetable[pagenum] != B_PAGE) + if (lpool.pagetable[pagenum] != Bins.B_PAGE) return 0; size_t psz = lpool.bPageOffsets[pagenum]; @@ -777,7 +778,7 @@ class ConservativeGC : GC if (pagenum + psz >= lpool.npages) return 0; - if (lpool.pagetable[pagenum + psz] != B_FREE) + if (lpool.pagetable[pagenum + psz] != Bins.B_FREE) return 0; size_t freesz = lpool.bPageOffsets[pagenum + psz]; @@ -785,7 +786,7 @@ class ConservativeGC : GC return 0; size_t sz = freesz > maxsz ? maxsz : freesz; debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE); - memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz); + memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz); lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz); for (auto offset = psz; offset < psz + sz; offset++) lpool.bPageOffsets[pagenum + offset] = cast(uint) offset; @@ -874,11 +875,11 @@ class ConservativeGC : GC debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]); debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]); - bin = cast(Bins)pool.pagetable[pagenum]; + bin = pool.pagetable[pagenum]; // Verify that the pointer is at the beginning of a block, // no action should be taken if p is an interior pointer - if (bin > B_PAGE) // B_PAGEPLUS or B_FREE + if (bin > Bins.B_PAGE) // B_PAGEPLUS or B_FREE return; size_t off = (sentinel_sub(p) - pool.baseAddr); size_t base = baseOffset(off, bin); @@ -893,7 +894,7 @@ class ConservativeGC : GC if (pool.isLargeObject) // if large alloc { biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large; - assert(bin == B_PAGE); + assert(bin == Bins.B_PAGE); auto lpool = cast(LargeObjectPool*) pool; // Free pages @@ -1094,13 +1095,13 @@ class ConservativeGC : GC pool = gcx.findPool(p); assert(pool); pagenum = pool.pagenumOf(p); - bin = cast(Bins)pool.pagetable[pagenum]; - assert(bin <= B_PAGE); + bin = pool.pagetable[pagenum]; + assert(bin <= Bins.B_PAGE); assert(p == cast(void*)baseOffset(cast(size_t)p, bin)); debug (PTRCHECK2) { - if (bin < B_PAGE) + if (bin < Bins.B_PAGE) { // Check that p is not on a free list List *list; @@ -1299,7 +1300,7 @@ class ConservativeGC : GC } - core.memory.GC.Stats stats() nothrow + core.memory.GC.Stats stats() @safe nothrow @nogc { typeof(return) ret; @@ -1332,13 +1333,20 @@ class ConservativeGC : GC // // Implementation of getStats // - private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow + private void getStatsNoSync(out core.memory.GC.Stats stats) @trusted nothrow @nogc { - foreach (pool; gcx.pooltable[0 .. gcx.npools]) + // This function is trusted for two reasons: `pool.pagetable` is a pointer, + // which is being sliced in the below foreach, and so is `binPageChain`, + // also sliced a bit later in this function. + // However, both usages are safe as long as the assumption that `npools` + // defines the limit for `pagetable`'s length holds true (see allocation). + // The slicing happens at __LINE__ + 4 and __LINE__ + 24. + // `@trusted` delegates are not used to prevent any performance issue. + foreach (pool; gcx.pooltable[]) { foreach (bin; pool.pagetable[0 .. pool.npages]) { - if (bin == B_FREE) + if (bin == Bins.B_FREE) stats.freeSize += PAGESIZE; else stats.usedSize += PAGESIZE; @@ -1346,13 +1354,13 @@ class ConservativeGC : GC } size_t freeListSize; - foreach (n; 0 .. B_PAGE) + foreach (n; 0 .. Bins.B_PAGE) { immutable sz = binsize[n]; for (List *list = gcx.bucket[n]; list; list = list.next) freeListSize += sz; - foreach (pool; gcx.pooltable[0 .. gcx.npools]) + foreach (pool; gcx.pooltable[]) { if (pool.isLargeObject) continue; @@ -1381,7 +1389,7 @@ enum } -enum +enum Bins : ubyte { B_16, B_32, @@ -1405,10 +1413,6 @@ enum B_MAX, } - -alias ubyte Bins; - - struct List { List *next; @@ -1416,12 +1420,12 @@ struct List } // non power of two sizes optimized for small remainder within page (<= 64 bytes) -immutable short[B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ]; -immutable short[PAGESIZE / 16][B_NUMSMALL + 1] binbase = calcBinBase(); +immutable short[Bins.B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ]; +immutable short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] binbase = calcBinBase(); -short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase() +short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase() { - short[PAGESIZE / 16][B_NUMSMALL + 1] bin; + short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] bin; foreach (i, size; binsize) { @@ -1440,7 +1444,7 @@ short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase() size_t baseOffset(size_t offset, Bins bin) @nogc nothrow { - assert(bin <= B_PAGE); + assert(bin <= Bins.B_PAGE); return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4]; } @@ -1448,9 +1452,9 @@ alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD]; static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0); // bitmask with bits set at base offsets of objects -immutable PageBits[B_NUMSMALL] baseOffsetBits = (){ - PageBits[B_NUMSMALL] bits; - foreach (bin; 0..B_NUMSMALL) +immutable PageBits[Bins.B_NUMSMALL] baseOffsetBits = (){ + PageBits[Bins.B_NUMSMALL] bits; + foreach (bin; 0 .. Bins.B_NUMSMALL) { size_t size = binsize[bin]; const top = PAGESIZE - size + 1; // ensure bytes available even if unaligned @@ -1475,7 +1479,6 @@ private void set(ref PageBits bits, size_t i) @nogc pure nothrow struct Gcx { - import core.internal.spinlock; auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); Treap!Root roots; @@ -1491,11 +1494,9 @@ struct Gcx debug(INVARIANT) bool inCollection; uint disabled; // turn off collections if >0 - import core.internal.gc.pooltable; - private @property size_t npools() pure const nothrow { return pooltable.length; } PoolTable!Pool pooltable; - List*[B_NUMSMALL] bucket; // free list for each small size + List*[Bins.B_NUMSMALL] bucket; // free list for each small size // run a collection when reaching those thresholds (number of used pages) float smallCollectThreshold, largeCollectThreshold; @@ -1508,7 +1509,7 @@ struct Gcx else alias leakDetector = LeakDetector; - SmallObjectPool*[B_NUMSMALL] recoverPool; + SmallObjectPool*[Bins.B_NUMSMALL] recoverPool; version (Posix) __gshared Gcx* instance; void initialize() @@ -1592,9 +1593,8 @@ struct Gcx debug(INVARIANT) initialized = false; - for (size_t i = 0; i < npools; i++) + foreach (Pool* pool; this.pooltable[]) { - Pool *pool = pooltable[i]; mappedPages -= pool.npages; pool.Dtor(); cstdlib.free(pool); @@ -1635,7 +1635,7 @@ struct Gcx if (!inCollection) (cast()rangesLock).unlock(); - for (size_t i = 0; i < B_NUMSMALL; i++) + for (size_t i = 0; i < Bins.B_NUMSMALL; i++) { size_t j = 0; List* prev, pprev, ppprev; // keep a short history to inspect in the debugger @@ -1752,7 +1752,7 @@ struct Gcx ConservativeGC._inFinalizer = true; scope (failure) ConservativeGC._inFinalizer = false; - foreach (pool; pooltable[0 .. npools]) + foreach (pool; this.pooltable[]) { if (!pool.finals.nbits) continue; @@ -1816,18 +1816,18 @@ struct Gcx /** * Computes the bin table using CTFE. */ - static byte[2049] ctfeBins() nothrow + static Bins[2049] ctfeBins() nothrow { - byte[2049] ret; + Bins[2049] ret; size_t p = 0; - for (Bins b = B_16; b <= B_2048; b++) + for (Bins b = Bins.B_16; b <= Bins.B_2048; b++) for ( ; p <= binsize[b]; p++) ret[p] = b; return ret; } - static const byte[2049] binTable = ctfeBins(); + static immutable Bins[2049] binTable = ctfeBins(); /** * Allocate a new pool of at least size bytes. @@ -1994,7 +1994,7 @@ struct Gcx bool tryAlloc() nothrow { - foreach (p; pooltable[0 .. npools]) + foreach (p; this.pooltable[]) { if (!p.isLargeObject || p.freepages < npages) continue; @@ -2094,10 +2094,11 @@ struct Gcx } // Allocate successively larger pools up to 8 megs - if (npools) - { size_t n; + if (this.pooltable.length) + { + size_t n; - n = config.minPoolSize + config.incPoolSize * npools; + n = config.minPoolSize + config.incPoolSize * this.pooltable.length; if (n > config.maxPoolSize) n = config.maxPoolSize; // cap pool size n /= PAGESIZE; // convert bytes to pages @@ -2139,9 +2140,8 @@ struct Gcx List* allocPage(Bins bin) nothrow { //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin); - for (size_t n = 0; n < npools; n++) + foreach (Pool* pool; this.pooltable[]) { - Pool* pool = pooltable[n]; if (pool.isLargeObject) continue; if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin)) @@ -2275,7 +2275,7 @@ struct Gcx // let dmd allocate a register for this.pools auto pools = pooltable.pools; - const highpool = pooltable.npools - 1; + const highpool = pooltable.length - 1; const minAddr = pooltable.minAddr; size_t memSize = pooltable.maxAddr - minAddr; Pool* pool = null; @@ -2300,7 +2300,6 @@ struct Gcx bitpos -= rng.bmplength; rng.pbase += rng.bmplength; } - import core.bitop; if (!core.bitop.bt(rng.ptrbmp, bitpos)) { debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n"); @@ -2335,7 +2334,7 @@ struct Gcx printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin); // Adjust bit to be at start of allocated memory block - if (bin < B_PAGE) + if (bin < Bins.B_PAGE) { // We don't care abou setting pointsToBase correctly // because it's ignored for small object pools anyhow. @@ -2356,7 +2355,7 @@ struct Gcx goto LaddRange; } } - else if (bin == B_PAGE) + else if (bin == Bins.B_PAGE) { biti = offset >> Pool.ShiftBy.Large; //debug(PRINTF) printf("\t\tbiti = x%x\n", biti); @@ -2376,7 +2375,7 @@ struct Gcx goto LaddLargeRange; } } - else if (bin == B_PAGEPLUS) + else if (bin == Bins.B_PAGEPLUS) { pn -= pool.bPageOffsets[pn]; biti = pn * (PAGESIZE >> Pool.ShiftBy.Large); @@ -2429,7 +2428,7 @@ struct Gcx else { // Don't mark bits in B_FREE pages - assert(bin == B_FREE); + assert(bin == Bins.B_FREE); } } LnextPtr: @@ -2526,9 +2525,8 @@ struct Gcx { debug(COLLECT_PRINTF) printf("preparing mark.\n"); - for (size_t n = 0; n < npools; n++) + foreach (Pool* pool; this.pooltable[]) { - Pool* pool = pooltable[n]; if (pool.isLargeObject) pool.mark.zero(); else @@ -2598,10 +2596,9 @@ struct Gcx size_t freedLargePages; size_t freedSmallPages; size_t freed; - for (size_t n = 0; n < npools; n++) + foreach (Pool* pool; this.pooltable[]) { size_t pn; - Pool* pool = pooltable[n]; if (pool.isLargeObject) { @@ -2612,12 +2609,12 @@ struct Gcx { npages = pool.bPageOffsets[pn]; Bins bin = cast(Bins)pool.pagetable[pn]; - if (bin == B_FREE) + if (bin == Bins.B_FREE) { numFree += npages; continue; } - assert(bin == B_PAGE); + assert(bin == Bins.B_PAGE); size_t biti = pn; if (!pool.mark.test(biti)) @@ -2637,7 +2634,7 @@ struct Gcx debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p); leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA)); - pool.pagetable[pn..pn+npages] = B_FREE; + pool.pagetable[pn..pn+npages] = Bins.B_FREE; if (pn < pool.searchStart) pool.searchStart = pn; freedLargePages += npages; pool.freepages += npages; @@ -2671,7 +2668,7 @@ struct Gcx { Bins bin = cast(Bins)pool.pagetable[pn]; - if (bin < B_PAGE) + if (bin < Bins.B_PAGE) { auto freebitsdata = pool.freebits.data + pn * PageBits.length; auto markdata = pool.mark.data + pn * PageBits.length; @@ -2767,7 +2764,7 @@ struct Gcx { pool.freeAllPageBits(pn); - pool.pagetable[pn] = B_FREE; + pool.pagetable[pn] = Bins.B_FREE; // add to free chain pool.binPageChain[pn] = cast(uint) pool.searchStart; pool.searchStart = pn; @@ -2789,7 +2786,8 @@ struct Gcx assert(freedLargePages <= usedLargePages); usedLargePages -= freedLargePages; - debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools); + debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", + freed, freedLargePages, this.pooltable.length); assert(freedSmallPages <= usedSmallPages); usedSmallPages -= freedSmallPages; @@ -2854,12 +2852,12 @@ struct Gcx private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow { Pool* pool; - while (poolIndex < npools && - ((pool = pooltable[poolIndex]).isLargeObject || + while (poolIndex < this.pooltable.length && + ((pool = this.pooltable[poolIndex]).isLargeObject || pool.recoverPageFirst[bin] >= pool.npages)) poolIndex++; - return recoverPool[bin] = poolIndex < npools ? cast(SmallObjectPool*)pool : null; + return recoverPool[bin] = poolIndex < this.pooltable.length ? cast(SmallObjectPool*)pool : null; } version (COLLECT_FORK) @@ -2928,7 +2926,6 @@ struct Gcx import core.stdc.stdlib : _Exit; debug (PRINTF_TO_FILE) { - import core.stdc.stdio : fflush; fflush(null); // avoid duplicated FILE* output } version (OSX) @@ -3153,7 +3150,7 @@ Lmark: // init bucket lists bucket[] = null; - foreach (Bins bin; 0..B_NUMSMALL) + foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL) setNextRecoverPool(bin, 0); stop = currTime; @@ -3188,24 +3185,24 @@ Lmark: auto pn = offset / PAGESIZE; auto bins = cast(Bins)pool.pagetable[pn]; size_t biti = void; - if (bins < B_PAGE) + if (bins < Bins.B_PAGE) { biti = baseOffset(offset, bins) >> pool.ShiftBy.Small; // doesn't need to check freebits because no pointer must exist // to a block that was free before starting the collection } - else if (bins == B_PAGE) + else if (bins == Bins.B_PAGE) { biti = pn * (PAGESIZE >> pool.ShiftBy.Large); } - else if (bins == B_PAGEPLUS) + else if (bins == Bins.B_PAGEPLUS) { pn -= pool.bPageOffsets[pn]; biti = pn * (PAGESIZE >> pool.ShiftBy.Large); } - else // bins == B_FREE + else // bins == Bins.B_FREE { - assert(bins == B_FREE); + assert(bins == Bins.B_FREE); return IsMarked.no; } return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no; @@ -3262,8 +3259,11 @@ Lmark: /* ============================ Parallel scanning =============================== */ version (COLLECT_PARALLEL): - import core.sync.event; + import core.atomic; + import core.cpuid; + import core.sync.event; + private: // disable invariants for background threads static struct ScanThreadData @@ -3334,7 +3334,6 @@ Lmark: int maxParallelThreads() nothrow { - import core.cpuid; auto threads = threadsPerCPU(); if (threads == 0) @@ -3512,7 +3511,7 @@ struct Pool GCBits is_pointer; // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only) size_t npages; size_t freepages; // The number of pages not in use. - ubyte* pagetable; + Bins* pagetable; bool isLargeObject; @@ -3541,7 +3540,7 @@ struct Pool enum PageRecovered = uint.max; // first of chain of pages to recover (SmallObjectPool only) - uint[B_NUMSMALL] recoverPageFirst; + uint[Bins.B_NUMSMALL] recoverPageFirst; // precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only) immutable(size_t)** rtinfo; @@ -3611,7 +3610,7 @@ struct Pool noscan.alloc(nbits); appendable.alloc(nbits); - pagetable = cast(ubyte*)cstdlib.malloc(npages); + pagetable = cast(Bins*)cstdlib.malloc(npages * Bins.sizeof); if (!pagetable) onOutOfMemoryErrorNoGC(); @@ -3635,7 +3634,7 @@ struct Pool } } - memset(pagetable, B_FREE, npages); + memset(pagetable, Bins.B_FREE, npages); this.npages = npages; this.freepages = npages; @@ -3852,7 +3851,7 @@ struct Pool } public - @property bool isFree() const pure nothrow + @property bool isFree() const scope @safe pure nothrow @nogc { return npages == freepages; } @@ -3883,10 +3882,10 @@ struct Pool { size_t offset = cast(size_t)(p - baseAddr); size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pagetable[pn]; + Bins bin = pagetable[pn]; // Adjust bit to be at start of allocated memory block - if (bin < B_NUMSMALL) + if (bin < Bins.B_NUMSMALL) { auto baseOff = baseOffset(offset, bin); const biti = baseOff >> Pool.ShiftBy.Small; @@ -3894,11 +3893,11 @@ struct Pool return null; return baseAddr + baseOff; } - if (bin == B_PAGE) + if (bin == Bins.B_PAGE) { return baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); } - if (bin == B_PAGEPLUS) + if (bin == Bins.B_PAGEPLUS) { size_t pageOffset = bPageOffsets[pn]; offset -= pageOffset * PAGESIZE; @@ -3907,7 +3906,7 @@ struct Pool return baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); } // we are in a B_FREE page - assert(bin == B_FREE); + assert(bin == Bins.B_FREE); return null; } @@ -3944,8 +3943,8 @@ struct Pool { for (size_t i = 0; i < npages; i++) { - Bins bin = cast(Bins)pagetable[i]; - assert(bin < B_MAX); + Bins bin = pagetable[i]; + assert(bin < Bins.B_MAX); } } } @@ -4053,19 +4052,19 @@ struct LargeObjectPool uint np = bPageOffsets[n]; assert(np > 0 && np <= npages - n); - if (pagetable[n] == B_PAGE) + if (pagetable[n] == Bins.B_PAGE) { for (uint p = 1; p < np; p++) { - assert(pagetable[n + p] == B_PAGEPLUS); + assert(pagetable[n + p] == Bins.B_PAGEPLUS); assert(bPageOffsets[n + p] == p); } } - else if (pagetable[n] == B_FREE) + else if (pagetable[n] == Bins.B_FREE) { for (uint p = 1; p < np; p++) { - assert(pagetable[n + p] == B_FREE); + assert(pagetable[n + p] == Bins.B_FREE); } assert(bPageOffsets[n + np - 1] == np); } @@ -4086,17 +4085,17 @@ struct LargeObjectPool //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n); size_t largest = 0; - if (pagetable[searchStart] == B_PAGEPLUS) + if (pagetable[searchStart] == Bins.B_PAGEPLUS) { searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE searchStart += bPageOffsets[searchStart]; } - while (searchStart < npages && pagetable[searchStart] == B_PAGE) + while (searchStart < npages && pagetable[searchStart] == Bins.B_PAGE) searchStart += bPageOffsets[searchStart]; for (size_t i = searchStart; i < npages; ) { - assert(pagetable[i] == B_FREE); + assert(pagetable[i] == Bins.B_FREE); auto p = bPageOffsets[i]; if (p > n) @@ -4107,11 +4106,11 @@ struct LargeObjectPool if (p == n) { L_found: - pagetable[i] = B_PAGE; + pagetable[i] = Bins.B_PAGE; bPageOffsets[i] = cast(uint) n; if (n > 1) { - memset(&pagetable[i + 1], B_PAGEPLUS, n - 1); + memset(&pagetable[i + 1], Bins.B_PAGEPLUS, n - 1); for (auto offset = 1; offset < n; offset++) bPageOffsets[i + offset] = cast(uint) offset; } @@ -4122,7 +4121,7 @@ struct LargeObjectPool largest = p; i += p; - while (i < npages && pagetable[i] == B_PAGE) + while (i < npages && pagetable[i] == Bins.B_PAGE) { // we have the size information, so we skip a whole bunch of pages. i += bPageOffsets[i]; @@ -4145,8 +4144,8 @@ struct LargeObjectPool for (size_t i = pagenum; i < npages + pagenum; i++) { - assert(pagetable[i] < B_FREE); - pagetable[i] = B_FREE; + assert(pagetable[i] < Bins.B_FREE); + pagetable[i] = Bins.B_FREE; } freepages += npages; largestFree = freepages; // invalidate @@ -4157,8 +4156,8 @@ struct LargeObjectPool */ void setFreePageOffsets(size_t page, size_t num) nothrow @nogc { - assert(pagetable[page] == B_FREE); - assert(pagetable[page + num - 1] == B_FREE); + assert(pagetable[page] == Bins.B_FREE); + assert(pagetable[page + num - 1] == Bins.B_FREE); bPageOffsets[page] = cast(uint)num; if (num > 1) bPageOffsets[page + num - 1] = cast(uint)num; @@ -4168,7 +4167,7 @@ struct LargeObjectPool { static if (bwd) { - if (page > 0 && pagetable[page - 1] == B_FREE) + if (page > 0 && pagetable[page - 1] == Bins.B_FREE) { auto sz = bPageOffsets[page - 1]; page -= sz; @@ -4177,7 +4176,7 @@ struct LargeObjectPool } static if (fwd) { - if (page + num < npages && pagetable[page + num] == B_FREE) + if (page + num < npages && pagetable[page + num] == Bins.B_FREE) num += bPageOffsets[page + num]; } setFreePageOffsets(page, num); @@ -4197,8 +4196,8 @@ struct LargeObjectPool if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer return 0; size_t pagenum = pagenumOf(p); - Bins bin = cast(Bins)pagetable[pagenum]; - if (bin != B_PAGE) + Bins bin = pagetable[pagenum]; + if (bin != Bins.B_PAGE) return 0; return bPageOffsets[pagenum]; } @@ -4208,7 +4207,7 @@ struct LargeObjectPool */ size_t getSize(size_t pn) const nothrow @nogc { - assert(pagetable[pn] == B_PAGE); + assert(pagetable[pn] == Bins.B_PAGE); return cast(size_t) bPageOffsets[pn] * PAGESIZE; } @@ -4221,11 +4220,11 @@ struct LargeObjectPool size_t offset = cast(size_t)(p - baseAddr); size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pagetable[pn]; + Bins bin = pagetable[pn]; - if (bin == B_PAGEPLUS) + if (bin == Bins.B_PAGEPLUS) pn -= bPageOffsets[pn]; - else if (bin != B_PAGE) + else if (bin != Bins.B_PAGE) return info; // no info for free pages info.base = baseAddr + pn * PAGESIZE; @@ -4238,8 +4237,8 @@ struct LargeObjectPool { foreach (pn; 0 .. npages) { - Bins bin = cast(Bins)pagetable[pn]; - if (bin > B_PAGE) + Bins bin = pagetable[pn]; + if (bin > Bins.B_PAGE) continue; size_t biti = pn; @@ -4265,7 +4264,7 @@ struct LargeObjectPool size_t n = 1; for (; pn + n < npages; ++n) - if (pagetable[pn + n] != B_PAGEPLUS) + if (pagetable[pn + n] != Bins.B_PAGEPLUS) break; debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE); freePages(pn, n); @@ -4285,7 +4284,7 @@ struct SmallObjectPool { //base.Invariant(); uint cntRecover = 0; - foreach (Bins bin; 0 .. B_NUMSMALL) + foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL) { for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn]) { @@ -4296,7 +4295,7 @@ struct SmallObjectPool uint cntFree = 0; for (auto pn = searchStart; pn < npages; pn = binPageChain[pn]) { - assert(pagetable[pn] == B_FREE); + assert(pagetable[pn] == Bins.B_FREE); cntFree++; } assert(cntFree == freepages); @@ -4315,8 +4314,8 @@ struct SmallObjectPool do { size_t pagenum = pagenumOf(p); - Bins bin = cast(Bins)pagetable[pagenum]; - assert(bin < B_PAGE); + Bins bin = pagetable[pagenum]; + assert(bin < Bins.B_PAGE); if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer return 0; const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small; @@ -4330,9 +4329,9 @@ struct SmallObjectPool BlkInfo info; size_t offset = cast(size_t)(p - baseAddr); size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pagetable[pn]; + Bins bin = pagetable[pn]; - if (bin >= B_PAGE) + if (bin >= Bins.B_PAGE) return info; auto base = cast(void*)baseOffset(cast(size_t)p, bin); @@ -4352,8 +4351,8 @@ struct SmallObjectPool { foreach (pn; 0 .. npages) { - Bins bin = cast(Bins)pagetable[pn]; - if (bin >= B_PAGE) + Bins bin = pagetable[pn]; + if (bin >= Bins.B_PAGE) continue; immutable size = binsize[bin]; @@ -4404,13 +4403,13 @@ struct SmallObjectPool if (searchStart >= npages) return null; - assert(pagetable[searchStart] == B_FREE); + assert(pagetable[searchStart] == Bins.B_FREE); L1: size_t pn = searchStart; searchStart = binPageChain[searchStart]; binPageChain[pn] = Pool.PageRecovered; - pagetable[pn] = cast(ubyte)bin; + pagetable[pn] = bin; freepages--; // Convert page to free list @@ -4537,7 +4536,7 @@ debug(PRINTF) void printFreeInfo(Pool* pool) nothrow { uint nReallyFree; foreach (i; 0..pool.npages) { - if (pool.pagetable[i] >= B_FREE) nReallyFree++; + if (pool.pagetable[i] >= Bins.B_FREE) nReallyFree++; } printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages); @@ -4770,7 +4769,7 @@ debug (LOGGING) size_t offset = cast(size_t)(p - pool.baseAddr); size_t biti; size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pool.pagetable[pn]; + Bins bin = pool.pagetable[pn]; biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy; debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti); } @@ -4921,7 +4920,7 @@ unittest assert(p + (260 << 20) == q); assert(q + (65 << 20) == r); GC.free(q); - // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q: + // should trigger "assert(bin == Bins.B_FREE);" in mark due to dangling pointer q: GC.collect(); // should trigger "break;" in extendNoSync: size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited) diff --git a/libphobos/libdruntime/core/internal/gc/pooltable.d b/libphobos/libdruntime/core/internal/gc/pooltable.d index 5924f9c1a55..096633825a2 100644 --- a/libphobos/libdruntime/core/internal/gc/pooltable.d +++ b/libphobos/libdruntime/core/internal/gc/pooltable.d @@ -13,15 +13,14 @@ struct PoolTable(Pool) { import core.stdc.string : memmove; -nothrow: - void Dtor() + void Dtor() nothrow @nogc { cstdlib.free(pools); pools = null; npools = 0; } - bool insert(Pool* pool) + bool insert(Pool* pool) nothrow @nogc { auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof); if (!newpools) @@ -51,25 +50,31 @@ nothrow: return true; } - @property size_t length() pure const + @property size_t length() const scope @safe pure nothrow @nogc { return npools; } - ref inout(Pool*) opIndex(size_t idx) inout pure + ref inout(Pool*) opIndex(size_t idx) inout return @trusted pure nothrow @nogc in { assert(idx < length); } do { return pools[idx]; } - inout(Pool*)[] opSlice(size_t a, size_t b) inout pure + inout(Pool*)[] opSlice(size_t a, size_t b) inout return @trusted pure nothrow @nogc in { assert(a <= length && b <= length); } do { return pools[a .. b]; } + /// Returns: A slice over all pools in this `PoolTable` + inout(Pool*)[] opSlice() inout return @trusted pure nothrow @nogc + { + return this.pools[0 .. this.length]; + } + alias opDollar = length; /** @@ -77,7 +82,7 @@ nothrow: * Return null if not in a Pool. * Assume pooltable[] is sorted. */ - Pool *findPool(void *p) nothrow + Pool *findPool(void *p) nothrow @nogc { if (p >= minAddr && p < maxAddr) { @@ -109,7 +114,7 @@ nothrow: } // semi-stable partition, returns right half for which pred is false - Pool*[] minimize() pure + Pool*[] minimize() pure nothrow @nogc { static void swap(ref Pool* a, ref Pool* b) { @@ -151,7 +156,7 @@ nothrow: return pools[npools .. len]; } - void Invariant() const + void Invariant() const nothrow @nogc { if (!npools) return; @@ -165,8 +170,8 @@ nothrow: assert(_maxAddr == pools[npools - 1].topAddr); } - @property const(void)* minAddr() pure const { return _minAddr; } - @property const(void)* maxAddr() pure const { return _maxAddr; } + @property const(void)* minAddr() const @safe pure nothrow @nogc { return _minAddr; } + @property const(void)* maxAddr() const @safe pure nothrow @nogc { return _maxAddr; } package: Pool** pools; @@ -184,7 +189,7 @@ unittest { byte* baseAddr, topAddr; size_t freepages, npages, ptIndex; - @property bool isFree() const pure nothrow { return freepages == npages; } + @property bool isFree() const scope pure nothrow @nogc { return freepages == npages; } } PoolTable!MockPool pooltable; diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d index 2c89472a558..695ef061a81 100644 --- a/libphobos/libdruntime/core/internal/gc/proxy.d +++ b/libphobos/libdruntime/core/internal/gc/proxy.d @@ -209,12 +209,12 @@ extern (C) return instance.query( p ); } - core.memory.GC.Stats gc_stats() nothrow + core.memory.GC.Stats gc_stats() @safe nothrow @nogc { return instance.stats(); } - core.memory.GC.ProfileStats gc_profileStats() nothrow @safe + core.memory.GC.ProfileStats gc_profileStats() @safe nothrow @nogc { return instance.profileStats(); } diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d index 6ba569a241c..f25ba6f1d46 100644 --- a/libphobos/libdruntime/core/memory.d +++ b/libphobos/libdruntime/core/memory.d @@ -133,7 +133,7 @@ private } extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow; - extern (C) GC.Stats gc_stats ( ) nothrow @nogc; + extern (C) GC.Stats gc_stats ( ) @safe nothrow @nogc; extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe; } @@ -766,7 +766,7 @@ extern(D): * Returns runtime stats for currently active GC implementation * See `core.memory.GC.Stats` for list of available metrics. */ - static Stats stats() nothrow + static Stats stats() @safe nothrow @nogc { return gc_stats(); } diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d index 1cdb0f40952..dfec1ec9f7e 100644 --- a/libphobos/libdruntime/core/stdcpp/string.d +++ b/libphobos/libdruntime/core/stdcpp/string.d @@ -343,7 +343,7 @@ extern(D): /// inout(T)* data() inout @safe { return _Get_data()._Myptr; } /// - inout(T)[] as_array() inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } + inout(T)[] as_array() return scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } @@ -1920,7 +1920,7 @@ extern(D): /// inout(T)* data() inout @safe { return __get_pointer(); } /// - inout(T)[] as_array() inout nothrow @trusted { return __get_pointer()[0 .. size()]; } + inout(T)[] as_array() return scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; } @@ -2497,8 +2497,8 @@ extern(C++, (StdNamespace)): extern(D) @safe @nogc: pragma(inline, true) { - ref inout(Alloc) _Getal() inout pure nothrow { return _Mypair._Myval1; } - ref inout(ValTy) _Get_data() inout pure nothrow { return _Mypair._Myval2; } + ref inout(Alloc) _Getal() return inout pure nothrow { return _Mypair._Myval1; } + ref inout(ValTy) _Get_data() return inout pure nothrow { return _Mypair._Myval2; } } void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); } diff --git a/libphobos/libdruntime/core/sys/posix/sys/stat.d b/libphobos/libdruntime/core/sys/posix/sys/stat.d index 22f4df66455..1fb4e44cbbf 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/stat.d +++ b/libphobos/libdruntime/core/sys/posix/sys/stat.d @@ -388,50 +388,93 @@ version (linux) { struct stat_t { - c_ulong st_dev; - ino_t st_ino; + dev_t st_dev; + static if (!__USE_FILE_OFFSET64) + { + ushort __pad1; + ino_t st_ino; + } + else + ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; - c_ulong st_rdev; + dev_t st_rdev; + ushort __pad2; off_t st_size; - c_ulong st_blksize; - c_ulong st_blocks; - c_ulong st_atime; - c_ulong st_atime_nsec; - c_ulong st_mtime; - c_ulong st_mtime_nsec; - c_ulong st_ctime; - c_ulong st_ctime_nsec; + blksize_t st_blksize; + blkcnt_t st_blocks; + static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700) + { + timespec st_atim; + timespec st_mtim; + timespec st_ctim; + extern(D) @safe @property inout pure nothrow + { + ref inout(time_t) st_atime() return { return st_atim.tv_sec; } + ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } + ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } + } + } + else + { + time_t st_atime; + c_ulong st_atimensec; + time_t st_mtime; + c_ulong st_mtimensec; + time_t st_ctime; + c_ulong st_ctimensec; + } c_ulong __unused4; c_ulong __unused5; } + static if (__USE_FILE_OFFSET64) + static assert(stat_t.sizeof == 104); + else + static assert(stat_t.sizeof == 88); } else version (PPC64) { struct stat_t { - c_ulong st_dev; + dev_t st_dev; ino_t st_ino; nlink_t st_nlink; mode_t st_mode; uid_t st_uid; gid_t st_gid; - c_ulong st_rdev; + int __pad2; + dev_t st_rdev; off_t st_size; - c_ulong st_blksize; - c_ulong st_blocks; - c_ulong st_atime; - c_ulong st_atime_nsec; - c_ulong st_mtime; - c_ulong st_mtime_nsec; - c_ulong st_ctime; - c_ulong st_ctime_nsec; + blksize_t st_blksize; + blkcnt_t st_blocks; + static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700) + { + timespec st_atim; + timespec st_mtim; + timespec st_ctim; + extern(D) @safe @property inout pure nothrow + { + ref inout(time_t) st_atime() return { return st_atim.tv_sec; } + ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } + ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } + } + } + else + { + time_t st_atime; + c_ulong st_atimensec; + time_t st_mtime; + c_ulong st_mtimensec; + time_t st_ctime; + c_ulong st_ctimensec; + } c_ulong __unused4; c_ulong __unused5; c_ulong __unused6; } + static assert(stat_t.sizeof == 144); } else version (RISCV_Any) { diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d index 0ddf62f478c..91f218e27fb 100644 --- a/libphobos/libdruntime/core/time.d +++ b/libphobos/libdruntime/core/time.d @@ -496,6 +496,81 @@ assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) == +/ struct Duration { + /++ + Converts this `Duration` to a `string`. + + The string is meant to be human readable, not machine parseable (e.g. + whether there is an `'s'` on the end of the unit name usually depends on + whether it's plural or not, and empty units are not included unless the + Duration is `zero`). Any code needing a specific string format should + use `total` or `split` to get the units needed to create the desired + string format and create the string itself. + + The format returned by toString may or may not change in the future. + + Params: + sink = A sink object, expected to be a delegate or aggregate + implementing `opCall` that accepts a `scope const(char)[]` + as argument. + +/ + void toString (SinkT) (scope SinkT sink) const scope + { + static immutable units = [ + "weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs" + ]; + + static void appListSep(SinkT sink, uint pos, bool last) + { + if (pos == 0) + return; + if (!last) + sink(", "); + else + sink(pos == 1 ? " and " : ", and "); + } + + static void appUnitVal(string units)(SinkT sink, long val) + { + immutable plural = val != 1; + string unit; + static if (units == "seconds") + unit = plural ? "secs" : "sec"; + else static if (units == "msecs") + unit = "ms"; + else static if (units == "usecs") + unit = "μs"; + else + unit = plural ? units : units[0 .. $-1]; + sink(signedToTempString(val)); + sink(" "); + sink(unit); + } + + if (_hnsecs == 0) + { + sink("0 hnsecs"); + return; + } + + long hnsecs = _hnsecs; + uint pos; + static foreach (unit; units) + { + if (auto val = splitUnitsFromHNSecs!unit(hnsecs)) + { + appListSep(sink, pos++, hnsecs == 0); + appUnitVal!unit(sink, val); + } + if (hnsecs == 0) + return; + } + if (hnsecs != 0) + { + appListSep(sink, pos++, true); + appUnitVal!"hnsecs"(sink, hnsecs); + } + } + @safe pure: public: @@ -1539,71 +1614,12 @@ public: } } - - /++ - Converts this `Duration` to a `string`. - - The string is meant to be human readable, not machine parseable (e.g. - whether there is an `'s'` on the end of the unit name usually depends on - whether it's plural or not, and empty units are not included unless the - Duration is `zero`). Any code needing a specific string format should - use `total` or `split` to get the units needed to create the desired - string format and create the string itself. - - The format returned by toString may or may not change in the future. - +/ - string toString() const nothrow pure @safe + /// Ditto + string toString() const scope nothrow { - static void appListSep(ref string res, uint pos, bool last) - { - if (pos == 0) - return; - if (!last) - res ~= ", "; - else - res ~= pos == 1 ? " and " : ", and "; - } - - static void appUnitVal(string units)(ref string res, long val) - { - immutable plural = val != 1; - string unit; - static if (units == "seconds") - unit = plural ? "secs" : "sec"; - else static if (units == "msecs") - unit = "ms"; - else static if (units == "usecs") - unit = "μs"; - else - unit = plural ? units : units[0 .. $-1]; - res ~= signedToTempString(val); - res ~= " "; - res ~= unit; - } - - if (_hnsecs == 0) - return "0 hnsecs"; - - template TT(T...) { alias T TT; } - alias units = TT!("weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs"); - - long hnsecs = _hnsecs; string res; uint pos; - foreach (unit; units) - { - if (auto val = splitUnitsFromHNSecs!unit(hnsecs)) - { - appListSep(res, pos++, hnsecs == 0); - appUnitVal!unit(res, val); - } - if (hnsecs == 0) - break; - } - if (hnsecs != 0) - { - appListSep(res, pos++, true); - appUnitVal!"hnsecs"(res, hnsecs); - } - return res; + string result; + this.toString((in char[] data) { result ~= data; }); + return result; } /// @@ -1731,6 +1747,20 @@ unittest assert(myTime == 123.msecs); } +// Ensure `toString` doesn't allocate if the sink doesn't +version (CoreUnittest) @safe pure nothrow @nogc unittest +{ + char[256] buffer; size_t len; + scope sink = (in char[] data) { + assert(data.length + len <= buffer.length); + buffer[len .. len + data.length] = data[]; + len += data.length; + }; + auto dur = Duration(-12_096_020_900_003); + dur.toString(sink); + assert(buffer[0 .. len] == "-2 weeks, -2 secs, -90 ms, and -3 hnsecs"); +} + /++ Converts a $(D TickDuration) to the given units as either an integral value or a floating point value. diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index cf7cf967aa6..56a2efe735a 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -2649,13 +2649,18 @@ class Throwable : Object /** * Get the message describing the error. - * Base behavior is to return the `Throwable.msg` field. - * Override to return some other error message. + * + * This getter is an alternative way to access the Exception's message, + * with the added advantage of being override-able in subclasses. + * Subclasses are hence free to do their own memory managements without + * being tied to the requirement of providing a `string` in a field. + * + * The default behavior is to return the `Throwable.msg` field. * * Returns: - * Error message + * A message representing the cause of the `Throwable` */ - @__future const(char)[] message() const + @__future const(char)[] message() const @safe nothrow { return this.msg; } diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index b5b939f41b3..e15541e181d 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -1a3e80ec25afab6123cdcfe20186f36f006b68bb +41aaf8c2636df0e2e3ad39933b321d2b4cd231fa The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index a99c5170afb..b09b82ab85e 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -425,10 +425,10 @@ version (Windows) private void[] readImpl(scope const(char)[] name, scope const( fileSize = makeUlong(sizeLow, sizeHigh); return result; } - static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) + static trustedReadFile(HANDLE hFile, void *lpBuffer, size_t nNumberOfBytesToRead) { // Read by chunks of size < 4GB (Windows API limit) - ulong totalNumRead = 0; + size_t totalNumRead = 0; while (totalNumRead != nNumberOfBytesToRead) { const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000); diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d index 482aae6306a..c1c5cd2b36e 100644 --- a/libphobos/src/std/getopt.d +++ b/libphobos/src/std/getopt.d @@ -438,7 +438,7 @@ GetoptResult getopt(T...)(ref string[] args, T opts) } /// -@system unittest +@safe unittest { auto args = ["prog", "--foo", "-b"]; @@ -1646,11 +1646,13 @@ Params: text = The text to printed at the beginning of the help output. opt = The `Option` extracted from the `getopt` parameter. */ -void defaultGetoptPrinter(string text, Option[] opt) +void defaultGetoptPrinter(string text, Option[] opt) @safe { import std.stdio : stdout; + // stdout global __gshared is trusted with a locked text writer + auto w = (() @trusted => stdout.lockingTextWriter())(); - defaultGetoptFormatter(stdout.lockingTextWriter(), text, opt); + defaultGetoptFormatter(w, text, opt); } /** This function writes the passed text and `Option` into an output range diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d index c092a9d0946..31f58fa5fa9 100644 --- a/libphobos/src/std/range/primitives.d +++ b/libphobos/src/std/range/primitives.d @@ -2055,9 +2055,14 @@ if (isBidirectionalRange!Range) } /** - Moves the front of `r` out and returns it. Leaves `r.front` in a - destroyable state that does not allocate any resources (usually equal - to its `.init` value). + Moves the front of `r` out and returns it. + + If `r.front` is a struct with a destructor or copy constructor defined, it + is reset to its `.init` value after its value is moved. Otherwise, it is + left unchanged. + + In either case, `r.front` is left in a destroyable state that does not + allocate any resources. */ ElementType!R moveFront(R)(R r) { diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d index 5e35a6b9cd5..0dd636ea67b 100644 --- a/libphobos/src/std/sumtype.d +++ b/libphobos/src/std/sumtype.d @@ -533,15 +533,35 @@ public: /** * Assigns a value to a `SumType`. * - * Assigning to a `SumType` is `@system` if any of the - * `SumType`'s members contain pointers or references, since - * those members may be reachable through external references, - * and overwriting them could therefore lead to memory - * corruption. + * If any of the `SumType`'s members other than the one being assigned + * to contain pointers or references, it is possible for the assignment + * to cause memory corruption (see the + * ["Memory corruption" example](#memory-corruption) below for an + * illustration of how). Therefore, such assignments are considered + * `@system`. * * An individual assignment can be `@trusted` if the caller can - * guarantee that there are no outstanding references to $(I any) - * of the `SumType`'s members when the assignment occurs. + * guarantee that there are no outstanding references to any `SumType` + * members that contain pointers or references at the time the + * assignment occurs. + * + * Examples: + * + * $(DIVID memory-corruption, $(H3 Memory corruption)) + * + * This example shows how assignment to a `SumType` can be used to + * cause memory corruption in `@system` code. In `@safe` code, the + * assignment `s = 123` would not be allowed. + * + * --- + * SumType!(int*, int) s = new int; + * s.tryMatch!( + * (ref int* p) { + * s = 123; // overwrites `p` + * return *p; // undefined behavior + * } + * ); + * --- */ ref SumType opAssign(T rhs); } @@ -553,14 +573,35 @@ public: /** * Assigns a value to a `SumType`. * - * Assigning to a `SumType` is `@system` if any of the `SumType`'s - * $(I other) members contain pointers or references, since those - * members may be reachable through external references, and - * overwriting them could therefore lead to memory corruption. + * If any of the `SumType`'s members other than the one being assigned + * to contain pointers or references, it is possible for the assignment + * to cause memory corruption (see the + * ["Memory corruption" example](#memory-corruption) below for an + * illustration of how). Therefore, such assignments are considered + * `@system`. * * An individual assignment can be `@trusted` if the caller can - * guarantee that, when the assignment occurs, there are no - * outstanding references to any such members. + * guarantee that there are no outstanding references to any `SumType` + * members that contain pointers or references at the time the + * assignment occurs. + * + * Examples: + * + * $(DIVID memory-corruption, $(H3 Memory corruption)) + * + * This example shows how assignment to a `SumType` can be used to + * cause memory corruption in `@system` code. In `@safe` code, the + * assignment `s = 123` would not be allowed. + * + * --- + * SumType!(int*, int) s = new int; + * s.tryMatch!( + * (ref int* p) { + * s = 123; // overwrites `p` + * return *p; // undefined behavior + * } + * ); + * --- */ ref SumType opAssign(T rhs) { @@ -1528,7 +1569,27 @@ private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...); } /// True if `T` is a [SumType] or implicitly converts to one, otherwise false. -enum bool isSumType(T) = is(T : SumType!Args, Args...); +template isSumType(T) +{ + static if (is(T : SumType!Args, Args...)) + { + enum isSumType = true; + } + else static if (is(T == struct) && __traits(getAliasThis, T).length > 0) + { + // Workaround for https://issues.dlang.org/show_bug.cgi?id=21975 + import std.traits : ReturnType; + + alias AliasThisType = ReturnType!((T t) => + __traits(getMember, t, __traits(getAliasThis, T)[0]) + ); + enum isSumType = .isSumType!AliasThisType; + } + else + { + enum isSumType = false; + } +} /// @safe unittest @@ -1549,6 +1610,25 @@ enum bool isSumType(T) = is(T : SumType!Args, Args...); assert(!isSumType!ContainsSumType); } +@safe unittest +{ + static struct AliasThisVar(T) + { + SumType!T payload; + alias payload this; + } + + static struct AliasThisFunc(T) + { + SumType!T payload; + ref get() { return payload; } + alias get this; + } + + static assert(isSumType!(AliasThisVar!int)); + static assert(isSumType!(AliasThisFunc!int)); +} + /** * Calls a type-appropriate function with the value held in a [SumType]. *