From 0dd21bce3afe9b0081d1d5bf4b53f9b43980c1c4 Mon Sep 17 00:00:00 2001
From: Iain Buclaw <ibuclaw@gdcproject.org>
Date: Wed, 8 Jan 2025 21:02:56 +0100
Subject: [PATCH] d: Merge upstream dmd, druntime c57da0cf59, phobos ad8ee5587

D front-end changes:

	- Import latest fixes from dmd v2.110.0-beta.1.
	- The `align' attribute now allows to specify `default'
	  explicitly.
	- Add primary expression of the form `__rvalue(expression)'
	  which causes `expression' to be treated as an rvalue, even if
	  it is an lvalue.
	- Shortened method syntax can now be used in constructors.

D runtime changes:

	- Import latest fixes from druntime v2.110.0-beta.1.

Phobos changes:

	- Import latest fixes from phobos v2.110.0-beta.1.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd c57da0cf59.
	* d-codegen.cc (can_elide_copy_p): New.
	(d_build_call): Use it.
	* d-lang.cc (d_post_options): Update for new front-end interface.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime c57da0cf59.
	* src/MERGE: Merge upstream phobos ad8ee5587.
	* testsuite/libphobos.init_fini/custom_gc.d: Adjust test.

gcc/testsuite/ChangeLog:

	* gdc.dg/copy1.d: New test.
---
 gcc/d/d-codegen.cc                            |  37 +-
 gcc/d/d-lang.cc                               |   3 +-
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/dmd/astenums.d                          |   2 +-
 gcc/d/dmd/attrib.d                            |  30 -
 gcc/d/dmd/attrib.h                            |   3 -
 gcc/d/dmd/clone.d                             |  16 +-
 gcc/d/dmd/constfold.d                         |  13 +-
 gcc/d/dmd/declaration.d                       |  12 +-
 gcc/d/dmd/declaration.h                       |   1 +
 gcc/d/dmd/dscope.d                            |   2 +
 gcc/d/dmd/dstruct.d                           |  19 +-
 gcc/d/dmd/dsymbol.d                           |  20 +-
 gcc/d/dmd/dsymbol.h                           |   1 +
 gcc/d/dmd/dsymbolsem.d                        | 101 ++-
 gcc/d/dmd/expression.d                        |  49 +-
 gcc/d/dmd/expression.h                        |   3 +-
 gcc/d/dmd/expressionsem.d                     | 405 ++++--------
 gcc/d/dmd/func.d                              |  10 +-
 gcc/d/dmd/funcsem.d                           |  26 +-
 gcc/d/dmd/globals.d                           |   9 +-
 gcc/d/dmd/globals.h                           |  11 +-
 gcc/d/dmd/hdrgen.d                            |  58 +-
 gcc/d/dmd/id.d                                |   1 +
 gcc/d/dmd/identifier.d                        |  17 +-
 gcc/d/dmd/initsem.d                           |   2 +-
 gcc/d/dmd/json.d                              |   3 +-
 gcc/d/dmd/mtype.d                             |  24 +-
 gcc/d/dmd/mtype.h                             |   2 +-
 gcc/d/dmd/objc.d                              |  61 +-
 gcc/d/dmd/parse.d                             |  56 +-
 gcc/d/dmd/printast.d                          |   6 +
 gcc/d/dmd/root/filename.d                     |   3 +-
 gcc/d/dmd/scope.h                             |   1 +
 gcc/d/dmd/semantic3.d                         |   2 +-
 gcc/d/dmd/statementsem.d                      |   2 +-
 gcc/d/dmd/templatesem.d                       |  35 +-
 gcc/d/dmd/tokens.d                            |   7 +
 gcc/d/dmd/tokens.h                            |   1 +
 gcc/d/dmd/traits.d                            |  14 +-
 gcc/d/dmd/typesem.d                           | 105 ++-
 gcc/testsuite/gdc.dg/copy1.d                  |  19 +
 .../gdc.test/compilable/aligndefault.d        |  28 +
 .../gdc.test/compilable/extra-files/header1.d |   5 +
 gcc/testsuite/gdc.test/compilable/rvalue2.d   |  22 +
 .../gdc.test/compilable/shortened_methods.d   |  13 +
 gcc/testsuite/gdc.test/compilable/test19227.d |   4 +-
 gcc/testsuite/gdc.test/compilable/traits.d    |   4 +
 .../gdc.test/fail_compilation/fail19871.d     |  20 -
 .../gdc.test/fail_compilation/fail19931.d     |  15 -
 .../gdc.test/fail_compilation/fail23036.d     |  22 -
 .../gdc.test/fail_compilation/ice14642.d      |   5 +-
 .../gdc.test/fail_compilation/ice9865.d       |   3 +-
 .../gdc.test/fail_compilation/parseStc2.d     |   4 +-
 .../gdc.test/fail_compilation/short_fn.d      |  15 +
 .../gdc.test/fail_compilation/test19473.d     |   3 +-
 .../gdc.test/fail_compilation/test20719.d     |   7 +-
 gcc/testsuite/gdc.test/runnable/aliasassign.d |  32 +-
 .../gdc.test/runnable/imports/test23722_2b.d  |  13 +
 gcc/testsuite/gdc.test/runnable/mars1.d       |  59 ++
 gcc/testsuite/gdc.test/runnable/nrvo.d        |  26 +
 gcc/testsuite/gdc.test/runnable/rvalue1.d     | 209 ++++++
 gcc/testsuite/gdc.test/runnable/test23036.d   |  13 +
 gcc/testsuite/gdc.test/runnable/test23722_2.d |  10 +
 libphobos/libdruntime/MERGE                   |   2 +-
 libphobos/libdruntime/core/builtins.d         |   7 +-
 libphobos/libdruntime/core/gc/gcinterface.d   |  69 ++
 .../libdruntime/core/internal/array/utils.d   |   6 +-
 .../libdruntime/core/internal/gc/blkcache.d   |  19 +-
 .../libdruntime/core/internal/gc/blockmeta.d  |  88 ++-
 .../core/internal/gc/impl/conservative/gc.d   | 187 ++++++
 .../core/internal/gc/impl/manual/gc.d         |  20 +
 .../core/internal/gc/impl/proto/gc.d          |  20 +
 .../libdruntime/core/internal/gc/proxy.d      |  20 +
 libphobos/libdruntime/core/internal/traits.d  | 159 ++++-
 libphobos/libdruntime/core/sys/posix/fcntl.d  |   1 +
 .../libdruntime/core/sys/windows/aclapi.d     |   2 +-
 .../libdruntime/core/sys/windows/aclui.d      |   2 +-
 .../libdruntime/core/sys/windows/commctrl.d   |  10 +-
 .../libdruntime/core/sys/windows/custcntl.d   |   1 +
 libphobos/libdruntime/core/sys/windows/dde.d  |   2 +-
 .../libdruntime/core/sys/windows/ddeml.d      |   2 +-
 .../libdruntime/core/sys/windows/dhcpcsdk.d   |   2 +-
 .../libdruntime/core/sys/windows/errorrep.d   |   2 +-
 .../libdruntime/core/sys/windows/httpext.d    |   2 +-
 .../libdruntime/core/sys/windows/imagehlp.d   |   1 +
 .../libdruntime/core/sys/windows/intshcut.d   |   2 +-
 .../libdruntime/core/sys/windows/iphlpapi.d   |   2 +-
 .../libdruntime/core/sys/windows/lmaccess.d   |   2 +-
 .../libdruntime/core/sys/windows/lmalert.d    |   2 +-
 .../libdruntime/core/sys/windows/lmapibuf.d   |   2 +-
 .../libdruntime/core/sys/windows/lmmsg.d      |   2 +-
 .../libdruntime/core/sys/windows/lmremutl.d   |   2 +-
 .../libdruntime/core/sys/windows/mgmtapi.d    |   2 +-
 .../libdruntime/core/sys/windows/ntdll.d      |   2 +-
 .../libdruntime/core/sys/windows/ntsecapi.d   |   2 +-
 libphobos/libdruntime/core/sys/windows/ole.d  |   2 +-
 libphobos/libdruntime/core/sys/windows/ole2.d |   2 +-
 .../libdruntime/core/sys/windows/oleacc.d     |   2 +-
 .../libdruntime/core/sys/windows/oleauto.d    |   2 +-
 .../libdruntime/core/sys/windows/oledlg.d     |   4 +-
 .../libdruntime/core/sys/windows/prsht.d      |   2 +-
 .../libdruntime/core/sys/windows/psapi.d      |   2 +-
 libphobos/libdruntime/core/sys/windows/rpc.d  |   2 +-
 .../libdruntime/core/sys/windows/winhttp.d    |   2 +-
 .../libdruntime/core/sys/windows/winnetwk.d   |   2 +-
 .../libdruntime/core/sys/windows/winsvc.d     |   2 +-
 .../libdruntime/core/sys/windows/winuser.d    |   2 +-
 .../libdruntime/core/sys/windows/winver.d     |   2 +-
 libphobos/libdruntime/rt/lifetime.d           | 553 ++++------------
 libphobos/src/MERGE                           |   2 +-
 libphobos/src/etc/c/curl.d                    |   2 +-
 libphobos/src/std/algorithm/searching.d       | 101 ++-
 libphobos/src/std/array.d                     |  58 ++
 .../building_blocks/allocator_list.d          | 621 ++++++++++++++----
 libphobos/src/std/format/package.d            |   4 +-
 libphobos/src/std/functional.d                | 294 +++++++--
 libphobos/src/std/logger/filelogger.d         |   2 +-
 libphobos/src/std/logger/package.d            |   4 +-
 libphobos/src/std/math/traits.d               |  52 +-
 libphobos/src/std/path.d                      |   2 -
 libphobos/src/std/process.d                   |   6 +-
 libphobos/src/std/stdio.d                     |  47 +-
 .../testsuite/libphobos.init_fini/custom_gc.d |  20 +
 124 files changed, 2867 insertions(+), 1299 deletions(-)
 create mode 100644 gcc/testsuite/gdc.dg/copy1.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/aligndefault.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/rvalue2.d
 delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail19871.d
 delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail19931.d
 delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail23036.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/short_fn.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/rvalue1.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/test23036.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/test23722_2.d

diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index e9962e6d56ba..66f06e9086ed 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -631,6 +631,37 @@ force_target_expr (tree exp)
   return build_target_expr (decl, exp);
 }
 
+/* Determine whether expression EXP can have a copy of its value elided.  */
+
+static bool
+can_elide_copy_p (Expression *exp)
+{
+  /* Explicit `__rvalue(exp)'.  */
+  if (exp->rvalue)
+    return true;
+
+  /* Look for variable expression.  */
+  Expression *last = exp;
+  while (CommaExp *ce = last->isCommaExp ())
+    last = ce->e2;
+
+  if (VarExp *ve = last->isVarExp ())
+    {
+      if (VarDeclaration *vd = ve->var->isVarDeclaration ())
+	{
+	  /* Variable is an implicit copy of an lvalue.  */
+	  if (vd->storage_class & STCrvalue)
+	    return true;
+
+	  /* The destructor is going to run on the variable.  */
+	  if (vd->isArgDtorVar ())
+	    return true;
+	}
+    }
+
+  return false;
+}
+
 /* Returns the address of the expression EXP.  */
 
 tree
