From patchwork Wed Jul 6 19:06:07 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Iain Buclaw X-Patchwork-Id: 1653117 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=IvA+veSk; 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 (2048 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4LdTYS6d2Zz9s07 for ; Thu, 7 Jul 2022 05:06:52 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0D7AE3858438 for ; Wed, 6 Jul 2022 19:06:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0D7AE3858438 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1657134409; bh=pBNjftFQUMBE1e2NrBq5Dsev/edXUcJ0tNdMfnkMQNs=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=IvA+veSkJfa9k1rogGSxiEB3kvmKl9NdhVqlhc1t+u76weBUvjTOEyV7ruaozZebH HkF0DoYsDeDYiVlEG+uApQm+O5rMjpyzMySQqTsY/P0e9GAxy+OYexnBPyyrsFsKkJ uzejg3DRk20zA1FlhuB6durFwPj/XQWk3sQfGMQI= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) by sourceware.org (Postfix) with ESMTPS id 726B33858D28 for ; Wed, 6 Jul 2022 19:06:19 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 726B33858D28 Received: from smtp202.mailbox.org (smtp202.mailbox.org [10.196.197.202]) (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-101.mailbox.org (Postfix) with ESMTPS id 4LdTXh4lvlz9sYX; Wed, 6 Jul 2022 21:06:12 +0200 (CEST) To: gcc-patches@gcc.gnu.org Subject: [committed] d: Merge upstream dmd 56589f0f4, druntime 651389b5, phobos 1516ecad9. Date: Wed, 6 Jul 2022 21:06:07 +0200 Message-Id: <20220706190607.1517849-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 X-Spam-Status: No, score=-13.7 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_FILL_THIS_FORM_SHORT, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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 with upstream dmd 56589f0f4, and the standard library with druntime 651389b5 and phobos 1516ecad9. D front-end changes: - Import latest bug fixes to mainline. D runtime changes: - Import latest bug fixes to mainline. Phobos changes: - Import latest bug fixes to mainline. Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, and committed to mainline. Regards, Iain. --- gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 56589f0f4. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 651389b5. * src/MERGE: Merge upstream phobos 1516ecad9. --- gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/cparse.d | 118 +++- gcc/d/dmd/dmodule.d | 2 +- gcc/d/dmd/expressionsem.d | 8 + gcc/d/dmd/globals.d | 2 +- gcc/d/dmd/globals.h | 2 +- gcc/d/dmd/hdrgen.d | 17 +- gcc/d/dmd/mtype.d | 15 +- gcc/d/dmd/tokens.d | 5 +- gcc/d/dmd/tokens.h | 1 + gcc/d/dmd/typesem.d | 9 +- gcc/testsuite/gdc.test/compilable/test3004.d | 4 +- gcc/testsuite/gdc.test/compilable/vcg-ast.d | 3 + .../gdc.test/fail_compilation/diag_in_array.d | 20 + libphobos/libdruntime/MERGE | 2 +- .../libdruntime/core/internal/parseoptions.d | 17 + libphobos/libdruntime/core/thread/osthread.d | 9 + libphobos/libdruntime/rt/aApply.d | 108 ++- libphobos/libdruntime/rt/aApplyR.d | 71 +- libphobos/libdruntime/rt/aaA.d | 39 +- libphobos/libdruntime/rt/arrayassign.d | 83 ++- libphobos/libdruntime/rt/lifetime.d | 378 +++++++--- libphobos/src/MERGE | 2 +- libphobos/src/std/complex.d | 4 +- libphobos/src/std/file.d | 35 +- libphobos/src/std/math/exponential.d | 648 +++++++++++------- 26 files changed, 1115 insertions(+), 489 deletions(-) create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag_in_array.d diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index f5c42f0ff00..8324c1cc88c 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -529110f66d7d301d62d943a4e4482edaddeb46ea +56589f0f4d724c1c8022c57509a243f16a04228a 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/cparse.d b/gcc/d/dmd/cparse.d index dff76345fa5..a3bebb7365a 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -1619,6 +1619,12 @@ final class CParser(AST) : Parser!AST return; } + if (token.value == TOK.__pragma) + { + uupragmaDirective(scanloc); + return; + } + if (token.value == TOK._import) // import declaration extension { auto a = parseImport(); @@ -2322,6 +2328,14 @@ final class CParser(AST) : Parser!AST break; } + case TOK.__declspec: + { + /* Microsoft extension + */ + cparseDeclspec(specifier); + break; + } + case TOK.typeof_: { nextToken(); @@ -3042,9 +3056,13 @@ final class CParser(AST) : Parser!AST * extended-decl-modifier: * dllimport * dllexport + * noreturn + * Params: + * specifier = filled in with the attribute(s) */ - private void cparseDeclspec() + private void cparseDeclspec(ref Specifier specifier) { + //printf("cparseDeclspec()\n"); /* Check for dllexport, dllimport * Ignore the rest */ @@ -3073,6 +3091,11 @@ final class CParser(AST) : Parser!AST dllexport = true; nextToken(); } + else if (token.ident == Id.noreturn) + { + specifier.noreturn = true; + nextToken(); + } else { nextToken(); @@ -3083,8 +3106,8 @@ final class CParser(AST) : Parser!AST else { error("extended-decl-modifier expected"); + break; } - break; } } @@ -4789,6 +4812,8 @@ final class CParser(AST) : Parser!AST // type function itself. if (auto tf = t.isTypeFunction()) tf.next = tf.next.addSTC(STC.const_); + else if (auto tt = t.isTypeTag()) + tt.mod |= MODFlags.const_; else t = t.addSTC(STC.const_); return t; @@ -4961,10 +4986,40 @@ final class CParser(AST) : Parser!AST return true; } } - error("C preprocessor directive `#%s` is not supported", n.toChars()); + if (n.ident != Id.undef) + error("C preprocessor directive `#%s` is not supported", n.toChars()); return false; } + /********************************************* + * VC __pragma + * https://docs.microsoft.com/en-us/cpp/preprocessor/pragma-directives-and-the-pragma-keyword?view=msvc-170 + * Scanner is on the `__pragma` + * Params: + * startloc = location to use for error messages + */ + private void uupragmaDirective(const ref Loc startloc) + { + const loc = startloc; + nextToken(); + if (token.value != TOK.leftParenthesis) + { + error(loc, "left parenthesis expected to follow `__pragma`"); + return; + } + nextToken(); + if (token.value == TOK.identifier && token.ident == Id.pack) + pragmaPack(startloc, false); + else + error(loc, "unrecognized __pragma"); + if (token.value != TOK.rightParenthesis) + { + error(loc, "right parenthesis expected to close `__pragma(...)`"); + return; + } + nextToken(); + } + /********************************************* * C11 6.10.6 Pragma directive * # pragma pp-tokens(opt) new-line @@ -4977,7 +5032,7 @@ final class CParser(AST) : Parser!AST Token n; scan(&n); if (n.value == TOK.identifier && n.ident == Id.pack) - return pragmaPack(loc); + return pragmaPack(loc, true); if (n.value != TOK.endOfLine) skipToNextLine(); } @@ -4989,10 +5044,27 @@ final class CParser(AST) : Parser!AST * Scanner is on the `pack` * Params: * startloc = location to use for error messages + * useScan = use scan() to retrieve next token, instead of nextToken() */ - private void pragmaPack(const ref Loc startloc) + private void pragmaPack(const ref Loc startloc, bool useScan) { const loc = startloc; + + /* Pull tokens from scan() or nextToken() + */ + void scan(Token* t) + { + if (useScan) + { + Lexer.scan(t); + } + else + { + nextToken(); + *t = token; + } + } + Token n; scan(&n); if (n.value != TOK.leftParenthesis) @@ -5155,13 +5227,35 @@ final class CParser(AST) : Parser!AST { if (!defines || defines.length < 10) // minimum length of a #define line return; - const length = defines.length; - defines.writeByte(0); - auto slice = defines.peekChars()[0 .. length]; + OutBuffer* buf = defines; + defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence() + // from appending to slice[] + const length = buf.length; + buf.writeByte(0); + auto slice = buf.peekChars()[0 .. length]; resetDefineLines(slice); // reset lexer const(char)* endp = &slice[length - 7]; + size_t[void*] defineTab; // hash table of #define's turned into Symbol's + // indexed by Identifier, returns index into symbols[] + // The memory for this is leaked + + void addVar(AST.VarDeclaration v) + { + /* If it's already defined, replace the earlier + * definition + */ + if (size_t* pd = cast(void*)v.ident in defineTab) + { + //printf("replacing %s\n", v.toChars()); + (*symbols)[*pd] = v; + return; + } + defineTab[cast(void*)v.ident] = symbols.length; + symbols.push(v); + } + Token n; while (p < endp) @@ -5200,7 +5294,7 @@ final class CParser(AST) : Parser!AST */ AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); - symbols.push(v); + addVar(v); nextDefineLine(); continue; } @@ -5223,7 +5317,7 @@ final class CParser(AST) : Parser!AST */ AST.Expression e = new AST.RealExp(scanloc, floatvalue, t); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); - symbols.push(v); + addVar(v); nextDefineLine(); continue; } @@ -5241,7 +5335,7 @@ final class CParser(AST) : Parser!AST */ AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix); auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest); - symbols.push(v); + addVar(v); nextDefineLine(); continue; } @@ -5263,6 +5357,8 @@ final class CParser(AST) : Parser!AST } nextDefineLine(); } + + defines = buf; } //} diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index f8e5073a5f1..0be938ffdf5 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -680,7 +680,7 @@ extern (C++) final class Module : Package FileName.equalsExt(srcfile.toString(), c_ext) && FileName.exists(srcfile.toString())) { - filename = global.preprocess(srcfile, loc, global.params.cppswitches, ifile, &defines); // run C preprocessor + filename = global.preprocess(srcfile, loc, ifile, &defines); // run C preprocessor } if (auto result = global.fileManager.lookup(filename)) diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 99e003b1856..35ba5fa83ed 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -11689,6 +11689,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor case Terror: return setError(); + case Tarray, Tsarray: + result = exp.incompatibleTypes(); + exp.errorSupplemental("`in` is only allowed on associative arrays"); + const(char)* slice = (t2b.ty == Tsarray) ? "[]" : ""; + exp.errorSupplemental("perhaps use `std.algorithm.find(%s, %s%s)` instead", + exp.e1.toChars(), exp.e2.toChars(), slice); + return; + default: result = exp.incompatibleTypes(); return; diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 3766a0be07d..05886f96f44 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -299,7 +299,7 @@ extern (C++) struct Global enum recursionLimit = 500; /// number of recursive template expansions before abort - extern (C++) FileName function(FileName, ref const Loc, ref Array!(const(char)*) cppswitches, out bool, OutBuffer* defines) preprocess; + extern (C++) FileName function(FileName, ref const Loc, out bool, OutBuffer*) preprocess; nothrow: diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 07298ddb792..41472b27348 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -272,7 +272,7 @@ struct Global FileManager* fileManager; - FileName (*preprocess)(FileName, const Loc&, Array& cppswitches, bool&, OutBuffer&); + FileName (*preprocess)(FileName, const Loc&, bool&, OutBuffer&); /* Start gagging. Return the current number of gagged errors */ diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index fcc9b61b54a..680d9c82a96 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -1449,7 +1449,20 @@ public: buf.writestring(" = "); if (stcToBuffer(buf, d.storage_class)) buf.writeByte(' '); - d.aliassym.accept(this); + /* + https://issues.dlang.org/show_bug.cgi?id=23223 + https://issues.dlang.org/show_bug.cgi?id=23222 + This special case (initially just for modules) avoids some segfaults + and nicer -vcg-ast output. + */ + if (d.aliassym.isModule()) + { + buf.writestring(d.aliassym.ident.toString()); + } + else + { + d.aliassym.accept(this); + } } else if (d.type.ty == Tfunction) { @@ -3916,6 +3929,8 @@ private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs) void visitTag(TypeTag t) { + if (t.mod & MODFlags.const_) + buf.writestring("const "); buf.writestring(Token.toChars(t.tok)); buf.writeByte(' '); if (t.id) diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 6b5389dfebb..860cfa9d611 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -4437,15 +4437,7 @@ extern (C++) final class TypeFunction : TypeNext // Check escaping through `this` if (tthis && tthis.isMutable()) { - auto tb = tthis.toBasetype(); - AggregateDeclaration ad; - if (auto tc = tb.isTypeClass()) - ad = tc.sym; - else if (auto ts = tb.isTypeStruct()) - ad = ts.sym; - else - assert(0); - foreach (VarDeclaration v; ad.fields) + foreach (VarDeclaration v; isAggregate(tthis).fields) { if (v.hasPointers()) return stc; @@ -6655,16 +6647,18 @@ extern (C++) final class TypeTag : Type Type resolved; /// type after semantic() in case there are more others /// pointing to this instance, which can happen with /// struct S { int a; } s1, *s2; + MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment) extern (D) this(const ref Loc loc, TOK tok, Identifier id, Type base, Dsymbols* members) { - //printf("TypeTag %p\n", this); + //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); super(Ttag); this.loc = loc; this.tok = tok; this.id = id; this.base = base; this.members = members; + this.mod = 0; } override const(char)* kind() const @@ -6674,6 +6668,7 @@ extern (C++) final class TypeTag : Type override TypeTag syntaxCopy() { + //printf("TypeTag syntaxCopy()\n"); // No semantic analysis done, no need to copy return this; } diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index 170a5344d2b..86abedf7660 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -273,6 +273,7 @@ enum TOK : ubyte __cdecl, __declspec, __stdcall, + __pragma, __attribute__, } @@ -582,6 +583,7 @@ private immutable TOK[] keywords = TOK.__cdecl, TOK.__declspec, TOK.__stdcall, + TOK.__pragma, TOK.__attribute__, ]; @@ -610,7 +612,7 @@ static immutable TOK[TOK.max + 1] Ckeywords = restrict, return_, int16, signed, sizeof_, static_, struct_, switch_, typedef_, union_, unsigned, void_, volatile, while_, asm_, typeof_, _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, - _Static_assert, _Thread_local, _import, __cdecl, __declspec, __stdcall, __attribute__ ]; + _Static_assert, _Thread_local, _import, __cdecl, __declspec, __stdcall, __pragma, __attribute__ ]; foreach (kw; Ckwds) tab[kw] = cast(TOK) kw; @@ -880,6 +882,7 @@ extern (C++) struct Token TOK.__cdecl : "__cdecl", TOK.__declspec : "__declspec", TOK.__stdcall : "__stdcall", + TOK.__pragma : "__pragma", TOK.__attribute__ : "__attribute__", ]; diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index f9b606256bc..35fd68b0ba5 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -282,6 +282,7 @@ enum class TOK : unsigned char cdecl_, declspec, stdcall, + pragma, attribute__, MAX, diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 8cacdb10a26..0469b927316 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -1778,8 +1778,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { /* struct S s, *p; */ - //printf("already resolved\n"); - return mtype.resolved; + return mtype.resolved.addSTC(mtype.mod); } /* Find the current scope by skipping tag scopes. @@ -1850,7 +1849,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { mtype.id = Identifier.generateId("__tag"[]); declareTag(); - return mtype.resolved; + return mtype.resolved.addSTC(mtype.mod); } /* look for pre-existing declaration @@ -1863,7 +1862,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (mtype.tok == TOK.enum_ && !mtype.members) .error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3 declareTag(); - return mtype.resolved; + return mtype.resolved.addSTC(mtype.mod); } /* A redeclaration only happens if both declarations are in @@ -1963,7 +1962,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) declareTag(); } } - return mtype.resolved; + return mtype.resolved.addSTC(mtype.mod); } switch (type.ty) diff --git a/gcc/testsuite/gdc.test/compilable/test3004.d b/gcc/testsuite/gdc.test/compilable/test3004.d index 9912b88e5ff..23ec46f2905 100644 --- a/gcc/testsuite/gdc.test/compilable/test3004.d +++ b/gcc/testsuite/gdc.test/compilable/test3004.d @@ -1,15 +1,13 @@ // https://issues.dlang.org/show_bug.cgi?id=3004 /* REQUIRED_ARGS: -ignore -v -TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|semantic|entry|library|function object|\s*$)") +TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|semantic|entry|library|function object|function core|\s*$)") TEST_OUTPUT: --- pragma GNU_attribute (__error) pragma GNU_attribute (__error) code test3004 function test3004.test -function core.internal.array.appending._d_arrayappendcTXImpl!(char[], char)._d_arrayappendcTX -function core.internal.array.utils._d_HookTraceImpl!(char[], _d_arrayappendcTX, "Cannot append to array if compiling without support for runtime type information!")._d_HookTraceImpl --- */ diff --git a/gcc/testsuite/gdc.test/compilable/vcg-ast.d b/gcc/testsuite/gdc.test/compilable/vcg-ast.d index cbb150cb1d5..4a7b8bc33c4 100644 --- a/gcc/testsuite/gdc.test/compilable/vcg-ast.d +++ b/gcc/testsuite/gdc.test/compilable/vcg-ast.d @@ -7,6 +7,9 @@ TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg module vcg; +alias xyz = __traits(parent, {}); +alias named = vcg; + template Seq(A...) { alias Seq = A; diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_in_array.d b/gcc/testsuite/gdc.test/fail_compilation/diag_in_array.d new file mode 100644 index 00000000000..f869b32856c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag_in_array.d @@ -0,0 +1,20 @@ +/** +TEST_OUTPUT: +--- +fail_compilation/diag_in_array.d(17): Error: incompatible types for `(3) in (a)`: `int` and `int[4]` +fail_compilation/diag_in_array.d(17): `in` is only allowed on associative arrays +fail_compilation/diag_in_array.d(17): perhaps use `std.algorithm.find(3, a[])` instead +fail_compilation/diag_in_array.d(19): Error: incompatible types for `("s") in (b)`: `string` and `string[]` +fail_compilation/diag_in_array.d(19): `in` is only allowed on associative arrays +fail_compilation/diag_in_array.d(19): perhaps use `std.algorithm.find("s", b)` instead +--- +*/ + +void main() +{ + int[4] a; + string[] b; + if (3 in a) + return; + auto c = "s" in b; +} diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 2fc1bc1cf6a..6e25a9d6f92 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -148608b7935c3f9a4ea3a26f74cb90cd07efc91c +651389b52243dcadb338dd0c14dd27e7850cda8d 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/internal/parseoptions.d b/libphobos/libdruntime/core/internal/parseoptions.d index 99204432374..ed6251f758b 100644 --- a/libphobos/libdruntime/core/internal/parseoptions.d +++ b/libphobos/libdruntime/core/internal/parseoptions.d @@ -168,6 +168,7 @@ inout(char)[] find(alias pred)(inout(char)[] str) } bool parse(T : size_t)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName, bool mayHaveSuffix = false) +if (is(T == size_t)) in { assert(str.length); } do { @@ -242,6 +243,22 @@ do if (v > res.max) return parseError("a number " ~ T.max.stringof ~ " or below", optname, str[0 .. i], errName); str = str[i .. $]; + res = v; + return true; +} + +bool parse(T : size_t)(const(char)[] optname, ref inout(char)[] str, ref T res, const(char)[] errName, bool mayHaveSuffix = false) +if (!is(T == size_t)) +in { assert(str.length); } +do +{ + const oldStr = str; + size_t v; + if (!parse!size_t(optname, str, v, errName, mayHaveSuffix)) + return false; + + if (v > res.max) + return parseError("a number " ~ T.max.stringof ~ " or below", optname, oldStr[0 .. $-str.length], errName); res = cast(T) v; return true; } diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d index 1165320f8f8..415430c80e1 100644 --- a/libphobos/libdruntime/core/thread/osthread.d +++ b/libphobos/libdruntime/core/thread/osthread.d @@ -2130,6 +2130,15 @@ extern (C) void thread_init() @nogc nothrow } else version (Posix) { + version (OpenBSD) + { + // OpenBSD does not support SIGRTMIN or SIGRTMAX + // Use SIGUSR1 for SIGRTMIN, SIGUSR2 for SIGRTMIN + 1 + // And use 32 for SIGRTMAX (32 is the max signal number on OpenBSD) + enum SIGRTMIN = SIGUSR1; + enum SIGRTMAX = 32; + } + if ( suspendSignalNumber == 0 ) { suspendSignalNumber = SIGRTMIN; diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d index bea441f550a..5d5ddb34740 100644 --- a/libphobos/libdruntime/rt/aApply.d +++ b/libphobos/libdruntime/rt/aApply.d @@ -1,7 +1,5 @@ /** - * This code handles decoding UTF strings for foreach loops. There are 6 - * combinations of conversions between char, wchar, and dchar, and 2 of each - * of those. + * This code handles decoding UTF strings for foreach loops. * * Copyright: Copyright Digital Mars 2004 - 2010. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). @@ -15,9 +13,64 @@ import core.internal.utf : decode, toUTF8; /**********************************************/ /* 1 argument versions */ -// dg is D, but _aApplycd() is C -extern (D) alias int delegate(void *) dg_t; +/** +Delegate type corresponding to transformed loop body + +The parameter is a pointer to the current `char`, `wchar` or `dchar` + +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg_t = int delegate(void* c); + +// Note: dg is extern(D), but _aApplycd() is extern(C) + +/** +Loop over a string while changing the UTF encoding + +There are 6 combinations of conversions between `char`, `wchar`, and `dchar`, +and 2 of each of those. + +The naming convention is as follows: + +_aApply{c,d,w}{c,d,w}{1,2} +The first letter corresponds to the input string encoding, and the second letter corresponds to the target character type. + +- c = `char` +- w = `wchar` +- d = `dchar` + +The `1` variant only produces the character, the `2` variant also produces a loop index. + +Examples: +--- +void main() +{ + string str; + wtring wstr; + dstring dstr; + + foreach (dchar c; str) {} + // _aApplycd1 + + foreach (wchar c; dstr) {} + // _aApplydw1 + + foreach (i, wchar c; str) {} + // _aApplycw2 + + foreach (wchar w; wstr) {} + // no conversion +} +--- + +Params: + aa = input string + dg = foreach body transformed into a delegate, similar to `opApply` + +Returns: + non-zero when the loop was exited through a `break` +*/ extern (C) int _aApplycd1(in char[] aa, dg_t dg) { int result; @@ -78,8 +131,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplywd1(in wchar[] aa, dg_t dg) { int result; @@ -140,8 +192,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplycw1(in char[] aa, dg_t dg) { int result; @@ -215,8 +266,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplywc1(in wchar[] aa, dg_t dg) { int result; @@ -296,8 +346,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydc1(in dchar[] aa, dg_t dg) { int result; @@ -373,8 +422,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydw1(in dchar[] aa, dg_t dg) { int result; @@ -446,9 +494,20 @@ unittest /****************************************************************************/ /* 2 argument versions */ -// dg is D, but _aApplycd2() is C -extern (D) alias int delegate(void *, void *) dg2_t; +/** +Delegate type corresponding to transformed loop body + +Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`. +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg2_t = int delegate(void* i, void* c); + +// Note: dg is extern(D), but _aApplycd2() is extern(C) + +/** +Variants of _aApplyXXX that include a loop index. +*/ extern (C) int _aApplycd2(in char[] aa, dg2_t dg) { int result; @@ -516,8 +575,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg) { int result; @@ -585,8 +643,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplycw2(in char[] aa, dg2_t dg) { int result; @@ -665,8 +722,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg) { int result; @@ -751,8 +807,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg) { int result; @@ -832,8 +887,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg) { int result; diff --git a/libphobos/libdruntime/rt/aApplyR.d b/libphobos/libdruntime/rt/aApplyR.d index 7f19fa81528..ce3bb9eaf70 100644 --- a/libphobos/libdruntime/rt/aApplyR.d +++ b/libphobos/libdruntime/rt/aApplyR.d @@ -1,7 +1,5 @@ /** - * This code handles decoding UTF strings for foreach_reverse loops. There are - * 6 combinations of conversions between char, wchar, and dchar, and 2 of each - * of those. + * This code handles decoding UTF strings for `foreach_reverse` loops. * * Copyright: Copyright Digital Mars 2004 - 2010. * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). @@ -15,9 +13,27 @@ import core.internal.utf; /**********************************************/ /* 1 argument versions */ -// dg is D, but _aApplyRcd() is C -extern (D) alias int delegate(void *) dg_t; +// Note: dg is extern(D), but _aApplyRcd() is extern(C) +/** +Delegate type corresponding to transformed loop body + +The parameter is a pointer to the current `char`, `wchar` or `dchar` + +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg_t = int delegate(void* c); + +/** +Same as `_aApplyXXX` functions, but for `foreach_reverse` + +Params: + aa = input string + dg = foreach body transformed into a delegate, similar to `opApply` + +Returns: + non-zero when the loop was exited through a `break` +*/ extern (C) int _aApplyRcd1(in char[] aa, dg_t dg) { int result; @@ -90,8 +106,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg) { int result; @@ -154,8 +169,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRcw1(in char[] aa, dg_t dg) { int result; @@ -241,8 +255,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg) { int result; @@ -326,8 +339,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg) { int result; @@ -405,8 +417,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg) { int result; @@ -477,9 +488,20 @@ unittest /****************************************************************************/ /* 2 argument versions */ -// dg is D, but _aApplyRcd2() is C -extern (D) alias int delegate(void *, void *) dg2_t; +/** +Delegate type corresponding to transformed loop body + +Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`. +Returns: non-zero when a `break` statement is hit +*/ +extern (D) alias dg2_t = int delegate(void* i, void* c); + +// Note: dg is extern(D), but _aApplyRcd2() is extern(C) + +/** +Variants of _aApplyRXXX that include a loop index. +*/ extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg) { int result; size_t i; @@ -555,8 +577,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg) { int result; @@ -621,8 +642,7 @@ unittest assert(i == 4); } -/*****************************/ - +/// ditto extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg) { int result; @@ -710,8 +730,7 @@ unittest assert(i == 5); } -/*****************************/ - +/// ditto extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg) { int result; @@ -797,8 +816,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg) { int result; @@ -877,8 +895,7 @@ unittest assert(i == 9); } -/*****************************/ - +/// ditto extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg) { int result; diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d index ab93f191634..f264b014789 100644 --- a/libphobos/libdruntime/rt/aaA.d +++ b/libphobos/libdruntime/rt/aaA.d @@ -50,7 +50,7 @@ struct AA private struct Impl { private: - this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) + this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow { keysz = cast(uint) ti.key.tsize; valsz = cast(uint) ti.value.tsize; @@ -125,7 +125,7 @@ private: } } - void grow(scope const TypeInfo keyti) + void grow(scope const TypeInfo keyti) pure nothrow { // If there are so many deleted entries, that growing would push us // below the shrink threshold, we just purge deleted entries instead. @@ -135,7 +135,7 @@ private: resize(GROW_FAC * dim); } - void shrink(scope const TypeInfo keyti) + void shrink(scope const TypeInfo keyti) pure nothrow { if (dim > INIT_NUM_BUCKETS) resize(dim / GROW_FAC); @@ -233,7 +233,7 @@ package void entryDtor(void* p, const TypeInfo_Struct sti) extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign)); } -private bool hasDtor(const TypeInfo ti) +private bool hasDtor(const TypeInfo ti) pure nothrow { import rt.lifetime : unqualify; @@ -246,7 +246,7 @@ private bool hasDtor(const TypeInfo ti) return false; } -private immutable(void)* getRTInfo(const TypeInfo ti) +private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow { // classes are references const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class); @@ -254,7 +254,7 @@ private immutable(void)* getRTInfo(const TypeInfo ti) } // build type info for Entry with additional key and value fields -TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) +TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) nothrow { import rt.lifetime : unqualify; @@ -319,7 +319,8 @@ TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo va } // build appropriate RTInfo at runtime -immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) +immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, + immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) pure nothrow { enum bitsPerWord = 8 * size_t.sizeof; @@ -456,7 +457,7 @@ private size_t mix(size_t h) @safe pure nothrow @nogc return h; } -private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) +private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) nothrow { immutable hash = keyti.getHash(pkey); // highest bit is set to distinguish empty/deleted from filled buckets @@ -485,6 +486,18 @@ pure nothrow @nogc unittest // API Implementation //------------------------------------------------------------------------------ +/** Allocate associative array data. + * Called for `new SomeAA` expression. + * Params: + * ti = TypeInfo for the associative array + * Returns: + * A new associative array. + */ +extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti) +{ + return new Impl(ti); +} + /// Determine number of entries in associative array. extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc { @@ -736,7 +749,15 @@ extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg) return 0; } -/// Construct an associative array of type ti from keys and value +/** Construct an associative array of type ti from corresponding keys and values. + * Called for an AA literal `[k1:v1, k2:v2]`. + * Params: + * ti = TypeInfo for the associative array + * keys = array of keys + * vals = array of values + * Returns: + * A new associative array opaque pointer, or null if `keys` is empty. + */ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals) { diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d index 9a34ec750f1..c9db2fc2c89 100644 --- a/libphobos/libdruntime/rt/arrayassign.d +++ b/libphobos/libdruntime/rt/arrayassign.d @@ -19,8 +19,10 @@ private debug(PRINTF) import core.stdc.stdio; } -/** - * Keep for backward binary compatibility. This function can be removed in the future. +/* + * Superseded array assignment hook. Does not take into account destructors: + * https://issues.dlang.org/show_bug.cgi?id=13661 + * Kept for backward binary compatibility. This function can be removed in the future. */ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) { @@ -40,15 +42,44 @@ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) } /** - * Does array assignment (not construction) from another - * lvalue array of the same element type. - * Handles overlapping copies. - * Input: - * ti TypeInfo of the element type. - * dst Points target memory. Its .length is equal to the element count, not byte length. - * src Points source memory. Its .length is equal to the element count, not byte length. - * ptmp Temporary memory for element swapping. - */ +Does array assignment (not construction) from another array of the same +element type. + +Handles overlapping copies. + +The `_d_arrayassign_l` variant assumes the right hand side is an lvalue, +while `_d_arrayassign_r` assumes it's an rvalue, which means it doesn't have to call copy constructors. + +Used for static array assignment with non-POD element types: +--- +struct S +{ + ~this() {} // destructor, so not Plain Old Data +} + +void main() +{ + S[3] arr; + S[3] lvalue; + + arr = lvalue; + // Generates: + // S _tmp; + // _d_arrayassign_l(typeid(S), (cast(void*) lvalue.ptr)[0..lvalue.length], (cast(void*) arr.ptr)[0..arr.length], &_tmp); + + S[3] getRvalue() {return lvalue;} + arr = getRvalue(); + // Similar, but `_d_arrayassign_r` +} +--- + +Params: + ti = `TypeInfo` of the array element type. + dst = target memory. Its `.length` is equal to the element count, not byte length. + src = source memory. Its `.length` is equal to the element count, not byte length. + ptmp = Temporary memory for element swapping, must have capacity of `ti.tsize` bytes. +Returns: `dst` +*/ extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) { debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); @@ -131,16 +162,7 @@ unittest // Bugzilla 14024 assert(op == "YzXy", op); } -/** - * Does array assignment (not construction) from another - * rvalue array of the same element type. - * Input: - * ti TypeInfo of the element type. - * dst Points target memory. Its .length is equal to the element count, not byte length. - * src Points source memory. Its .length is equal to the element count, not byte length. - * It is always allocated on stack and never overlapping with dst. - * ptmp Temporary memory for element swapping. - */ +/// ditto extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) { debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); @@ -163,9 +185,22 @@ extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* pt } /** - * Do assignment to an array. - * p[0 .. count] = value; - */ +Set all elements of an array to a single value. + +--- +p[0 .. count] = value; +--- + +Takes into account postblits and destructors, for Plain Old Data elements, +`rt/memset.d` is used. + +Params: + p = pointer to start of array + value = bytes of the element to set. Size is derived from `ti`. + count = amount of array elements to set + ti = type info of the array element type / `value` +Returns: `p` +*/ extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) { void* pstart = p; diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 18ecc31311c..026001f5ad7 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -48,16 +48,39 @@ deprecated extern (C) void lifetime_init() } /** - * - */ +Allocate memory using the garbage collector + +DMD uses this to allocate closures: +--- +void f(byte[24] x) +{ + return () => x; // `x` is on stack, must be moved to heap to keep it alive +} +--- + +Params: + sz = number of bytes to allocate + +Returns: pointer to `sz` bytes of free, uninitialized memory, managed by the GC. +*/ extern (C) void* _d_allocmemory(size_t sz) @weak { return GC.malloc(sz); } /** - * - */ +Create a new class instance. + +Allocates memory and sets fields to their initial value, but does not call a constructor. + +--- +new Object() // _d_newclass(typeid(Object)) +--- +Params: + ci = `TypeInfo_Class` object, to provide instance size and initial bytes to copy + +Returns: newly created object +*/ extern (C) Object _d_newclass(const ClassInfo ci) @weak { import core.stdc.stdlib; @@ -352,7 +375,7 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, co /** get the allocation size of the array for the given block (without padding or type info) */ -size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow +private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow { if (info.size <= 256) return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); @@ -366,7 +389,7 @@ size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow /** get the start of the array for the given block */ -void *__arrayStart(return scope BlkInfo info) nothrow pure +private void *__arrayStart(return scope BlkInfo info) nothrow pure { return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0); } @@ -376,7 +399,7 @@ void *__arrayStart(return scope BlkInfo info) nothrow pure NOT included in the passed in size. Therefore, do NOT call this function with the size of an allocated block. */ -size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted +private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted { return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); } @@ -401,7 +424,7 @@ private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) n allocate an array memory block by applying the proper padding and assigning block attributes if not inherited from the existing block */ -BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure +private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure { import core.checkedint; @@ -423,7 +446,7 @@ BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tin return bi; } -BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) +private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) { import core.checkedint; @@ -446,7 +469,7 @@ BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, /** cache for the lookup of the block info */ -enum N_CACHE_BLOCKS=8; +private enum N_CACHE_BLOCKS=8; // note this is TLS, so no need to sync. BlkInfo *__blkcache_storage; @@ -644,10 +667,15 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow } /** - * Shrink the "allocated" length of an array to be the exact size of the array. - * It doesn't matter what the current allocated length of the array is, the - * user is telling the runtime that he knows what he is doing. - */ +Shrink the "allocated" length of an array to be the exact size of the array. + +It doesn't matter what the current allocated length of the array is, the +user is telling the runtime that he knows what he is doing. + +Params: + ti = `TypeInfo` of array type + arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type +*/ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/ { // note, we do not care about shared. We are setting the length no matter @@ -690,7 +718,7 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/ } } -package bool hasPostblit(in TypeInfo ti) +package bool hasPostblit(in TypeInfo ti) nothrow pure { return (&ti.postblit).funcptr !is &TypeInfo.postblit; } @@ -726,12 +754,21 @@ void __doPostblit(void *ptr, size_t len, const TypeInfo ti) /** - * set the array capacity. If the array capacity isn't currently large enough - * to hold the requested capacity (in number of elements), then the array is - * resized/reallocated to the appropriate size. Pass in a requested capacity - * of 0 to get the current capacity. Returns the number of elements that can - * actually be stored once the resizing is done. - */ +Set the array capacity. + +If the array capacity isn't currently large enough +to hold the requested capacity (in number of elements), then the array is +resized/reallocated to the appropriate size. + +Pass in a requested capacity of 0 to get the current capacity. + +Params: + ti = type info of element type + newcapacity = requested new capacity + p = pointer to array to set. Its `length` is left unchanged. + +Returns: the number of elements that can actually be stored once the resizing is done +*/ extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak in { @@ -902,9 +939,18 @@ Lcontinue: } /** - * Allocate a new uninitialized array of length elements. - * ti is the type of the resulting array, or pointer to element. - */ +Allocate an array with the garbage collector. + +Has three variants: +- `_d_newarrayU` leave elements uninitialized +- `_d_newarrayT` initializes to 0 (e.g `new int[]`) +- `_d_newarrayiT` initializes based on initializer retrieved from TypeInfo (e.g `new float[]`) + +Params: + ti = the type of the resulting array, (may also be the corresponding `array.ptr` type) + length = `.length` of resulting array +Returns: newly allocated array +*/ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak { import core.exception : onOutOfMemoryError; @@ -961,11 +1007,7 @@ Lcontinue: return arrstart[0..length]; } -/** - * Allocate a new array of length elements. - * ti is the type of the resulting array, or pointer to element. - * (For when the array is initialized to 0) - */ +/// ditto extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak { import core.stdc.string; @@ -978,9 +1020,7 @@ extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @w return result; } -/** - * For when the array has a non-zero initializer. - */ +/// ditto extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak { import core.internal.traits : AliasSeq; @@ -1016,10 +1056,10 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @ } -/** - * +/* + * Helper for creating multi-dimensional arrays */ -void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) +private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) { debug(PRINTF) printf("_d_newarrayOpT(ndims = %d)\n", dims.length); if (dims.length == 0) @@ -1058,8 +1098,30 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) /** - * - */ +Create a new multi-dimensional array + +Has two variants: +- `_d_newarraymTX` which initializes to 0 +- `_d_newarraymiTX` which initializes elements based on `TypeInfo` + +--- +void main() +{ + new int[][](10, 20); + // _d_newarraymTX(typeid(float), [10, 20]); + + new float[][][](10, 20, 30); + // _d_newarraymiTX(typeid(float), [10, 20, 30]); +} +--- + +Params: + ti = `TypeInfo` of the array type + dims = array length values for each dimension + +Returns: + newly allocated array +*/ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak { debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length); @@ -1072,10 +1134,7 @@ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak } } - -/** - * - */ +/// ditto extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak { debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length); @@ -1089,9 +1148,31 @@ extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak } /** - * Allocate an uninitialized non-array item. - * This is an optimization to avoid things needed for arrays like the __arrayPad(size). - */ +Allocate an uninitialized non-array item. + +This is an optimization to avoid things needed for arrays like the __arrayPad(size). + +- `_d_newitemU` leaves the item uninitialized +- `_d_newitemT` zero initializes the item +- `_d_newitemiT` uses a non-zero initializer from `TypeInfo` + +Used to allocate struct instances on the heap. +--- +struct Sz {int x = 0;} +struct Si {int x = 3;} + +void main() +{ + new Sz(); // _d_newitemT(typeid(Sz)) + new Si(); // _d_newitemiT(typeid(Si)) +} +--- + +Params: + _ti = `TypeInfo` of item to allocate +Returns: + newly allocated item +*/ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak { auto ti = unqualify(_ti); @@ -1115,7 +1196,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak return p; } -/// Same as above, zero initializes the item. +/// ditto extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak { import core.stdc.string; @@ -1135,15 +1216,6 @@ extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak return p; } -/** - * - */ -struct Array -{ - size_t length; - byte* data; -} - debug(PRINTF) { extern(C) void printArrayCache() @@ -1426,6 +1498,7 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) } } +/// Backwards compatibility extern (C) void rt_finalize(void* p, bool det = true) nothrow { rt_finalize2(p, det, true); @@ -1444,8 +1517,29 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow /** - * Resize dynamic arrays with 0 initializers. - */ +Resize a dynamic array by setting the `.length` property + +Newly created elements are initialized to their default value. + +Has two variants: +- `_d_arraysetlengthT` for arrays with elements that initialize to 0 +- `_d_arraysetlengthiT` for non-zero initializers retrieved from `TypeInfo` + +--- +void main() +{ + int[] a = [1, 2]; + a.length = 3; // gets lowered to `_d_arraysetlengthT(typeid(int[]), 3, &a)` +} +--- + +Params: + ti = `TypeInfo` of array + newlength = new value for the array's `.length` + p = pointer to array to update the `.length` of. + While it's cast to `void[]`, its `.length` is still treated as element length. +Returns: `*p` after being updated +*/ extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak in { @@ -1639,15 +1733,7 @@ do return *p; } - -/** - * Resize arrays for non-zero initializers. - * p pointer to array lvalue to be updated - * newlength new .length property of array - * sizeelem size of each element of array - * initsize size of initializer - * ... initializer - */ +/// ditto extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak in { @@ -1859,8 +1945,31 @@ do /** - * - */ +Given an array of length `size` that needs to be expanded to `newlength`, +compute a new capacity. + +Better version by Dave Fladebo: +This uses an inverse logorithmic algorithm to pre-allocate a bit more +space for larger arrays. +- Arrays smaller than PAGESIZE bytes are left as-is, so for the most +common cases, memory allocation is 1 to 1. The small overhead added +doesn't affect small array perf. (it's virtually the same as +current). +- Larger arrays have some space pre-allocated. +- As the arrays grow, the relative pre-allocated space shrinks. +- The logorithmic algorithm allocates relatively more space for +mid-size arrays, making it very fast for medium arrays (for +mid-to-large arrays, this turns out to be quite a bit faster than the +equivalent realloc() code in C, on Linux at least. Small arrays are +just as fast as GCC). +- Perhaps most importantly, overall memory usage and stress on the GC +is decreased significantly for demanding environments. + +Params: + newlength = new `.length` + size = old `.length` +Returns: new capacity for array +*/ size_t newCapacity(size_t newlength, size_t size) { version (none) @@ -1869,24 +1978,6 @@ size_t newCapacity(size_t newlength, size_t size) } else { - /* - * Better version by Dave Fladebo: - * This uses an inverse logorithmic algorithm to pre-allocate a bit more - * space for larger arrays. - * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most - * common cases, memory allocation is 1 to 1. The small overhead added - * doesn't affect small array perf. (it's virtually the same as - * current). - * - Larger arrays have some space pre-allocated. - * - As the arrays grow, the relative pre-allocated space shrinks. - * - The logorithmic algorithm allocates relatively more space for - * mid-size arrays, making it very fast for medium arrays (for - * mid-to-large arrays, this turns out to be quite a bit faster than the - * equivalent realloc() code in C, on Linux at least. Small arrays are - * just as fast as GCC). - * - Perhaps most importantly, overall memory usage and stress on the GC - * is decreased significantly for demanding environments. - */ size_t newcap = newlength * size; size_t newext = 0; @@ -1940,10 +2031,17 @@ size_t newCapacity(size_t newlength, size_t size) } -/************************************** - * Extend an array by n elements. - * Caller must initialize those elements. - */ +/** +Extend an array by n elements. + +Caller must initialize those elements. + +Params: + ti = type info of array type (not element type) + px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated. + n = number of elements to append +Returns: `px` after being appended to +*/ extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak { @@ -2047,8 +2145,21 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n /** - * Append dchar to char[] - */ +Append `dchar` to `char[]`, converting UTF-32 to UTF-8 + +--- +void main() +{ + char[] s; + s ~= 'α'; +} +--- + +Params: + x = array to append to cast to `byte[]`. Will be modified. + c = `dchar` to append +Returns: updated `x` cast to `void[]` +*/ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak { // c could encode into from 1 to 4 characters @@ -2129,8 +2240,23 @@ unittest /** - * Append dchar to wchar[] - */ +Append `dchar` to `wchar[]`, converting UTF-32 to UTF-16 + +--- +void main() +{ + dchar x; + wchar[] s; + s ~= 'α'; +} +--- + +Params: + x = array to append to cast to `byte[]`. Will be modified. + c = `dchar` to append + +Returns: updated `x` cast to `void[]` +*/ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak { // c could encode into from 1 to 2 w characters @@ -2162,8 +2288,24 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak /** - * - */ +Concatenate two arrays into a new array + +--- +void main() +{ + int[] x = [10, 20, 30]; + int[] y = [40, 50]; + int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]); +} +--- + +Params: + ti = type that the two arrays share + x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length + y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length +Returns: + resulting concatenated array, with `.length` equal to new element length despite `byte` type +*/ extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak out (result) { @@ -2228,8 +2370,27 @@ do /** - * - */ +Concatenate multiple arrays at once + +This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance. + +``` +void main() +{ + int[] a, b, c; + int[] res = a ~ b ~ c; + // _d_arraycatnTX(typeid(int[]), + // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]); +} +``` + +Params: + ti = type of arrays to concatenate and resulting array + arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same + +Returns: + newly created concatenated array, `.length` equal to the total element length despite `void` type +*/ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak { import core.stdc.string; @@ -2268,8 +2429,27 @@ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak /** - * Allocate the array, rely on the caller to do the initialization of the array. - */ +Allocate an array literal + +Rely on the caller to do the initialization of the array. + +--- +int[] getArr() +{ + return [10, 20]; + // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2); + // res[0] = 10; + // res[1] = 20; + // return res[0..2]; +} +--- + +Params: + ti = `TypeInfo` of resulting array type + length = `.length` of array literal + +Returns: pointer to allocated array +*/ extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak { diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index a4daa8419bf..744e5ad5e78 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -a4a18d21c4ea7930f80309f85e38c571c5f6d4b8 +1516ecad932d88a1618163384e6f69009d125391 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/complex.d b/libphobos/src/std/complex.d index 6b1541a2532..5a155387adc 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -1695,9 +1695,9 @@ Complex!T log(T)(Complex!T x) @safe pure nothrow @nogc */ Complex!T log10(T)(Complex!T x) @safe pure nothrow @nogc { - static import std.math; + import std.math.constants : LN10; - return log(x) / Complex!T(std.math.log(10.0)); + return log(x) / Complex!T(LN10); } /// diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index 05fad6724e0..b8b4a8ce6c6 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -176,9 +176,9 @@ class FileException : Exception private this(scope const(char)[] name, scope const(char)[] msg, string file, size_t line, uint errno) @safe pure { if (msg.empty) - super(name.idup, file, line); + super(name is null ? "(null)" : name.idup, file, line); else - super(text(name, ": ", msg), file, line); + super(text(name is null ? "(null)" : name, ": ", msg), file, line); this.errno = errno; } @@ -1067,11 +1067,38 @@ private void removeImpl(scope const(char)[] name, scope const(FSChar)* namez) @t if (!name) { import core.stdc.string : strlen; - auto len = strlen(namez); + + auto len = namez ? strlen(namez) : 0; name = namez[0 .. len]; } cenforce(core.stdc.stdio.remove(namez) == 0, - "Failed to remove file " ~ name); + "Failed to remove file " ~ (name is null ? "(null)" : name)); + } +} + +@safe unittest +{ + import std.exception : collectExceptionMsg, assertThrown; + + string filename = null; // e.g. as returned by File.tmpfile.name + + version (linux) + { + // exact exception message is OS-dependent + auto msg = filename.remove.collectExceptionMsg!FileException; + assert("Failed to remove file (null): Bad address" == msg, msg); + } + else version (Windows) + { + import std.algorithm.searching : startsWith; + + // don't test exact message on windows, it's language dependent + auto msg = filename.remove.collectExceptionMsg!FileException; + assert(msg.startsWith("(null):"), msg); + } + else + { + assertThrown!FileException(filename.remove); } } diff --git a/libphobos/src/std/math/exponential.d b/libphobos/src/std/math/exponential.d index daf2cecbebe..e32330fd932 100644 --- a/libphobos/src/std/math/exponential.d +++ b/libphobos/src/std/math/exponential.d @@ -2862,14 +2862,16 @@ float ldexp(float n, int exp) @safe pure nothrow @nogc { return core.math.ldex private { - import std.math : floatTraits, RealFormat; - - version (INLINE_YL2X) {} else + // Coefficients shared across log(), log2(), log10(). + template LogCoeffs(T) { - static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + import std.math : floatTraits, RealFormat; + + static if (floatTraits!T.realFormat == RealFormat.ieeeQuadruple) { - // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x) - static immutable real[13] logCoeffsP = [ + // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) + // Theoretical peak relative error = 5.3e-37 + static immutable real[13] logP = [ 1.313572404063446165910279910527789794488E4L, 7.771154681358524243729929227226708890930E4L, 2.014652742082537582487669938141683759923E5L, @@ -2884,7 +2886,7 @@ private 4.998469661968096229986658302195402690910E-1L, 1.538612243596254322971797716843006400388E-6L ]; - static immutable real[13] logCoeffsQ = [ + static immutable real[13] logQ = [ 3.940717212190338497730839731583397586124E4L, 2.626900195321832660448791748036714883242E5L, 7.777690340007566932935753241556479363645E5L, @@ -2900,9 +2902,18 @@ private 1.0 ]; - // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2) + // log2 uses the same coefficients as log. + alias log2P = logP; + alias log2Q = logQ; + + // log10 uses the same coefficients as log. + alias log10P = logP; + alias log10Q = logQ; + + // Coefficients for log(x) = z + z^^3 P(z^^2)/Q(z^^2) // where z = 2(x-1)/(x+1) - static immutable real[6] logCoeffsR = [ + // Theoretical peak relative error = 1.1e-35 + static immutable real[6] logR = [ 1.418134209872192732479751274970992665513E5L, -8.977257995689735303686582344659576526998E4L, 2.048819892795278657810231591630928516206E4L, @@ -2910,7 +2921,7 @@ private 8.057002716646055371965756206836056074715E1L, -8.828896441624934385266096344596648080902E-1L ]; - static immutable real[7] logCoeffsS = [ + static immutable real[7] logS = [ 1.701761051846631278975701529965589676574E6L, -1.332535117259762928288745111081235577029E6L, 4.001557694070773974936904547424676279307E5L, @@ -2922,8 +2933,9 @@ private } else { - // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x) - static immutable real[7] logCoeffsP = [ + // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) + // Theoretical peak relative error = 2.32e-20 + static immutable real[7] logP = [ 2.0039553499201281259648E1L, 5.7112963590585538103336E1L, 6.0949667980987787057556E1L, @@ -2932,7 +2944,7 @@ private 4.9854102823193375972212E-1L, 4.5270000862445199635215E-5L, ]; - static immutable real[7] logCoeffsQ = [ + static immutable real[7] logQ = [ 6.0118660497603843919306E1L, 2.1642788614495947685003E2L, 3.0909872225312059774938E2L, @@ -2942,15 +2954,42 @@ private 1.0000000000000000000000E0L, ]; - // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2) + // Coefficients for log(1 + x) = x - x^^2/2 + x^^3 P(x)/Q(x) + // Theoretical peak relative error = 6.2e-22 + static immutable real[7] log2P = [ + 1.0747524399916215149070E2L, + 3.4258224542413922935104E2L, + 4.2401812743503691187826E2L, + 2.5620629828144409632571E2L, + 7.7671073698359539859595E1L, + 1.0767376367209449010438E1L, + 4.9962495940332550844739E-1L, + ]; + static immutable real[8] log2Q = [ + 3.2242573199748645407652E2L, + 1.2695660352705325274404E3L, + 2.0307734695595183428202E3L, + 1.6911722418503949084863E3L, + 7.7952888181207260646090E2L, + 1.9444210022760132894510E2L, + 2.3479774160285863271658E1L, + 1.0000000000000000000000E0, + ]; + + // log10 uses the same coefficients as log2. + alias log10P = log2P; + alias log10Q = log2Q; + + // Coefficients for log(x) = z + z^^3 P(z^^2)/Q(z^^2) // where z = 2(x-1)/(x+1) - static immutable real[4] logCoeffsR = [ + // Theoretical peak relative error = 6.16e-22 + static immutable real[4] logR = [ -3.5717684488096787370998E1L, 1.0777257190312272158094E1L, -7.1990767473014147232598E-1L, 1.9757429581415468984296E-3L, ]; - static immutable real[4] logCoeffsS = [ + static immutable real[4] logS = [ -4.2861221385716144629696E2L, 1.9361891836232102174846E2L, -2.6201045551331104417768E1L, @@ -2972,92 +3011,100 @@ private */ real log(real x) @safe pure nothrow @nogc { - import std.math.constants : LN2, LOG2, SQRT1_2; - import std.math.traits : isInfinity, isNaN, signbit; - import std.math.algebraic : poly; - version (INLINE_YL2X) + { + import std.math.constants : LN2; return core.math.yl2x(x, LN2); + } else - { - // C1 + C2 = LN2. - enum real C1 = 6.93145751953125E-1L; - enum real C2 = 1.428606820309417232121458176568075500134E-6L; + return logImpl(x); +} - // Special cases. - if (isNaN(x)) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == 0.0) - return -real.infinity; - if (x < 0.0) - return real.nan; +/// +@safe pure nothrow @nogc unittest +{ + import std.math.operations : feqrel; + import std.math.constants : E; - // Separate mantissa from exponent. - // Note, frexp is used so that denormal numbers will be handled properly. - real y, z; - int exp; + assert(feqrel(log(E), 1) >= real.mant_dig - 1); +} - x = frexp(x, exp); +private T logImpl(T)(T x) @safe pure nothrow @nogc +{ + import std.math.constants : SQRT1_2; + import std.math.algebraic : poly; + import std.math.traits : isInfinity, isNaN, signbit; - // Logarithm using log(x) = z + z^^3 R(z) / S(z), - // where z = 2(x - 1)/(x + 1) - if ((exp > 2) || (exp < -2)) - { - if (x < SQRT1_2) - { // 2(2x - 1)/(2x + 1) - exp -= 1; - z = x - 0.5; - y = 0.5 * z + 0.5; - } - else - { // 2(x - 1)/(x + 1) - z = x - 0.5; - z -= 0.5; - y = 0.5 * x + 0.5; - } - x = z / y; - z = x * x; - z = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS)); - z += exp * C2; - z += x; - z += exp * C1; + alias coeffs = LogCoeffs!T; - return z; - } + // C1 + C2 = LN2. + enum T C1 = 6.93145751953125E-1L; + enum T C2 = 1.428606820309417232121458176568075500134E-6L; - // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) + // Special cases. + if (isNaN(x)) + return x; + if (isInfinity(x) && !signbit(x)) + return x; + if (x == 0.0) + return -T.infinity; + if (x < 0.0) + return T.nan; + + // Separate mantissa from exponent. + // Note, frexp is used so that denormal numbers will be handled properly. + T y, z; + int exp; + + x = frexp(x, exp); + + // Logarithm using log(x) = z + z^^3 R(z) / S(z), + // where z = 2(x - 1)/(x + 1) + if ((exp > 2) || (exp < -2)) + { if (x < SQRT1_2) - { + { // 2(2x - 1)/(2x + 1) exp -= 1; - x = 2.0 * x - 1.0; + z = x - 0.5; + y = 0.5 * z + 0.5; } else - { - x = x - 1.0; + { // 2(x - 1)/(x + 1) + z = x - 0.5; + z -= 0.5; + y = 0.5 * x + 0.5; } + x = z / y; z = x * x; - y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ)); - y += exp * C2; - z = y - 0.5 * z; - - // Note, the sum of above terms does not exceed x/4, - // so it contributes at most about 1/4 lsb to the error. + z = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS)); + z += exp * C2; z += x; z += exp * C1; return z; } -} -/// -@safe pure nothrow @nogc unittest -{ - import std.math.operations : feqrel; - import std.math.constants : E; + // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) + if (x < SQRT1_2) + { + exp -= 1; + x = 2.0 * x - 1.0; + } + else + { + x = x - 1.0; + } + z = x * x; + y = x * (z * poly(x, coeffs.logP) / poly(x, coeffs.logQ)); + y += exp * C2; + z = y - 0.5 * z; - assert(feqrel(log(E), 1) >= real.mant_dig - 1); + // Note, the sum of above terms does not exceed x/4, + // so it contributes at most about 1/4 lsb to the error. + z += x; + z += exp * C1; + + return z; } /************************************** @@ -3072,95 +3119,103 @@ real log(real x) @safe pure nothrow @nogc */ real log10(real x) @safe pure nothrow @nogc { - import std.math.constants : LOG2, LN2, SQRT1_2; - import std.math.algebraic : poly; - import std.math.traits : isNaN, isInfinity, signbit; - version (INLINE_YL2X) + { + import std.math.constants : LOG2; return core.math.yl2x(x, LOG2); + } else - { - // log10(2) split into two parts. - enum real L102A = 0.3125L; - enum real L102B = -1.14700043360188047862611052755069732318101185E-2L; + return log10Impl(x); +} - // log10(e) split into two parts. - enum real L10EA = 0.5L; - enum real L10EB = -6.570551809674817234887108108339491770560299E-2L; +/// +@safe pure nothrow @nogc unittest +{ + import std.math.algebraic : fabs; - // Special cases are the same as for log. - if (isNaN(x)) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == 0.0) - return -real.infinity; - if (x < 0.0) - return real.nan; + assert(fabs(log10(1000) - 3) < .000001); +} - // Separate mantissa from exponent. - // Note, frexp is used so that denormal numbers will be handled properly. - real y, z; - int exp; +private T log10Impl(T)(T x) @safe pure nothrow @nogc +{ + import std.math.constants : SQRT1_2; + import std.math.algebraic : poly; + import std.math.traits : isNaN, isInfinity, signbit; - x = frexp(x, exp); + alias coeffs = LogCoeffs!T; - // Logarithm using log(x) = z + z^^3 R(z) / S(z), - // where z = 2(x - 1)/(x + 1) - if ((exp > 2) || (exp < -2)) - { - if (x < SQRT1_2) - { // 2(2x - 1)/(2x + 1) - exp -= 1; - z = x - 0.5; - y = 0.5 * z + 0.5; - } - else - { // 2(x - 1)/(x + 1) - z = x - 0.5; - z -= 0.5; - y = 0.5 * x + 0.5; - } - x = z / y; - z = x * x; - y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS)); - goto Ldone; - } + // log10(2) split into two parts. + enum T L102A = 0.3125L; + enum T L102B = -1.14700043360188047862611052755069732318101185E-2L; + + // log10(e) split into two parts. + enum T L10EA = 0.5L; + enum T L10EB = -6.570551809674817234887108108339491770560299E-2L; + + // Special cases are the same as for log. + if (isNaN(x)) + return x; + if (isInfinity(x) && !signbit(x)) + return x; + if (x == 0.0) + return -T.infinity; + if (x < 0.0) + return T.nan; + + // Separate mantissa from exponent. + // Note, frexp is used so that denormal numbers will be handled properly. + T y, z; + int exp; + + x = frexp(x, exp); - // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) + // Logarithm using log(x) = z + z^^3 R(z) / S(z), + // where z = 2(x - 1)/(x + 1) + if ((exp > 2) || (exp < -2)) + { if (x < SQRT1_2) - { + { // 2(2x - 1)/(2x + 1) exp -= 1; - x = 2.0 * x - 1.0; + z = x - 0.5; + y = 0.5 * z + 0.5; } else - x = x - 1.0; - + { // 2(x - 1)/(x + 1) + z = x - 0.5; + z -= 0.5; + y = 0.5 * x + 0.5; + } + x = z / y; z = x * x; - y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ)); - y = y - 0.5 * z; - - // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). - // This sequence of operations is critical and it may be horribly - // defeated by some compiler optimizers. - Ldone: - z = y * L10EB; - z += x * L10EB; - z += exp * L102B; - z += y * L10EA; - z += x * L10EA; - z += exp * L102A; + y = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS)); + goto Ldone; + } - return z; + // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) + if (x < SQRT1_2) + { + exp -= 1; + x = 2.0 * x - 1.0; } -} + else + x = x - 1.0; -/// -@safe pure nothrow @nogc unittest -{ - import std.math.algebraic : fabs; + z = x * x; + y = x * (z * poly(x, coeffs.log10P) / poly(x, coeffs.log10Q)); + y = y - 0.5 * z; - assert(fabs(log10(1000) - 3) < .000001); + // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). + // This sequence of operations is critical and it may be horribly + // defeated by some compiler optimizers. +Ldone: + z = y * L10EB; + z += x * L10EB; + z += exp * L102B; + z += y * L10EA; + z += x * L10EA; + z += exp * L102A; + + return z; } /** @@ -3179,29 +3234,15 @@ real log10(real x) @safe pure nothrow @nogc */ real log1p(real x) @safe pure nothrow @nogc { - import std.math.traits : isNaN, isInfinity, signbit; - import std.math.constants : LN2; - version (INLINE_YL2X) { // On x87, yl2xp1 is valid if and only if -0.5 <= lg(x) <= 0.5, // ie if -0.29 <= x <= 0.414 + import std.math.constants : LN2; return (core.math.fabs(x) <= 0.25) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2); } else - { - // Special cases. - if (isNaN(x) || x == 0.0) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == -1.0) - return -real.infinity; - if (x < -1.0) - return real.nan; - - return log(x + 1.0); - } + return log1pImpl(x); } /// @@ -3220,6 +3261,23 @@ real log1p(real x) @safe pure nothrow @nogc assert(log1p(real.infinity) == real.infinity); } +private T log1pImpl(T)(T x) @safe pure nothrow @nogc +{ + import std.math.traits : isNaN, isInfinity, signbit; + + // Special cases. + if (isNaN(x) || x == 0.0) + return x; + if (isInfinity(x) && !signbit(x)) + return x; + if (x == -1.0) + return -T.infinity; + if (x < -1.0) + return T.nan; + + return logImpl(x + 1.0); +} + /*************************************** * Calculates the base-2 logarithm of x: * $(SUB log, 2)x @@ -3233,78 +3291,10 @@ real log1p(real x) @safe pure nothrow @nogc */ real log2(real x) @safe pure nothrow @nogc { - import std.math.traits : isNaN, isInfinity, signbit; - import std.math.constants : SQRT1_2, LOG2E; - import std.math.algebraic : poly; - version (INLINE_YL2X) return core.math.yl2x(x, 1.0L); else - { - // Special cases are the same as for log. - if (isNaN(x)) - return x; - if (isInfinity(x) && !signbit(x)) - return x; - if (x == 0.0) - return -real.infinity; - if (x < 0.0) - return real.nan; - - // Separate mantissa from exponent. - // Note, frexp is used so that denormal numbers will be handled properly. - real y, z; - int exp; - - x = frexp(x, exp); - - // Logarithm using log(x) = z + z^^3 R(z) / S(z), - // where z = 2(x - 1)/(x + 1) - if ((exp > 2) || (exp < -2)) - { - if (x < SQRT1_2) - { // 2(2x - 1)/(2x + 1) - exp -= 1; - z = x - 0.5; - y = 0.5 * z + 0.5; - } - else - { // 2(x - 1)/(x + 1) - z = x - 0.5; - z -= 0.5; - y = 0.5 * x + 0.5; - } - x = z / y; - z = x * x; - y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS)); - goto Ldone; - } - - // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) - if (x < SQRT1_2) - { - exp -= 1; - x = 2.0 * x - 1.0; - } - else - x = x - 1.0; - - z = x * x; - y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ)); - y = y - 0.5 * z; - - // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). - // This sequence of operations is critical and it may be horribly - // defeated by some compiler optimizers. - Ldone: - z = y * (LOG2E - 1.0); - z += x * (LOG2E - 1.0); - z += y; - z += x; - z += exp; - - return z; - } + return log2Impl(x); } /// @@ -3323,6 +3313,79 @@ real log2(real x) @safe pure nothrow @nogc assert(isClose(log2(1024.0L), 10, 1e-18)); } +private T log2Impl(T)(T x) @safe pure nothrow @nogc +{ + import std.math.traits : isNaN, isInfinity, signbit; + import std.math.constants : SQRT1_2, LOG2E; + import std.math.algebraic : poly; + + alias coeffs = LogCoeffs!T; + + // Special cases are the same as for log. + if (isNaN(x)) + return x; + if (isInfinity(x) && !signbit(x)) + return x; + if (x == 0.0) + return -T.infinity; + if (x < 0.0) + return T.nan; + + // Separate mantissa from exponent. + // Note, frexp is used so that denormal numbers will be handled properly. + T y, z; + int exp; + + x = frexp(x, exp); + + // Logarithm using log(x) = z + z^^3 R(z) / S(z), + // where z = 2(x - 1)/(x + 1) + if ((exp > 2) || (exp < -2)) + { + if (x < SQRT1_2) + { // 2(2x - 1)/(2x + 1) + exp -= 1; + z = x - 0.5; + y = 0.5 * z + 0.5; + } + else + { // 2(x - 1)/(x + 1) + z = x - 0.5; + z -= 0.5; + y = 0.5 * x + 0.5; + } + x = z / y; + z = x * x; + y = x * (z * poly(z, coeffs.logR) / poly(z, coeffs.logS)); + goto Ldone; + } + + // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x) + if (x < SQRT1_2) + { + exp -= 1; + x = 2.0 * x - 1.0; + } + else + x = x - 1.0; + + z = x * x; + y = x * (z * poly(x, coeffs.log2P) / poly(x, coeffs.log2Q)); + y = y - 0.5 * z; + + // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). + // This sequence of operations is critical and it may be horribly + // defeated by some compiler optimizers. +Ldone: + z = y * (LOG2E - 1.0); + z += x * (LOG2E - 1.0); + z += y; + z += x; + z += exp; + + return z; +} + /***************************************** * Extracts the exponent of x as a signed integral value. * @@ -3337,35 +3400,23 @@ real log2(real x) @safe pure nothrow @nogc * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) ) * ) */ -real logb(real x) @trusted nothrow @nogc +pragma(inline, true) +real logb(real x) @trusted pure nothrow @nogc { version (InlineAsm_X87_MSVC) - { - version (X86_64) - { - asm pure nothrow @nogc - { - naked ; - fld real ptr [RCX] ; - fxtract ; - fstp ST(0) ; - ret ; - } - } - else - { - asm pure nothrow @nogc - { - fld x ; - fxtract ; - fstp ST(0) ; - } - } - } + return logbAsm(x); else - return core.stdc.math.logbl(x); + return logbImpl(x); } +/// ditto +pragma(inline, true) +double logb(double x) @trusted pure nothrow @nogc { return logbImpl(x); } + +/// ditto +pragma(inline, true) +float logb(float x) @trusted pure nothrow @nogc { return logbImpl(x); } + /// @safe @nogc nothrow unittest { @@ -3377,6 +3428,83 @@ real logb(real x) @trusted nothrow @nogc assert(logb(-real.infinity) == real.infinity); } +@safe @nogc nothrow unittest +{ + import std.meta : AliasSeq; + import std.typecons : Tuple; + import std.math.traits : isNaN; + static foreach (F; AliasSeq!(float, double, real)) + {{ + alias T = Tuple!(F, F); + T[17] vals = // x, logb(x) + [ + T(1.0 , 0 ), + T(100.0 , 6 ), + T(0.0 , -F.infinity), + T(-0.0 , -F.infinity), + T(1024 , 10 ), + T(-2000 , 10 ), + T(0x0.1p-127 , -131 ), + T(0x0.01p-127 , -135 ), + T(0x0.011p-127 , -135 ), + T(F.nan , F.nan ), + T(-F.nan , F.nan ), + T(F.infinity , F.infinity ), + T(-F.infinity , F.infinity ), + T(F.min_normal , F.min_exp-1), + T(-F.min_normal, F.min_exp-1), + T(F.max , F.max_exp-1), + T(-F.max , F.max_exp-1), + ]; + + foreach (elem; vals) + { + if (isNaN(elem[1])) + assert(isNaN(logb(elem[1]))); + else + assert(logb(elem[0]) == elem[1]); + } + }} +} + +version (InlineAsm_X87_MSVC) +private T logbAsm(T)(T x) @trusted pure nothrow @nogc +{ + version (X86_64) + { + asm pure nothrow @nogc + { + naked ; + fld real ptr [RCX] ; + fxtract ; + fstp ST(0) ; + ret ; + } + } + else + { + asm pure nothrow @nogc + { + fld x ; + fxtract ; + fstp ST(0) ; + } + } +} + +private T logbImpl(T)(T x) @trusted pure nothrow @nogc +{ + import std.math.traits : isFinite; + + // Handle special cases. + if (!isFinite(x)) + return x * x; + if (x == 0) + return -1 / (x * x); + + return ilogb(x); +} + /************************************* * Efficiently calculates x * 2$(SUPERSCRIPT n). *