diff mbox series

[committed] d: Merge upstream dmd cf63dd8e5, druntime caf14b0f, phobos 41aaf8c26.

Message ID 20220228212523.3878326-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd cf63dd8e5, druntime caf14b0f, phobos 41aaf8c26. | expand

Commit Message

Iain Buclaw Feb. 28, 2022, 9:25 p.m. UTC
Hi,

This patch merges the D front-end implementation with upstream dmd
cf63dd8e5, as well as the D runtime libraries with druntime caf14b0f,
and phobos 41aaf8c26, synchronizing with the release of 2.099.0-rc1.

D front-end changes:

    - Import dmd v2.099.0-rc.1.
    - The `main' can now return type `noreturn' and supports return
      inference.

D Runtime changes:

    - Import druntime v2.099.0-rc.1.
    - C bindings for stat_t on powerpc-linux has been fixed.

Phobos changes:

    - Import phobos v2.099.0-rc.1.

Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, and
powerpc-linux-gnu.  Committed to mainline.

Regards,
Iain.

---
gcc/d/ChangeLog:

	* d-target.cc (Target::_init): Initialize C type size fields.
	* dmd/MERGE: Merge upstream dmd cf63dd8e5.
	* dmd/VERSION: Update version to v2.099.0-rc.1.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime caf14b0f.
	* src/MERGE: Merge upstream phobos 41aaf8c26.

gcc/testsuite/ChangeLog:

	* gdc.dg/torture/simd7413a.d: Update.
	* gdc.dg/ubsan/pr88957.d: Update.
	* gdc.dg/simd18489.d: New test.
	* gdc.dg/torture/simd21727.d: New test.
---
 gcc/d/d-target.cc                             |   9 +-
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/dmd/VERSION                             |   2 +-
 gcc/d/dmd/common/outbuffer.d                  |  32 +-
 gcc/d/dmd/cparse.d                            |  66 +-
 gcc/d/dmd/cppmangle.d                         |  44 +-
 gcc/d/dmd/dmangle.d                           | 626 +++++++++---------
 gcc/d/dmd/dmodule.d                           |   8 +
 gcc/d/dmd/dsymbolsem.d                        |   3 +-
 gcc/d/dmd/expressionsem.d                     |   6 +-
 gcc/d/dmd/file_manager.d                      |   6 +-
 gcc/d/dmd/func.d                              |  19 +-
 gcc/d/dmd/lexer.d                             |  12 +-
 gcc/d/dmd/mtype.d                             |   8 +
 gcc/d/dmd/root/file.d                         |  40 +-
 gcc/d/dmd/root/speller.d                      |  23 +-
 gcc/d/dmd/root/string.d                       |  11 +-
 gcc/d/dmd/semantic3.d                         |  22 +-
 gcc/d/dmd/target.d                            |   4 +
 gcc/d/dmd/target.h                            |   4 +
 gcc/d/dmd/tokens.h                            |  20 +-
 gcc/d/dmd/traits.d                            |   7 +-
 gcc/d/dmd/typesem.d                           |  13 +-
 gcc/testsuite/gdc.dg/simd18489.d              |   8 +
 .../ice21727.d => gdc.dg/torture/simd21727.d} |  11 +-
 gcc/testsuite/gdc.dg/torture/simd7413a.d      |   1 -
 gcc/testsuite/gdc.dg/ubsan/pr88957.d          |   3 +-
 gcc/testsuite/gdc.test/compilable/b18489.d    |   8 -
 .../gdc.test/compilable/issue21390.d          |   3 +
 .../gdc.test/fail_compilation/fail17927.d     |   2 +-
 .../gdc.test/fail_compilation/fix17751.d      |  22 -
 .../gdc.test/fail_compilation/issue22826.d    |   7 +
 .../gdc.test/fail_compilation/test21546.d     |  59 ++
 .../gdc.test/fail_compilation/test22023.d     |  26 +
 .../gdc.test/fail_compilation/test22818.d     |  21 +
 gcc/testsuite/gdc.test/runnable/nan.d         |  17 +-
 gcc/testsuite/gdc.test/runnable/previewin.d   |   6 +-
 gcc/testsuite/gdc.test/runnable/sroa13220.d   | 103 ---
 gcc/testsuite/gdc.test/runnable/test15.d      |   2 +-
 gcc/testsuite/gdc.test/runnable/testconst.d   |  16 +-
 gcc/testsuite/gdc.test/runnable/testscope2.d  |   2 +-
 .../runnable/traits_getPointerBitmap.d        |   2 +-
 libphobos/libdruntime/MERGE                   |   2 +-
 libphobos/libdruntime/core/gc/gcinterface.d   |   4 +-
 libphobos/libdruntime/core/internal/gc/bits.d |  12 +-
 .../core/internal/gc/impl/conservative/gc.d   | 257 ++++---
 .../libdruntime/core/internal/gc/pooltable.d  |  29 +-
 .../libdruntime/core/internal/gc/proxy.d      |   4 +-
 libphobos/libdruntime/core/memory.d           |   4 +-
 libphobos/libdruntime/core/stdcpp/string.d    |   8 +-
 .../libdruntime/core/sys/posix/sys/stat.d     |  85 ++-
 libphobos/libdruntime/core/time.d             | 158 +++--
 libphobos/libdruntime/object.d                |  13 +-
 libphobos/src/MERGE                           |   2 +-
 libphobos/src/std/file.d                      |   4 +-
 libphobos/src/std/getopt.d                    |   8 +-
 libphobos/src/std/range/primitives.d          |  11 +-
 libphobos/src/std/sumtype.d                   | 108 ++-
 58 files changed, 1164 insertions(+), 851 deletions(-)
 create mode 100644 gcc/testsuite/gdc.dg/simd18489.d
 rename gcc/testsuite/{gdc.test/runnable/ice21727.d => gdc.dg/torture/simd21727.d} (71%)
 delete mode 100644 gcc/testsuite/gdc.test/compilable/b18489.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/issue21390.d
 delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fix17751.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/issue22826.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test21546.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test22023.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test22818.d
 delete mode 100644 gcc/testsuite/gdc.test/runnable/sroa13220.d
diff mbox series

Patch

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