@@ -2280,8 +2311,10 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
 		 - The ABI of the function expects the callee to destroy its
 		 arguments; when the caller is handles destruction, then `targ'
 		 has already been made into a temporary. */
-	      if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor)
-		  || target.isCalleeDestroyingArgs (tf))
+	      if (!can_elide_copy_p (arg)
+		  && (arg->op == EXP::structLiteral
+		      || (!sd->postblit && !sd->dtor)
+		      || target.isCalleeDestroyingArgs (tf)))
 		targ = force_target_expr (targ);
 
 	      targ = convert (build_reference_type (TREE_TYPE (targ)),
diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
index 3e9067ea9c2b..a4cde584f43f 100644
--- a/gcc/d/d-lang.cc
+++ b/gcc/d/d-lang.cc
@@ -925,7 +925,8 @@ d_post_options (const char ** fn)
     global.params.v.errorLimit = flag_max_errors;
 
   global.params.v.showColumns = flag_show_column;
-  global.params.v.printErrorContext = flag_diagnostics_show_caret;
+  global.params.v.errorPrintMode = flag_diagnostics_show_caret
+    ? ErrorPrintMode::printErrorContext : ErrorPrintMode::simpleError;
 
   /* Keep the front-end location type in sync with params.  */
   Loc::set (global.params.v.showColumns, global.params.v.messageStyle);
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index bfdc9ea21e1e..e5884c6a798a 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
+c57da0cf5945cfb45eed06f1fd820435cda3ee3a
 
 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/astenums.d b/gcc/d/dmd/astenums.d
index 1e30b9f8fc25..551b45317e2d 100644
--- a/gcc/d/dmd/astenums.d
+++ b/gcc/d/dmd/astenums.d
@@ -302,7 +302,7 @@ enum ThreeState : ubyte
 enum TRUST : ubyte
 {
     default_   = 0,
-    system     = 1,    // @system (same as TRUST.default)
+    system     = 1,    // @system (same as TRUST.default_ unless feature "safer" is enabled)
     trusted    = 2,    // @trusted
     safe       = 3,    // @safe
 }
diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d
index 0b5cd9d41c5d..5da4721bba5c 100644
--- a/gcc/d/dmd/attrib.d
+++ b/gcc/d/dmd/attrib.d
@@ -104,16 +104,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol
         return sc2;
     }
 
-
-    override void addComment(const(char)* comment)
-    {
-        //printf("AttribDeclaration::addComment %s\n", comment);
-        if (comment)
-        {
-            this.include(null).foreachDsymbol( s => s.addComment(comment) );
-        }
-    }
-
     override const(char)* kind() const
     {
         return "attribute";
@@ -653,20 +643,6 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration
         }
     }
 
-    override final void addComment(const(char)* comment)
-    {
-        /* Because addComment is called by the parser, if we called
-         * include() it would define a version before it was used.
-         * But it's no problem to drill down to both decl and elsedecl,
-         * so that's the workaround.
-         */
-        if (comment)
-        {
-            decl    .foreachDsymbol( s => s.addComment(comment) );
-            elsedecl.foreachDsymbol( s => s.addComment(comment) );
-        }
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -762,12 +738,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration
         return false;
     }
 
-    override void addComment(const(char)* comment)
-    {
-        // do nothing
-        // change this to give semantics to documentation comments on static foreach declarations
-    }
-
     override const(char)* kind() const
     {
         return "static foreach";
diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h
index d2d18dba18d9..ef01d0f9538f 100644
--- a/gcc/d/dmd/attrib.h
+++ b/gcc/d/dmd/attrib.h
@@ -28,7 +28,6 @@ class AttribDeclaration : public Dsymbol
 {
 public:
     Dsymbols *decl;     // array of Dsymbol's
-    void addComment(const utf8_t *comment) override;
     const char *kind() const override;
     bool oneMember(Dsymbol *&ps, Identifier *ident) override;
     bool hasPointers() override final;
@@ -148,7 +147,6 @@ public:
 
     ConditionalDeclaration *syntaxCopy(Dsymbol *s) override;
     bool oneMember(Dsymbol *&ps, Identifier *ident) override final;
-    void addComment(const utf8_t *comment) override final;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -176,7 +174,6 @@ public:
 
     StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override;
     bool oneMember(Dsymbol *&ps, Identifier *ident) override;
-    void addComment(const utf8_t *comment) override;
     const char *kind() const override;
     void accept(Visitor *v) override { v->visit(this); }
 };
diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d
index bbfb1ee9f87d..1b838603c7e8 100644
--- a/gcc/d/dmd/clone.d
+++ b/gcc/d/dmd/clone.d
@@ -1610,13 +1610,15 @@ private Statement generateCopyCtorBody(StructDeclaration sd)
  * Params:
  *  sd = the `struct` for which the copy constructor is generated
  *  hasCpCtor = set to true if a copy constructor is already present
+ *  hasMoveCtor = set to true if a move constructor is already present
  *
  * Returns:
  *  `true` if one needs to be generated
  *  `false` otherwise
  */
-bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
+bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor, out bool hasMoveCtor)
 {
+    //printf("needCopyCtor() %s\n", sd.toChars());
     if (global.errors)
         return false;
 
@@ -1648,14 +1650,17 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
             return 0;
         }
 
-        if (isRvalueConstructor(sd, ctorDecl))
+        if (ctorDecl.isMoveCtor)
             rvalueCtor = ctorDecl;
         return 0;
     });
 
+    if (rvalueCtor)
+        hasMoveCtor = true;
+
     if (cpCtor)
     {
-        if (rvalueCtor)
+        if (0 && rvalueCtor)
         {
             .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
             errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
@@ -1710,6 +1715,7 @@ LcheckFields:
  * Params:
  *  sd = the `struct` for which the copy constructor is generated
  *  sc = the scope where the copy constructor is generated
+ *  hasMoveCtor = set to true when a move constructor is also detected
  *
  * Returns:
  *  `true` if `struct` sd defines a copy constructor (explicitly or generated),
@@ -1717,10 +1723,10 @@ LcheckFields:
  * References:
  *   https://dlang.org/spec/struct.html#struct-copy-constructor
  */
-bool buildCopyCtor(StructDeclaration sd, Scope* sc)
+bool buildCopyCtor(StructDeclaration sd, Scope* sc, out bool hasMoveCtor)
 {
     bool hasCpCtor;
-    if (!needCopyCtor(sd, hasCpCtor))
+    if (!needCopyCtor(sd, hasCpCtor, hasMoveCtor))
         return hasCpCtor;
 
     //printf("generating copy constructor for %s\n", sd.toChars());
diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d
index 1578ef770a09..9d5aa96416b5 100644
--- a/gcc/d/dmd/constfold.d
+++ b/gcc/d/dmd/constfold.d
@@ -806,6 +806,7 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e
 
 UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2)
 {
+    //printf("Identity %s %s\n", e1.toChars(), e2.toChars());
     UnionExp ue = void;
     int cmp;
     if (e1.op == EXP.null_)
@@ -820,7 +821,17 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio
     {
         SymOffExp es1 = e1.isSymOffExp();
         SymOffExp es2 = e2.isSymOffExp();
-        cmp = (es1.var == es2.var && es1.offset == es2.offset);
+        cmp = es1.offset == es2.offset;
+        if (cmp)
+        {
+            cmp = es1.var == es2.var;
+            if (!cmp && (es1.var.isParameter() || es2.var.isParameter()))
+            {
+                // because of ref's, they may still be the same, we cannot tell
+                cantExp(ue);
+                return ue;
+            }
+        }
     }
     else
     {
diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d
index 995995400a55..e6fb1ed65cd9 100644
--- a/gcc/d/dmd/declaration.d
+++ b/gcc/d/dmd/declaration.d
@@ -30,6 +30,7 @@ import dmd.func;
 import dmd.funcsem : overloadApply, getLevelAndCheck;
 import dmd.globals;
 import dmd.gluelayer;
+import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
@@ -95,6 +96,7 @@ extern (C++) abstract class Declaration : Dsymbol
       enum ignoreRead = 2; // ignore any reads of AliasDeclaration
       enum nounderscore = 4; // don't prepend _ to mangled name
       enum hidden       = 8; // don't print this in .di files
+      enum nrvo = 0x10;      /// forward to fd.nrvo_var when generating code
 
     // overridden symbol with pragma(mangle, "...")
     const(char)[] mangleOverride;
@@ -441,8 +443,7 @@ extern (C++) final class AliasDeclaration : Declaration
     extern (D) this(const ref Loc loc, Identifier ident, Type type) @safe
     {
         super(loc, ident);
-        //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type);
-        //printf("type = '%s'\n", type.toChars());
+        //debug printf("AliasDeclaration(id = '%s', type = `%s`, %p)\n", ident.toChars(), dmd.hdrgen.toChars(type), type.isTypeIdentifier());
         this.type = type;
         assert(type);
     }
@@ -450,7 +451,7 @@ extern (C++) final class AliasDeclaration : Declaration
     extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) @safe
     {
         super(loc, ident);
-        //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s);
+        //debug printf("AliasDeclaration(id = '%s', s = `%s`)\n", ident.toChars(), s.toChars());
         assert(s != this);
         this.aliassym = s;
         assert(s);
@@ -611,8 +612,9 @@ extern (C++) final class AliasDeclaration : Declaration
 
     override Dsymbol toAlias()
     {
-        //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n",
-        //    loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse);
+        static if (0)
+        printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
+            loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse);
         assert(this != aliassym);
         //static int count; if (++count == 10) *(char*)0=0;
 
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index bdefd2d9f864..a98213d9198c 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -782,6 +782,7 @@ class CtorDeclaration final : public FuncDeclaration
 {
 public:
     d_bool isCpCtor;
+    d_bool isMoveCtor;
     CtorDeclaration *syntaxCopy(Dsymbol *) override;
     const char *kind() const override;
     const char *toChars() const override;
diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d
index aa48d57ba999..76627bec3343 100644
--- a/gcc/d/dmd/dscope.d
+++ b/gcc/d/dmd/dscope.d
@@ -24,6 +24,7 @@ import dmd.dclass;
 import dmd.declaration;
 import dmd.dmodule;
 import dmd.doc;
+import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dsymbolsem;
 import dmd.dtemplate;
@@ -146,6 +147,7 @@ extern (C++) struct Scope
 
     AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value,
                                /// do not set wasRead for it
+    StructDeclaration argStruct;    /// elimiate recursion when looking for rvalue construction
 
     extern (D) __gshared Scope* freelist;
 
diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d
index d7b1ace34f0a..abeffcb39d2d 100644
--- a/gcc/d/dmd/dstruct.d
+++ b/gcc/d/dmd/dstruct.d
@@ -101,6 +101,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
         bool hasIdentityEquals;     // true if has identity opEquals
         bool hasNoFields;           // has no fields
         bool hasCopyCtor;           // copy constructor
+        bool hasMoveCtor;           // move constructor
         bool hasPointerField;       // members with indirections
         bool hasVoidInitPointers;   // void-initialized unsafe fields
         bool hasUnsafeBitpatterns;  // @system members, pointers, bool
@@ -171,11 +172,12 @@ extern (C++) class StructDeclaration : AggregateDeclaration
         {
             Dsymbol s = (*members)[i];
             s.setFieldOffset(this, &fieldState, isunion);
-        }
-        if (type.ty == Terror)
-        {
-            errors = true;
-            return;
+            if (type.ty == Terror)
+            {
+                errorSupplemental(s.loc, "error on member `%s`", s.toPrettyChars);
+                errors = true;
+                return;
+            }
         }
 
         if (structsize == 0)
@@ -320,11 +322,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration
         import dmd.clone;
 
         bool hasCpCtorLocal;
-        needCopyCtor(this, hasCpCtorLocal);
+        bool hasMoveCtorLocal;
+        needCopyCtor(this, hasCpCtorLocal, hasMoveCtorLocal);
 
         if (enclosing                      || // is nested
             search(this, loc, Id.postblit) || // has postblit
             search(this, loc, Id.dtor)     || // has destructor
+            /* This is commented out because otherwise buildkite vibe.d:
+               `canCAS!Task` fails to compile
+             */
+            //hasMoveCtorLocal               || // has move constructor
             hasCpCtorLocal)                   // has copy constructor
         {
             ispod = ThreeState.no;
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index af32f7afa1e8..3aed16ab29af 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -925,22 +925,8 @@ extern (C++) class Dsymbol : ASTNode
      */
     void addComment(const(char)* comment)
     {
-        if (!comment || !*comment)
-            return;
-
-        //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars());
-        void* h = cast(void*)this;      // just the pointer is the key
-        auto p = h in commentHashTable;
-        if (!p)
-        {
-            commentHashTable[h] = comment;
-            return;
-        }
-        if (strcmp(*p, comment) != 0)
-        {
-            // Concatenate the two
-            *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true);
-        }
+        import dmd.dsymbolsem;
+        dmd.dsymbolsem.addComment(this, comment);
     }
 
     /// get documentation comment for this Dsymbol
@@ -958,7 +944,7 @@ extern (C++) class Dsymbol : ASTNode
     /* Shell around addComment() to avoid disruption for the moment */
     final void comment(const(char)* comment) { addComment(comment); }
 
-    private extern (D) __gshared const(char)*[void*] commentHashTable;
+    extern (D) __gshared const(char)*[void*] commentHashTable;
 
 
     /**********************************
diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h
index 7b33ed2910bf..3936c3e4cfbb 100644
--- a/gcc/d/dmd/dsymbol.h
+++ b/gcc/d/dmd/dsymbol.h
@@ -433,4 +433,5 @@ namespace dmd
     Dsymbols *include(Dsymbol *d, Scope *sc);
     void setScope(Dsymbol *d, Scope *sc);
     void importAll(Dsymbol *d, Scope *sc);
+    void addComment(Dsymbol *d, const char *comment);
 }
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 173532af397c..7e9843617c92 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -53,6 +53,7 @@ import dmd.init;
 import dmd.initsem;
 import dmd.intrange;
 import dmd.hdrgen;
+import dmd.lexer;
 import dmd.location;
 import dmd.mtype;
 import dmd.mustuse;
@@ -64,6 +65,7 @@ import dmd.parse;
 debug import dmd.printast;
 import dmd.root.array;
 import dmd.root.filename;
+import dmd.root.string;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
 import dmd.rootobject;
@@ -256,6 +258,10 @@ Return:
 */
 bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
 {
+    //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
+    /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
+     * so use isRvalueConstructor()
+     */
     if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
     {
         .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
@@ -280,6 +286,7 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem
  */
 bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
 {
+    // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
     auto tf = ctor.type.isTypeFunction();
     const dim = tf.parameterList.length;
     if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
@@ -306,6 +313,7 @@ bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
  */
 Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
 {
+    //printf("resolveAliasThis() %s\n", toChars(e));
     import dmd.typesem : dotExp;
     for (AggregateDeclaration ad = isAggregate(e.type); ad;)
     {
@@ -2399,7 +2407,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
     override void visit(CtorDeclaration ctd)
     {
-        //printf("CtorDeclaration::semantic() %s\n", toChars());
+        //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
         if (ctd.semanticRun >= PASS.semanticdone)
             return;
         if (ctd._scope)
@@ -2432,6 +2440,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         sc.stc &= ~STC.static_; // not a static constructor
 
         funcDeclarationSemantic(sc, ctd);
+        // Check short constructor: this() => expr;
+        if (ctd.fbody)
+        {
+            if (auto s = ctd.fbody.isExpStatement())
+            {
+                if (s.exp)
+                {
+                    auto ce = s.exp.isCallExp();
+                    // check this/super before semantic
+                    if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp()))
+                    {
+                        s.exp = s.exp.expressionSemantic(sc);
+                        if (s.exp.type.ty != Tvoid)
+                            error(s.loc, "can only return void expression, `this` call or `super` call from constructor");
+                    }
+                }
+            }
+        }
 
         sc.pop();
 
@@ -2482,12 +2508,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             }
             else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
             {
-                //printf("tf: %s\n", tf.toChars());
+                //printf("tf: %s\n", toChars(tf));
                 auto param = tf.parameterList[0];
-                if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+                if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
                 {
-                    //printf("copy constructor\n");
-                    ctd.isCpCtor = true;
+                    //printf("copy constructor %p\n", ctd);
+                    if (param.storageClass & STC.ref_)
+                        ctd.isCpCtor = true;            // copy constructor
+                    else
+                        ctd.isMoveCtor = true;          // move constructor
                 }
             }
         }
@@ -2978,7 +3007,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
         buildDtors(sd, sc2);
 
-        sd.hasCopyCtor = buildCopyCtor(sd, sc2);
+        bool hasMoveCtor;
+        sd.hasCopyCtor = buildCopyCtor(sd, sc2, hasMoveCtor);
+        sd.hasMoveCtor = hasMoveCtor;
+
         sd.postblit = buildPostBlit(sd, sc2);
 
         buildOpAssign(sd, sc2);
@@ -5211,7 +5243,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara
 // function used to perform semantic on AliasDeclaration
 void aliasSemantic(AliasDeclaration ds, Scope* sc)
 {
-    //printf("AliasDeclaration::semantic() %s\n", ds.toChars());
+    //printf("AliasDeclaration::semantic() %s %p\n", ds.toChars(), ds.aliassym);
 
     // as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first.
     // see https://issues.dlang.org/show_bug.cgi?id=21001
@@ -7782,7 +7814,6 @@ private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
         Expression ec;
         ec = new VarExp(vd.loc, vd);
         e = new DeleteExp(vd.loc, ec, true);
-        e.type = Type.tvoid;
         break;
     }
     return e;
@@ -7845,3 +7876,57 @@ Lfail:
     }
     return false;
 }
+
+extern (C++) void addComment(Dsymbol d, const(char)* comment)
+{
+    scope v = new AddCommentVisitor(comment);
+    d.accept(v);
+}
+
+extern (C++) class AddCommentVisitor: Visitor
+{
+    alias visit = Visitor.visit;
+
+    const(char)* comment;
+
+    this(const(char)* comment)
+    {
+        this.comment = comment;
+    }
+
+    override void visit(Dsymbol d)
+    {
+        if (!comment || !*comment)
+            return;
+
+        //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars());
+        void* h = cast(void*)d;      // just the pointer is the key
+        auto p = h in d.commentHashTable;
+        if (!p)
+        {
+            d.commentHashTable[h] = comment;
+            return;
+        }
+        if (strcmp(*p, comment) != 0)
+        {
+            // Concatenate the two
+            *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true);
+        }
+    }
+    override void visit(AttribDeclaration atd)
+    {
+        if (comment)
+        {
+            atd.include(null).foreachDsymbol( s => s.addComment(comment) );
+        }
+    }
+    override void visit(ConditionalDeclaration cd)
+    {
+        if (comment)
+        {
+            cd.decl    .foreachDsymbol( s => s.addComment(comment) );
+            cd.elsedecl.foreachDsymbol( s => s.addComment(comment) );
+        }
+    }
+    override void visit(StaticForeachDeclaration sfd) {}
+}
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index dc72b3a8df1b..355bdc42468e 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -293,10 +293,16 @@ enum WANTexpand = 1;    // expand const/immutable variables if possible
  */
 extern (C++) abstract class Expression : ASTNode
 {
-    Type type;      // !=null means that semantic() has been run
+    /// Usually, this starts out as `null` and gets set to the final expression type by
+    /// `expressionSemantic`. However, for some expressions (such as `TypeExp`,`RealExp`,
+    /// `VarExp`), the field can get set to an assigned type before running semantic.
+    /// See `expressionSemanticDone`
+    Type type;
+
     Loc loc;        // file location
     const EXP op;   // to minimize use of dynamic_cast
     bool parens;    // if this is a parenthesized expression
+    bool rvalue;    // true if this is considered to be an rvalue, even if it is an lvalue
 
     extern (D) this(const ref Loc loc, EXP op) scope @safe
     {
@@ -724,7 +730,7 @@ extern (C++) abstract class Expression : ASTNode
         inout(TypeidExp)    isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; }
         inout(TraitsExp)    isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; }
         inout(HaltExp)      isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; }
-        inout(IsExp)        isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
+        inout(IsExp)        isIsExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
         inout(MixinExp)     isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; }
         inout(ImportExp)    isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; }
         inout(AssertExp)    isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; }
@@ -1307,7 +1313,7 @@ extern (C++) class IdentifierExp : Expression
 
     override final bool isLvalue()
     {
-        return true;
+        return !this.rvalue;
     }
 
     override void accept(Visitor v)
@@ -1351,7 +1357,7 @@ extern (C++) final class DsymbolExp : Expression
 
     override bool isLvalue()
     {
-        return true;
+        return !rvalue;
     }
 
     override void accept(Visitor v)
@@ -1397,7 +1403,7 @@ extern (C++) class ThisExp : Expression
     override final bool isLvalue()
     {
         // Class `this` should be an rvalue; struct `this` should be an lvalue.
-        return type.toBasetype().ty != Tclass;
+        return !rvalue && type.toBasetype().ty != Tclass;
     }
 
     override void accept(Visitor v)
@@ -1782,7 +1788,7 @@ extern (C++) final class StringExp : Expression
         /* string literal is rvalue in default, but
          * conversion to reference of static array is only allowed.
          */
-        return (type && type.toBasetype().ty == Tsarray);
+        return !rvalue && (type && type.toBasetype().ty == Tsarray);
     }
 
     /********************************
@@ -2719,7 +2725,7 @@ extern (C++) final class VarExp : SymbolExp
 
     override bool isLvalue()
     {
-        if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
+        if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
             return false;
         return true;
     }
@@ -3098,7 +3104,7 @@ extern (C++) class BinAssignExp : BinExp
 
     override final bool isLvalue()
     {
-        return true;
+        return !rvalue;
     }
 
     override void accept(Visitor v)
@@ -3212,7 +3218,6 @@ extern (C++) final class ThrowExp : UnaExp
     extern (D) this(const ref Loc loc, Expression e)
     {
         super(loc, EXP.throw_, e);
-        this.type = Type.tnoreturn;
     }
 
     override ThrowExp syntaxCopy()
@@ -3303,6 +3308,8 @@ extern (C++) final class DotVarExp : UnaExp
 
     override bool isLvalue()
     {
+        if (rvalue)
+            return false;
         if (e1.op != EXP.structLiteral)
             return true;
         auto vd = var.isVarDeclaration();
@@ -3530,6 +3537,8 @@ extern (C++) final class CallExp : UnaExp
 
     override bool isLvalue()
     {
+        if (rvalue)
+            return false;
         Type tb = e1.type.toBasetype();
         if (tb.ty == Tdelegate || tb.ty == Tpointer)
             tb = tb.nextOf();
@@ -3648,7 +3657,7 @@ extern (C++) final class PtrExp : UnaExp
 
     override bool isLvalue()
     {
-        return true;
+        return !rvalue;
     }
 
     override void accept(Visitor v)
@@ -3777,7 +3786,7 @@ extern (C++) final class CastExp : UnaExp
     override bool isLvalue()
     {
         //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
-        if (!e1.isLvalue())
+        if (rvalue || !e1.isLvalue())
             return false;
         return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
             e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf());
@@ -3834,7 +3843,7 @@ extern (C++) final class VectorArrayExp : UnaExp
 
     override bool isLvalue()
     {
-        return e1.isLvalue();
+        return !rvalue && e1.isLvalue();
     }
 
     override void accept(Visitor v)
@@ -3891,7 +3900,7 @@ extern (C++) final class SliceExp : UnaExp
         /* slice expression is rvalue in default, but
          * conversion to reference of static array is only allowed.
          */
-        return (type && type.toBasetype().ty == Tsarray);
+        return !rvalue && (type && type.toBasetype().ty == Tsarray);
     }
 
     override Optional!bool toBool()
@@ -3956,6 +3965,8 @@ extern (C++) final class ArrayExp : UnaExp
 
     override bool isLvalue()
     {
+        if (rvalue)
+            return false;
         if (type && type.toBasetype().ty == Tvoid)
             return false;
         return true;
@@ -4005,7 +4016,7 @@ extern (C++) final class CommaExp : BinExp
 
     override bool isLvalue()
     {
-        return e2.isLvalue();
+        return !rvalue && e2.isLvalue();
     }
 
     override Optional!bool toBool()
@@ -4080,7 +4091,7 @@ extern (C++) final class DelegatePtrExp : UnaExp
 
     override bool isLvalue()
     {
-        return e1.isLvalue();
+        return !rvalue && e1.isLvalue();
     }
 
     override void accept(Visitor v)
@@ -4103,7 +4114,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
 
     override bool isLvalue()
     {
-        return e1.isLvalue();
+        return !rvalue && e1.isLvalue();
     }
 
     override void accept(Visitor v)
@@ -4143,6 +4154,8 @@ extern (C++) final class IndexExp : BinExp
 
     override bool isLvalue()
     {
+        if (rvalue)
+            return false;
         auto t1b = e1.type.toBasetype();
         if (t1b.isTypeAArray() || t1b.isTypeSArray() ||
             (e1.isIndexExp() && t1b != t1b.isTypeDArray()))
@@ -4251,7 +4264,7 @@ extern (C++) class AssignExp : BinExp
         {
             return false;
         }
-        return true;
+        return !rvalue;
     }
 
     override void accept(Visitor v)
@@ -4982,7 +4995,7 @@ extern (C++) final class CondExp : BinExp
 
     override bool isLvalue()
     {
-        return e1.isLvalue() && e2.isLvalue();
+        return !rvalue && e1.isLvalue() && e2.isLvalue();
     }
 
     override void accept(Visitor v)
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index c353a191a662..d62aea86d637 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -78,6 +78,7 @@ public:
     Loc loc;                    // file location
     EXP op;                     // to minimize use of dynamic_cast
     d_bool parens;              // if this is a parenthesized expression
+    d_bool rvalue;              // consider this an rvalue, even if it is an lvalue
 
     size_t size() const;
     static void _init();
@@ -138,7 +139,7 @@ public:
     TypeidExp* isTypeidExp();
     TraitsExp* isTraitsExp();
     HaltExp* isHaltExp();
-    IsExp* isExp();
+    IsExp* isIsExp();
     MixinExp* isMixinExp();
     ImportExp* isImportExp();
     AssertExp* isAssertExp();
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index e1baa4887e79..413d31a6339f 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -816,21 +816,58 @@ extern(D) bool arrayExpressionSemantic(
  * Params:
  *   sc = the scope where the expression is encountered
  *   e = the expression the needs to be moved or copied (source)
- *   t = if the struct defines a copy constructor, the type of the destination
- *
+ *   t = if the struct defines a copy constructor, the type of the destination (can be NULL)
+ *   nrvo = true if the generated copy can be treated as NRVO
+ *   move = true to allow a move constructor to be used, false to prevent infinite recursion
  * Returns:
  *  The expression that copy constructs or moves the value.
  */
-extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
+extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo, bool move = false)
 {
+    //printf("doCopyOrMove() %s\n", toChars(e));
+    StructDeclaration sd;
+    if (t)
+    {
+        if (auto ts = t.isTypeStruct())
+            sd = ts.sym;
+    }
+
     if (auto ce = e.isCondExp())
     {
-        ce.e1 = doCopyOrMove(sc, ce.e1);
-        ce.e2 = doCopyOrMove(sc, ce.e2);
+        ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo);
+        ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo);
+    }
+    else if (e.isLvalue())
+    {
+        e = callCpCtor(sc, e, t, nrvo);
+    }
+    else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp())
+    {
+        // #move
+        /* Rewrite as:
+         *    S __copyrvalue;
+         *    __copyrvalue.moveCtor(e);
+         *    __copyrvalue;
+         */
+        VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null);
+        if (nrvo)
+            vd.adFlags |= Declaration.nrvo;
+        vd.storage_class |= STC.nodtor;
+        vd.dsymbolSemantic(sc);
+        Expression de = new DeclarationExp(e.loc, vd);
+        Expression ve = new VarExp(e.loc, vd);
+
+        Expression er;
+        er = new DotIdExp(e.loc, ve, Id.ctor);  // ve.ctor
+        er = new CallExp(e.loc, er, e);         // ve.ctor(e)
+        er = new CommaExp(e.loc, er, new VarExp(e.loc, vd)); // ve.ctor(e),vd
+        er = Expression.combine(de, er);        // de,ve.ctor(e),vd
+
+        e = er.expressionSemantic(sc);
     }
     else
     {
-        e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e);
+        e = valueNoDtor(e);
     }
     return e;
 }
@@ -839,13 +876,15 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
  * If e is an instance of a struct, and that struct has a copy constructor,
  * rewrite e as:
  *    (tmp = e),tmp
- * Input:
+ * Params:
  *      sc = just used to specify the scope of created temporary variable
  *      destinationType = the type of the object on which the copy constructor is called;
  *                        may be null if the struct defines a postblit
+ *      nrvo = true if the generated copy can be treated as NRVO
  */
-private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
+private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo)
 {
+    //printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType));
     auto ts = e.type.baseElemOf().isTypeStruct();
 
     if (!ts)
@@ -860,7 +899,9 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
      * This is not the most efficient, ideally tmp would be constructed
      * directly onto the stack.
      */
-    auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+    VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+    if (nrvo)
+        tmp.adFlags |= Declaration.nrvo;
     if (sd.hasCopyCtor && destinationType)
     {
         // https://issues.dlang.org/show_bug.cgi?id=22619
@@ -888,6 +929,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
  */
 Expression valueNoDtor(Expression e)
 {
+    //printf("valueNoDtor() %s\n", toChars(e));
     auto ex = lastComma(e);
 
     if (auto ce = ex.isCallExp())
@@ -2706,7 +2748,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
             continue;
         }
 
-        e = doCopyOrMove(sc, e);
+        e = doCopyOrMove(sc, e, null, false);
 
         if (!foundType && t0 && !t0.equals(e.type))
         {
@@ -2952,7 +2994,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
     Type* prettype, Expression* peprefix)
 {
     Expressions* arguments = argumentList.arguments;
-    //printf("functionParameters() %s\n", fd ? fd.toChars() : "");
+    //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf));
     assert(arguments);
     assert(fd || tf.next);
     const size_t nparams = tf.parameterList.length;
@@ -3677,7 +3719,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                  */
                 Type tv = arg.type.baseElemOf();
                 if (!isRef && tv.ty == Tstruct)
-                    arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null);
+                    arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null, false);
             }
 
             (*arguments)[i] = arg;
@@ -3886,11 +3928,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
         }
-        if (exp.type) // This is used as the dummy expression
-        {
-            result = exp;
-            return;
-        }
+
+        scope (exit) result.rvalue = exp.rvalue;
 
         Dsymbol scopesym;
         Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
@@ -4109,11 +4148,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("ThisExp::semantic()\n");
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
         AggregateDeclaration ad;
@@ -4174,11 +4208,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("SuperExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         FuncDeclaration fd = hasThis(sc);
         ClassDeclaration cd;
@@ -4255,11 +4284,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("NullExp::semantic('%s')\n", e.toChars());
         }
         // NULL is the same as (void *)0
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
         e.type = Type.tnull;
         result = e;
     }
@@ -4348,11 +4372,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("StringExp::semantic() %s\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         OutBuffer buffer;
         size_t newlen = 0;
@@ -4461,11 +4480,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("+TupleExp::semantic(%s)\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (exp.e0)
             exp.e0 = exp.e0.expressionSemantic(sc);
@@ -4503,11 +4517,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("ArrayLiteralExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         /* Perhaps an empty array literal [ ] should be rewritten as null?
          */
@@ -4550,11 +4559,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         // Run semantic() on each element
         bool err_keys = arrayExpressionSemantic(e.keys.peekSlice(), sc);
@@ -4593,11 +4597,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("StructLiteralExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         e.sd.size(e.loc);
         if (e.sd.sizeok != Sizeok.done)
@@ -4702,11 +4701,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         ScopeDsymbol sds2 = exp.sds;
         TemplateInstance ti = sds2.isTemplateInstance();
@@ -4895,11 +4889,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 printf("\tthisexp = %s\n", exp.thisexp.toChars());
             printf("\tnewtype: %s\n", exp.newtype.toChars());
         }
-        if (exp.type) // if semantic() already run
-        {
-            result = exp;
-            return;
-        }
 
         //for error messages if the argument in [] is not convertible to size_t
         const originalNewtype = exp.newtype;
@@ -5677,7 +5666,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             symtab = sds.symtab;
         }
         assert(symtab);
-        Identifier id = Identifier.generateIdWithLoc(s, exp.loc);
+        Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars()));
         exp.fd.ident = id;
         if (exp.td)
             exp.td.ident = id;
@@ -5693,11 +5682,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 printf("  treq = %s\n", exp.fd.treq.toChars());
         }
 
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         Expression e = exp;
 
@@ -5891,11 +5875,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("CallExp::semantic() %s\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return; // semantic() already run
-        }
 
         Objects* tiargs = null; // initial list of template arguments
         Expression ethis = null;
@@ -6718,7 +6697,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     errorSupplemental(exp.loc, "%s", failMessage);
             }
 
-            if (tf.callMatch(null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch)
+            if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch)
                 return setError();
 
             // Purity and safety check should run after testing arguments matching
@@ -6801,7 +6780,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     exp.f = null;
                 }
 
-                if (tf.callMatch(null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch)
+                if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch)
                     exp.f = null;
             }
             if (!exp.f || exp.f.errors)
@@ -6954,11 +6933,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(DeclarationExp e)
     {
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
         static if (LOGSEMANTIC)
         {
             printf("DeclarationExp::semantic() %s\n", e.toChars());
@@ -7575,11 +7549,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(BinAssignExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         Expression e = exp.op_overload(sc);
         if (e)
@@ -8157,6 +8126,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     {
         import dmd.statementsem;
 
+        te.type = Type.tnoreturn;
         if (throwSemantic(te.loc, te.e1, sc))
             result = te;
         else
@@ -8168,7 +8138,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         static if (LOGSEMANTIC)
         {
             printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
-            //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op));
+            printAST(exp);
         }
 
         if (sc.inCfile)
@@ -8251,11 +8221,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(DotTemplateExp e)
     {
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
         if (Expression ex = unaSemantic(e, sc))
         {
             result = ex;
@@ -8272,11 +8237,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("DotVarExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         exp.var = exp.var.toAlias().isDeclaration();
 
@@ -8444,11 +8404,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
         // Indicate we need to resolve by UFCS.
         Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag);
         if (!e)
@@ -8464,11 +8419,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("DelegateExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         e.e1 = e.e1.expressionSemantic(sc);
 
@@ -8530,11 +8480,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("DotTypeExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (auto e = unaSemantic(exp, sc))
         {
@@ -8552,11 +8497,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("AddrExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = unaSemantic(exp, sc))
         {
@@ -8853,11 +8793,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("PtrExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         Expression e = exp.op_overload(sc);
         if (e)
@@ -8916,11 +8851,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("NegExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         Expression e = exp.op_overload(sc);
         if (e)
@@ -8962,7 +8892,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("UAddExp::semantic('%s')\n", exp.toChars());
         }
-        assert(!exp.type);
 
         Expression e = exp.op_overload(sc);
         if (e)
@@ -8989,11 +8918,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(ComExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         Expression e = exp.op_overload(sc);
         if (e)
@@ -9031,11 +8955,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(NotExp e)
     {
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         e.setNoderefOperand();
 
@@ -9140,11 +9059,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("CastExp::semantic('%s')\n", exp.toChars());
         }
         //static int x; assert(++x < 10);
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if ((sc && sc.inCfile) &&
             exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) &&
@@ -9429,11 +9343,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("VectorExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         exp.e1 = exp.e1.expressionSemantic(sc);
         exp.type = exp.to.typeSemantic(exp.loc, sc);
@@ -9502,11 +9411,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("SliceExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         // operator overloading should be handled in ArrayExp already.
         if (Expression ex = unaSemantic(exp, sc))
@@ -9789,11 +9693,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("ArrayLengthExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         if (Expression ex = unaSemantic(e, sc))
         {
@@ -9812,7 +9711,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("ArrayExp::semantic('%s')\n", exp.toChars());
         }
-        assert(!exp.type);
 
         if (sc.inCfile)
         {
@@ -9886,11 +9784,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     override void visit(CommaExp e)
     {
         //printf("Semantic.CommaExp() %s\n", e.toChars());
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         // Allow `((a,b),(x,y))`
         if (e.allowCommaExp)
@@ -9936,11 +9829,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("IntervalExp::semantic('%s')\n", e.toChars());
         }
-        if (e.type)
-        {
-            result = e;
-            return;
-        }
 
         Expression le = e.lwr;
         le = le.expressionSemantic(sc);
@@ -10015,11 +9903,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("IndexExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         // operator overloading should be handled in ArrayExp already.
         if (!exp.e1.type)
@@ -10242,11 +10125,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("PostExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (sc.inCfile)
         {
@@ -10404,9 +10282,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     {
         static if (LOGSEMANTIC)
         {
-            if (exp.op == EXP.blit)      printf("BlitExp.toElem('%s')\n", exp.toChars());
-            if (exp.op == EXP.assign)    printf("AssignExp.toElem('%s')\n", exp.toChars());
-            if (exp.op == EXP.construct) printf("ConstructExp.toElem('%s')\n", exp.toChars());
+            if (exp.op == EXP.blit)      printf("BlitExp.semantic('%s')\n", exp.toChars());
+            if (exp.op == EXP.assign)    printf("AssignExp.semantic('%s')\n", exp.toChars());
+            if (exp.op == EXP.construct) printf("ConstructExp.semantic('%s')\n", exp.toChars());
         }
 
         void setResult(Expression e, int line = __LINE__)
@@ -10415,11 +10293,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = e;
         }
 
-        if (exp.type)
-        {
-            return setResult(exp);
-        }
-
         Expression e1old = exp.e1;
 
         if (auto e2comma = exp.e2.isCommaExp())
@@ -10876,6 +10749,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                         /* We have a copy constructor for this
                          */
 
+                        //printf("exp: %s\n", toChars(exp));
+                        //printf("e2x: %s\n", toChars(e2x));
                         if (e2x.isLvalue())
                         {
                             if (sd.hasCopyCtor)
@@ -10922,6 +10797,38 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                                 return;
                             }
                         }
+                        else if (sd.hasMoveCtor && !e2x.isCallExp() && !e2x.isStructLiteralExp())
+                        {
+                            // #move
+                            /* The !e2x.isCallExp() is because it is already an rvalue
+                               and the move constructor is unnecessary:
+                                struct S {
+                                    alias TT this;
+                                    long TT();
+                                    this(T)(int x) {}
+                                    this(S);
+                                    this(ref S);
+                                    ~this();
+                                }
+                                S fun(ref S arg);
+                                void test() { S st; fun(st); }
+                             */
+                            /* Rewrite as:
+                             * e1 = init, e1.moveCtor(e2);
+                             */
+                            Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1));
+                            einit.type = e1x.type;
+
+                            Expression e;
+                            e = new DotIdExp(exp.loc, e1x, Id.ctor);
+                            e = new CallExp(exp.loc, e, e2x);
+                            e = new CommaExp(exp.loc, einit, e);
+
+                            //printf("e: %s\n", e.toChars());
+
+                            result = e.expressionSemantic(sc);
+                            return;
+                        }
                         else
                         {
                             /* The struct value returned from the function is transferred
@@ -11815,11 +11722,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(PowAssignExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         Expression e = exp.op_overload(sc);
         if (e)
@@ -11899,11 +11801,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(CatAssignExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         //printf("CatAssignExp::semantic() %s\n", exp.toChars());
         Expression e = exp.op_overload(sc);
@@ -11985,7 +11882,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 ce.trusted = true;
 
             exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast);
-            exp.e2 = doCopyOrMove(sc, exp.e2);
+            exp.e2 = doCopyOrMove(sc, exp.e2, null, false);
         }
         else if (tb1.ty == Tarray &&
                  (tb1next.ty == Tchar || tb1next.ty == Twchar) &&
@@ -12195,11 +12092,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("AddExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -12302,11 +12194,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("MinExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -12554,11 +12441,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     {
         // https://dlang.org/spec/expression.html#cat_expressions
         //printf("CatExp.semantic() %s\n", toChars());
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -12610,7 +12492,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             if (exp.e1.op == EXP.arrayLiteral)
             {
-                exp.e2 = doCopyOrMove(sc, exp.e2);
+                exp.e2 = doCopyOrMove(sc, exp.e2, null, false);
                 // https://issues.dlang.org/show_bug.cgi?id=14686
                 // Postblit call appears in AST, and this is
                 // finally translated  to an ArrayLiteralExp in below optimize().
@@ -12649,7 +12531,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             if (exp.e2.op == EXP.arrayLiteral)
             {
-                exp.e1 = doCopyOrMove(sc, exp.e1);
+                exp.e1 = doCopyOrMove(sc, exp.e1, null, false);
             }
             else if (exp.e2.op == EXP.string_)
             {
@@ -12741,11 +12623,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("MulExp::semantic() %s\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -12841,11 +12718,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(DivExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -12942,11 +12814,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(ModExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -13000,11 +12867,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(PowExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         //printf("PowExp::semantic() %s\n", toChars());
         if (Expression ex = binSemanticProp(exp, sc))
@@ -13080,11 +12942,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     private void visitShift(BinExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -13133,11 +12990,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     private void visitBinaryBitOp(BinExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -13206,11 +13058,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("LogicalExp::semantic() %s\n", exp.toChars());
         }
 
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         exp.setNoderefOperands();
 
@@ -13292,11 +13139,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("CmpExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         exp.setNoderefOperands();
 
@@ -13461,11 +13303,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(InExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (Expression ex = binSemanticProp(exp, sc))
         {
@@ -13531,11 +13368,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     override void visit(EqualExp exp)
     {
         //printf("EqualExp::semantic('%s')\n", exp.toChars());
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         exp.setNoderefOperands();
 
@@ -13754,11 +13586,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(IdentityExp exp)
     {
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         exp.setNoderefOperands();
 
@@ -13822,11 +13649,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("CondExp::semantic('%s')\n", exp.toChars());
         }
-        if (exp.type)
-        {
-            result = exp;
-            return;
-        }
 
         if (auto die = exp.econd.isDotIdExp())
             die.noderef = true;
@@ -14188,9 +14010,25 @@ Expression binSemanticProp(BinExp e, Scope* sc)
     return null;
 }
 
+/// Returns: whether expressionSemantic() has been run on expression `e`
+private bool expressionSemanticDone(Expression e)
+{
+    // Usually, Expression.type gets set by expressionSemantic and is `null` beforehand
+    // There are some exceptions however:
+    return e.type !is null && !(
+        e.isRealExp() // type sometimes gets set already before semantic
+        || e.isTypeExp() // stores its type in the Expression.type field
+        || e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal
+        || e.isVarExp() // type sometimes gets set already before semantic
+    );
+}
+
 // entrypoint for semantic ExpressionSemanticVisitor
 Expression expressionSemantic(Expression e, Scope* sc)
 {
+    if (e.expressionSemanticDone)
+        return e;
+
     scope v = new ExpressionSemanticVisitor(sc);
     e.accept(v);
     return v.result;
@@ -14198,7 +14036,7 @@ Expression expressionSemantic(Expression e, Scope* sc)
 
 private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
 {
-    //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
+    //printf("dotIdSemanticPropX() %s\n", toChars(exp));
     if (Expression ex = unaSemantic(exp, sc))
         return ex;
 
@@ -14326,7 +14164,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
  */
 Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
 {
-    //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars());
+    //printf("dotIdSemanticProp('%s')\n", exp.toChars());
 
     //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
 
@@ -14664,7 +14502,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
 
         const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag);
 
-        Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag);
+        Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag);
         if (e)
         {
             e = e.expressionSemantic(sc);
@@ -15482,6 +15320,7 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
  */
 Expression addDtorHook(Expression e, Scope* sc)
 {
+    //printf("addDtorHook() %s\n", toChars(e));
     Expression visit(Expression exp)
     {
         return exp;
@@ -16510,7 +16349,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
         if (e.op == EXP.error)
             return false;
 
-        (*elements)[i] = doCopyOrMove(sc, e);
+        (*elements)[i] = doCopyOrMove(sc, e, null, false);
     }
     return true;
 }
diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d
index 153befdb9a98..9c5a3d0bbb96 100644
--- a/gcc/d/dmd/func.d
+++ b/gcc/d/dmd/func.d
@@ -269,6 +269,10 @@ extern (C++) class FuncDeclaration : Declaration
      */
     VarDeclarations outerVars;
 
+    // Most recent encountered `main` (`WinMain` or `DllMain`) function.
+    // Track it to give error messages for multiple entrypoints
+    __gshared FuncDeclaration lastMain;
+
     /// Sibling nested functions which called this one
     FuncDeclarations siblingCallers;
 
@@ -1366,11 +1370,13 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration
  */
 extern (C++) final class CtorDeclaration : FuncDeclaration
 {
-    bool isCpCtor;
-    extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false)
+    bool isCpCtor;    // copy constructor
+    bool isMoveCtor;  // move constructor (aka rvalue constructor)
+    extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false, bool isMoveCtor = false)
     {
         super(loc, endloc, Id.ctor, stc, type);
         this.isCpCtor = isCpCtor;
+        this.isMoveCtor = isMoveCtor;
         //printf("CtorDeclaration(loc = %s) %s %p\n", loc.toChars(), toChars(), this);
     }
 
diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d
index bfa0faca60f3..eba93973a48e 100644
--- a/gcc/d/dmd/funcsem.d
+++ b/gcc/d/dmd/funcsem.d
@@ -154,6 +154,28 @@ public:
     }
 }
 
+/****************************************
+ * Only one entry point function is allowed. Print error if more than one.
+ * Params:
+ *      fd = a "main" function
+ * Returns:
+ *      true if haven't seen "main" before
+ */
+extern (C++) bool onlyOneMain(FuncDeclaration fd)
+{
+    if (auto lastMain = FuncDeclaration.lastMain)
+    {
+        const format = (target.os == Target.OS.Windows)
+            ? "only one entry point `main`, `WinMain` or `DllMain` is allowed"
+            : "only one entry point `main` is allowed";
+        error(fd.loc, format.ptr);
+        errorSupplemental(lastMain.loc, "previously found `%s` here", lastMain.toFullSignature());
+        return false;
+    }
+    FuncDeclaration.lastMain = fd;
+    return true;
+}
+
 /**********************************
  * Main semantic routine for functions.
  */
@@ -1507,6 +1529,7 @@ enum FuncResolveFlag : ubyte
 FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
     Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags)
 {
+    //printf("resolveFuncCall() %s\n", s.toChars());
     auto fargs = argumentList.arguments;
     if (!s)
         return null; // no match
@@ -2066,7 +2089,7 @@ MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* name
         args.push(e);
     }
 
-    MATCH m = tg.callMatch(null, ArgumentList(&args, names), 1);
+    MATCH m = callMatch(g, tg, null, ArgumentList(&args, names), 1);
     if (m > MATCH.nomatch)
     {
         /* A variadic parameter list is less specialized than a
@@ -2939,6 +2962,7 @@ extern (D) void checkMain(FuncDeclaration fd)
  */
 extern (D) bool checkNRVO(FuncDeclaration fd)
 {
+    //printf("checkNRVO*() %s\n", fd.ident.toChars());
     if (!fd.isNRVO() || fd.returns is null)
         return false;
 
diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d
index 88b27d20bb54..901561fcf8e1 100644
--- a/gcc/d/dmd/globals.d
+++ b/gcc/d/dmd/globals.d
@@ -82,6 +82,13 @@ enum CLIIdentifierTable : ubyte
     All      = 4, /// The least restrictive set of all other tables
 }
 
+/// Specifies the mode for error printing
+enum ErrorPrintMode : ubyte
+{
+    simpleError,      // Print errors without squiggles and carets
+    printErrorContext, // Print errors with context (source line and caret)
+}
+
 extern(C++) struct Output
 {
     bool doOutput;      // Output is enabled
@@ -126,10 +133,10 @@ extern(C++) struct Verbose
     bool complex = true;    // identify complex/imaginary type usage
     bool vin;               // identify 'in' parameters
     bool showGaggedErrors;  // print gagged errors anyway
-    bool printErrorContext; // print errors with the error context (the error line in the source file)
     bool logo;              // print compiler logo
     bool color;             // use ANSI colors in console output
     bool cov;               // generate code coverage data
+    ErrorPrintMode errorPrintMode; // enum for error printing mode
     MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages
     uint errorLimit = 20;
     uint errorSupplementLimit = 6;      // Limit the number of supplemental messages for each error (0 means unlimited)
diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h
index c5659ea10b62..669f83e2101f 100644
--- a/gcc/d/dmd/globals.h
+++ b/gcc/d/dmd/globals.h
@@ -94,6 +94,13 @@ enum class CLIIdentifierTable : unsigned char
     All      = 4, /// The least restrictive set of all other tables
 };
 
+/// Specifies the mode for error printing
+enum class ErrorPrintMode : unsigned char
+{
+    simpleError,        // Print errors without squiggles and carets
+    printErrorContext,  // Print errors with the error line and caret
+};
+
 struct Output
 {
     /// Configuration for the compiler generator
@@ -138,10 +145,10 @@ struct Verbose
     d_bool complex = true;     // identify complex/imaginary type usage
     d_bool vin;                // identify 'in' parameters
     d_bool showGaggedErrors;   // print gagged errors anyway
-    d_bool printErrorContext;  // print errors with the error context (the error line in the source file)
     d_bool logo;               // print compiler logo
     d_bool color;              // use ANSI colors in console output
     d_bool cov;                // generate code coverage data
+    ErrorPrintMode errorPrintMode; // enum for error printing mode
     MessageStyle messageStyle; // style of file/line annotations on messages
     unsigned errorLimit;
     unsigned errorSupplementLimit; // Limit the number of supplemental messages for each error (0 means unlimited)
@@ -192,7 +199,7 @@ struct Param
                                  // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
                                  // Implementation: https://github.com/dlang/dmd/pull/9817
     FeatureState safer;          // safer by default (more @safe checks in unattributed code)
-				 // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
+                                 // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
 
     FeatureState noSharedAccess; // read/write access to shared memory objects
     d_bool previewIn;              // `in` means `[ref] scope const`, accepts rvalues
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index 86131f2f11c9..cfe4262ce44f 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -305,10 +305,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
         buf.writenl();
     }
 
-    void visitWhile(WhileStatement s)
+    void printConditionAssignment(Parameter p, Expression condition)
     {
-        buf.writestring("while (");
-        if (auto p = s.param)
+        if (p)
         {
             // Print condition assignment
             StorageClass stc = p.storageClass;
@@ -322,7 +321,13 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
                 buf.writestring(p.ident.toString());
             buf.writestring(" = ");
         }
-        s.condition.expressionToBuffer(buf, hgs);
+        condition.expressionToBuffer(buf, hgs);
+    }
+
+    void visitWhile(WhileStatement s)
+    {
+        buf.writestring("while (");
+        printConditionAssignment(s.param, s.condition);
         buf.writeByte(')');
         buf.writenl();
         if (s._body)
@@ -460,20 +465,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
     void visitIf(IfStatement s)
     {
         buf.writestring("if (");
-        if (Parameter p = s.prm)
-        {
-            StorageClass stc = p.storageClass;
-            if (!p.type && !stc)
-                stc = STC.auto_;
-            if (stcToBuffer(buf, stc))
-                buf.writeByte(' ');
-            if (p.type)
-                typeToBuffer(p.type, p.ident, buf, hgs);
-            else
-                buf.writestring(p.ident.toString());
-            buf.writestring(" = ");
-        }
-        s.condition.expressionToBuffer(buf, hgs);
+        printConditionAssignment(s.prm, s.condition);
         buf.writeByte(')');
         buf.writenl();
         if (s.ifbody.isScopeStatement())
@@ -572,21 +564,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
     void visitSwitch(SwitchStatement s)
     {
         buf.writestring(s.isFinal ? "final switch (" : "switch (");
-        if (auto p = s.param)
-        {
-            // Print condition assignment
-            StorageClass stc = p.storageClass;
-            if (!p.type && !stc)
-                stc = STC.auto_;
-            if (stcToBuffer(buf, stc))
-                buf.writeByte(' ');
-            if (p.type)
-                typeToBuffer(p.type, p.ident, buf, hgs);
-            else
-                buf.writestring(p.ident.toString());
-            buf.writestring(" = ");
-        }
-        s.condition.expressionToBuffer(buf, hgs);
+        printConditionAssignment(s.param, s.condition);
         buf.writeByte(')');
         buf.writenl();
         if (s._body)
@@ -1718,7 +1696,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     void visitFuncDeclaration(FuncDeclaration f)
     {
         //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars());
-        if (stcToBuffer(buf, f.storage_class))
+
+        // https://issues.dlang.org/show_bug.cgi?id=24891
+        // return/scope storage classes are printed as part of function type
+        if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope)))
             buf.writeByte(' ');
         typeToBuffer(f.type, f.ident, buf, hgs);
         auto tf = f.type.isTypeFunction();
@@ -2888,6 +2869,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         buf.writestring(e.value.toChars());
     }
 
+    if (e.rvalue)
+        buf.writestring("__rvalue(");
+
+    scope (exit)
+        if (e.rvalue)
+            buf.writeByte(')');
+
     switch (e.op)
     {
         default:
@@ -2929,7 +2917,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         case EXP.typeid_:       return visitTypeid(e.isTypeidExp());
         case EXP.traits:        return visitTraits(e.isTraitsExp());
         case EXP.halt:          return visitHalt(e.isHaltExp());
-        case EXP.is_:           return visitIs(e.isExp());
+        case EXP.is_:           return visitIs(e.isIsExp());
         case EXP.comma:         return visitComma(e.isCommaExp());
         case EXP.mixin_:        return visitMixin(e.isMixinExp());
         case EXP.import_:       return visitImport(e.isImportExp());
diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d
index aae07bc15370..ee4214a4c123 100644
--- a/gcc/d/dmd/id.d
+++ b/gcc/d/dmd/id.d
@@ -519,6 +519,7 @@ immutable Msgtable[] msgtable =
     { "getLocation" },
     { "hasPostblit" },
     { "hasCopyConstructor" },
+    { "hasMoveConstructor" },
     { "isCopyable" },
     { "toType" },
     { "parameters" },
diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d
index 6fd0d3ad5ec6..74be1beb29f9 100644
--- a/gcc/d/dmd/identifier.d
+++ b/gcc/d/dmd/identifier.d
@@ -211,11 +211,14 @@ nothrow:
      * Params:
      *      prefix      = first part of the identifier name.
      *      loc         = source location to use in the identifier name.
+     *      parent      = (optional) extra part to be used in uniqueness check,
+     *                    if (prefix1, loc1) == (prefix2, loc2), but
+     *                    parent1 != parent2, no new name will be generated.
      * Returns:
      *      Identifier (inside Identifier.idPool) with deterministic name based
      *      on the source location.
      */
-    extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc)
+    extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc, string parent = "")
     {
         // generate `<prefix>_L<line>_C<col>`
         OutBuffer idBuf;
@@ -234,14 +237,20 @@ nothrow:
          * https://issues.dlang.org/show_bug.cgi?id=18880
          * https://issues.dlang.org/show_bug.cgi?id=18868
          * https://issues.dlang.org/show_bug.cgi?id=19058
+         *
+         * It is a bit trickier for lambdas/dgliterals: we want them to be unique per
+         * module/mixin + function/template instantiation context. So we use extra parent
+         * argument for that when dealing with lambdas. We could have added it to prefix
+         * directly, but that would unnecessary lengthen symbols names. See issue:
+         * https://issues.dlang.org/show_bug.cgi?id=23722
          */
-        static struct Key { Loc loc; string prefix; }
+        static struct Key { Loc loc; string prefix; string parent; }
         __gshared uint[Key] counters;
 
         static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
         {
             // 2.082+
-            counters.update(Key(loc, prefix),
+            counters.update(Key(loc, prefix, parent),
                 () => 1u,          // insertion
                 (ref uint counter) // update
                 {
@@ -253,7 +262,7 @@ nothrow:
         }
         else
         {
-            const key = Key(loc, prefix);
+            const key = Key(loc, prefix, parent);
             if (auto pCounter = key in counters)
             {
                 idBuf.writestring("_");
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index 505a3e13636f..0c13bc70dadc 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -1630,7 +1630,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope*
             continue;
         }
 
-        elems[fieldi] = doCopyOrMove(sc, ex);
+        elems[fieldi] = doCopyOrMove(sc, ex, null, false);
         ++fieldi;
     }
     if (errors)
diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d
index 021387d6f438..174a2d95cd20 100644
--- a/gcc/d/dmd/json.d
+++ b/gcc/d/dmd/json.d
@@ -42,7 +42,8 @@ import dmd.target;
 import dmd.visitor;
 
 version(Windows) {
-    extern (C) char* getcwd(char* buffer, size_t maxlen);
+    extern (C) char* _getcwd(char* buffer, size_t maxlen);
+    alias getcwd = _getcwd;
 } else {
     import core.sys.posix.unistd : getcwd;
 }
diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d
index 6d57467314ce..4a4254fcbdd3 100644
--- a/gcc/d/dmd/mtype.d
+++ b/gcc/d/dmd/mtype.d
@@ -1609,7 +1609,7 @@ extern (C++) abstract class TypeNext : Type
 
     /*******************************
      * For TypeFunction, nextOf() can return NULL if the function return
-     * type is meant to be inferred, and semantic() hasn't yet ben run
+     * type is meant to be inferred, and semantic() hasn't yet been run
      * on the function. After semantic(), it must no longer be NULL.
      */
     override final Type nextOf() @safe
@@ -3543,17 +3543,19 @@ extern (C++) final class TypeTuple : Type
     extern (D) this(Expressions* exps)
     {
         super(Ttuple);
-        auto arguments = new Parameters(exps ? exps.length : 0);
-        if (exps)
+        if (!exps)
         {
-            for (size_t i = 0; i < exps.length; i++)
-            {
-                Expression e = (*exps)[i];
-                if (e.type.ty == Ttuple)
-                    error(e.loc, "cannot form sequence of sequences");
-                auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null);
-                (*arguments)[i] = arg;
-            }
+            this.arguments = new Parameters(0);
+            return;
+        }
+        auto arguments = new Parameters(exps.length);
+
+        for (size_t i = 0; i < exps.length; i++)
+        {
+            Expression e = (*exps)[i];
+            assert(e.type.ty != Ttuple);
+            auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null);
+            (*arguments)[i] = arg;
         }
         this.arguments = arguments;
         //printf("TypeTuple() %p, %s\n", this, toChars());
diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h
index eced43eeacff..e69d2ba5fbaa 100644
--- a/gcc/d/dmd/mtype.h
+++ b/gcc/d/dmd/mtype.h
@@ -469,7 +469,7 @@ enum RET
 enum class TRUST : unsigned char
 {
     default_ = 0,
-    system = 1,    // @system (same as TRUSTdefault)
+    system = 1,    // @system (same as TRUST.default_ unless feature "safer" is enabled)
     trusted = 2,   // @trusted
     safe = 3       // @safe
 };
diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d
index 5cd928ca5f41..c5ee77ae791e 100644
--- a/gcc/d/dmd/objc.d
+++ b/gcc/d/dmd/objc.d
@@ -93,39 +93,66 @@ struct ObjcSelector
         return sel;
     }
 
+    static const(char)[] toPascalCase(const(char)[] id) {
+        OutBuffer buf;
+        char firstChar = id[0];
+        if (firstChar >= 'a' && firstChar <= 'z')
+            firstChar = cast(char)(firstChar - 'a' + 'A');
+
+        buf.writeByte(firstChar);
+        buf.writestring(id[1..$]);
+        return cast(const(char)[])buf.extractSlice(false);
+    }
+
     extern (C++) static ObjcSelector* create(FuncDeclaration fdecl)
     {
         OutBuffer buf;
         auto ftype = cast(TypeFunction)fdecl.type;
         const id = fdecl.ident.toString();
         const nparams = ftype.parameterList.length;
+
         // Special case: property setter
         if (ftype.isProperty && nparams == 1)
         {
-            // rewrite "identifier" as "setIdentifier"
-            char firstChar = id[0];
-            if (firstChar >= 'a' && firstChar <= 'z')
-                firstChar = cast(char)(firstChar - 'a' + 'A');
-            buf.writestring("set");
-            buf.writeByte(firstChar);
-            buf.write(id[1 .. id.length - 1]);
+
+            // Special case: "isXYZ:"
+            if (id.length >= 2 && id[0..2] == "is")
+            {
+                buf.writestring("set");
+                buf.write(toPascalCase(id[2..$]));
+            }
+            else
+            {
+                buf.writestring("set");
+                buf.write(toPascalCase(id));
+            }
             buf.writeByte(':');
             goto Lcomplete;
         }
+
         // write identifier in selector
         buf.write(id[]);
-        // add mangled type and colon for each parameter
-        if (nparams)
+
+        // To make it easier to match the selectors of objects nicely,
+        // the implementation has been replaced so that the parameter name followed by a colon
+        // is used instead.
+        // eg. void myFunction(int a, int b, int c) would be mangled to a selector as `myFunction:b:c:
+        if (nparams > 1)
         {
-            buf.writeByte('_');
-            foreach (i, fparam; ftype.parameterList)
+            buf.writeByte(':');
+            foreach(i; 1..nparams)
             {
-                mangleToBuffer(fparam.type, buf);
+                buf.write(ftype.parameterList[i].ident.toString());
                 buf.writeByte(':');
             }
         }
