diff mbox series

[committed] d: Merge upstream dmd 56589f0f4, druntime 651389b5, phobos 1516ecad9.

Message ID 20220706190607.1517849-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd 56589f0f4, druntime 651389b5, phobos 1516ecad9. | expand

Commit Message

Iain Buclaw July 6, 2022, 7:06 p.m. UTC
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 mbox series

Patch

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<const char *>& 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).
  *