+        else if (nparams == 1)
+        {
+            buf.writeByte(':');
+        }
     Lcomplete:
         buf.writeByte('\0');
+
         // the slice is not expected to include a terminating 0
         return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams);
     }
@@ -565,6 +592,16 @@ extern(C++) private final class Supported : Objc
 
             return 0;
         });
+
+        // Avoid attempting to generate selectors for template instances.
+        if (fd.parent && fd.parent.isTemplateInstance())
+            return;
+
+        // No selector declared, generate one.
+        if (fd._linkage == LINK.objc && !fd.objc.selector)
+        {
+            fd.objc.selector = ObjcSelector.create(fd);
+        }
     }
 
     override void validateSelector(FuncDeclaration fd)
diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d
index 3e145be5499f..fd428389f618 100644
--- a/gcc/d/dmd/parse.d
+++ b/gcc/d/dmd/parse.d
@@ -932,22 +932,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 {
                     const attrLoc = token.loc;
 
-                    nextToken();
-
-                    AST.Expression e = null; // default
-                    if (token.value == TOK.leftParenthesis)
-                    {
-                        nextToken();
-                        e = parseAssignExp();
-                        check(TOK.rightParenthesis);
-                    }
+                    AST.Expression e = parseAlign();
 
                     if (pAttrs.setAlignment)
                     {
                         if (e)
                             error("redundant alignment attribute `align(%s)`", e.toChars());
                         else
-                            error("redundant alignment attribute `align`");
+                            error("redundant alignment attribute `align(default)`");
                     }
 
                     pAttrs.setAlignment = true;
@@ -4371,14 +4363,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 }
             case TOK.align_:
                 {
-                    nextToken();
                     setAlignment = true;
-                    if (token.value == TOK.leftParenthesis)
-                    {
-                        nextToken();
-                        ealign = parseExpression();
-                        check(TOK.rightParenthesis);
-                    }
+                    ealign = parseAlign();
                     continue;
                 }
             default:
@@ -4388,6 +4374,27 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         }
     }
 
+    /**
+     * Parse `align` or `align(n)`
+     * Returns:
+     *  expression `n` if it is present, or `null` otherwise.
+     */
+    private AST.Expression parseAlign()
+    {
+        assert(token.value == TOK.align_);
+        AST.Expression e = null;
+        nextToken();
+        if (token.value == TOK.leftParenthesis)
+        {
+            nextToken();
+            if (token.value == TOK.default_)
+                nextToken();
+            else
+                e = parseAssignExp();
+            check(TOK.rightParenthesis);
+        }
+        return e;
+    }
     /**********************************
      * Parse Declarations.
      * These can be:
@@ -5252,7 +5259,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 error("missing `do { ... }` after `in` or `out`");
             const returnloc = token.loc;
             nextToken();
-            f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
+            if (f.isCtorDeclaration)
+                f.fbody = new AST.ExpStatement(returnloc, parseExpression());
+            else
+                f.fbody = new AST.ReturnStatement(returnloc, parseExpression());
             f.endloc = token.loc;
             check(TOK.semicolon);
             break;
@@ -5877,6 +5887,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         case TOK.moduleString:
         case TOK.functionString:
         case TOK.prettyFunction:
+        case TOK.rvalue:
         Lexp:
             {
                 AST.Expression exp = parseExpression();
@@ -8423,6 +8434,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 e = new AST.TypeidExp(loc, o);
                 break;
             }
+        case TOK.rvalue:
+            {
+                nextToken();
+                check(TOK.leftParenthesis, "`__rvalue`");
+                e = parseAssignExp();
+                e.rvalue = true;
+                check(TOK.rightParenthesis);
+                break;
+            }
         case TOK.traits:
             {
                 /* __traits(identifier, args...)
diff --git a/gcc/d/dmd/printast.d b/gcc/d/dmd/printast.d
index 02dc65390b03..26d27707c169 100644
--- a/gcc/d/dmd/printast.d
+++ b/gcc/d/dmd/printast.d
@@ -51,6 +51,12 @@ extern (C++) final class PrintASTVisitor : Visitor
         printf("%.*s %s\n", cast(int)s.length, s.ptr, e.type ? e.type.toChars() : "");
     }
 
+    override void visit(IdentifierExp e)
+    {
+        printIndent(indent);
+        printf("Identifier `%s` %s\n", e.ident.toChars(), e.type ? e.type.toChars() : "");
+    }
+
     override void visit(IntegerExp e)
     {
         printIndent(indent);
diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d
index a2f074dbfaec..cf00d8ad7f63 100644
--- a/gcc/d/dmd/root/filename.d
+++ b/gcc/d/dmd/root/filename.d
@@ -42,7 +42,8 @@ version (Windows)
 
     extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc;
     extern (Windows) void SetLastError(DWORD) nothrow @nogc;
-    extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow;
+    extern (C) char* _getcwd(char* buffer, size_t maxlen) nothrow;
+    alias getcwd = _getcwd;
 }
 
 version (CRuntime_Glibc)
diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h
index 7983a7ac93f2..ac112664ae8a 100644
--- a/gcc/d/dmd/scope.h
+++ b/gcc/d/dmd/scope.h
@@ -143,6 +143,7 @@ struct Scope final
 
     AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value,
                                 // do not set wasRead for it
+    StructDeclaration *argStruct; // elimiate recursion when looking for rvalue construction
 
     Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all);
 };
diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d
index 4f10982a2d1c..1ea1ca12950f 100644
--- a/gcc/d/dmd/semantic3.d
+++ b/gcc/d/dmd/semantic3.d
@@ -940,7 +940,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                              * If NRVO is not possible, all returned lvalues should call their postblits.
                              */
                             if (!funcdecl.isNRVO())
-                                exp = doCopyOrMove(sc2, exp, f.next);
+                                exp = doCopyOrMove(sc2, exp, f.next, true, true);
 
                             if (tret.hasPointers())
                                 checkReturnEscape(*sc2, exp, false);
diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d
index 8138bd2eb952..d259abf47b0e 100644
--- a/gcc/d/dmd/statementsem.d
+++ b/gcc/d/dmd/statementsem.d
@@ -2460,7 +2460,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
         /* https://dlang.org/spec/statement.html#return-statement
          */
 
-        //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars());
+        //printf("ReturnStatement.dsymbolSemantic() %s\n", toChars(rs));
 
         FuncDeclaration fd = sc.parent.isFuncDeclaration();
         if (fd.fes)
diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d
index 0e9c4338a052..8be9730dda5b 100644
--- a/gcc/d/dmd/templatesem.d
+++ b/gcc/d/dmd/templatesem.d
@@ -623,7 +623,7 @@ MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td, TemplateDeclaration
     enum LOG_LEASTAS = 0;
     static if (LOG_LEASTAS)
     {
-        printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars());
+        printf("%s.leastAsSpecialized(%s)\n", td.toChars(), td2.toChars());
     }
 
     /* This works by taking the template parameters to this template
@@ -1890,23 +1890,22 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
 {
     version (none)
     {
-        printf("functionResolve() dstart = %s\n", dstart.toChars());
-        printf("    tiargs:\n");
-        if (tiargs)
+        printf("functionResolve() dstart: %s (", dstart.toChars());
+        for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++)
         {
-            for (size_t i = 0; i < tiargs.length; i++)
-            {
-                RootObject arg = (*tiargs)[i];
-                printf("\t%s\n", arg.toChars());
-            }
+            if (i) printf(", ");
+            RootObject arg = (*tiargs)[i];
+            printf("%s", arg.toChars());
         }
-        printf("    fargs:\n");
-        for (size_t i = 0; i < (fargs ? fargs.length : 0); i++)
+        printf(")(");
+        for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++)
         {
-            Expression arg = (*fargs)[i];
-            printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
+            if (i) printf(", ");
+            Expression arg = (*argumentList.arguments)[i];
+            printf("%s %s", arg.type.toChars(), arg.toChars());
             //printf("\tty = %d\n", arg.type.ty);
         }
+        printf(")\n");
         //printf("stc = %llx\n", dstart._scope.stc);
         //printf("match:t/f = %d/%d\n", ta_last, m.last);
     }
@@ -1993,7 +1992,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
                 tf.mod = tthis_fd.mod;
         }
         const(char)* failMessage;
-        MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc);
+        MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, errorHelper, sc);
         //printf("test1: mfa = %d\n", mfa);
         if (failMessage)
             errorHelper(failMessage);
@@ -2198,7 +2197,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
             Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null;
 
             auto tf = fd.type.isTypeFunction();
-            MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc);
+            MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, null, sc);
             if (mfa < m.last)
                 return 0;
 
@@ -2300,8 +2299,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
                 // Disambiguate by tf.callMatch
                 auto tf1 = fd.type.isTypeFunction();
                 auto tf2 = m.lastf.type.isTypeFunction();
-                MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc);
-                MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc);
+                MATCH c1 = callMatch(fd,      tf1, tthis_fd,   argumentList, 0, null, sc);
+                MATCH c2 = callMatch(m.lastf, tf2, tthis_best, argumentList, 0, null, sc);
                 //printf("2: c1 = %d, c2 = %d\n", c1, c2);
                 if (c1 > c2) goto Ltd;
                 if (c1 < c2) goto Ltd_best;
@@ -2404,7 +2403,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
         if (m.lastf.type.ty == Terror)
             goto Lerror;
         auto tf = m.lastf.type.isTypeFunction();
-        if (!tf.callMatch(tthis_best, argumentList, 0, null, sc))
+        if (callMatch(m.lastf, tf, tthis_best, argumentList, 0, null, sc) == MATCH.nomatch)
             goto Lnomatch;
 
         /* As https://issues.dlang.org/show_bug.cgi?id=3682 shows,
diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d
index da4a3ee209ef..b499c008eab3 100644
--- a/gcc/d/dmd/tokens.d
+++ b/gcc/d/dmd/tokens.d
@@ -27,6 +27,9 @@ enum TOK : ubyte
 {
     reserved,
 
+    // if this list changes, update
+    // tokens.h, ../tests/cxxfrontend.cc and ../../test/unit/lexer/location_offset.d to match
+
     // Other
     leftParenthesis,
     rightParenthesis,
@@ -249,6 +252,7 @@ enum TOK : ubyte
     wchar_tLiteral,
     endOfLine,  // \n, \r, \u2028, \u2029
     whitespace,
+    rvalue,
 
     // C only keywords
     inline,
@@ -425,6 +429,7 @@ enum EXP : ubyte
     interval,
 
     loweredAssignExp,
+    rvalue,
 }
 
 enum FirstCKeyword = TOK.inline;
@@ -556,6 +561,7 @@ private immutable TOK[] keywords =
     TOK.prettyFunction,
     TOK.shared_,
     TOK.immutable_,
+    TOK.rvalue,
 
     // C only keywords
     TOK.inline,
@@ -680,6 +686,7 @@ extern (C++) struct Token
         TOK.pragma_: "pragma",
         TOK.typeof_: "typeof",
         TOK.typeid_: "typeid",
+        TOK.rvalue: "__rvalue",
         TOK.template_: "template",
         TOK.void_: "void",
         TOK.int8: "byte",
diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h
index 929897a3fa6f..2a984b4b7b62 100644
--- a/gcc/d/dmd/tokens.h
+++ b/gcc/d/dmd/tokens.h
@@ -258,6 +258,7 @@ enum class TOK : unsigned char
     wchar_tLiteral,
     endOfLine,  // \n, \r, \u2028, \u2029
     whitespace,
+    rvalue,
 
     // C only keywords
     inline_,
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
index f7f4cd2bb5e2..34cdf816eb0f 100644
--- a/gcc/d/dmd/traits.d
+++ b/gcc/d/dmd/traits.d
@@ -544,7 +544,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
         }
         return True();
     }
-    if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit)
+    if (e.ident == Id.hasCopyConstructor ||
+        e.ident == Id.hasMoveConstructor ||
+        e.ident == Id.hasPostblit)
     {
         if (dim != 1)
             return dimError(1);
@@ -562,8 +564,14 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
         auto ts = tb.isTypeStruct();
         if (auto sd = ts ? ts.sym : null)
         {
-            return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False())
-                 : (sd.hasCopyCtor ? True() : False());
+            bool result;
+            if (e.ident == Id.hasPostblit)
+                result = sd.postblit !is null;
+            else if (e.ident == Id. hasCopyConstructor)
+                result = sd.hasCopyCtor;
+            else
+                result = sd.hasMoveCtor;
+            return result ? True() : False();
         }
         return False();
     }
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index aea969a2e08d..2ec88f2363a0 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -681,6 +681,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc)
  * 'args' are being matched to function type 'tf'
  * Determine match level.
  * Params:
+ *      fd = function being called, if a symbol
  *      tf = function type
  *      tthis = type of `this` pointer, null if not member function
  *      argumentList = arguments to function call
@@ -690,9 +691,10 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc)
  * Returns:
  *      MATCHxxxx
  */
-extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentList, int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null)
+extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, ArgumentList argumentList,
+        int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null)
 {
-    //printf("TypeFunction::callMatch() %s\n", tf.toChars());
+    //printf("callMatch() fd: %s, tf: %s\n", fd ? fd.ident.toChars() : "null", toChars(tf));
     MATCH match = MATCH.exact; // assume exact match
     ubyte wildmatch = 0;
 
@@ -820,7 +822,7 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis
             Expression arg = args[u];
             if (!arg)
                 continue; // default argument
-            m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage);
+            m = argumentMatchParameter(fd, tf, p, arg, wildmatch, flag, sc, pMessage);
             if (failMessage)
             {
                 buf.reset();
@@ -887,14 +889,15 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis
  *
  * This is done by seeing if a call to the copy constructor can be made:
  * ```
- * typeof(tprm) __copytmp;
- * copytmp.__copyCtor(arg);
+ * typeof(tprm) __copytemp;
+ * copytemp.__copyCtor(arg);
  * ```
  */
 private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
     Expression arg, Type tprm, Scope* sc, const(char)** pMessage)
 {
-    auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null);
+    //printf("isCopyConstructorCallable() argStruct: %s arg: %s tprm: %s\n", argStruct.toChars(), toChars(arg), toChars(tprm));
+    auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytemp"), null);
     tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe;
     tmp.dsymbolSemantic(sc);
     Expression ve = new VarExp(arg.loc, tmp);
@@ -980,25 +983,28 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
  *
  * This function is called by `TypeFunction.callMatch` while iterating over
  * the list of parameter. Here we check if `arg` is a match for `p`,
- * which is mostly about checking if `arg.type` converts to `p`'s type
+ * which is mostly about checking if `arg.type` converts to type of `p`
  * and some check about value reference.
  *
  * Params:
+ *   fd = the function being called if symbol, null if not
  *   tf = The `TypeFunction`, only used for error reporting
  *   p = The parameter of `tf` being matched
  *   arg = Argument being passed (bound) to `p`
  *   wildmatch = Wild (`inout`) matching level, derived from the full argument list
- *   flag = A non-zero value means we're doing a partial ordering check
+ *   flag = A non-zero value means we are doing a partial ordering check
  *          (no value semantic check)
  *   sc = Scope we are in
  *   pMessage = A buffer to write the error in, or `null`
  *
  * Returns: Whether `trailingArgs` match `p`.
  */
-private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
+private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction tf, Parameter p,
     Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage)
 {
-    //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars());
+    static if (0)
+    printf("argumentMatchParameter() sc: %p, fd: %s, tf: %s, p: %s, arg: %s, arg.type: %s\n",
+        sc, fd ? fd.ident.toChars() : "null", tf.toChars(), parameterToChars(p, tf, false), arg.toChars(), arg.type.toChars());
     MATCH m;
     Type targ = arg.type;
     Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type;
@@ -1013,18 +1019,47 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
     else
     {
         const isRef = p.isReference();
-        StructDeclaration argStruct, prmStruct;
 
-        // first look for a copy constructor
-        if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct)
+        StructDeclaration argStruct, prmStruct;
+        if (targ.ty == Tstruct && tprm.ty == Tstruct)
         {
             // if the argument and the parameter are of the same unqualified struct type
             argStruct = (cast(TypeStruct)targ).sym;
             prmStruct = (cast(TypeStruct)tprm).sym;
+
+            /* if both a copy constructor and move constructor exist, then match
+             * the lvalue to the copy constructor only and the rvalue to the move constructor
+             * only
+             */
+            if (argStruct == prmStruct && fd)
+            {
+                if (auto cfd = fd.isCtorDeclaration())
+                {
+                    /* Get struct that constructor is making
+                     */
+
+                    auto t1 = cfd.type.toBasetype();
+                    auto t2 = t1.nextOf();
+                    auto t3 = t2.isTypeStruct();
+                    if (t3)
+                    {
+                    auto ctorStruct = t3.sym;
+//                    StructDeclaration ctorStruct = cfd.type.toBasetype().nextOf().isTypeStruct().sym;
+
+                    if (prmStruct == ctorStruct && ctorStruct.hasCopyCtor && ctorStruct.hasMoveCtor)
+                    {
+                        if (cfd.isCpCtor && !arg.isLvalue())
+                            return MATCH.nomatch;       // copy constructor is only for lvalues
+                        else if (cfd.isMoveCtor && arg.isLvalue())
+                            return MATCH.nomatch;       // move constructor is only for rvalues
+                    }
+                    }
+                }
+            }
         }
 
         // check if the copy constructor may be called to copy the argument
-        if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
+        if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
         {
             if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
                 return MATCH.nomatch;
@@ -4427,6 +4462,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
  */
 Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag)
 {
+    enum LOGDOTEXP = false;
+    if (LOGDOTEXP)
+        printf("dotExp()\n");
+
     Expression visitType(Type mt)
     {
         VarDeclaration v = null;
@@ -5047,8 +5086,41 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
             return noMember(mt, sc, e, ident, flag);
         }
         // check before alias resolution; the alias itself might be deprecated!
-        if (s.isAliasDeclaration)
+        if (auto ad = s.isAliasDeclaration)
+        {
             s.checkDeprecated(e.loc, sc);
+
+            // Fix for https://github.com/dlang/dmd/issues/20610
+            if (ad.originalType)
+            {
+                if (auto tid = ad.originalType.isTypeIdentifier())
+                {
+                    if (tid.idents.length)
+                    {
+                        static if (0)
+                        {
+                            printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
+                            printf("AliasDeclaration: %s\n", ad.toChars());
+                            if (ad.aliassym)
+                                printf("aliassym: %s\n", ad.aliassym.toChars());
+                            printf("tid type: %s\n", toChars(tid));
+                        }
+                        /* Rewrite e.s as e.(tid.ident).(tid.idents)
+                         */
+                        Expression die = new DotIdExp(e.loc, e, tid.ident);
+                        foreach (id; tid.idents) // maybe use typeToExpressionHelper()
+                            die = new DotIdExp(e.loc, die, cast(Identifier)id);
+                        /* Ambiguous syntax, only way to disambiguate it to try it
+                         */
+                        die = dmd.expressionsem.trySemantic(die, sc);
+                        if (die && die.isDotVarExp())   // shrink wrap around DotVarExp()
+                        {
+                            return die;
+                        }
+                    }
+                }
+            }
+        }
         s = s.toAlias();
 
         if (auto em = s.isEnumMember())
@@ -6039,7 +6111,7 @@ Dsymbol toDsymbol(Type type, Scope* sc)
 
     Dsymbol visitIdentifier(TypeIdentifier type)
     {
-        //printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
+        //printf("TypeIdentifier::toDsymbol('%s')\n", toChars(type));
         if (!sc)
             return null;
 
@@ -6051,7 +6123,6 @@ Dsymbol toDsymbol(Type type, Scope* sc)
             s = t.toDsymbol(sc);
         if (e)
             s = getDsymbol(e);
-
         return s;
     }
 
diff --git a/gcc/testsuite/gdc.dg/copy1.d b/gcc/testsuite/gdc.dg/copy1.d
new file mode 100644
index 000000000000..7b539fac2f07
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/copy1.d
@@ -0,0 +1,19 @@
+// { dg-do compile }
+// { dg-options "-fno-moduleinfo -fdump-tree-original" }
+struct S
+{
+    int i;
+    this(ref S);
+    void opAssign(S s)
+    {
+        i = s.i + 1;
+    }
+}
+
+void test_opAssign()
+{
+    S s;
+    S t;
+    // { dg-final { scan-tree-dump-not "&TARGET_EXPR" "original" } }
+    t = s;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/aligndefault.d b/gcc/testsuite/gdc.test/compilable/aligndefault.d
new file mode 100644
index 000000000000..5c66bffdc24b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/aligndefault.d
@@ -0,0 +1,28 @@
+
+struct S
+{
+    align(1)
+    {
+        short x1;
+        int y1;
+        long z1;
+
+        align(default)
+        {
+            short x;
+            int y;
+            long z;
+        }
+    }
+}
+
+void fun()
+{
+    static assert(S.x1.alignof == 1);
+    static assert(S.y1.alignof == 1);
+    static assert(S.z1.alignof == 1);
+
+    static assert(S.x.alignof == short.alignof);
+    static assert(S.y.alignof == int.alignof);
+    static assert(S.z.alignof == long.alignof);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
index 9756264fcc3e..ee698f16dcf8 100644
--- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
@@ -564,6 +564,11 @@ ref int* foo(scope return ref int* a) @safe
 
 struct SafeS
 {
+    this(int[1] x) scope {}
+	this(int[2] x) return scope {}
+	this(int[3] x) scope return {}
+	this(int[4] x) return {}
+
 @safe:
     ref SafeS foo() return
     {
diff --git a/gcc/testsuite/gdc.test/compilable/rvalue2.d b/gcc/testsuite/gdc.test/compilable/rvalue2.d
new file mode 100644
index 000000000000..e1dc44e3bbf4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/rvalue2.d
@@ -0,0 +1,22 @@
+/* PERMUTE_ARGS: -preview=rvaluerefparam
+ */
+
+struct S
+{
+    alias TT this;
+    long TT();
+    this(T)(int x) {}   // works if `int` is `long`
+
+    this(S);
+    this(ref S);
+
+    ~this();
+}
+
+S fun(ref S arg);
+
+void test()
+{
+    S st;
+    fun(st);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/shortened_methods.d b/gcc/testsuite/gdc.test/compilable/shortened_methods.d
index 785cb8e5cd0a..76a4c8a55db6 100644
--- a/gcc/testsuite/gdc.test/compilable/shortened_methods.d
+++ b/gcc/testsuite/gdc.test/compilable/shortened_methods.d
@@ -13,6 +13,10 @@ class A {
 
     // or normal method defintions
     bool isNull() => this is null;
+
+    this() {}
+    this(int x) { _x = x; }
+    this(float y) => this(cast(int) y);
 }
 
 class B : A{
@@ -36,3 +40,12 @@ void func() {
     // Issue 24088 - https://issues.dlang.org/show_bug.cgi?id=24088
     S!int f() => S!int();
 }
+
+struct T
+{
+    void inc() {}
+    this(this) => inc();
+
+    void free() {}
+    ~this() => free();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test19227.d b/gcc/testsuite/gdc.test/compilable/test19227.d
index 6ff5349f3338..55fa59164f43 100644
--- a/gcc/testsuite/gdc.test/compilable/test19227.d
+++ b/gcc/testsuite/gdc.test/compilable/test19227.d
@@ -1,9 +1,7 @@
 // https://issues.dlang.org/show_bug.cgi?id=19227
 /* TEST_OUTPUT:
 ---
-compilable/test19227.d(18): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead
-Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
-Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
+compilable/test19227.d(16): Deprecation: use of complex type `cfloat` is deprecated, use `std.complex.Complex!(float)` instead
 Deprecation: use of complex type `const(cfloat)` is deprecated, use `std.complex.Complex!(float)` instead
 ---
 */
diff --git a/gcc/testsuite/gdc.test/compilable/traits.d b/gcc/testsuite/gdc.test/compilable/traits.d
index d5e2cb0f2201..09538b1dd4ba 100644
--- a/gcc/testsuite/gdc.test/compilable/traits.d
+++ b/gcc/testsuite/gdc.test/compilable/traits.d
@@ -137,6 +137,8 @@ struct DisabledPostblit
 struct NoCpCtor { }
 class C19902 { }
 
+struct MoveCtor { this(MoveCtor) { } }
+
 static assert(__traits(hasCopyConstructor, S));
 static assert(__traits(hasCopyConstructor, OuterS.S));
 static assert(__traits(hasCopyConstructor, OuterS));
@@ -147,6 +149,8 @@ static assert(__traits(hasCopyConstructor, U!S));
 static assert(!__traits(hasPostblit, U!S));
 static assert(__traits(hasPostblit, SPostblit));
 static assert(!__traits(hasCopyConstructor, SPostblit));
+static assert(__traits(hasMoveConstructor, MoveCtor));
+static assert(!__traits(hasMoveConstructor, NoCpCtor));
 
 static assert(!__traits(hasCopyConstructor, NoCpCtor));
 static assert(!__traits(hasCopyConstructor, C19902));
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19871.d b/gcc/testsuite/gdc.test/fail_compilation/fail19871.d
deleted file mode 100644
index ad458df20019..000000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19871.d
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail19871.d(10): Error: `struct Struct` may not define both a rvalue constructor and a copy constructor
-fail_compilation/fail19871.d(19):        rvalue constructor defined here
-fail_compilation/fail19871.d(13):        copy constructor defined here
----
-*/
-
-struct Struct
-{
-    @disable this();
-    this(ref Struct other)
-    {
-        const Struct s = void;
-        this(s);
-    }
-
-    this(Struct) {}
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail19931.d b/gcc/testsuite/gdc.test/fail_compilation/fail19931.d
deleted file mode 100644
index 940a1faee028..000000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail19931.d
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail19931.d(10): Error: `struct S` may not define both a rvalue constructor and a copy constructor
-fail_compilation/fail19931.d(12):        rvalue constructor defined here
-fail_compilation/fail19931.d(13):        copy constructor defined here
----
-*/
-
-struct S
-{
-    this(S s) {}
-    this(ref S s) {}
-    this(this) {}
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23036.d b/gcc/testsuite/gdc.test/fail_compilation/fail23036.d
deleted file mode 100644
index 8920586c67a8..000000000000
--- a/gcc/testsuite/gdc.test/fail_compilation/fail23036.d
+++ /dev/null
@@ -1,22 +0,0 @@
-// https://issues.dlang.org/show_bug.cgi?id=23036
-
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail23036.d(12): Error: `struct S` may not define both a rvalue constructor and a copy constructor
-fail_compilation/fail23036.d(15):        rvalue constructor defined here
-fail_compilation/fail23036.d(14):        copy constructor defined here
----
-*/
-
-struct S
-{
-    this(ref S) {}
-    this(S, int a = 2) {}
-}
-
-void main()
-{
-    S a;
-    S b = a;
-}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice14642.d b/gcc/testsuite/gdc.test/fail_compilation/ice14642.d
index 90b9867527bf..91b1f950ca20 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice14642.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice14642.d
@@ -1,8 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice14642.d(47): Error: undefined identifier `errorValue`
-fail_compilation/ice14642.d(23): Error: template instance `ice14642.X.NA!()` error instantiating
+fail_compilation/ice14642.d(48): Error: undefined identifier `errorValue`
+fail_compilation/ice14642.d(52):        error on member `ice14642.Z.v`
+fail_compilation/ice14642.d(24): Error: template instance `ice14642.X.NA!()` error instantiating
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9865.d b/gcc/testsuite/gdc.test/fail_compilation/ice9865.d
index 3d8e997946f7..ec8426341605 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9865.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9865.d
@@ -2,7 +2,8 @@
 EXTRA_FILES: imports/ice9865b.d
 TEST_OUTPUT:
 ---
-fail_compilation/ice9865.d(9): Error: alias `ice9865.Baz` recursive alias declaration
+fail_compilation/ice9865.d(10): Error: alias `ice9865.Baz` recursive alias declaration
+fail_compilation/ice9865.d(11):        error on member `ice9865.Foo.f`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d
index 936769a59d06..dbc98f91ca99 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d
@@ -53,10 +53,10 @@ public private void f10() {}
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align`
+fail_compilation/parseStc2.d(63): Error: redundant alignment attribute `align(default)`
 fail_compilation/parseStc2.d(64): Error: redundant alignment attribute `align(1)`
 fail_compilation/parseStc2.d(65): Error: redundant alignment attribute `align(1)`
-fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align`
+fail_compilation/parseStc2.d(66): Error: redundant alignment attribute `align(default)`
 fail_compilation/parseStc2.d(67): Error: redundant alignment attribute `align(2)`
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/short_fn.d b/gcc/testsuite/gdc.test/fail_compilation/short_fn.d
new file mode 100644
index 000000000000..04f3e3fffbfa
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/short_fn.d
@@ -0,0 +1,15 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/short_fn.d(13): Error: can only return void expression, `this` call or `super` call from constructor
+fail_compilation/short_fn.d(14): Error: can only return void expression, `this` call or `super` call from constructor
+---
+*/
+
+struct Number
+{
+    int x;
+
+    this(int x) => this.x = x;
+    this(byte x) => Number();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19473.d b/gcc/testsuite/gdc.test/fail_compilation/test19473.d
index ba6024b000b8..67a66c57206a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test19473.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19473.d
@@ -1,6 +1,7 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/test19473.d(14): Error: union `test19473.P` no size because of forward reference
+fail_compilation/test19473.d(15): Error: union `test19473.P` no size because of forward reference
+fail_compilation/test19473.d(31):        error on member `test19473.P.p`
 ---
  */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20719.d b/gcc/testsuite/gdc.test/fail_compilation/test20719.d
index 4db59d62ae1f..56b80bbb1a64 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test20719.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test20719.d
@@ -1,8 +1,9 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/test20719.d(13): Error: struct `test20719.SumType` no size because of forward reference
-fail_compilation/test20719.d(32): Error: variable `test20719.isCopyable!(SumType).__lambda_L32_C22.foo` - size of type `SumType` is invalid
-fail_compilation/test20719.d(18): Error: template instance `test20719.isCopyable!(SumType)` error instantiating
+fail_compilation/test20719.d(14): Error: struct `test20719.SumType` no size because of forward reference
+fail_compilation/test20719.d(17):        error on member `test20719.SumType.storage`
+fail_compilation/test20719.d(33): Error: variable `test20719.isCopyable!(SumType).__lambda_L33_C22.foo` - size of type `SumType` is invalid
+fail_compilation/test20719.d(19): Error: template instance `test20719.isCopyable!(SumType)` error instantiating
 ---
 */
 struct SumType
diff --git a/gcc/testsuite/gdc.test/runnable/aliasassign.d b/gcc/testsuite/gdc.test/runnable/aliasassign.d
index 986cccc4db89..4f61e3787e32 100644
--- a/gcc/testsuite/gdc.test/runnable/aliasassign.d
+++ b/gcc/testsuite/gdc.test/runnable/aliasassign.d
@@ -15,7 +15,7 @@ template Qual(alias T)
     alias Qual = T;
 }
 
-void test()
+void test1()
 {
     int x = 3;
     int y = 4;
@@ -25,7 +25,33 @@ void test()
     assert(XY[1] == 4);
 }
 
-void main()
+/**********************************************/
+
+struct T
+{
+    int k,i = 2;
+}
+
+struct S
+{
+    int x;
+    T t;
+    alias ti = t.i;
+}
+
+void test2()
+{
+    T t = T(1, 2);
+    S s;
+    assert(s.ti == 2);
+}
+
+/**********************************************/
+
+int main()
 {
-    test();
+    test1();
+    test2();
+
+    return 0;
 }
diff --git a/gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d b/gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d
new file mode 100644
index 000000000000..b0e54a936fae
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/imports/test23722_2b.d
@@ -0,0 +1,13 @@
+module imports.test23722_2b;
+
+struct T(alias fun) { }
+
+struct  S(int i) {
+    auto t = T!(x => i)();
+}
+
+string g() {
+    S!0 s0;
+    S!1 s1;
+    return s1.t.init.mangleof;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/mars1.d b/gcc/testsuite/gdc.test/runnable/mars1.d
index f493eac181de..1c2ae55b79c4 100644
--- a/gcc/testsuite/gdc.test/runnable/mars1.d
+++ b/gcc/testsuite/gdc.test/runnable/mars1.d
@@ -2491,6 +2491,62 @@ void testDoWhileContinue()
     while(--i > 0);
 }
 
+////////////////////////////////////////////////////////////////////////
+// https://github.com/dlang/dmd/issues/20574
+
+int test20574x(int i, int y)
+{
+    return i ? y : y;
+}
+
+void test20574()
+{
+    assert(test20574x(1, 2) == 2);
+    assert(test20574x(0, 2) == 2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+struct S8
+{
+    int x,y,z;
+}
+
+int test8x(S8 s)
+{
+    s = s;
+    return s.y;
+}
+
+void test8()
+{
+    S8 s;
+    s.y = 2;
+    assert(test8x(s) == 2);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+struct S9
+{
+    int a,b;
+    ~this() { }
+}
+
+
+S9 test9x(ref S9 arg)
+{
+    return arg;
+}
+
+void test9()
+{
+    S9 s;
+    s.b = 3;
+    S9 t = test9x(s);
+    assert(t.b == 3);
+}
+
 ////////////////////////////////////////////////////////////////////////
 
 int main()
@@ -2592,6 +2648,9 @@ int main()
     test21816();
     test21835();
     testDoWhileContinue();
+    test20574();
+    test8();
+    test9();
 
     printf("Success\n");
     return 0;
diff --git a/gcc/testsuite/gdc.test/runnable/nrvo.d b/gcc/testsuite/gdc.test/runnable/nrvo.d
index 5c653fe45ad6..bceba100e0bd 100644
--- a/gcc/testsuite/gdc.test/runnable/nrvo.d
+++ b/gcc/testsuite/gdc.test/runnable/nrvo.d
@@ -22,9 +22,35 @@ void test1()
     assert(&r == s1ptr);
 }
 
+/***************************************************/
+// https://github.com/dlang/dmd/issues/20567
+
+struct S2
+{
+    int x;
+    this(ref S2 s) { x = s.x; }
+}
+
+S2 returnRval(ref S2 arg1, ref S2 arg2, int i)
+{
+    return i ? arg1 : arg2;
+}
+
+void test2()
+{
+    S2 s1, s2;
+    s1.x = 3;
+    s2.x = 4;
+    S2 s = returnRval(s1, s2, 0);
+    assert(s.x == 4);
+    s = returnRval(s1, s2, 1);
+    assert(s.x == 3);
+}
+
 /***************************************************/
 
 void main()
 {
     test1();
+    test2();
 }
diff --git a/gcc/testsuite/gdc.test/runnable/rvalue1.d b/gcc/testsuite/gdc.test/runnable/rvalue1.d
new file mode 100644
index 000000000000..f8134f718a9f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/rvalue1.d
@@ -0,0 +1,209 @@
+/* PERMUTE_ARGS: -preview=rvaluerefparam
+/* testing __rvalue */
+
+import core.stdc.stdio;
+
+/********************************/
+
+int foo(int) { printf("foo(int)\n"); return 1; }
+int foo(ref int) { printf("foo(ref int)\n"); return 2; }
+
+void test1()
+{
+    int s;
+    assert(foo(s) == 2);
+    assert(foo(__rvalue(s)) == 1);
+}
+
+/********************************/
+
+struct S
+{
+  nothrow:
+    ~this() { printf("~this() %p\n", &this); }
+    this(ref S) { printf("this(ref S)\n"); }
+    void opAssign(S) { printf("opAssign(S)\n"); }
+}
+
+void test2()
+{
+    S s;
+    S t;
+
+    t = __rvalue(s);
+}
+
+/********************************/
+
+struct S3
+{
+    int a, b, c;
+
+    this(S3) {}
+    this(ref S3) {}
+}
+
+void test3()
+{
+    S3 s;
+    S3 x = s; // this line causes the compiler to crash
+}
+
+/********************************/
+
+struct S4
+{
+    void* p;
+
+    this(ref S4) { }
+
+    this(S4 s)
+    {
+        assert(&s is &x); // confirm the rvalue reference
+    }
+}
+
+__gshared S4 x;
+
+void test4()
+{
+    S4 t = __rvalue(x);
+}
+
+/********************************/
+
+struct S5
+{
+    this(S5 s) { printf("this(S5 s)\n"); }
+    this(ref inout S5 s) inout { printf("this(ref inout S5 s) inout\n"); }
+}
+
+void test5()
+{
+    S5 t;
+    S5 t1 = t;
+    S5 t2 = __rvalue(t);
+}
+
+/********************************/
+
+int moveCtor, copyCtor, moveAss, copyAss;
+
+struct S6
+{
+    this(S6 s) { ++moveCtor; }
+    this(ref S6 s) { ++copyCtor; }
+    void opAssign(S6 s) { ++moveAss; }
+    void opAssign(ref S6 s) { ++copyAss; }
+}
+
+void test6()
+{
+    S6 x;
+    S6 s = x;
+//    printf("S6 s = x;            moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+    assert(copyCtor == 1);
+    S6 s2 = __rvalue(x);
+//    printf("S6 s2 = __rvalue(x); moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+    assert(moveCtor == 1);
+    s2 = s;
+//    printf("s2 = s;             moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+    assert(copyAss == 1);
+    s2 = __rvalue(s);
+//    printf("s2 = __rvalue(s);   moveCtor %d copyCtor %d moveAss %d copyAss %d\n", moveCtor, copyCtor, moveAss, copyAss);
+    assert(moveAss == 1);
+    assert(copyCtor == 1 && moveCtor == 1 && copyAss == 1 && moveAss == 1);
+}
+
+/********************************/
+// https://github.com/dlang/dmd/pull/17050#issuecomment-2543370370
+
+struct MutableString(size_t E) {}
+
+struct StringTest
+{
+    alias toString this;
+    const(char)[] toString() const pure
+        => null;
+
+    this(StringTest rhs) {}
+    this(ref inout StringTest rhs) inout pure {}
+
+    this(typeof(null)) inout pure {}
+    this(size_t Embed)(MutableString!Embed str) inout {}
+
+    ~this() {}
+}
+
+
+void test7()
+{
+    StringTest s = StringTest(null);
+}
+
+/********************************/
+// https://github.com/dlang/dmd/issues/20562
+
+struct S8
+{
+    int a,b;
+    this(S8) { printf("this(S)\n"); b = 4; }
+    this(ref S8) { printf("this(ref S)\n"); assert(0); }
+}
+
+
+S8 returnRval(ref S8 arg)
+{
+static if (0)
+{
+    /*  __copytmp2 =  0 ;
+        _D7rvalue51S6__ctorMFNcKSQxQrZQg call  (arg param  #__copytmp2);
+        * __HID1 streq 1 __copytmp2;
+        __HID1;
+     */
+    return arg;
+}
+else static if (1)
+{
+    /*  * __HID1 streq 1 * arg;
+        __HID1;
+     */
+    return __rvalue(arg); // should move-construct the NRVO value
+}
+else
+{
+    /*  * t =  0 ;
+        t;
+        _TMP0 =  t;
+        _D7rvalue51S6__ctorMFNcSQwQqZQg call  (arg param  _TMP0);
+        t;
+     */
+    S8 t = __rvalue(arg);
+    return t;
+}
+}
+
+
+void test8()
+{
+   S8 s;
+   S8 t = returnRval(s);
+   printf("t.b: %d\n", t.b);
+   assert(t.b == 4);
+}
+
+/********************************/
+
+int main()
+{
+    test1();
+    test2();
+    test3();
+    test4();
+    test5();
+    test6();
+    test7();
+    test8();
+
+    return 0;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test23036.d b/gcc/testsuite/gdc.test/runnable/test23036.d
new file mode 100644
index 000000000000..efb6fef3953b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test23036.d
@@ -0,0 +1,13 @@
+// https://issues.dlang.org/show_bug.cgi?id=23036
+
+struct S
+{
+    this(ref S) {}
+    this(S, int a = 2) {}
+}
+
+void main()
+{
+    S a;
+    S b = a;
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test23722_2.d b/gcc/testsuite/gdc.test/runnable/test23722_2.d
new file mode 100644
index 000000000000..cce4629933fd
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test23722_2.d
@@ -0,0 +1,10 @@
+// COMPILE_SEPARATELY:
+// EXTRA_SOURCES: imports/test23722_2b.d
+// https://issues.dlang.org/show_bug.cgi?id=23722
+// Lambdas are mangled incorrectly when using multiple compilation units, resulting in incorrect code
+import imports.test23722_2b;
+
+void main() {
+    S!1 s1;
+    assert(s1.t.init.mangleof == g);
+}
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index bfdc9ea21e1e..e5884c6a798a 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
-82a5d2a7c4dd3d270537bcede2981e047bfd0e6a
+c57da0cf5945cfb45eed06f1fd820435cda3ee3a
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/libphobos/libdruntime/core/builtins.d b/libphobos/libdruntime/core/builtins.d
index f10bb9f6f8b3..bc12759e9a18 100644
--- a/libphobos/libdruntime/core/builtins.d
+++ b/libphobos/libdruntime/core/builtins.d
@@ -78,6 +78,8 @@ else version (DigitalMars)
         return val;
     }
 
+    /// Execute target dependent trap instruction, if supported.
+    /// Otherwise, abort execution.
     pragma(inline, true)
     void trap()
     {
@@ -90,7 +92,8 @@ else version (DigitalMars)
     }
 }
 
-/// Provide static branch hints
+/// Provide static branch and value hints for the LDC/GDC compilers.
+/// DMD ignores these hints.
 pragma(inline, true) bool likely(bool b)   { return !!expect(b, true);  }
-///
+/// ditto
 pragma(inline, true) bool unlikely(bool b) { return !!expect(b, false); }
diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d
index fa3d859ce361..173e6abecffe 100644
--- a/libphobos/libdruntime/core/gc/gcinterface.d
+++ b/libphobos/libdruntime/core/gc/gcinterface.d
@@ -190,4 +190,73 @@ interface GC
      * GC.stats().allocatedInCurrentThread, but faster.
      */
     ulong allocatedInCurrentThread() nothrow;
+
+    // ARRAY FUNCTIONS
+    /**
+     * Get the current used capacity of an array block. Note that this is only
+     * needed if you are about to change the array used size and need to deal
+     * with the memory that is about to go away. For appending or shrinking
+     * arrays that have no destructors, you probably don't need this function.
+     * Params:
+     *   ptr = The pointer to check. This can be an interior pointer, but if it
+     *       is beyond the end of the used space, the return value may not be
+     *       valid.
+     *   atomic = If true, the value is fetched atomically (for shared arrays)
+     * Returns: Current array slice, or null if the pointer does not point to a
+     *   valid appendable GC block.
+     */
+    void[] getArrayUsed(void *ptr, bool atomic = false) nothrow;
+
+    /**
+     * Expand the array used size. Used for appending and expanding the length
+     * of the array slice. If the operation can be performed without
+     * reallocating, the function succeeds. Newly expanded data is not
+     * initialized.
+     *
+     * slices that do not point at expandable GC blocks cannot be affected, and
+     * this function will always return false.
+     * Params:
+     *   slice = the slice to attempt expanding in place.
+     *   newUsed = the size that should be stored as used.
+     *   atomic = if true, the array may be shared between threads, and this
+     *   operation should be done atomically.
+     * Returns: true if successful.
+     */
+    bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe;
+
+    /**
+     * Expand the array capacity. Used for reserving space that can be used for
+     * appending. If the operation can be performed without reallocating, the
+     * function succeeds. The used size is not changed.
+     *
+     * slices that do not point at expandable GC blocks cannot be affected, and
+     * this function will always return zero.
+     * Params:
+     *   slice = the slice to attempt reserving capacity for.
+     *   request = the requested size to expand to. Includes the existing data.
+     *      Passing a value less than the current array size will result in no
+     *      changes, but will return the current capacity.
+     *   atomic = if true, the array may be shared between threads, and this
+     *      operation should be done atomically.
+     * Returns: resulting capacity size, 0 if the operation could not be performed.
+     */
+    size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe;
+
+    /**
+     * Shrink used space of a slice. Unlike the other array functions, the
+     * array slice passed in is the target slice, and the existing used space
+     * is passed separately. This is to discourage code that ends up with a
+     * slice to dangling valid data.
+     * If slice.ptr[0 .. existingUsed] does not point to the end of a valid GC
+     * appendable slice, then the operation fails.
+     * Params:
+     *   slice = The proposed valid slice data.
+     *   existingUsed = The amount of data in the block (starting at slice.ptr)
+     *       that is currently valid in the array. If this amount does not match
+     *       the current used size, the operation fails.
+     *   atomic = If true, the slice may be shared between threads, and the
+     *       operation should be atomic.
+     * Returns: true if successful.
+     */
+    bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow;
 }
diff --git a/libphobos/libdruntime/core/internal/array/utils.d b/libphobos/libdruntime/core/internal/array/utils.d
index c45913d87e1b..54cfc26af564 100644
--- a/libphobos/libdruntime/core/internal/array/utils.d
+++ b/libphobos/libdruntime/core/internal/array/utils.d
@@ -264,6 +264,8 @@ void *__arrayStart()(return scope BlkInfo info) nothrow pure
 bool __setArrayAllocLength(T)(ref BlkInfo info, size_t newLength, bool isShared, size_t oldLength = ~0)
 {
     import core.lifetime : TypeInfoSize;
-    import core.internal.gc.blockmeta : __setArrayAllocLengthImpl;
-    return __setArrayAllocLengthImpl(info, newLength, isShared, typeid(T), oldLength, TypeInfoSize!T);
+    import core.internal.gc.blockmeta : __setArrayAllocLengthImpl, __setBlockFinalizerInfo;
+    static if (TypeInfoSize!T)
+        __setBlockFinalizerInfo(info, typeid(T));
+    return __setArrayAllocLengthImpl(info, newLength, isShared, oldLength, TypeInfoSize!T);
 }
diff --git a/libphobos/libdruntime/core/internal/gc/blkcache.d b/libphobos/libdruntime/core/internal/gc/blkcache.d
index c555c22f636f..908f66656e13 100644
--- a/libphobos/libdruntime/core/internal/gc/blkcache.d
+++ b/libphobos/libdruntime/core/internal/gc/blkcache.d
@@ -38,7 +38,7 @@ else
     int __nextBlkIdx;
 }
 
-@property BlkInfo *__blkcache() nothrow
+@property BlkInfo *__blkcache() nothrow @nogc
 {
     if (!__blkcache_storage)
     {
@@ -135,7 +135,7 @@ unittest
         so any use of the returned BlkInfo should copy it and then check the
         base ptr of the copy before actually using it.
   */
-BlkInfo *__getBlkInfo(void *interior) nothrow
+BlkInfo *__getBlkInfo(void *interior) nothrow @nogc
 {
     BlkInfo *ptr = __blkcache;
     if (ptr is null)
@@ -175,7 +175,7 @@ BlkInfo *__getBlkInfo(void *interior) nothrow
     return null; // not in cache.
 }
 
-void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
+void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow @nogc
 {
     auto cache = __blkcache;
     if (cache is null)
@@ -241,3 +241,16 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
         }
     }
 }
+
+debug(PRINTF)
+{
+    extern(C) void printArrayCache()
+    {
+        auto ptr = __blkcache;
+        printf("CACHE: \n");
+        foreach (i; 0 .. N_CACHE_BLOCKS)
+        {
+            printf("  %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr);
+        }
+    }
+}
diff --git a/libphobos/libdruntime/core/internal/gc/blockmeta.d b/libphobos/libdruntime/core/internal/gc/blockmeta.d
index c7dfeb650752..9d959d54721d 100644
--- a/libphobos/libdruntime/core/internal/gc/blockmeta.d
+++ b/libphobos/libdruntime/core/internal/gc/blockmeta.d
@@ -71,12 +71,15 @@ size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
   */
 bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow
 {
-    size_t typeInfoSize = structTypeInfoSize(tinext);
-    return __setArrayAllocLengthImpl(info, newlength, isshared, tinext, oldlength, typeInfoSize);
+    size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+    if (typeInfoSize)
+        __setBlockFinalizerInfo(info, tinext);
+
+    return __setArrayAllocLengthImpl(info, newlength, isshared, oldlength, typeInfoSize);
 }
 
 // the impl function, used both above and in core.internal.array.utils
-bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength, size_t typeInfoSize) pure nothrow
+bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength, size_t typeInfoSize) pure nothrow
 {
     import core.atomic;
 
@@ -113,11 +116,6 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared
             // setting the initial length, no cas needed
             *length = cast(ubyte)newlength;
         }
-        if (typeInfoSize)
-        {
-            auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
-            *typeInfo = cast() tinext;
-        }
     }
     else if (info.size < PAGESIZE)
     {
@@ -144,11 +142,6 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared
             // setting the initial length, no cas needed
             *length = cast(ushort)newlength;
         }
-        if (typeInfoSize)
-        {
-            auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
-            *typeInfo = cast() tinext;
-        }
     }
     else
     {
@@ -175,29 +168,80 @@ bool __setArrayAllocLengthImpl(ref BlkInfo info, size_t newlength, bool isshared
             // setting the initial length, no cas needed
             *length = newlength;
         }
-        if (typeInfoSize)
-        {
-            auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
-            *typeInfo = cast()tinext;
-        }
     }
     return true; // resize succeeded
 }
 
 /**
-  get the allocation size of the array for the given block (without padding or type info)
+  The block finalizer info is set separately from the array length, as that is
+  only needed on the initial setup of the block. No shared is needed, since
+  this should only happen when the block is new.
+  */
+void __setBlockFinalizerInfo(ref BlkInfo info, const TypeInfo ti) pure nothrow
+{
+    if ((info.attr & BlkAttr.APPENDABLE) && info.size >= PAGESIZE)
+    {
+        // array used size goes at the beginning. We can stuff the typeinfo
+        // right after it, as we need to use 16 bytes anyway.
+        auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
+        *typeInfo = cast() ti;
+    }
+    else
+    {
+        // all other cases the typeinfo gets put at the end of the block
+        auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
+        *typeInfo = cast() ti;
+    }
+}
+
+/**
+  get the used size of the array for the given block
   */
-size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
+size_t __arrayAllocLength(ref BlkInfo info) pure nothrow
+    in(info.attr & BlkAttr.APPENDABLE)
 {
+    auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
     if (info.size <= 256)
-        return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD);
+        return *cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
 
     if (info.size < PAGESIZE)
-        return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD);
+        return *cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
 
     return *cast(size_t *)(info.base);
 }
 
+/**
+  Atomically get the used size of the array for the given block
+  */
+size_t __arrayAllocLengthAtomic(ref BlkInfo info) pure nothrow
+    in(info.attr & BlkAttr.APPENDABLE)
+{
+    import core.atomic;
+    auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+    if (info.size <= 256)
+        return atomicLoad(*cast(shared(ubyte)*)(info.base + info.size - typeInfoSize - SMALLPAD));
+
+    if (info.size < PAGESIZE)
+        return atomicLoad(*cast(shared(ushort)*)(info.base + info.size - typeInfoSize - MEDPAD));
+
+    return atomicLoad(*cast(shared(size_t)*)(info.base));
+}
+
+/**
+  Get the maximum bytes that can be stored in the given block.
+  */
+size_t __arrayAllocCapacity(ref BlkInfo info) pure nothrow
+    in(info.attr & BlkAttr.APPENDABLE)
+{
+    // Capacity is a calculation based solely on the block info.
+    if (info.size >= PAGESIZE)
+        return info.size - LARGEPAD;
+
+    auto typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+    auto padsize = info.size <= 256 ? SMALLPAD : MEDPAD;
+    return info.size - typeInfoSize - padsize;
+}
+
 /**
   get the padding required to allocate size bytes.  Note that the padding is
   NOT included in the passed in size.  Therefore, do NOT call this function
diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
index 149cc5d4cfbd..6ddd4c1fb605 100644
--- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
+++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
@@ -1377,6 +1377,193 @@ class ConservativeGC : GC
         stats.freeSize += freeListSize;
         stats.allocatedInCurrentThread = bytesAllocated;
     }
+
+    // ARRAY FUNCTIONS
+    void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+    {
+        import core.internal.gc.blockmeta;
+        import core.internal.gc.blkcache;
+        import core.internal.array.utils;
+
+        // lookup the block info, using the cache if possible.
+        auto bic = atomic ? null : __getBlkInfo(ptr);
+        auto info = bic ? *bic : query(ptr);
+
+        if (!(info.attr & BlkAttr.APPENDABLE))
+            // not appendable
+            return null;
+
+        assert(info.base); // sanity check.
+        if (!bic && !atomic)
+            // cache the lookup for next time
+            __insertBlkInfoCache(info, null);
+
+        auto usedSize = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info);
+        return __arrayStart(info)[0 .. usedSize];
+    }
+
+    /* NOTE about @trusted in these functions:
+     * These functions do a lot of pointer manipulation, and has writeable
+     * access to BlkInfo which is used to interface with other parts of the GC,
+     * including the block metadata and block cache. Marking these functions as
+     * @safe would mean that any modification of BlkInfo fields should be
+     * considered @safe, which is not the case. For example, it would be
+     * perfectly legal to change the BlkInfo size to some huge number, and then
+     * store it in the block cache to blow up later. The utility functions
+     * count on the BlkInfo representing the correct information inside the GC.
+     *
+     * In order to mark these @safe, we would need a BlkInfo that has
+     * restrictive access (i.e. @system only) to the information inside the
+     * BlkInfo. Until then any use of these structures needs to be @trusted,
+     * and therefore the entire functions are @trusted. The API is still @safe
+     * because the information is stored and looked up by the GC, not the
+     * caller.
+     */
+    bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @trusted
+    {
+        import core.internal.gc.blockmeta;
+        import core.internal.gc.blkcache;
+        import core.internal.array.utils;
+
+        if (newUsed < slice.length)
+            // cannot "expand" by shrinking.
+            return false;
+
+        // lookup the block info, using the cache if possible
+        auto bic = atomic ? null : __getBlkInfo(slice.ptr);
+        auto info = bic ? *bic : query(slice.ptr);
+
+        if (!(info.attr & BlkAttr.APPENDABLE))
+            // not appendable
+            return false;
+
+        assert(info.base); // sanity check.
+
+        immutable offset = slice.ptr - __arrayStart(info);
+        newUsed += offset;
+        auto existingUsed = slice.length + offset;
+
+        size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+        if (__setArrayAllocLengthImpl(info, offset + newUsed, atomic, existingUsed, typeInfoSize))
+        {
+            // could expand without extending
+            if (!bic && !atomic)
+                // cache the lookup for next time
+                __insertBlkInfoCache(info, null);
+            return true;
+        }
+
+        // if we got here, just setting the used size did not work.
+        if (info.size < PAGESIZE)
+            // nothing else we can do
+            return false;
+
+        // try extending the block into subsequent pages.
+        immutable requiredExtension = newUsed - info.size - LARGEPAD;
+        auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null);
+        if (extendedSize == 0)
+            // could not extend, can't satisfy the request
+            return false;
+
+        info.size = extendedSize;
+        if (bic)
+            *bic = info;
+        else if (!atomic)
+            __insertBlkInfoCache(info, null);
+
+        // this should always work.
+        return __setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize);
+    }
+
+    bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+    {
+        import core.internal.gc.blockmeta;
+        import core.internal.gc.blkcache;
+        import core.internal.array.utils;
+
+        if (existingUsed < slice.length)
+            // cannot "shrink" by growing.
+            return false;
+
+        // lookup the block info, using the cache if possible.
+        auto bic = atomic ? null : __getBlkInfo(slice.ptr);
+        auto info = bic ? *bic : query(slice.ptr);
+
+        if (!(info.attr & BlkAttr.APPENDABLE))
+            // not appendable
+            return false;
+
+        assert(info.base); // sanity check
+
+        immutable offset = slice.ptr - __arrayStart(info);
+        existingUsed += offset;
+        auto newUsed = slice.length + offset;
+
+        size_t typeInfoSize = (info.attr & BlkAttr.STRUCTFINAL) ? size_t.sizeof : 0;
+
+        if (__setArrayAllocLengthImpl(info, newUsed, atomic, existingUsed, typeInfoSize))
+        {
+            if (!bic && !atomic)
+                __insertBlkInfoCache(info, null);
+            return true;
+        }
+
+        return false;
+    }
+
+    size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @trusted
+    {
+        import core.internal.gc.blockmeta;
+        import core.internal.gc.blkcache;
+        import core.internal.array.utils;
+
+        // lookup the block info, using the cache if possible.
+        auto bic = atomic ? null : __getBlkInfo(slice.ptr);
+        auto info = bic ? *bic : query(slice.ptr);
+
+        if (!(info.attr & BlkAttr.APPENDABLE))
+            // not appendable
+            return 0;
+
+        assert(info.base); // sanity check
+
+        immutable offset = slice.ptr - __arrayStart(info);
+        request += offset;
+        auto existingUsed = slice.length + offset;
+
+        // make sure this slice ends at the used space
+        auto blockUsed = atomic ? __arrayAllocLengthAtomic(info) : __arrayAllocLength(info);
+        if (existingUsed != blockUsed)
+            // not an expandable slice.
+            return 0;
+
+        // see if the capacity can contain the existing data
+        auto existingCapacity = __arrayAllocCapacity(info);
+        if (existingCapacity < request)
+        {
+            if (info.size < PAGESIZE)
+                // no possibility to extend
+                return 0;
+
+            immutable requiredExtension = request - existingCapacity;
+            auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null);
+            if (extendedSize == 0)
+                // could not extend, can't satisfy the request
+                return 0;
+
+            info.size = extendedSize;
+
+            // update the block info cache if it was used
+            if (bic)
+                *bic = info;
+            else if (!atomic)
+                __insertBlkInfoCache(info, null);
+
+            existingCapacity = __arrayAllocCapacity(info);
+        }
+
+        return existingCapacity - offset;
+    }
 }
 
 
diff --git a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d
index 36add7a2d6af..3da92db14e78 100644
--- a/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d
+++ b/libphobos/libdruntime/core/internal/gc/impl/manual/gc.d
@@ -267,4 +267,24 @@ class ManualGC : GC
     {
         return typeof(return).init;
     }
+
+    void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+    {
+        return null;
+    }
+
+    bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe
+    {
+        return false;
+    }
+
+    size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe
+    {
+        return 0;
+    }
+
+    bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+    {
+        return false;
+    }
 }
diff --git a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d
index dbe86007115e..cbdcdb8852b5 100644
--- a/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d
+++ b/libphobos/libdruntime/core/internal/gc/impl/proto/gc.d
@@ -241,4 +241,24 @@ class ProtoGC : GC
     {
         return stats().allocatedInCurrentThread;
     }
+
+    void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+    {
+        return null;
+    }
+
+    bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe
+    {
+        return false;
+    }
+
+    size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe
+    {
+        return 0;
+    }
+
+    bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+    {
+        return false;
+    }
 }
diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d
index c7dab714e6c5..d46cb8cd775b 100644
--- a/libphobos/libdruntime/core/internal/gc/proxy.d
+++ b/libphobos/libdruntime/core/internal/gc/proxy.d
@@ -246,6 +246,26 @@ extern (C)
         return instance.allocatedInCurrentThread();
     }
 
+    void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow
+    {
+        return instance.getArrayUsed( ptr, atomic );
+    }
+
+    bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow
+    {
+        return instance.expandArrayUsed( slice, newUsed, atomic );
+    }
+
+    size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow
+    {
+        return instance.reserveArrayCapacity( slice, request, atomic );
+    }
+
+    bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow
+    {
+        return instance.shrinkArrayUsed( slice, existingUsed, atomic );
+    }
+
     GC gc_getProxy() nothrow
     {
         return instance;
diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d
index 02898082e402..b1b29130eff3 100644
--- a/libphobos/libdruntime/core/internal/traits.d
+++ b/libphobos/libdruntime/core/internal/traits.d
@@ -538,18 +538,173 @@ unittest
 
 template hasIndirections(T)
 {
-    static if (is(T == struct) || is(T == union))
+    static if (is(T == enum))
+        enum hasIndirections = hasIndirections!(OriginalType!T);
+    else static if (is(T == struct) || is(T == union))
         enum hasIndirections = anySatisfy!(.hasIndirections, typeof(T.tupleof));
     else static if (__traits(isAssociativeArray, T) || is(T == class) || is(T == interface))
         enum hasIndirections = true;
     else static if (is(T == E[N], E, size_t N))
-        enum hasIndirections = T.sizeof && is(E == void) ? true : hasIndirections!(BaseElemOf!E);
+        enum hasIndirections = T.sizeof && (is(E == void) || hasIndirections!(BaseElemOf!E));
     else static if (isFunctionPointer!T)
         enum hasIndirections = false;
     else
         enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T;
 }
 
+@safe unittest
+{
+    static assert(!hasIndirections!int);
+    static assert(!hasIndirections!(const int));
+
+    static assert( hasIndirections!(int*));
+    static assert( hasIndirections!(const int*));
+
+    static assert( hasIndirections!(int[]));
+    static assert(!hasIndirections!(int[42]));
+    static assert(!hasIndirections!(int[0]));
+
+    static assert( hasIndirections!(int*));
+    static assert( hasIndirections!(int*[]));
+    static assert( hasIndirections!(int*[42]));
+    static assert(!hasIndirections!(int*[0]));
+
+    static assert( hasIndirections!string);
+    static assert( hasIndirections!(string[]));
+    static assert( hasIndirections!(string[42]));
+    static assert(!hasIndirections!(string[0]));
+
+    static assert( hasIndirections!(void[]));
+    static assert( hasIndirections!(void[17]));
+    static assert(!hasIndirections!(void[0]));
+
+    static assert( hasIndirections!(string[int]));
+    static assert( hasIndirections!(string[int]*));
+    static assert( hasIndirections!(string[int][]));
+    static assert( hasIndirections!(string[int][12]));
+    static assert(!hasIndirections!(string[int][0]));
+
+    static assert(!hasIndirections!(int function(string)));
+    static assert( hasIndirections!(int delegate(string)));
+    static assert(!hasIndirections!(const(int function(string))));
+    static assert( hasIndirections!(const(int delegate(string))));
+    static assert(!hasIndirections!(immutable(int function(string))));
+    static assert( hasIndirections!(immutable(int delegate(string))));
+
+    static class C {}
+    static assert( hasIndirections!C);
+
+    static interface I {}
+    static assert( hasIndirections!I);
+
+    {
+        enum E : int { a }
+        static assert(!hasIndirections!E);
+    }
+    {
+        enum E : int* { a }
+        static assert( hasIndirections!E);
+    }
+    {
+        enum E : string { a = "" }
+        static assert( hasIndirections!E);
+    }
+    {
+        enum E : int[] { a = null }
+        static assert( hasIndirections!E);
+    }
+    {
+        enum E : int[3] { a = [1, 2, 3]  }
+        static assert(!hasIndirections!E);
+    }
+    {
+        enum E : int*[3] { a = [null, null, null] }
+        static assert( hasIndirections!E);
+    }
+    {
+        enum E : int*[0] { a = int*[0].init }
+        static assert(!hasIndirections!E);
+    }
+    {
+        enum E : C { a = null }
+        static assert( hasIndirections!E);
+    }
+    {
+        enum E : I { a = null }
+        static assert( hasIndirections!E);
+    }
+
+    {
+        static struct S {}
+        static assert(!hasIndirections!S);
+
+        enum E : S { a = S.init }
+        static assert(!hasIndirections!S);
+    }
+    {
+        static struct S { int i; }
+        static assert(!hasIndirections!S);
+
+        enum E : S { a = S.init }
+        static assert(!hasIndirections!S);
+    }
+    {
+        static struct S { C c; }
+        static assert( hasIndirections!S);
+
+        enum E : S { a = S.init }
+        static assert( hasIndirections!S);
+    }
+    {
+        static struct S { int[] arr; }
+        static assert( hasIndirections!S);
+
+        enum E : S { a = S.init }
+        static assert( hasIndirections!S);
+    }
+    {
+        int local;
+        struct S { void foo() { ++local; } }
+        static assert( hasIndirections!S);
+
+        enum E : S { a = S.init }
+        static assert( hasIndirections!S);
+    }
+
+    {
+        static union U {}
+        static assert(!hasIndirections!U);
+    }
+    {
+        static union U { int i; }
+        static assert(!hasIndirections!U);
+    }
+    {
+        static union U { C c; }
+        static assert( hasIndirections!U);
+    }
+    {
+        static union U { int[] arr; }
+        static assert( hasIndirections!U);
+    }
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=12000
+@safe unittest
+{
+    static struct S(T)
+    {
+        static assert(hasIndirections!T);
+    }
+
+    static class A(T)
+    {
+        S!A a;
+    }
+
+    A!int dummy;
+}
+
 template hasUnsharedIndirections(T)
 {
     static if (is(T == immutable))
diff --git a/libphobos/libdruntime/core/sys/posix/fcntl.d b/libphobos/libdruntime/core/sys/posix/fcntl.d
index c0e9006a0438..3da9fd72e6d7 100644
--- a/libphobos/libdruntime/core/sys/posix/fcntl.d
+++ b/libphobos/libdruntime/core/sys/posix/fcntl.d
@@ -475,6 +475,7 @@ else version (Darwin)
     enum F_UNLCK        = 2;
     enum F_WRLCK        = 3;
 
+    enum O_NOFOLLOW     = 0x0100;
     enum O_CREAT        = 0x0200;
     enum O_EXCL         = 0x0800;
     enum O_NOCTTY       = 0;
diff --git a/libphobos/libdruntime/core/sys/windows/aclapi.d b/libphobos/libdruntime/core/sys/windows/aclapi.d
index 4905351b565c..0b9f755ed655 100644
--- a/libphobos/libdruntime/core/sys/windows/aclapi.d
+++ b/libphobos/libdruntime/core/sys/windows/aclapi.d
@@ -15,7 +15,7 @@ pragma(lib, "advapi32");
 
 import core.sys.windows.accctrl, core.sys.windows.basetyps, core.sys.windows.w32api, core.sys.windows.winnt;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     VOID BuildExplicitAccessWithNameA(PEXPLICIT_ACCESS_A, LPSTR, DWORD,
       ACCESS_MODE, DWORD);
     VOID BuildExplicitAccessWithNameW(PEXPLICIT_ACCESS_W, LPWSTR, DWORD,
diff --git a/libphobos/libdruntime/core/sys/windows/aclui.d b/libphobos/libdruntime/core/sys/windows/aclui.d
index 08be626ad2d6..d7db31738f95 100644
--- a/libphobos/libdruntime/core/sys/windows/aclui.d
+++ b/libphobos/libdruntime/core/sys/windows/aclui.d
@@ -114,7 +114,7 @@ alias ISecurityInformation LPSECURITYINFO;
 // FIXME: linkage attribute?
 extern (C) /+DECLSPEC_IMPORT+/ extern const IID IID_ISecurityInformation;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     HPROPSHEETPAGE CreateSecurityPage(LPSECURITYINFO psi);
     BOOL EditSecurity(HWND hwndOwner, LPSECURITYINFO psi);
 }
diff --git a/libphobos/libdruntime/core/sys/windows/commctrl.d b/libphobos/libdruntime/core/sys/windows/commctrl.d
index fe4ebbc75d8f..073587f01c15 100644
--- a/libphobos/libdruntime/core/sys/windows/commctrl.d
+++ b/libphobos/libdruntime/core/sys/windows/commctrl.d
@@ -5014,7 +5014,7 @@ BOOL Animate_Seek(HWND hwnd, int frame) {
     return Animate_Play(hwnd, frame, frame, 1);
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     HBITMAP CreateMappedBitmap(HINSTANCE, INT_PTR, UINT, LPCOLORMAP, int);
     HWND CreateStatusWindowA(LONG, LPCSTR, HWND, UINT);
     HWND CreateStatusWindowW(LONG, LPCWSTR, HWND, UINT);
@@ -5068,7 +5068,7 @@ BOOL DateTime_SetSystemtime(HWND hwnd, WPARAM flag, LPSYSTEMTIME lpSysTime) {
       cast(LPARAM) lpSysTime);
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     void DrawInsert(HWND, HWND, int);
     void DrawStatusTextA(HDC, LPRECT, LPCSTR, UINT);
     void DrawStatusTextW(HDC, LPRECT, LPCWSTR, UINT);
@@ -5142,7 +5142,7 @@ static if (_WIN32_IE >= 0x400) {
     }
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     HDSA DSA_Create(INT, INT);
     BOOL DSA_Destroy(HDSA);
     VOID DSA_DestroyCallback(HDSA, PFNDSAENUMCALLBACK, PVOID);
@@ -5758,7 +5758,7 @@ BOOL MonthCal_SetRange(HWND w, DWORD f, LPSYSTEMTIME st) {
       cast(LPARAM) st);
 }
 
-extern (Windows) BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT);
+extern (Windows) nothrow @nogc BOOL ShowHideMenuCtl(HWND, UINT_PTR, PINT);
 
 BOOL TabCtrl_GetItem(HWND w, int i, LPTCITEM p) {
     return cast(BOOL) SendMessage(w, TCM_GETITEM, i, cast(LPARAM) p);
@@ -6069,7 +6069,7 @@ static if (_WIN32_IE >= 0x300) {
         return cast(BOOL) SendMessage(w, LVM_SETITEMCOUNT, i, cast(LPARAM) f);
     }
 
-    extern (Windows) {
+    extern (Windows) nothrow @nogc {
         WINBOOL ImageList_SetImageCount(HIMAGELIST, UINT);
         WINBOOL ImageList_Copy(HIMAGELIST, int, HIMAGELIST, int, UINT);
         WINBOOL ImageList_DrawIndirect(IMAGELISTDRAWPARAMS*);
diff --git a/libphobos/libdruntime/core/sys/windows/custcntl.d b/libphobos/libdruntime/core/sys/windows/custcntl.d
index f9234ac4f893..ddac9607382c 100644
--- a/libphobos/libdruntime/core/sys/windows/custcntl.d
+++ b/libphobos/libdruntime/core/sys/windows/custcntl.d
@@ -99,6 +99,7 @@ extern (Windows) {
     alias INT function(DWORD, DWORD, HFONT, LPWSTR) LPFNCCSIZETOTEXTW;
     alias UINT function(LPCCINFOA) LPFNCCINFOA;
     alias UINT function(LPCCINFOW) LPFNCCINFOW;
+nothrow @nogc:
     UINT CustomControlInfoA(LPCCINFOA acci);
     UINT CustomControlInfoW(LPCCINFOW acci);
 }
diff --git a/libphobos/libdruntime/core/sys/windows/dde.d b/libphobos/libdruntime/core/sys/windows/dde.d
index d9b8bec51521..975c62e42f31 100644
--- a/libphobos/libdruntime/core/sys/windows/dde.d
+++ b/libphobos/libdruntime/core/sys/windows/dde.d
@@ -148,7 +148,7 @@ deprecated struct DDEUP {
     @property bool   fAckReq(bool f)   { _bf = cast(ushort) ((_bf & ~0x8000) | (f << 15)); return f; }
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL DdeSetQualityOfService(HWND, const(SECURITY_QUALITY_OF_SERVICE)*,
       PSECURITY_QUALITY_OF_SERVICE);
     BOOL ImpersonateDdeClientWindow(HWND, HWND);
diff --git a/libphobos/libdruntime/core/sys/windows/ddeml.d b/libphobos/libdruntime/core/sys/windows/ddeml.d
index 2e8e3ee25d65..0ff1a3f4f1eb 100644
--- a/libphobos/libdruntime/core/sys/windows/ddeml.d
+++ b/libphobos/libdruntime/core/sys/windows/ddeml.d
@@ -332,7 +332,7 @@ struct MONMSGSTRUCT {
 }
 alias MONMSGSTRUCT* PMONMSGSTRUCT;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL DdeAbandonTransaction(DWORD, HCONV, DWORD);
     PBYTE DdeAccessData(HDDEDATA, PDWORD);
     HDDEDATA DdeAddData(HDDEDATA, PBYTE, DWORD, DWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d b/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d
index d21169a7346c..6171469c8c73 100644
--- a/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d
+++ b/libphobos/libdruntime/core/sys/windows/dhcpcsdk.d
@@ -45,7 +45,7 @@ struct DHCPCAPI_PARAMS_ARRAY {
 }
 alias DHCPCAPI_PARAMS_ARRAY* PDHCPCAPI_PARAMS_ARRAY, LPDHCPCAPI_PARAMS_ARRAY;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     void DhcpCApiCleanup();
     DWORD DhcpCApiInitialize(LPDWORD);
     DWORD DhcpDeRegisterParamChange(DWORD, LPVOID, LPVOID);
diff --git a/libphobos/libdruntime/core/sys/windows/errorrep.d b/libphobos/libdruntime/core/sys/windows/errorrep.d
index 42fad9a39ec3..840ea61f3ceb 100644
--- a/libphobos/libdruntime/core/sys/windows/errorrep.d
+++ b/libphobos/libdruntime/core/sys/windows/errorrep.d
@@ -29,7 +29,7 @@ enum EFaultRepRetVal {
     frrvOkHeadless // = 7
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL AddERExcludedApplicationA(LPCSTR);
     BOOL AddERExcludedApplicationW(LPCWSTR);
     EFaultRepRetVal ReportFault(LPEXCEPTION_POINTERS, DWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/httpext.d b/libphobos/libdruntime/core/sys/windows/httpext.d
index 200b2843c4df..b7f6aedcbdcf 100644
--- a/libphobos/libdruntime/core/sys/windows/httpext.d
+++ b/libphobos/libdruntime/core/sys/windows/httpext.d
@@ -109,7 +109,7 @@ struct HSE_SEND_HEADER_EX_INFO {
 }
 alias HSE_SEND_HEADER_EX_INFO* LPHSE_SEND_HEADER_EX_INF;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL GetExtensionVersion(HSE_VERSION_INFO*);
     DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK*);
     BOOL TerminateExtension(DWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/imagehlp.d b/libphobos/libdruntime/core/sys/windows/imagehlp.d
index 68d88d787807..c6d28e68fd12 100644
--- a/libphobos/libdruntime/core/sys/windows/imagehlp.d
+++ b/libphobos/libdruntime/core/sys/windows/imagehlp.d
@@ -295,6 +295,7 @@ extern (Windows) {
     alias BOOL function(DIGEST_HANDLE refdata, PBYTE pData, DWORD dwLength)
       DIGEST_FUNCTION;
 
+nothrow @nogc:
     PIMAGE_NT_HEADERS CheckSumMappedFile(LPVOID, DWORD, LPDWORD, LPDWORD);
     DWORD MapFileAndCheckSumA(LPSTR, LPDWORD, LPDWORD);
     DWORD MapFileAndCheckSumW(PWSTR, LPDWORD, LPDWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/intshcut.d b/libphobos/libdruntime/core/sys/windows/intshcut.d
index ab662e418e7d..cc4bf6235425 100644
--- a/libphobos/libdruntime/core/sys/windows/intshcut.d
+++ b/libphobos/libdruntime/core/sys/windows/intshcut.d
@@ -70,7 +70,7 @@ interface IUniformResourceLocator : IUnknown {
 alias IUniformResourceLocator PIUniformResourceLocator,
   PCIUniformResourceLocator;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL InetIsOffline(DWORD);
     HRESULT MIMEAssociationDialogA(HWND, DWORD, PCSTR, PCSTR, PSTR, UINT);
     HRESULT MIMEAssociationDialogW(HWND, DWORD, PCWSTR, PCWSTR, PWSTR, UINT);
diff --git a/libphobos/libdruntime/core/sys/windows/iphlpapi.d b/libphobos/libdruntime/core/sys/windows/iphlpapi.d
index 4a8e64cbef01..d8f0948ca3f5 100644
--- a/libphobos/libdruntime/core/sys/windows/iphlpapi.d
+++ b/libphobos/libdruntime/core/sys/windows/iphlpapi.d
@@ -13,7 +13,7 @@ version (Windows):
 import core.sys.windows.ipexport, core.sys.windows.iprtrmib, core.sys.windows.iptypes;
 import core.sys.windows.winbase, core.sys.windows.windef;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     DWORD AddIPAddress(IPAddr, IPMask, DWORD, PULONG, PULONG);
     DWORD CreateIpForwardEntry(PMIB_IPFORWARDROW);
     DWORD CreateIpNetEntry(PMIB_IPNETROW);
diff --git a/libphobos/libdruntime/core/sys/windows/lmaccess.d b/libphobos/libdruntime/core/sys/windows/lmaccess.d
index 9791ff6e3917..cf6f9d4c4d19 100644
--- a/libphobos/libdruntime/core/sys/windows/lmaccess.d
+++ b/libphobos/libdruntime/core/sys/windows/lmaccess.d
@@ -708,7 +708,7 @@ struct NETLOGON_INFO_3{
 }
 alias NETLOGON_INFO_3* PNETLOGON_INFO_3;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
 deprecated {
     /* These are obsolete */
     NET_API_STATUS NetAccessAdd(LPCWSTR,DWORD,PBYTE,PDWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/lmalert.d b/libphobos/libdruntime/core/sys/windows/lmalert.d
index ad0c3ca7b18e..acb96cfbc907 100644
--- a/libphobos/libdruntime/core/sys/windows/lmalert.d
+++ b/libphobos/libdruntime/core/sys/windows/lmalert.d
@@ -71,7 +71,7 @@ struct USER_OTHER_INFO{
 }
 alias USER_OTHER_INFO* PUSER_OTHER_INFO, LPUSER_OTHER_INFO;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
 NET_API_STATUS NetAlertRaise(LPCWSTR,PVOID,DWORD);
 NET_API_STATUS NetAlertRaiseEx(LPCWSTR,PVOID,DWORD,LPCWSTR);
 }
diff --git a/libphobos/libdruntime/core/sys/windows/lmapibuf.d b/libphobos/libdruntime/core/sys/windows/lmapibuf.d
index e8559543f3ea..85ccd1f49f25 100644
--- a/libphobos/libdruntime/core/sys/windows/lmapibuf.d
+++ b/libphobos/libdruntime/core/sys/windows/lmapibuf.d
@@ -12,7 +12,7 @@ pragma(lib, "netapi32");
 
 import core.sys.windows.lmcons, core.sys.windows.windef;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     NET_API_STATUS NetApiBufferAllocate(DWORD, PVOID*);
     NET_API_STATUS NetApiBufferFree(PVOID);
     NET_API_STATUS NetApiBufferReallocate(PVOID, DWORD, PVOID*);
diff --git a/libphobos/libdruntime/core/sys/windows/lmmsg.d b/libphobos/libdruntime/core/sys/windows/lmmsg.d
index a3abd6054226..eab878827472 100644
--- a/libphobos/libdruntime/core/sys/windows/lmmsg.d
+++ b/libphobos/libdruntime/core/sys/windows/lmmsg.d
@@ -32,7 +32,7 @@ struct MSG_INFO_1 {
 }
 alias MSG_INFO_1* PMSG_INFO_1, LPMSG_INFO_1;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     NET_API_STATUS NetMessageBufferSend(LPCWSTR, LPCWSTR, LPCWSTR, PBYTE,
       DWORD);
     NET_API_STATUS NetMessageNameAdd(LPCWSTR, LPCWSTR);
diff --git a/libphobos/libdruntime/core/sys/windows/lmremutl.d b/libphobos/libdruntime/core/sys/windows/lmremutl.d
index 8c90df73f2a3..196be667feca 100644
--- a/libphobos/libdruntime/core/sys/windows/lmremutl.d
+++ b/libphobos/libdruntime/core/sys/windows/lmremutl.d
@@ -52,7 +52,7 @@ struct TIME_OF_DAY_INFO {
 }
 alias TIME_OF_DAY_INFO* PTIME_OF_DAY_INFO, LPTIME_OF_DAY_INFO;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     NET_API_STATUS NetRemoteTOD(LPCWSTR, PBYTE*);
     NET_API_STATUS NetRemoteComputerSupports(LPCWSTR, DWORD, PDWORD);
     NET_API_STATUS RxRemoteApi(DWORD, LPCWSTR, LPDESC, LPDESC, LPDESC,
diff --git a/libphobos/libdruntime/core/sys/windows/mgmtapi.d b/libphobos/libdruntime/core/sys/windows/mgmtapi.d
index 8f84eea54e88..336d24e750e0 100644
--- a/libphobos/libdruntime/core/sys/windows/mgmtapi.d
+++ b/libphobos/libdruntime/core/sys/windows/mgmtapi.d
@@ -29,7 +29,7 @@ enum MGMCTL_SETAGENTPORT = 1;
 
 alias PVOID LPSNMP_MGR_SESSION;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL SnmpMgrClose(LPSNMP_MGR_SESSION);
     BOOL SnmpMgrCtl(LPSNMP_MGR_SESSION, DWORD, LPVOID, DWORD, LPVOID, DWORD,
       LPDWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/ntdll.d b/libphobos/libdruntime/core/sys/windows/ntdll.d
index 28d560c537b3..7f0c47a15fc2 100644
--- a/libphobos/libdruntime/core/sys/windows/ntdll.d
+++ b/libphobos/libdruntime/core/sys/windows/ntdll.d
@@ -19,4 +19,4 @@ enum SHUTDOWN_ACTION {
     ShutdownPowerOff
 }
 
-extern (Windows) uint NtShutdownSystem(SHUTDOWN_ACTION Action);
+extern (Windows) nothrow @nogc uint NtShutdownSystem(SHUTDOWN_ACTION Action);
diff --git a/libphobos/libdruntime/core/sys/windows/ntsecapi.d b/libphobos/libdruntime/core/sys/windows/ntsecapi.d
index fa08a74ee5bb..77101f669b4a 100644
--- a/libphobos/libdruntime/core/sys/windows/ntsecapi.d
+++ b/libphobos/libdruntime/core/sys/windows/ntsecapi.d
@@ -726,7 +726,7 @@ struct TRUSTED_DOMAIN_FULL_INFORMATION {
 }
 alias TRUSTED_DOMAIN_FULL_INFORMATION* PTRUSTED_DOMAIN_FULL_INFORMATION;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     NTSTATUS LsaAddAccountRights(LSA_HANDLE, PSID, PLSA_UNICODE_STRING,
       ULONG);
     NTSTATUS LsaCallAuthenticationPackage(HANDLE, ULONG, PVOID, ULONG,
diff --git a/libphobos/libdruntime/core/sys/windows/ole.d b/libphobos/libdruntime/core/sys/windows/ole.d
index b9e55a3b1298..7c51d7a1be83 100644
--- a/libphobos/libdruntime/core/sys/windows/ole.d
+++ b/libphobos/libdruntime/core/sys/windows/ole.d
@@ -283,7 +283,7 @@ struct OLESERVERDOC {
 }
 alias OLESERVERDOC* LPOLESERVERDOC;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     OLESTATUS OleDelete(LPOLEOBJECT);
     OLESTATUS OleRelease(LPOLEOBJECT);
     OLESTATUS OleSaveToStream(LPOLEOBJECT, LPOLESTREAM);
diff --git a/libphobos/libdruntime/core/sys/windows/ole2.d b/libphobos/libdruntime/core/sys/windows/ole2.d
index 3fef058411f8..fcbbc8a3d2cb 100644
--- a/libphobos/libdruntime/core/sys/windows/ole2.d
+++ b/libphobos/libdruntime/core/sys/windows/ole2.d
@@ -48,7 +48,7 @@ extern (Windows) {
 }
 alias OLESTREAMVTBL* LPOLESTREAMVTBL;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     HRESULT CreateDataAdviseHolder(LPDATAADVISEHOLDER*);
     DWORD OleBuildVersion();
     HRESULT ReadClassStg(LPSTORAGE, CLSID*);
diff --git a/libphobos/libdruntime/core/sys/windows/oleacc.d b/libphobos/libdruntime/core/sys/windows/oleacc.d
index b19855d9af07..8a97a93e3479 100644
--- a/libphobos/libdruntime/core/sys/windows/oleacc.d
+++ b/libphobos/libdruntime/core/sys/windows/oleacc.d
@@ -186,7 +186,7 @@ interface IAccessible : IDispatch {
 
 alias IAccessible LPACCESSIBLE;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     HRESULT AccessibleChildren(IAccessible, LONG, LONG, VARIANT*, LONG*);
     HRESULT AccessibleObjectFromEvent(HWND, DWORD, DWORD, IAccessible, VARIANT*);
     HRESULT AccessibleObjectFromPoint(POINT, IAccessible*, VARIANT*);
diff --git a/libphobos/libdruntime/core/sys/windows/oleauto.d b/libphobos/libdruntime/core/sys/windows/oleauto.d
index 188813609ff6..f4bb40259ab8 100644
--- a/libphobos/libdruntime/core/sys/windows/oleauto.d
+++ b/libphobos/libdruntime/core/sys/windows/oleauto.d
@@ -226,7 +226,7 @@ deprecated {  // not actually deprecated, but they aren't converted yet.
     alias ICreateTypeLib2 LPCREATETYPELIB2;
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BSTR SysAllocString(const(OLECHAR)*);
     int SysReAllocString(BSTR*, const(OLECHAR)*);
     BSTR SysAllocStringLen(const(OLECHAR)*, uint);
diff --git a/libphobos/libdruntime/core/sys/windows/oledlg.d b/libphobos/libdruntime/core/sys/windows/oledlg.d
index f810f6c13dae..26e0a791609e 100644
--- a/libphobos/libdruntime/core/sys/windows/oledlg.d
+++ b/libphobos/libdruntime/core/sys/windows/oledlg.d
@@ -387,7 +387,7 @@ struct OLEUIINSERTOBJECTA {
 }
 alias OLEUIINSERTOBJECTA* POLEUIINSERTOBJECTA, LPOLEUIINSERTOBJECTA;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     UINT OleUIInsertObjectW(LPOLEUIINSERTOBJECTW);
     UINT OleUIInsertObjectA(LPOLEUIINSERTOBJECTA);
 }
@@ -849,7 +849,7 @@ struct OLEUIOBJECTPROPSA {
 }
 alias OLEUIOBJECTPROPSA* POLEUIOBJECTPROPSA, LPOLEUIOBJECTPROPSA;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL OleUIAddVerbMenuW(LPOLEOBJECT, LPCWSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*);
     BOOL OleUIAddVerbMenuA(LPOLEOBJECT, LPCSTR, HMENU, UINT, UINT, UINT, BOOL, UINT, HMENU*);
     UINT OleUIBusyW(LPOLEUIBUSYW);
diff --git a/libphobos/libdruntime/core/sys/windows/prsht.d b/libphobos/libdruntime/core/sys/windows/prsht.d
index 053b1fa2ac98..570d1689b38c 100644
--- a/libphobos/libdruntime/core/sys/windows/prsht.d
+++ b/libphobos/libdruntime/core/sys/windows/prsht.d
@@ -331,7 +331,7 @@ struct PSHNOTIFY {
 }
 alias PSHNOTIFY* LPPSHNOTIFY;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     HPROPSHEETPAGE CreatePropertySheetPageA(LPCPROPSHEETPAGEA);
     HPROPSHEETPAGE CreatePropertySheetPageW(LPCPROPSHEETPAGEW);
     BOOL DestroyPropertySheetPage(HPROPSHEETPAGE);
diff --git a/libphobos/libdruntime/core/sys/windows/psapi.d b/libphobos/libdruntime/core/sys/windows/psapi.d
index 968ce6c45306..42fc5fdd6bd9 100644
--- a/libphobos/libdruntime/core/sys/windows/psapi.d
+++ b/libphobos/libdruntime/core/sys/windows/psapi.d
@@ -90,7 +90,7 @@ alias BOOL function(LPVOID, PENUM_PAGE_FILE_INFORMATION, LPCSTR)
 
 
 // Grouped by application, not in alphabetical order.
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     /* Process Information
      * http://windowssdk.msdn.microsoft.com/library/ms684870.aspx */
     BOOL EnumProcesses(DWORD*, DWORD, DWORD*); /* NT/2000/XP/Server2003/Vista/Longhorn */
diff --git a/libphobos/libdruntime/core/sys/windows/rpc.d b/libphobos/libdruntime/core/sys/windows/rpc.d
index b432bc7c6811..3b7e28d213c4 100644
--- a/libphobos/libdruntime/core/sys/windows/rpc.d
+++ b/libphobos/libdruntime/core/sys/windows/rpc.d
@@ -26,6 +26,6 @@ public import core.sys.windows.winerror;
 alias MIDL_user_allocate midl_user_allocate;
 alias MIDL_user_free midl_user_free;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     int I_RpcMapWin32Status(RPC_STATUS);
 }
diff --git a/libphobos/libdruntime/core/sys/windows/winhttp.d b/libphobos/libdruntime/core/sys/windows/winhttp.d
index 63d59cb157c4..1de3b9366a9f 100644
--- a/libphobos/libdruntime/core/sys/windows/winhttp.d
+++ b/libphobos/libdruntime/core/sys/windows/winhttp.d
@@ -771,7 +771,7 @@ static if (_WIN32_WINNT >= 0x602)
 }
 
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL WinHttpAddRequestHeaders(HINTERNET hRequest, LPCWSTR pwszHeaders, DWORD dwHeadersLength, DWORD dwModifiers);
 
     BOOL WinHttpCheckPlatform();
diff --git a/libphobos/libdruntime/core/sys/windows/winnetwk.d b/libphobos/libdruntime/core/sys/windows/winnetwk.d
index 9bf2e64614a1..568c2e89e4f9 100644
--- a/libphobos/libdruntime/core/sys/windows/winnetwk.d
+++ b/libphobos/libdruntime/core/sys/windows/winnetwk.d
@@ -315,7 +315,7 @@ struct NETCONNECTINFOSTRUCT {
 }
 alias NETCONNECTINFOSTRUCT* LPNETCONNECTINFOSTRUCT;
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     DWORD WNetAddConnection2A(LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD);
     DWORD WNetAddConnection2W(LPNETRESOURCEW, LPCWSTR, LPCWSTR, DWORD);
     DWORD WNetAddConnection3A(HWND, LPNETRESOURCEA, LPCSTR, LPCSTR, DWORD);
diff --git a/libphobos/libdruntime/core/sys/windows/winsvc.d b/libphobos/libdruntime/core/sys/windows/winsvc.d
index e6418ac8ac6b..a34cd2582057 100644
--- a/libphobos/libdruntime/core/sys/windows/winsvc.d
+++ b/libphobos/libdruntime/core/sys/windows/winsvc.d
@@ -272,7 +272,7 @@ static if (_WIN32_WINNT >= 0x500) {
     alias SERVICE_FAILURE_ACTIONSW* LPSERVICE_FAILURE_ACTIONSW;
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     BOOL ChangeServiceConfigA(SC_HANDLE, DWORD, DWORD, DWORD, LPCSTR,
       LPCSTR, LPDWORD, LPCSTR, LPCSTR, LPCSTR, LPCSTR);
     BOOL ChangeServiceConfigW(SC_HANDLE, DWORD, DWORD, DWORD, LPCWSTR,
diff --git a/libphobos/libdruntime/core/sys/windows/winuser.d b/libphobos/libdruntime/core/sys/windows/winuser.d
index 469e68e66684..f6414440783a 100644
--- a/libphobos/libdruntime/core/sys/windows/winuser.d
+++ b/libphobos/libdruntime/core/sys/windows/winuser.d
@@ -3848,7 +3848,7 @@ int GetMenuStringA(HMENU, UINT, LPSTR, int, UINT);
 int GetMenuStringW(HMENU, UINT, LPWSTR, int, UINT);
 BOOL GetMessageA(LPMSG, HWND, UINT, UINT);
 BOOL GetMessageW(LPMSG, HWND, UINT, UINT);
-LONG GetMessageExtraInfo();
+LPARAM GetMessageExtraInfo();
 DWORD GetMessagePos();
 LONG GetMessageTime();
 
diff --git a/libphobos/libdruntime/core/sys/windows/winver.d b/libphobos/libdruntime/core/sys/windows/winver.d
index dc3d881d2c4e..646891b1be9a 100644
--- a/libphobos/libdruntime/core/sys/windows/winver.d
+++ b/libphobos/libdruntime/core/sys/windows/winver.d
@@ -139,7 +139,7 @@ struct VS_FIXEDFILEINFO {
     DWORD dwFileDateLS;
 }
 
-extern (Windows) {
+extern (Windows) nothrow @nogc {
     DWORD VerFindFileA(DWORD, LPCSTR, LPCSTR, LPCSTR, LPSTR, PUINT, LPSTR,
       PUINT);
     DWORD VerFindFileW(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, PUINT, LPWSTR,
diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d
index 86f5f820cc02..7851f664297f 100644
--- a/libphobos/libdruntime/rt/lifetime.d
+++ b/libphobos/libdruntime/rt/lifetime.d
@@ -23,6 +23,14 @@ static import rt.tlsgc;
 alias BlkInfo = GC.BlkInfo;
 alias BlkAttr = GC.BlkAttr;
 
+// for now, all GC array functions are not exposed via core.memory.
+extern(C) {
+    void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow;
+    bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow;
+    size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow;
+    bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow;
+}
+
 private
 {
     alias bool function(Object) CollectHandler;
@@ -210,31 +218,13 @@ private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const Type
 {
     import core.checkedint;
 
-    size_t typeInfoSize = structTypeInfoSize(tinext);
-    size_t padsize = arrsize > MAXMEDSIZE ? LARGEPAD : ((arrsize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize);
-
-    bool overflow;
-    auto padded_size = addu(arrsize, padsize, overflow);
-
-    if (overflow)
-        return BlkInfo();
-
-    uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE;
-    if (typeInfoSize)
-        attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE;
-
-    auto bi = GC.qalloc(padded_size, attr, tinext);
-    __arrayClearPad(bi, arrsize, padsize);
-    return bi;
+    return __arrayAlloc(arrsize, null, ti, tinext);
 }
 
-private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
+private BlkInfo __arrayAlloc(size_t arrsize, void* copyAttrsFrom, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
 {
     import core.checkedint;
 
-    if (!info.base)
-        return __arrayAlloc(arrsize, ti, tinext);
-
     immutable padsize = __arrayPad(arrsize, tinext);
     bool overflow;
     auto padded_size = addu(arrsize, padsize, overflow);
@@ -243,7 +233,18 @@ private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeI
         return BlkInfo();
     }
 
-    auto bi = GC.qalloc(padded_size, info.attr, tinext);
+    uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0);
+    if (copyAttrsFrom)
+    {
+        // try to copy attrs from the given block
+        auto info = GC.query(copyAttrsFrom);
+        if (info.base)
+            attr = info.attr;
+    }
+    // always make sure the appendable attr is set.
+    attr |= BlkAttr.APPENDABLE;
+
+    auto bi = GC.qalloc(padded_size, attr, tinext);
     __arrayClearPad(bi, arrsize, padsize);
     return bi;
 }
@@ -260,54 +261,43 @@ Params:
 */
 extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow
 {
-    // note, we do not care about shared.  We are setting the length no matter
-    // what, so no lock is required.
     debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %d, arr.ptr = x%x arr.length = %d\n", ti.next.tsize, arr.ptr, arr.length);
     auto tinext = unqualify(ti.next);
     auto size = tinext.tsize;                  // array element size
-    auto cursize = arr.length * size;
+    auto reqsize = arr.length * size;
     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-    auto bic = isshared ? null : __getBlkInfo(arr.ptr);
-    auto info = bic ? *bic : GC.query(arr.ptr);
-    if (info.base && (info.attr & BlkAttr.APPENDABLE))
-    {
-        auto newsize = (arr.ptr - __arrayStart(info)) + cursize;
 
-        debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize);
+    auto curArr = gc_getArrayUsed(arr.ptr, isshared);
+    if (curArr.ptr is null)
+        // not a valid GC pointer
+        return;
+
+    // align the array.
+    auto offset = arr.ptr - curArr.ptr;
+    auto cursize = curArr.length - offset;
+    if (cursize <= reqsize)
+        // invalid situation, or no change.
+        return;
 
-        // destroy structs that become unused memory when array size is shrinked
-        if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
+    // if the type has a destructor, destroy elements we are about to remove.
+    if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
+    {
+        auto sti = cast(TypeInfo_Struct)cast(void*)tinext;
+        if (sti.xdtor)
         {
-            auto sti = cast(TypeInfo_Struct)cast(void*)tinext;
-            if (sti.xdtor)
+            try
             {
-                auto oldsize = __arrayAllocLength(info, tinext);
-                if (oldsize > cursize)
-                {
-                    try
-                    {
-                        finalize_array(arr.ptr + cursize, oldsize - cursize, sti);
-                    }
-                    catch (Exception e)
-                    {
-                        import core.exception : onFinalizeError;
-                        onFinalizeError(sti, e);
-                    }
-                }
+                finalize_array(arr.ptr + reqsize, cursize - reqsize, sti);
+            }
+            catch (Exception e)
+            {
+                import core.exception : onFinalizeError;
+                onFinalizeError(sti, e);
             }
         }
-        // Note: Since we "assume" the append is safe, it means it is not shared.
-        // Since it is not shared, we also know it won't throw (no lock).
-        if (!__setArrayAllocLength(info, newsize, false, tinext))
-        {
-            import core.exception : onInvalidMemoryOperationError;
-            onInvalidMemoryOperationError();
-        }
-
-        // cache the block if not already done.
-        if (!isshared && !bic)
-            __insertBlkInfoCache(info, null);
     }
+
+    gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared);
 }
 
 package bool hasPostblit(in TypeInfo ti) nothrow pure
@@ -372,10 +362,7 @@ do
     import core.stdc.string;
     import core.exception : onOutOfMemoryError;
 
-    // step 1, get the block
     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-    auto bic = isshared ? null : __getBlkInfo((*p).ptr);
-    auto info = bic ? *bic : GC.query((*p).ptr);
     auto tinext = unqualify(ti.next);
     auto size = tinext.tsize;
     version (D_InlineAsm_X86)
@@ -416,77 +403,21 @@ Loverflow:
     assert(0);
 Lcontinue:
 
-    // step 2, get the actual "allocated" size.  If the allocated size does not
-    // match what we expect, then we will need to reallocate anyways.
-
-    // TODO: this probably isn't correct for shared arrays
-    size_t curallocsize = void;
-    size_t curcapacity = void;
-    size_t offset = void;
-    size_t arraypad = void;
-    if (info.base && (info.attr & BlkAttr.APPENDABLE))
-    {
-        if (info.size <= 256)
-        {
-            arraypad = SMALLPAD + structTypeInfoSize(tinext);
-            curallocsize = *(cast(ubyte *)(info.base + info.size - arraypad));
-        }
-        else if (info.size < PAGESIZE)
-        {
-            arraypad = MEDPAD + structTypeInfoSize(tinext);
-            curallocsize = *(cast(ushort *)(info.base + info.size - arraypad));
-        }
-        else
-        {
-            curallocsize = *(cast(size_t *)(info.base));
-            arraypad = LARGEPAD;
-        }
-
-
-        offset = (*p).ptr - __arrayStart(info);
-        if (offset + (*p).length * size != curallocsize)
-        {
-            curcapacity = 0;
-        }
-        else
-        {
-            // figure out the current capacity of the block from the point
-            // of view of the array.
-            curcapacity = info.size - offset - arraypad;
-        }
-    }
-    else
-    {
-        curallocsize = curcapacity = offset = 0;
-    }
-    debug(PRINTF) printf("_d_arraysetcapacity, p = x%d,%d, newcapacity=%d, info.size=%d, reqsize=%d, curallocsize=%d, curcapacity=%d, offset=%d\n", (*p).ptr, (*p).length, newcapacity, info.size, reqsize, curallocsize, curcapacity, offset);
-
-    if (curcapacity >= reqsize)
-    {
-        // no problems, the current allocated size is large enough.
-        return curcapacity / size;
-    }
-
-    // step 3, try to extend the array in place.
-    if (info.size >= PAGESIZE && curcapacity != 0)
-    {
-        auto extendsize = reqsize + offset + LARGEPAD - info.size;
-        auto u = GC.extend(info.base, extendsize, extendsize);
-        if (u)
-        {
-            // extend worked, save the new current allocated size
-            if (bic)
-                bic.size = u; // update cache
-            curcapacity = u - offset - LARGEPAD;
-            return curcapacity / size;
-        }
-    }
-
-    // step 4, if extending doesn't work, allocate a new array with at least the requested allocated size.
+    // step 1, see if we can ensure the capacity is valid in-place
     auto datasize = (*p).length * size;
-    // copy attributes from original block, or from the typeinfo if the
-    // original block doesn't exist.
-    info = __arrayAlloc(reqsize, info, ti, tinext);
+    auto curCapacity = gc_reserveArrayCapacity((*p).ptr[0 .. datasize], reqsize, isshared);
+    if (curCapacity != 0)
+        // in-place worked!
+        return curCapacity / size;
+
+    if (reqsize <= datasize)
+        // requested size is less than array size, the current array satisfies
+        // the request. But this is not an appendable GC array, so return 0.
+        return 0;
+
+    // step 2, if reserving in-place doesn't work, allocate a new array with at
+    // least the requested allocated size.
+    auto info = __arrayAlloc(reqsize, (*p).ptr, ti, tinext);
     if (info.base is null)
         goto Loverflow;
     // copy the data over.
@@ -512,22 +443,12 @@ Lcontinue:
     // set up the correct length
     __setArrayAllocLength(info, datasize, isshared, tinext);
     if (!isshared)
-        __insertBlkInfoCache(info, bic);
+        __insertBlkInfoCache(info, null);
 
     *p = (cast(void*)tgt)[0 .. (*p).length];
 
-    // determine the padding.  This has to be done manually because __arrayPad
-    // assumes you are not counting the pad size, and info.size does include
-    // the pad.
-    if (info.size <= 256)
-        arraypad = SMALLPAD + structTypeInfoSize(tinext);
-    else if (info.size < PAGESIZE)
-        arraypad = MEDPAD + structTypeInfoSize(tinext);
-    else
-        arraypad = LARGEPAD;
-
-    curcapacity = info.size - arraypad;
-    return curcapacity / size;
+    curCapacity = __arrayAllocCapacity(info);
+    return curCapacity / size;
 }
 
 /**
@@ -673,25 +594,12 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
     {
         // the GC might not have cleared the padding area in the block
         *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null;
-        *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti;
+        __setBlockFinalizerInfo(blkInf, cast() ti);
     }
 
     return p;
 }
 
-debug(PRINTF)
-{
-    extern(C) void printArrayCache()
-    {
-        auto ptr = __blkcache;
-        printf("CACHE: \n");
-        foreach (i; 0 .. N_CACHE_BLOCKS)
-        {
-            printf("  %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr);
-        }
-    }
-}
-
 /**
  *
  */
@@ -804,33 +712,30 @@ void finalize_array2(void* p, size_t size) nothrow
 {
     debug(PRINTF) printf("rt_finalize_array2(p = %p)\n", p);
 
-    TypeInfo_Struct si = void;
+    // construct a BlkInfo to match the array API
+    auto info = BlkInfo(
+            base: p,
+            size: size,
+            attr: BlkAttr.APPENDABLE | BlkAttr.STRUCTFINAL
+    );
+    auto usedsize = __arrayAllocLength(info);
+    auto arrptr = __arrayStart(info);
+
     debug (VALGRIND)
     {
         auto block = p[0..size];
         disableAddrReportingInRange(block);
     }
-    if (size <= 256)
-    {
-        si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
-        size = *cast(ubyte*)(p + size - size_t.sizeof - SMALLPAD);
-    }
-    else if (size < PAGESIZE)
-    {
-        si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
-        size = *cast(ushort*)(p + size - size_t.sizeof - MEDPAD);
-    }
-    else
-    {
-        si = *cast(TypeInfo_Struct*)(p + size_t.sizeof);
-        size = *cast(size_t*)p;
-        p += LARGEPREFIX;
-    }
+
+    TypeInfo_Struct si = size < PAGESIZE ?
+        *cast(TypeInfo_Struct*)(p + size - size_t.sizeof) : // small
+        *cast(TypeInfo_Struct*)(p + size_t.sizeof); // large
+
     debug (VALGRIND) enableAddrReportingInRange(block);
 
     try
     {
-        finalize_array(p, size, si);
+        finalize_array(arrptr, usedsize, si);
     }
     catch (Exception e)
     {
@@ -977,8 +882,7 @@ do
     if (newlength <= (*p).length)
     {
         *p = (*p)[0 .. newlength];
-        void* newdata = (*p).ptr;
-        return newdata[0 .. newlength];
+        return *p;
     }
     auto tinext = unqualify(ti.next);
     size_t sizeelem = tinext.tsize;
@@ -1027,6 +931,7 @@ do
 
     if (!(*p).ptr)
     {
+        assert((*p).length == 0);
         // pointer was null, need to allocate
         auto info = __arrayAlloc(newsize, ti, tinext);
         if (info.base is null)
@@ -1044,102 +949,31 @@ do
     }
 
     const size_t size = (*p).length * sizeelem;
-    auto   bic = isshared ? null : __getBlkInfo((*p).ptr);
-    auto   info = bic ? *bic : GC.query((*p).ptr);
 
     /* Attempt to extend past the end of the existing array.
      * If not possible, allocate new space for entire array and copy.
      */
-    bool allocateAndCopy = false;
     void* newdata = (*p).ptr;
-    if (info.base && (info.attr & BlkAttr.APPENDABLE))
-    {
-        // calculate the extent of the array given the base.
-        const size_t offset = (*p).ptr - __arrayStart(info);
-        if (info.size >= PAGESIZE)
-        {
-            // size of array is at the front of the block
-            if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-            {
-                // check to see if it failed because there is not
-                // enough space
-                if (*(cast(size_t*)info.base) == size + offset)
-                {
-                    // not enough space, try extending
-                    auto extendsize = newsize + offset + LARGEPAD - info.size;
-                    auto u = GC.extend(info.base, extendsize, extendsize);
-                    if (u)
-                    {
-                        // extend worked, now try setting the length
-                        // again.
-                        info.size = u;
-                        if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-                        {
-                            if (!isshared)
-                                __insertBlkInfoCache(info, bic);
-                            memset(newdata + size, 0, newsize - size);
-                            *p = newdata[0 .. newlength];
-                            return *p;
-                        }
-                    }
-                }
-
-                // couldn't do it, reallocate
-                allocateAndCopy = true;
-            }
-            else if (!isshared && !bic)
-            {
-                // add this to the cache, it wasn't present previously.
-                __insertBlkInfoCache(info, null);
-            }
-        }
-        else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-        {
-            // could not resize in place
-            allocateAndCopy = true;
-        }
-        else if (!isshared && !bic)
-        {
-            // add this to the cache, it wasn't present previously.
-            __insertBlkInfoCache(info, null);
-        }
-    }
-    else
-        allocateAndCopy = true;
-
-    if (allocateAndCopy)
+    if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared))
     {
-        if (info.base)
-        {
-            if (bic)
-            {
-                // a chance that flags have changed since this was cached, we should fetch the most recent flags
-                info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
-            }
-            info = __arrayAlloc(newsize, info, ti, tinext);
-        }
-        else
-        {
-            info = __arrayAlloc(newsize, ti, tinext);
-        }
-
+        auto info = __arrayAlloc(newsize, (*p).ptr, ti, tinext);
         if (info.base is null)
         {
             onOutOfMemoryError();
             assert(0);
         }
 
-        __setArrayAllocLength(info, newsize, isshared, tinext);
-        if (!isshared)
-            __insertBlkInfoCache(info, bic);
-        newdata = cast(byte *)__arrayStart(info);
+        newdata = __arrayStart(info);
         newdata[0 .. size] = (*p).ptr[0 .. size];
 
-        /* Do postblit processing, as we are making a copy and the
-         * original array may have references.
-         * Note that this may throw.
-         */
+        __setArrayAllocLength(info, newsize, isshared, tinext);
+
+        // Do postblit processing, as we are making a copy.
         __doPostblit(newdata, size, tinext);
+
+        // this hasn't been added to the cache yet.
+        if (!isshared)
+            __insertBlkInfoCache(info, null);
     }
 
     // Zero the unused portion of the newly allocated space
@@ -1162,7 +996,7 @@ do
 
     debug(PRINTF)
     {
-        //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
+        //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
         if (p)
             printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length);
     }
@@ -1170,8 +1004,7 @@ do
     if (newlength <= (*p).length)
     {
         *p = (*p)[0 .. newlength];
-        void* newdata = (*p).ptr;
-        return newdata[0 .. newlength];
+        return *p;
     }
     auto tinext = unqualify(ti.next);
     size_t sizeelem = tinext.tsize;
@@ -1237,6 +1070,7 @@ do
 
     if (!(*p).ptr)
     {
+        assert((*p).length == 0);
         // pointer was null, need to allocate
         auto info = __arrayAlloc(newsize, ti, tinext);
         if (info.base is null)
@@ -1254,103 +1088,31 @@ do
     }
 
     const size_t size = (*p).length * sizeelem;
-    auto   bic = isshared ? null : __getBlkInfo((*p).ptr);
-    auto   info = bic ? *bic : GC.query((*p).ptr);
 
     /* Attempt to extend past the end of the existing array.
      * If not possible, allocate new space for entire array and copy.
      */
-    bool allocateAndCopy = false;
     void* newdata = (*p).ptr;
-
-    if (info.base && (info.attr & BlkAttr.APPENDABLE))
+    if (!gc_expandArrayUsed(newdata[0 .. size], newsize, isshared))
     {
-        // calculate the extent of the array given the base.
-        const size_t offset = (*p).ptr - __arrayStart(info);
-        if (info.size >= PAGESIZE)
-        {
-            // size of array is at the front of the block
-            if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-            {
-                // check to see if it failed because there is not
-                // enough space
-                if (*(cast(size_t*)info.base) == size + offset)
-                {
-                    // not enough space, try extending
-                    auto extendsize = newsize + offset + LARGEPAD - info.size;
-                    auto u = GC.extend(info.base, extendsize, extendsize);
-                    if (u)
-                    {
-                        // extend worked, now try setting the length
-                        // again.
-                        info.size = u;
-                        if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-                        {
-                            if (!isshared)
-                                __insertBlkInfoCache(info, bic);
-                            doInitialize(newdata + size, newdata + newsize, tinext.initializer);
-                            *p = newdata[0 .. newlength];
-                            return *p;
-                        }
-                    }
-                }
-
-                // couldn't do it, reallocate
-                allocateAndCopy = true;
-            }
-            else if (!isshared && !bic)
-            {
-                // add this to the cache, it wasn't present previously.
-                __insertBlkInfoCache(info, null);
-            }
-        }
-        else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-        {
-            // could not resize in place
-            allocateAndCopy = true;
-        }
-        else if (!isshared && !bic)
-        {
-            // add this to the cache, it wasn't present previously.
-            __insertBlkInfoCache(info, null);
-        }
-    }
-    else
-        allocateAndCopy = true;
-
-    if (allocateAndCopy)
-    {
-        if (info.base)
-        {
-            if (bic)
-            {
-                // a chance that flags have changed since this was cached, we should fetch the most recent flags
-                info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
-            }
-            info = __arrayAlloc(newsize, info, ti, tinext);
-        }
-        else
-        {
-            info = __arrayAlloc(newsize, ti, tinext);
-        }
-
+        auto info = __arrayAlloc(newsize, (*p).ptr, ti, tinext);
         if (info.base is null)
         {
             onOutOfMemoryError();
             assert(0);
         }
 
-        __setArrayAllocLength(info, newsize, isshared, tinext);
-        if (!isshared)
-            __insertBlkInfoCache(info, bic);
-        newdata = cast(byte *)__arrayStart(info);
+        newdata = __arrayStart(info);
         newdata[0 .. size] = (*p).ptr[0 .. size];
 
-        /* Do postblit processing, as we are making a copy and the
-         * original array may have references.
-         * Note that this may throw.
-         */
+        __setArrayAllocLength(info, newsize, isshared, tinext);
+
+        // Do postblit processing, as we are making a copy.
         __doPostblit(newdata, size, tinext);
+
+        // this hasn't been added to the cache yet.
+        if (!isshared)
+            __insertBlkInfoCache(info, null);
     }
 
     // Initialize the unused portion of the newly allocated space
@@ -1442,100 +1204,53 @@ extern (C)
 byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
 {
     import core.stdc.string;
+    import core.exception : onOutOfMemoryError;
     // This is a cut&paste job from _d_arrayappendT(). Should be refactored.
 
     // only optimize array append where ti is not a shared type
     auto tinext = unqualify(ti.next);
     auto sizeelem = tinext.tsize;              // array element size
     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-    auto bic = isshared ? null : __getBlkInfo(px.ptr);
-    auto info = bic ? *bic : GC.query(px.ptr);
     auto length = px.length;
     auto newlength = length + n;
     auto newsize = newlength * sizeelem;
     auto size = length * sizeelem;
-    size_t newcap = void; // for scratch space
 
-    // calculate the extent of the array given the base.
-    size_t offset = cast(void*)px.ptr - __arrayStart(info);
-    if (info.base && (info.attr & BlkAttr.APPENDABLE))
+    if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared))
     {
-        if (info.size >= PAGESIZE)
-        {
-            // size of array is at the front of the block
-            if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-            {
-                // check to see if it failed because there is not
-                // enough space
-                newcap = newCapacity(newlength, sizeelem);
-                if (*(cast(size_t*)info.base) == size + offset)
-                {
-                    // not enough space, try extending
-                    auto extendoffset = offset + LARGEPAD - info.size;
-                    auto u = GC.extend(info.base, newsize + extendoffset, newcap + extendoffset);
-                    if (u)
-                    {
-                        // extend worked, now try setting the length
-                        // again.
-                        info.size = u;
-                        if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-                        {
-                            if (!isshared)
-                                __insertBlkInfoCache(info, bic);
-                            goto L1;
-                        }
-                    }
-                }
-
-                // couldn't do it, reallocate
-                goto L2;
-            }
-            else if (!isshared && !bic)
-            {
-                __insertBlkInfoCache(info, null);
-            }
-        }
-        else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
-        {
-            // could not resize in place
-            newcap = newCapacity(newlength, sizeelem);
-            goto L2;
-        }
-        else if (!isshared && !bic)
-        {
-            __insertBlkInfoCache(info, null);
-        }
-    }
-    else
-    {
-        // not appendable or is null
-        newcap = newCapacity(newlength, sizeelem);
-        if (info.base)
-        {
-    L2:
-            if (bic)
-            {
-                // a chance that flags have changed since this was cached, we should fetch the most recent flags
-                info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
-            }
-            info = __arrayAlloc(newcap, info, ti, tinext);
-        }
-        else
+        // could not set the size, we must reallocate.
+        auto newcap = newCapacity(newlength, sizeelem);
+        auto info = __arrayAlloc(newcap, px.ptr, ti, tinext);
+        if (info.base is null)
         {
-            info = __arrayAlloc(newcap, ti, tinext);
+            onOutOfMemoryError();
+            assert(0);
         }
+
         __setArrayAllocLength(info, newsize, isshared, tinext);
         if (!isshared)
-            __insertBlkInfoCache(info, bic);
+            __insertBlkInfoCache(info, null);
+
         auto newdata = cast(byte*)__arrayStart(info);
-        memcpy(newdata, px.ptr, length * sizeelem);
-        // do postblit processing
-        __doPostblit(newdata, length * sizeelem, tinext);
-        (cast(void**)&px)[1] = newdata;
+        memcpy(newdata, px.ptr, size);
+
+
+        // For small blocks that are always fully scanned, if we allocated more
+        // capacity than was requested, we are responsible for zeroing that
+        // memory.
+        // large blocks are only scanned up to the used size.
+        if (!(info.attr & BlkAttr.NO_SCAN) && newcap > newcap && info.size < PAGESIZE)
+            memset(newdata + newsize, 0, newcap - newsize);
+
+        // do potsblit processing.
+        __doPostblit(newdata, size, tinext);
+
+        px = newdata[0 .. newlength];
+        return px;
     }
 
-  L1:
-    *cast(size_t*)&px = newlength;
+    // we were able to expand in place, just update the length
+    px = px.ptr[0 .. newlength];
     return px;
 }
 
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 0522cf85a3a8..b6f3488306cd 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-dbc09d8230f0e273af8a78546e5431a7783478b5
+ad8ee55872a25c93beacfb0ae1cca80dc8115916
 
 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/etc/c/curl.d b/libphobos/src/etc/c/curl.d
index e6a10435d2bf..a32a6b86dfa8 100644
--- a/libphobos/src/etc/c/curl.d
+++ b/libphobos/src/etc/c/curl.d
@@ -117,7 +117,7 @@ alias curl_socket_t = socket_t;
 /// jdrewsen - Would like to get socket error constant from std.socket by it is private atm.
 version (Windows)
 {
-  import core.sys.windows.windows, core.sys.windows.winsock2;
+  import core.sys.windows.winsock2;
   enum CURL_SOCKET_BAD = SOCKET_ERROR;
 }
 version (Posix) enum CURL_SOCKET_BAD = -1;
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index b7119d24b608..e0d24359a516 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -792,19 +792,18 @@ if (isInputRange!R && !isInfinite!R)
     `haystack` before reaching an element for which
     `startsWith!pred(haystack, needles)` is `true`. If
     `startsWith!pred(haystack, needles)` is not `true` for any element in
-    `haystack`, then `-1` is returned. If only `pred` is provided,
-    `pred(haystack)` is tested for each element.
+    `haystack`, then `-1` is returned. If more than one needle is provided,
+    `countUntil` will wrap the result     in a tuple similar to
+    `Tuple!(ptrdiff_t, "steps", ptrdiff_t needle)`
 
     See_Also: $(REF indexOf, std,string)
   +/
-ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
+auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
 if (isForwardRange!R
     && Rs.length > 0
     && isForwardRange!(Rs[0]) == isInputRange!(Rs[0])
     && allSatisfy!(canTestStartsWith!(pred, R), Rs))
 {
-    typeof(return) result;
-
     static if (needles.length == 1)
     {
         static if (hasLength!R) //Note: Narrow strings don't have length.
@@ -814,14 +813,16 @@ if (isForwardRange!R
             auto len = haystack.length;
             auto r2 = find!pred(haystack, needles[0]);
             if (!r2.empty)
-              return cast(typeof(return)) (len - r2.length);
+                return ptrdiff_t(len - r2.length);
         }
         else
         {
             import std.range : dropOne;
 
             if (needles[0].empty)
-              return 0;
+                return ptrdiff_t(0);
+
+            ptrdiff_t result;
 
             //Default case, slower route doing startsWith iteration
             for ( ; !haystack.empty ; ++result )
@@ -836,21 +837,39 @@ if (isForwardRange!R
                     //haystack we pop in all paths, so we do that, and then save.
                     haystack.popFront();
                     if (startsWith!pred(haystack.save, needles[0].save.dropOne()))
-                      return result;
+                        return result;
                 }
                 else
-                  haystack.popFront();
+                {
+                    haystack.popFront();
+                }
             }
         }
+        return ptrdiff_t(-1);
     }
     else
     {
+        static struct Result
+        {
+            ptrdiff_t steps, needle; // both -1 when nothing was found
+            alias steps this; // compatible with previous ptrdiff_t return type
+            ptrdiff_t opIndex(size_t idx) // faking a tuple
+            {
+                assert(idx < 2, "Type has only two members: pos and needle");
+                return idx == 0 ? steps : needle;
+            }
+        }
+        Result result;
+
         foreach (i, Ri; Rs)
         {
             static if (isForwardRange!Ri)
             {
                 if (needles[i].empty)
-                  return 0;
+                {
+                    result.needle = i;
+                    return result;
+                }
             }
         }
         Tuple!Rs t;
@@ -861,7 +880,7 @@ if (isForwardRange!R
                 t[i] = needles[i];
             }
         }
-        for (; !haystack.empty ; ++result, haystack.popFront())
+        for (; !haystack.empty ; ++result.steps, haystack.popFront())
         {
             foreach (i, Ri; Rs)
             {
@@ -870,18 +889,18 @@ if (isForwardRange!R
                     t[i] = needles[i].save;
                 }
             }
-            if (startsWith!pred(haystack.save, t.expand))
+            if (auto needle = startsWith!pred(haystack.save, t.expand))
             {
+                result.needle = needle - 1;
                 return result;
             }
         }
-    }
 
-    // Because of https://issues.dlang.org/show_bug.cgi?id=8804
-    // Avoids both "unreachable code" or "no return statement"
-    static if (isInfinite!R) assert(false, R.stringof ~ " must not be an"
-            ~ " infinite range");
-    else return -1;
+        // no match was found
+        result.needle = -1;
+        result.steps = -1;
+        return result;
+    }
 }
 
 /// ditto
@@ -906,6 +925,16 @@ if (isInputRange!R &&
     assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2);
     assert(countUntil([0, 7, 12, 22, 9], 9) == 4);
     assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3);
+
+    // supports multiple needles
+    auto res = "...hello".countUntil("ha", "he");
+    assert(res.steps == 3);
+    assert(res.needle == 1);
+
+    // returns -1 if no needle was found
+    res = "hello".countUntil("ha", "hu");
+    assert(res.steps == -1);
+    assert(res.needle == -1);
 }
 
 @safe unittest
@@ -943,6 +972,42 @@ if (isInputRange!R &&
     assert(countUntil("hello world", "world", 'l') == 2);
 }
 
+@safe unittest
+{
+    auto res = "...hello".countUntil("hella", "hello");
+    assert(res == 3);
+    assert(res.steps == 3);
+    assert(res.needle == 1);
+    // test tuple emulation
+    assert(res[0] == 3);
+    assert(res[1] == 1);
+
+    // the first matching needle is chosen
+    res = "...hello".countUntil("hell", "hello");
+    assert(res == 3);
+    assert(res.needle == 0);
+
+    // no match
+    auto noMatch = "hello world".countUntil("ha", "hu");
+    assert(noMatch == -1);
+    assert(noMatch.steps == -1);
+    assert(noMatch.needle  == -1);
+    // test tuple emulation
+    assert(noMatch[0] == -1);
+    assert(noMatch[1] == -1);
+}
+
+// test infinite ranges
+@safe unittest
+{
+    import std.algorithm.iteration : joiner;
+    import std.range : iota, repeat;
+    import std.stdio;
+    assert(10.iota.repeat.joiner.countUntil(9) == 9);
+    assert(10.iota.repeat.joiner.countUntil(1, 2) == 1);
+    assert(10.iota.repeat.joiner.countUntil!(a => a >= 9) == 9);
+}
+
 /// ditto
 ptrdiff_t countUntil(alias pred, R)(R haystack)
 if (isInputRange!R &&
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
index 3313dbb28d44..fea70258ebf0 100644
--- a/libphobos/src/std/array.d
+++ b/libphobos/src/std/array.d
@@ -1297,6 +1297,64 @@ if (is(typeof(a.ptr < b.ptr) == bool))
     static assert(test == "three"d);
 }
 
+///
+@safe pure nothrow unittest
+{
+    import std.meta : AliasSeq;
+
+    // can be used as an alternative implementation of overlap that returns
+    // `true` or `false` instead of a slice of the overlap
+    bool isSliceOf(T)(const scope T[] part, const scope T[] whole)
+    {
+        return part.overlap(whole) is part;
+    }
+
+    auto x = [1, 2, 3, 4, 5];
+
+    assert(isSliceOf(x[3..$], x));
+    assert(isSliceOf(x[], x));
+    assert(!isSliceOf(x, x[3..$]));
+    assert(!isSliceOf([7, 8], x));
+    assert(isSliceOf(null, x));
+
+    // null is a slice of itself
+    assert(isSliceOf(null, null));
+
+    foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
+    {
+        T a = [1, 2, 3, 4, 5];
+        T b = a;
+        T c = a[1 .. $];
+        T d = a[0 .. 1];
+        T e = null;
+
+        assert(isSliceOf(a, a));
+        assert(isSliceOf(b, a));
+        assert(isSliceOf(a, b));
+
+        assert(isSliceOf(c, a));
+        assert(isSliceOf(c, b));
+        assert(!isSliceOf(a, c));
+        assert(!isSliceOf(b, c));
+
+        assert(isSliceOf(d, a));
+        assert(isSliceOf(d, b));
+        assert(!isSliceOf(a, d));
+        assert(!isSliceOf(b, d));
+
+        assert(isSliceOf(e, a));
+        assert(isSliceOf(e, b));
+        assert(isSliceOf(e, c));
+        assert(isSliceOf(e, d));
+
+        //verifies R-value compatibilty
+        assert(!isSliceOf(a[$ .. $], a));
+        assert(isSliceOf(a[0 .. 0], a));
+        assert(isSliceOf(a, a[0.. $]));
+        assert(isSliceOf(a[0 .. $], a));
+    }
+}
+
 @safe pure nothrow unittest
 {
     static void test(L, R)(L l, R r)
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
index ca83785914eb..c6d5fca3dde2 100644
--- a/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
+++ b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
@@ -40,6 +40,11 @@ longer used. It does so by destroying empty allocators. However, in order to
 avoid thrashing (excessive creation/destruction of allocators under certain use
 patterns), it keeps unused allocators for a while.
 
+The shared version of `AllocatorList` is `SharedAllocatorList`, which has
+identical semantics to its single-threaded version. Both `BookkeepingAllocator`
+and `Allocator` provided by `factoryFunction` must be shared, in order to
+ensure corectness.
+
 Params:
 factoryFunction = A function or template function (including function literals).
 New allocators are created by calling `factoryFunction(n)` with strictly
@@ -661,65 +666,285 @@ version (Posix) @system unittest
     assert(b1.length == 1024 * 10);
 }
 
+/// Ditto
+shared struct SharedAllocatorList(Factory, BookkeepingAllocator = GCAllocator)
+{
+    import std.typecons : Ternary;
+    import std.traits : hasMember;
+    import core.internal.spinlock : SpinLock;
+
+private:
+    // Forward all calls to 'impl' and protect them by the lock below
+    AllocatorList!(Factory, BookkeepingAllocator) impl;
+    SpinLock lock = SpinLock(SpinLock.Contention.brief);
+
+    // This could be potentially removed in the future,
+    // should a successor to <https://github.com/dlang/druntime/pull/2156>
+    // or a solution to <https://github.com/dlang/dmd/issues/17128> get merged.
+    static ref T assumeUnshared(T)(ref shared T val) @trusted @nogc pure nothrow
+    {
+        return *cast(T*) &val;
+    }
+
+    // Debug function used for testing
+    version (unittest)
+    auto allocators()
+    {
+        return impl.allocators;
+    }
+
+// Everything is inherited from the 'AllocatorList' implementation
+public:
+
+    /*
+    Note: This does not work well with rvalues because it copies them once more.
+    Probably not a problem here because all parameters are cheap.
+    <https://github.com/dlang/phobos/pull/6465/files#r189629862>
+    */
+
+    /**
+    The alignment offered.
+    */
+    enum alignment = impl.alignment;
+
+    /**
+    Allocate a block of size `s`. First tries to allocate from the existing
+    list of already-created allocators. If neither can satisfy the request,
+    creates a new allocator by calling `make(s)` and delegates the request
+    to it. However, if the allocation fresh off a newly created allocator
+    fails, subsequent calls to `allocate` will not cause more calls to $(D
+    make).
+    */
+    static if (hasMember!(typeof(impl), "allocate"))
+    void[] allocate(size_t s)
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).allocate(s);
+    }
+
+    /**
+    Allocate a block of size `s` with alignment `a`. First tries to allocate
+    from the existing list of already-created allocators. If neither can
+    satisfy the request, creates a new allocator by calling `make(s + a - 1)`
+    and delegates the request to it. However, if the allocation fresh off a
+    newly created allocator fails, subsequent calls to `alignedAllocate`
+    will not cause more calls to `make`.
+    */
+    static if (hasMember!(typeof(impl), "alignedAllocate"))
+    void[] alignedAllocate(size_t s, uint a)
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).alignedAllocate(s, a);
+    }
+
+    /**
+     Defined if `Allocator.deallocate` and `Allocator.owns` are defined.
+    */
+    static if (hasMember!(typeof(impl), "deallocate"))
+    bool deallocate(void[] b)
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).deallocate(b);
+    }
+
+    /**
+    Defined only if `Allocator` defines `owns`. Tries each allocator in
+    turn, in most-recently-used order. If the owner is found, it is moved to
+    the front of the list as a side effect under the assumption it will be used
+    soon.
+
+    Returns: `Ternary.yes` if one allocator was found to return `Ternary.yes`,
+    `Ternary.no` if all component allocators returned `Ternary.no`, and
+    `Ternary.unknown` if no allocator returned `Ternary.yes` and at least one
+    returned  `Ternary.unknown`.
+    */
+    static if (hasMember!(typeof(impl), "owns"))
+    Ternary owns(void[] b)
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).owns(b);
+    }
+
+    /**
+    Defined only if `Allocator.expand` is defined. Finds the owner of `b`
+    and calls `expand` for it. The owner is not brought to the head of the
+    list.
+    */
+    static if (hasMember!(typeof(impl), "expand"))
+    bool expand(ref void[] b, size_t delta)
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).expand(b, delta);
+    }
+
+    /**
+    Defined only if `Allocator.reallocate` is defined. Finds the owner of
+    `b` and calls `reallocate` for it. If that fails, calls the global
+    `reallocate`, which allocates a new block and moves memory.
+    */
+    static if (hasMember!(typeof(impl), "reallocate"))
+    bool reallocate(ref void[] b, size_t s)
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).reallocate(b, s);
+    }
+
+    /**
+    Defined only if `Allocator.owns` and `Allocator.deallocateAll` are
+    defined.
+    */
+    static if (hasMember!(typeof(impl), "deallocateAll"))
+    bool deallocateAll()
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).deallocateAll();
+    }
+
+    /**
+     Returns `Ternary.yes` if no allocators are currently active,
+    `Ternary.no` otherwise. This methods never returns `Ternary.unknown`.
+    */
+    static if (hasMember!(typeof(impl), "empty"))
+    Ternary empty()
+    {
+        lock.lock();
+        scope(exit) lock.unlock();
+
+        return assumeUnshared(impl).empty();
+    }
+}
+
+/// Ditto
+template SharedAllocatorList(alias factoryFunction,
+    BookkeepingAllocator = GCAllocator)
+{
+    alias A = typeof(factoryFunction(1));
+    static assert(
+        // is a template function (including literals)
+        is(typeof({A function(size_t) @system x = factoryFunction!size_t;}))
+        ||
+        // or a function (including literals)
+        is(typeof({A function(size_t) @system x = factoryFunction;}))
+        ,
+        "Only function names and function literals that take size_t"
+            ~ " and return an allocator are accepted, not "
+            ~ typeof(factoryFunction).stringof
+    );
+    static struct Factory
+    {
+        A opCall(size_t n) { return factoryFunction(n); }
+    }
+    alias SharedAllocatorList = .SharedAllocatorList!(Factory, BookkeepingAllocator);
+}
+
 @system unittest
 {
-    // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : Region, SharedRegion;
+
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        const b1 = a.allocate(1024 * 8192);
+        assert(b1 !is null); // still works due to overdimensioning
+        const b2 = a.allocate(1024 * 10);
+        assert(b2.length == 1024 * 10);
+        a.deallocateAll();
+    }
+
+     // Create an allocator based upon 4MB regions, fetched from the GC heap.
     AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]),
-        NullAllocator) a;
-    const b1 = a.allocate(1024 * 8192);
-    assert(b1 !is null); // still works due to overdimensioning
-    const b2 = a.allocate(1024 * 10);
-    assert(b2.length == 1024 * 10);
-    a.deallocateAll();
+        NullAllocator) reg1;
+
+    SharedAllocatorList!((n) => SharedRegion!GCAllocator(new ubyte[max(n, 1024 * 4096)]),
+        NullAllocator) reg2;
+
+    testAlloc(reg1);
+    testAlloc(reg2);
 }
 
 @system unittest
 {
-    // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
-    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
-    auto b1 = a.alignedAllocate(1024 * 8192, 1024);
-    assert(b1 !is null); // still works due to overdimensioning
-    assert(b1.length == 1024 * 8192);
-    assert(b1.ptr.alignedAt(1024));
-    assert(a.allocators.length == 1);
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
 
-    b1 = a.alignedAllocate(0, 1024);
-    assert(b1.length == 0);
-    assert(a.allocators.length == 1);
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        auto b1 = a.alignedAllocate(1024 * 8192, 1024);
+        assert(b1 !is null); // still works due to overdimensioning
+        assert(b1.length == 1024 * 8192);
+        assert(b1.ptr.alignedAt(1024));
+        assert(a.allocators.length == 1);
 
-    b1 = a.allocate(1024 * 10);
-    assert(b1.length == 1024 * 10);
+        b1 = a.alignedAllocate(0, 1024);
+        assert(b1.length == 0);
+        assert(a.allocators.length == 1);
 
-    assert(a.reallocate(b1, 1024));
-    assert(b1.length == 1024);
+        b1 = a.allocate(1024 * 10);
+        assert(b1.length == 1024 * 10);
 
-    a.deallocateAll();
+        assert(a.reallocate(b1, 1024));
+        assert(b1.length == 1024);
+
+        a.deallocateAll();
+    }
+
+    // Create an allocator based upon 4MB regions, fetched from the GC heap.
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+    SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+    testAlloc(a1);
+    testAlloc(a2);
 }
 
 @system unittest
 {
     import core.exception : AssertError;
     import std.exception : assertThrown;
-
-    // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
-    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
-    auto b1 = a.alignedAllocate(0, 1);
-    assert(b1 is null);
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
+
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        auto b1 = a.alignedAllocate(0, 1);
+        assert(b1 is null);
+
+        b1 = a.alignedAllocate(1, 0);
+        assert(b1 is null);
 
-    b1 = a.alignedAllocate(1, 0);
-    assert(b1 is null);
+        b1 = a.alignedAllocate(0, 0);
+        assert(b1 is null);
 
-    b1 = a.alignedAllocate(0, 0);
-    assert(b1 is null);
+        assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
 
-    assertThrown!AssertError(a.alignedAllocate(size_t.max, 1024));
-    a.deallocateAll();
+        // FIXME: This special-casing might note be necessary.
+        // At the moment though, this call would take potentially forever
+        // for the `SharedAllocatorList` from below.
+        static if (!is(Allocator == shared))
+        {
+            a.deallocateAll();
+        }
+    }
+
+    // Create an allocator based upon 4MB regions, fetched from the GC heap.
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+    SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+    testAlloc(a1);
+    testAlloc(a2);
 }
 
 @system unittest
@@ -728,52 +953,74 @@ version (Posix) @system unittest
 
     // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
-    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
-    auto b0 = a.alignedAllocate(1, 1024);
-    assert(b0.length == 1);
-    assert(b0.ptr.alignedAt(1024));
-    assert(a.allocators.length == 1);
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
 
-    auto b1 = a.alignedAllocate(1024 * 4096, 1024);
-    assert(b1.length == 1024 * 4096);
-    assert(b1.ptr.alignedAt(1024));
-    assert(a.allocators.length == 2);
-
-    auto b2 = a.alignedAllocate(1024, 128);
-    assert(b2.length == 1024);
-    assert(b2.ptr.alignedAt(128));
-    assert(a.allocators.length == 2);
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        auto b0 = a.alignedAllocate(1, 1024);
+        assert(b0.length == 1);
+        assert(b0.ptr.alignedAt(1024));
+        assert(a.allocators.length == 1);
+
+        auto b1 = a.alignedAllocate(1024 * 4096, 1024);
+        assert(b1.length == 1024 * 4096);
+        assert(b1.ptr.alignedAt(1024));
+        assert(a.allocators.length == 2);
+
+        auto b2 = a.alignedAllocate(1024, 128);
+        assert(b2.length == 1024);
+        assert(b2.ptr.alignedAt(128));
+        assert(a.allocators.length == 2);
+
+        auto b3 = a.allocate(1024);
+        assert(b3.length == 1024);
+        assert(a.allocators.length == 2);
+
+        auto b4 = a.allocate(1024 * 4096);
+        assert(b4.length == 1024 * 4096);
+        assert(a.allocators.length == 3);
+
+        static if (!is(Allocator == shared))
+        {
+            assert(a.root.empty == Ternary.no);
+            assert(a.deallocate(b4));
+            assert(a.root.empty == Ternary.yes);
 
-    auto b3 = a.allocate(1024);
-    assert(b3.length == 1024);
-    assert(a.allocators.length == 2);
+            assert(a.deallocate(b1));
+        }
 
-    auto b4 = a.allocate(1024 * 4096);
-    assert(b4.length == 1024 * 4096);
-    assert(a.allocators.length == 3);
+        a.deallocateAll();
+    }
 
-    assert(a.root.empty == Ternary.no);
-    assert(a.deallocate(b4));
-    assert(a.root.empty == Ternary.yes);
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+    SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
 
-    assert(a.deallocate(b1));
-    a.deallocateAll();
+    testAlloc(a1);
+    testAlloc(a2);
 }
 
 @system unittest
 {
-    // Create an allocator based upon 4MB regions, fetched from the GC heap.
     import std.algorithm.comparison : max;
-    import std.experimental.allocator.building_blocks.region : BorrowedRegion;
-    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a;
-    auto b1 = a.allocate(1024 * 8192);
-    assert(b1 !is null); // still works due to overdimensioning
-    b1 = a.allocate(1024 * 10);
-    assert(b1.length == 1024 * 10);
-    assert(a.reallocate(b1, 1024));
-    assert(b1.length == 1024);
-    a.deallocateAll();
+    import std.experimental.allocator.building_blocks.region : BorrowedRegion, SharedBorrowedRegion;
+
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        auto b1 = a.allocate(1024 * 8192);
+        assert(b1 !is null); // still works due to overdimensioning
+        b1 = a.allocate(1024 * 10);
+        assert(b1.length == 1024 * 10);
+        assert(a.reallocate(b1, 1024));
+        assert(b1.length == 1024);
+        a.deallocateAll();
+    }
+
+    // Create an allocator based upon 4MB regions, fetched from the GC heap.
+    AllocatorList!((n) => BorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a1;
+    SharedAllocatorList!((n) => SharedBorrowedRegion!()(new ubyte[max(n, 1024 * 4096)])) a2;
+
+    testAlloc(a1);
+    testAlloc(a2);
 }
 
 @system unittest
@@ -798,24 +1045,33 @@ version (Posix) @system unittest
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.region : Region, SharedRegion;
     enum bs = GCAllocator.alignment;
-    AllocatorList!((n) => Region!GCAllocator(256 * bs)) a;
-    auto b1 = a.allocate(192 * bs);
-    assert(b1.length == 192 * bs);
-    assert(a.allocators.length == 1);
-    auto b2 = a.allocate(64 * bs);
-    assert(b2.length == 64 * bs);
-    assert(a.allocators.length == 1);
-    auto b3 = a.allocate(192 * bs);
-    assert(b3.length == 192 * bs);
-    assert(a.allocators.length == 2);
-    // Ensure deallocate inherits from parent allocators
-    () nothrow @nogc { a.deallocate(b1); }();
-    b1 = a.allocate(64 * bs);
-    assert(b1.length == 64 * bs);
-    assert(a.allocators.length == 2);
-    a.deallocateAll();
+
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        auto b1 = a.allocate(192 * bs);
+        assert(b1.length == 192 * bs);
+        assert(a.allocators.length == 1);
+        auto b2 = a.allocate(64 * bs);
+        assert(b2.length == 64 * bs);
+        assert(a.allocators.length == 1);
+        auto b3 = a.allocate(192 * bs);
+        assert(b3.length == 192 * bs);
+        assert(a.allocators.length == 2);
+        // Ensure deallocate inherits from parent allocators
+        () nothrow @nogc { a.deallocate(b1); }();
+        b1 = a.allocate(64 * bs);
+        assert(b1.length == 64 * bs);
+        assert(a.allocators.length == 2);
+        a.deallocateAll();
+    }
+
+    AllocatorList!((n) => Region!GCAllocator(256 * bs)) a1;
+    SharedAllocatorList!((n) => SharedRegion!GCAllocator(256 * bs)) a2;
+
+    testAlloc(a1);
+    testAlloc(a2);
 }
 
 @system unittest
@@ -878,11 +1134,15 @@ version (Posix) @system unittest
 
 @system unittest
 {
-    import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+    import std.experimental.allocator.building_blocks.ascending_page_allocator :
+        AscendingPageAllocator, SharedAscendingPageAllocator;
     import std.experimental.allocator.mallocator : Mallocator;
     import std.algorithm.comparison : max;
     import std.typecons : Ternary;
 
+    enum pageSize = 4096;
+    enum numPages = 2;
+
     static void testrw(void[] b)
     {
         ubyte* buf = cast(ubyte*) b.ptr;
@@ -893,53 +1153,56 @@ version (Posix) @system unittest
         }
     }
 
-    enum numPages = 2;
-    AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+    static void testAlloc(Allocator)(ref Allocator a)
+    {
+        void[] b1 = a.allocate(1);
+        assert(b1.length == 1);
+        b1 = a.allocate(2);
+        assert(b1.length == 2);
+        testrw(b1);
 
-    void[] b1 = a.allocate(1);
-    assert(b1.length == 1);
-    b1 = a.allocate(2);
-    assert(b1.length == 2);
-    testrw(b1);
+        void[] b2 = a.allocate((numPages + 1) * pageSize);
+        assert(b2.length == (numPages + 1) * pageSize);
+        testrw(b2);
 
-    void[] b2 = a.allocate((numPages + 1) * pageSize);
-    assert(b2.length == (numPages + 1) * pageSize);
-    testrw(b2);
+        void[] b3 = a.allocate(3);
+        assert(b3.length == 3);
+        testrw(b3);
 
-    void[] b3 = a.allocate(3);
-    assert(b3.length == 3);
-    testrw(b3);
+        void[] b4 = a.allocate(0);
+        assert(b4.length == 0);
 
-    void[] b4 = a.allocate(0);
-    assert(b4.length == 0);
+        assert(a.allocators.length == 3);
+        assert(a.owns(b1) == Ternary.yes);
+        assert(a.owns(b2) == Ternary.yes);
+        assert(a.owns(b3) == Ternary.yes);
 
-    assert(a.allocators.length == 3);
-    assert(a.owns(b1) == Ternary.yes);
-    assert(a.owns(b2) == Ternary.yes);
-    assert(a.owns(b3) == Ternary.yes);
+        assert(a.expand(b1, pageSize - b1.length));
+        assert(b1.length == pageSize);
+        assert(!a.expand(b1, 1));
+        assert(!a.expand(b2, 1));
 
-    assert(a.expand(b1, pageSize - b1.length));
-    assert(b1.length == pageSize);
-    assert(!a.expand(b1, 1));
-    assert(!a.expand(b2, 1));
+        testrw(b1);
+        testrw(b2);
+        testrw(b3);
 
-    testrw(b1);
-    testrw(b2);
-    testrw(b3);
+        assert(a.deallocate(b1));
+        assert(a.deallocate(b2));
 
-    assert(a.deallocate(b1));
-    assert(a.deallocate(b2));
+        const alignment = cast(uint) (70 * pageSize);
+        b3 = a.alignedAllocate(70 * pageSize, alignment);
+        assert(b3.length == 70 * pageSize);
+        assert(b3.ptr.alignedAt(alignment));
+        testrw(b3);
+        assert(a.allocators.length == 4);
+        assert(a.deallocate(b3));
 
-    const alignment = cast(uint) (70 * pageSize);
-    b3 = a.alignedAllocate(70 * pageSize, alignment);
-    assert(b3.length == 70 * pageSize);
-    assert(b3.ptr.alignedAt(alignment));
-    testrw(b3);
-    assert(a.allocators.length == 4);
-    assert(a.deallocate(b3));
 
+        assert(a.deallocateAll());
+    }
 
-    assert(a.deallocateAll());
+    AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a1;
+    SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a2;
 }
 
 @system unittest
@@ -998,3 +1261,109 @@ version (Posix) @system unittest
 
     assert(a.deallocateAll());
 }
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.algorithm.comparison : max;
+    import std.typecons : Ternary;
+
+    enum pageSize = 4096;
+
+    static void testrw(void[] b)
+    {
+        ubyte* buf = cast(ubyte*) b.ptr;
+        for (int i = 0; i < b.length; i += pageSize)
+        {
+            buf[i] = cast(ubyte) (i % 256);
+            assert(buf[i] == cast(ubyte) (i % 256));
+        }
+    }
+
+    enum numPages = 5;
+    AllocatorList!((n) => AscendingPageAllocator(max(n, numPages * pageSize)), NullAllocator) a;
+    auto b = a.alignedAllocate(1, pageSize * 2);
+    assert(b.length == 1);
+    assert(a.expand(b, 4095));
+    assert(b.ptr.alignedAt(2 * 4096));
+    assert(b.length == 4096);
+
+    b = a.allocate(4096);
+    assert(b.length == 4096);
+    assert(a.allocators.length == 1);
+
+    assert(a.allocate(4096 * 5).length == 4096 * 5);
+    assert(a.allocators.length == 2);
+
+    assert(a.deallocateAll());
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.region : SharedRegion;
+    import core.thread : ThreadGroup;
+    import std.algorithm.comparison : max;
+
+    enum numThreads = 10;
+    SharedAllocatorList!((n) => SharedRegion!(GCAllocator)(new ubyte[max(n, 1024)])) a;
+
+    void fun()
+    {
+        void[] b1 = a.allocate(1024);
+        assert(b1.length == 1024);
+
+        void[] b2 = a.alignedAllocate(1024, 1024);
+        assert(b2.length == 1024);
+        assert(b2.ptr.alignedAt(1024));
+
+        assert(a.deallocate(b1));
+        assert(a.deallocate(b2));
+    }
+
+    auto tg = new ThreadGroup;
+    foreach (i; 0 .. numThreads)
+    {
+        tg.create(&fun);
+    }
+    tg.joinAll();
+
+    assert(a.deallocateAll());
+}
+
+@system unittest
+{
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
+    import core.thread : ThreadGroup;
+    import std.algorithm.comparison : max;
+
+    enum numThreads = 100;
+    enum pageSize = 4096;
+    enum numPages = 10;
+    SharedAllocatorList!((n) => SharedAscendingPageAllocator(max(n, pageSize * numPages)), Mallocator) a;
+
+    void fun()
+    {
+        void[] b1 = a.allocate(512);
+        assert(b1.length == 512);
+        assert(a.expand(b1, 512));
+        assert(b1.length == 1024);
+
+        void[] b2 = a.alignedAllocate(1024, 4096);
+        assert(b2.length == 1024);
+        assert(b2.ptr.alignedAt(1024));
+
+        assert(a.deallocate(b1));
+        assert(a.deallocate(b2));
+    }
+
+    auto tg = new ThreadGroup;
+    foreach (i; 0 .. numThreads)
+    {
+        tg.create(&fun);
+    }
+    tg.joinAll();
+
+    assert(a.deallocateAll());
+}
diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d
index e02c1202a4b5..5dadce0877ac 100644
--- a/libphobos/src/std/format/package.d
+++ b/libphobos/src/std/format/package.d
@@ -147,7 +147,7 @@ limited to a $(B '-') flag.
 $(H4 $(LNAME2 format-indicator, Format Indicator))
 
 The $(I format indicator) can either be a single character or an
-expression surrounded by $(B %\() and $(B %\)). It specifies the
+expression surrounded by $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). It specifies the
 basic manner in which a value will be formatted and is the minimum
 requirement to format a value.
 
@@ -187,7 +187,7 @@ $(BOOKTABLE ,
 
 The $(I compound indicator) can be used to describe compound types
 like arrays or structs in more detail. A compound type is enclosed
-within $(B '%\(') and $(B '%\)'). The enclosed sub-format string is
+within $(B '%$(LPAREN)') and $(B '%$(RPAREN)'). The enclosed sub-format string is
 applied to individual elements. The trailing portion of the
 sub-format string following the specifier for the element is
 interpreted as the delimiter, and is therefore omitted following the
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
index 5022702ace90..cf7e3ff8cc2e 100644
--- a/libphobos/src/std/functional.d
+++ b/libphobos/src/std/functional.d
@@ -1289,6 +1289,15 @@ alias pipe(fun...) = compose!(Reverse!(fun));
     assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
 }
 
+private template getOverloads(alias fun)
+{
+    import std.meta : AliasSeq;
+    static if (__traits(compiles, __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true)))
+        alias getOverloads = __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun), true);
+    else
+        alias getOverloads = AliasSeq!fun;
+}
+
 /**
  * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as
  * to avoid repeated computation. The memoization structure is a hash table keyed by a
@@ -1324,87 +1333,131 @@ Note:
 */
 template memoize(alias fun)
 {
-    import std.traits : ReturnType;
-     // https://issues.dlang.org/show_bug.cgi?id=13580
-    // alias Args = Parameters!fun;
+    import std.traits : Parameters;
+    import std.meta : anySatisfy;
+
+    // Specific overloads:
+    alias overloads = getOverloads!fun;
+    static foreach (fn; overloads)
+        static if (is(Parameters!fn))
+            alias memoize = impl!(Parameters!fn);
+
+    enum isTemplate(alias a) = __traits(isTemplate, a);
+    static if (anySatisfy!(isTemplate, overloads))
+    {
+        // Generic implementation
+        alias memoize = impl;
+    }
 
-    ReturnType!fun memoize(Parameters!fun args)
+    auto impl(Args...)(Args args) if (is(typeof(fun(args))))
     {
-        alias Args = Parameters!fun;
-        import std.typecons : Tuple;
+        import std.typecons : Tuple, tuple;
         import std.traits : Unqual;
 
-        static Unqual!(ReturnType!fun)[Tuple!Args] memo;
-        auto t = Tuple!Args(args);
-        if (auto p = t in memo)
-            return *p;
-        auto r = fun(args);
-        memo[t] = r;
-        return r;
+        static if (args.length > 0)
+        {
+            static Unqual!(typeof(fun(args)))[Tuple!(typeof(args))] memo;
+
+            auto t = Tuple!Args(args);
+            if (auto p = t in memo)
+                return *p;
+            auto r = fun(args);
+            memo[t] = r;
+            return r;
+        }
+        else
+        {
+            static typeof(fun(args)) result;
+            result = fun(args);
+            return result;
+        }
     }
 }
 
 /// ditto
 template memoize(alias fun, uint maxSize)
 {
-    import std.traits : ReturnType;
-     // https://issues.dlang.org/show_bug.cgi?id=13580
-    // alias Args = Parameters!fun;
-    ReturnType!fun memoize(Parameters!fun args)
+    import std.traits : Parameters;
+    import std.meta : anySatisfy;
+
+    // Specific overloads:
+    alias overloads = getOverloads!fun;
+    static foreach (fn; overloads)
+        static if (is(Parameters!fn))
+            alias memoize = impl!(Parameters!fn);
+
+    enum isTemplate(alias a) = __traits(isTemplate, a);
+    static if (anySatisfy!(isTemplate, overloads))
     {
-        import std.meta : staticMap;
-        import std.traits : hasIndirections, Unqual;
-        import std.typecons : tuple;
-        static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; }
-        static Value[] memo;
-        static size_t[] initialized;
+        // Generic implementation
+        alias memoize = impl;
+    }
 
-        if (!memo.length)
+    auto impl(Args...)(Args args) if (is(typeof(fun(args))))
+    {
+        static if (args.length > 0)
         {
-            import core.memory : GC;
+            import std.meta : staticMap;
+            import std.traits : hasIndirections, Unqual;
+            import std.typecons : tuple;
+            alias returnType = typeof(fun(args));
+            static struct Value { staticMap!(Unqual, Args) args; Unqual!returnType res; }
+            static Value[] memo;
+            static size_t[] initialized;
 
-            // Ensure no allocation overflows
-            static assert(maxSize < size_t.max / Value.sizeof);
-            static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
+            if (!memo.length)
+            {
+                import core.memory : GC;
 
-            enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
-            memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
-            enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
-            initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
-        }
+                // Ensure no allocation overflows
+                static assert(maxSize < size_t.max / Value.sizeof);
+                static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));
 
-        import core.bitop : bt, bts;
-        import core.lifetime : emplace;
+                enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
+                memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
+                enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
+                initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
+            }
 
-        size_t hash;
-        foreach (ref arg; args)
-            hash = hashOf(arg, hash);
-        // cuckoo hashing
-        immutable idx1 = hash % maxSize;
-        if (!bt(initialized.ptr, idx1))
-        {
-            emplace(&memo[idx1], args, fun(args));
-            // only set to initialized after setting args and value
-            // https://issues.dlang.org/show_bug.cgi?id=14025
-            bts(initialized.ptr, idx1);
+            import core.bitop : bt, bts;
+            import core.lifetime : emplace;
+
+            size_t hash;
+            foreach (ref arg; args)
+                hash = hashOf(arg, hash);
+            // cuckoo hashing
+            immutable idx1 = hash % maxSize;
+            if (!bt(initialized.ptr, idx1))
+            {
+                emplace(&memo[idx1], args, fun(args));
+                // only set to initialized after setting args and value
+                // https://issues.dlang.org/show_bug.cgi?id=14025
+                bts(initialized.ptr, idx1);
+                return memo[idx1].res;
+            }
+            else if (memo[idx1].args == args)
+                return memo[idx1].res;
+            // FNV prime
+            immutable idx2 = (hash * 16_777_619) % maxSize;
+            if (!bt(initialized.ptr, idx2))
+            {
+                emplace(&memo[idx2], memo[idx1]);
+                bts(initialized.ptr, idx2);
+            }
+            else if (memo[idx2].args == args)
+                return memo[idx2].res;
+            else if (idx1 != idx2)
+                memo[idx2] = memo[idx1];
+
+            memo[idx1] = Value(args, fun(args));
             return memo[idx1].res;
         }
-        else if (memo[idx1].args == args)
-            return memo[idx1].res;
-        // FNV prime
-        immutable idx2 = (hash * 16_777_619) % maxSize;
-        if (!bt(initialized.ptr, idx2))
+        else
         {
-            emplace(&memo[idx2], memo[idx1]);
-            bts(initialized.ptr, idx2);
+            static typeof(fun(args)) result;
+            result = fun(args);
+            return result;
         }
-        else if (memo[idx2].args == args)
-            return memo[idx2].res;
-        else if (idx1 != idx2)
-            memo[idx2] = memo[idx1];
-
-        memo[idx1] = Value(args, fun(args));
-        return memo[idx1].res;
     }
 }
 
@@ -1464,6 +1517,37 @@ unittest
     assert(fact(10) == 3628800);
 }
 
+// Issue 20099
+@system unittest // not @safe due to memoize
+{
+    int i = 3;
+    alias a = memoize!((n) => i + n);
+    alias b = memoize!((n) => i + n, 3);
+
+    assert(a(3) == 6);
+    assert(b(3) == 6);
+}
+
+@system unittest // not @safe due to memoize
+{
+    static Object objNum(int a) { return new Object(); }
+    assert(memoize!objNum(0) is memoize!objNum(0U));
+    assert(memoize!(objNum, 3)(0) is memoize!(objNum, 3)(0U));
+}
+
+@system unittest // not @safe due to memoize
+{
+    struct S
+    {
+        static int fun() { return 0; }
+        static int fun(int i) { return 1; }
+    }
+    assert(memoize!(S.fun)() == 0);
+    assert(memoize!(S.fun)(3) == 1);
+    assert(memoize!(S.fun, 3)() == 0);
+    assert(memoize!(S.fun, 3)(3) == 1);
+}
+
 @system unittest // not @safe due to memoize
 {
     import core.math : sqrt;
@@ -1626,6 +1710,19 @@ unittest
     }}
 }
 
+// memoize should continue to work with functions that cannot be evaluated at compile time
+@system unittest
+{
+    __gshared string[string] glob;
+
+    static bool foo()
+    {
+        return (":-)" in glob) is null;
+    }
+
+    assert(memoize!foo);
+}
+
 private struct DelegateFaker(F)
 {
     import std.typecons : FuncInfo, MemberFunctionGenerator;
@@ -1717,13 +1814,45 @@ if (isCallable!(F))
     {
         return fp;
     }
+    else static if (is(F Func == Func*) && is(Func == function))
+    {
+        return function(ref F fp) @trusted
+        {
+            return buildDelegate(fp);
+        }(fp);
+    }
     else static if (is(typeof(&F.opCall) == delegate)
                 || (is(typeof(&F.opCall) V : V*) && is(V == function)))
     {
         return toDelegate(&fp.opCall);
     }
+    else static if (is(typeof(&fp.opCall!())))
+    {
+        return toDelegate(&fp.opCall!());
+    }
     else
     {
+        static assert(false, "Unsupported type of callable, please open an issue.");
+    }
+}
+
+///
+@safe unittest
+{
+    static int inc(ref uint num) {
+        num++;
+        return 8675309;
+    }
+
+    uint myNum = 0;
+    auto incMyNumDel = toDelegate(&inc);
+    auto returnVal = incMyNumDel(myNum);
+    assert(myNum == 1);
+}
+
+private template buildDelegate(F)
+{
+    auto buildDelegate(auto ref F fp) {
         alias DelType = typeof(&(new DelegateFaker!(F)).doIt);
 
         static struct DelegateFields {
@@ -1753,21 +1882,22 @@ if (isCallable!(F))
     }
 }
 
-///
-@system unittest
+@safe unittest
 {
     static int inc(ref uint num) {
         num++;
         return 8675309;
     }
 
-    uint myNum = 0;
-    auto incMyNumDel = toDelegate(&inc);
-    auto returnVal = incMyNumDel(myNum);
-    assert(myNum == 1);
+    uint myNum = 0x1337;
+    struct S1 { int opCall() { inc(myNum); return myNum; } }
+    static assert(!is(typeof(&s1.opCall) == delegate));
+    S1 s1;
+    auto getvals1 = toDelegate(s1);
+    assert(getvals1() == 0x1338);
 }
 
-@system unittest // not @safe due to toDelegate
+@system unittest
 {
     static int inc(ref uint num) {
         num++;
@@ -1852,6 +1982,36 @@ if (isCallable!(F))
     }
 }
 
+
+@safe unittest
+{
+    static struct S1 { static void opCall()() { } }
+    static struct S2 { static T opCall(T = int)(T x) {return x; } }
+
+    S1 i1;
+    const dg1 = toDelegate(i1);
+    dg1();
+
+    S2 i2;
+    assert(toDelegate(i2)(0xBED) == 0xBED);
+}
+
+@safe unittest
+{
+    static void fun() @system pure nothrow @nogc
+    {
+        return;
+    }
+
+    auto fn = &fun;
+    static assert( is(typeof(fn) == void function() @system pure nothrow @nogc));
+    static assert(!is(typeof(fn) == void function() @safe   pure nothrow @nogc));
+
+    auto dg = fn.toDelegate();
+    static assert( is(typeof(dg) == void delegate() @system pure nothrow @nogc));
+    static assert(!is(typeof(dg) == void delegate() @safe   pure nothrow @nogc));
+}
+
 /**
  * Passes the fields of a struct as arguments to a function.
  *
diff --git a/libphobos/src/std/logger/filelogger.d b/libphobos/src/std/logger/filelogger.d
index 5ba167c7bdb3..754175c7dc82 100644
--- a/libphobos/src/std/logger/filelogger.d
+++ b/libphobos/src/std/logger/filelogger.d
@@ -276,7 +276,7 @@ class FileLogger : Logger
 {
     // we don't need to actually run the code, only make sure
     // it compiles
-    static _() {
+    static void _() {
         auto l = new shared FileLogger("");
     }
 }
diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d
index 215ca20b8772..5a8474914785 100644
--- a/libphobos/src/std/logger/package.d
+++ b/libphobos/src/std/logger/package.d
@@ -17,7 +17,7 @@ The easiest way to create a log message is to write:
 import std.logger;
 
 void main() {
-    log("Hello World");
+    info("Hello World");
 }
 -------------
 This will print a message to the `stderr` device. The message will contain
@@ -59,7 +59,7 @@ $(UL
     $(LI `fatal`)
 )
 The default `Logger` will by default log to `stderr` and has a default
-`LogLevel` of `LogLevel.all`. The default Logger can be accessed by
+`LogLevel` of `LogLevel.info`. The default Logger can be accessed by
 using the property called `sharedLog`. This property is a reference to the
 current default `Logger`. This reference can be used to assign a new
 default `Logger`.
diff --git a/libphobos/src/std/math/traits.d b/libphobos/src/std/math/traits.d
index 81ab1b789db0..8aba6a6b1c67 100644
--- a/libphobos/src/std/math/traits.d
+++ b/libphobos/src/std/math/traits.d
@@ -21,6 +21,7 @@ module std.math.traits;
 
 import std.traits : isFloatingPoint, isIntegral, isNumeric, isSigned;
 
+
 /*********************************
  * Determines if $(D_PARAM x) is NaN.
  * Params:
@@ -466,8 +467,27 @@ if (isFloatingPoint!(X))
  */
 bool isIdentical(real x, real y) @trusted pure nothrow @nogc
 {
-    import std.math.traits : floatTraits, RealFormat;
-
+    if (__ctfe)
+    {
+        if (x !is y) return false;
+        if (x == x) return true; // If not NaN `is` implies identical representation.
+        static if (double.mant_dig != real.mant_dig)
+        {
+            // Works because we are in CTFE and there is no way in CTFE to set more
+            // bits of NaN payload than can fit in a double, and since 2.087
+            // changed real.init to be non-signaling I *think* there is no way in
+            // CTFE for a real to be a signaling NaN unless real and double have
+            // the same representation so real's bits can be manipulated directly.
+            double d1 = x, d2 = y;
+        }
+        else
+        {
+            // Alias to avoid converting signaling to quiet.
+            alias d1 = x;
+            alias d2 = y;
+        }
+        return *cast(long*) &d1 == *cast(long*) &d2;
+    }
     // We're doing a bitwise comparison so the endianness is irrelevant.
     long*   pxs = cast(long *)&x;
     long*   pys = cast(long *)&y;
@@ -491,20 +511,44 @@ bool isIdentical(real x, real y) @trusted pure nothrow @nogc
         assert(0, "isIdentical not implemented");
     }
 }
-
 ///
 @safe @nogc pure nothrow unittest
 {
+    // We're forcing the CTFE to run by assigning the result of the function to an enum
+    enum test1 = isIdentical(1.0,1.0);
+    enum test2 = isIdentical(real.nan,real.nan);
+    enum test3 = isIdentical(real.infinity, real.infinity);
+    enum test4 = isIdentical(real.infinity, real.infinity);
+    enum test5 = isIdentical(0.0, 0.0);
+
+    assert(test1);
+    assert(test2);
+    assert(test3);
+    assert(test4);
+    assert(test5);
+
+    enum test6 = !isIdentical(0.0, -0.0);
+    enum test7 = !isIdentical(real.nan, -real.nan);
+    enum test8 = !isIdentical(real.infinity, -real.infinity);
+
+    assert(test6);
+    assert(test7);
+    assert(test8);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    assert( !isIdentical(1.2,1.3));
     assert( isIdentical(0.0, 0.0));
     assert( isIdentical(1.0, 1.0));
     assert( isIdentical(real.infinity, real.infinity));
     assert( isIdentical(-real.infinity, -real.infinity));
+    assert( isIdentical(real.nan, real.nan));
 
     assert(!isIdentical(0.0, -0.0));
     assert(!isIdentical(real.nan, -real.nan));
     assert(!isIdentical(real.infinity, -real.infinity));
 }
-
 /*********************************
  * Return 1 if sign bit of e is set, 0 if not.
  */
diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d
index 9dae2a65530a..5fb31566baf3 100644
--- a/libphobos/src/std/path.d
+++ b/libphobos/src/std/path.d
@@ -311,7 +311,6 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
 @safe unittest
 {
     import std.array;
-    import std.utf : byDchar;
 
     assert(rtrimDirSeparators("//abc//").array == "//abc");
     assert(rtrimDirSeparators("//abc//"d).array == "//abc"d);
@@ -329,7 +328,6 @@ if (isBidirectionalRange!R && isSomeChar!(ElementType!R) ||
 @safe unittest
 {
     import std.array;
-    import std.utf : byDchar;
 
     assert(trimDirSeparators("//abc//").array == "abc");
     assert(trimDirSeparators("//abc//"d).array == "abc"d);
diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d
index 2efbcaa84c8c..1cb2264f6d34 100644
--- a/libphobos/src/std/process.d
+++ b/libphobos/src/std/process.d
@@ -2815,6 +2815,10 @@ void kill(Pid pid, int codeOrSignal)
     else version (Posix)
     {
         import core.sys.posix.signal : kill;
+        if (pid.osHandle == Pid.invalid)
+            throw new ProcessException("Pid is invalid");
+        if (pid.osHandle == Pid.terminated)
+            throw new ProcessException("Pid is already terminated");
         if (kill(pid.osHandle, codeOrSignal) == -1)
             throw ProcessException.newFromErrno();
     }
@@ -2856,7 +2860,7 @@ void kill(Pid pid, int codeOrSignal)
     do { s = tryWait(pid); } while (!s.terminated);
     version (Windows)    assert(s.status == 123);
     else version (Posix) assert(s.status == -SIGKILL);
-    assertThrown!ProcessException(kill(pid));
+    assertThrown!ProcessException(kill(pid)); // Already terminated
 }
 
 @system unittest // wait() and kill() detached process
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
index e844297b8dc4..b0d96f2d54a8 100644
--- a/libphobos/src/std/stdio.d
+++ b/libphobos/src/std/stdio.d
@@ -2409,13 +2409,13 @@ void main()
     private struct ByLineCopy(Char, Terminator)
     {
     private:
-        import std.typecons : RefCounted, RefCountedAutoInitialize;
+        import std.typecons : SafeRefCounted, RefCountedAutoInitialize;
 
         /* Ref-counting stops the source range's ByLineCopyImpl
          * from getting out of sync after the range is copied, e.g.
          * when accessing range.front, then using std.range.take,
          * then accessing range.front again. */
-        alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
+        alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator),
             RefCountedAutoInitialize.no);
         Impl impl;
 
@@ -4626,7 +4626,7 @@ struct lines
     f = File to read lines from.
     terminator = Line separator (`'\n'` by default).
     */
-    this(File f, dchar terminator = '\n')
+    this(File f, dchar terminator = '\n') @safe
     {
         this.f = f;
         this.terminator = terminator;
@@ -4721,6 +4721,47 @@ struct lines
     }
 }
 
+@safe unittest
+{
+    /*
+        As pointed out in <https://github.com/dlang/phobos/issues/10605>,
+        it's a pity that `byLine()` & co. aren't @safe to use yet.
+
+        This is a first attempt at working towards that goal.
+        For now, this test doesn't do much; as there isn't much to do safely yet.
+     */
+    auto deleteMe = testFilename();
+    scope(exit) { imported!"std.file".remove(deleteMe); }
+
+    // Setup
+    {
+        auto f = File(deleteMe, "w");
+        scope(exit) { f.close(); }
+        foreach (i; 1 .. 11)
+            f.writeln(i);
+    }
+
+    // Actual tests
+    {
+        auto f = File(deleteMe, "r");
+        scope(exit) { f.close(); }
+
+        auto myLines = lines(f);
+        foreach (string line; myLines)
+            continue;
+
+        auto myByLineCopy = f.byLineCopy; // but cannot safely iterate yet
+        /*
+            still `@system`:
+            - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.empty`
+            - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.popFront`
+            - cannot call `@system` function `std.stdio.File.ByLineCopy!(immutable(char), char).ByLineCopy.front`
+        */
+        //foreach (line; myByLineCopy)
+        //    continue;
+    }
+}
+
 @system unittest
 {
     static import std.file;
diff --git a/libphobos/testsuite/libphobos.init_fini/custom_gc.d b/libphobos/testsuite/libphobos.init_fini/custom_gc.d
index ee2dcee92e4e..cb7238409594 100644
--- a/libphobos/testsuite/libphobos.init_fini/custom_gc.d
+++ b/libphobos/testsuite/libphobos.init_fini/custom_gc.d
@@ -179,6 +179,26 @@ nothrow @nogc:
         return stats().allocatedInCurrentThread;
     }
 
+    void[] getArrayUsed(void *ptr, bool atomic = false) nothrow
+    {
+        return null;
+    }
+
+    bool expandArrayUsed(void[] slice, size_t newUsed, bool atomic = false) nothrow @safe
+    {
+        return false;
+    }
+
+    size_t reserveArrayCapacity(void[] slice, size_t request, bool atomic = false) nothrow @safe
+    {
+        return 0;
+    }
+
+    bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow
+    {
+        return false;
+    }
+
 private:
     // doesn't care for alignment
     static void* sentinelAdd(void* p, size_t value)
-- 
GitLab