diff mbox series

[committed] d: Merge upstream dmd, druntime f8bae04558, phobos ba2ade9dec

Message ID 20240303224448.857034-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd, druntime f8bae04558, phobos ba2ade9dec | expand

Commit Message

Iain Buclaw March 3, 2024, 10:44 p.m. UTC
Hi,

This patch merges the D front-end and runtime library with upstream dmd
f8bae04558, and the standard library with phobos ba2ade9dec

D front-end changes:

    - Import dmd v2.108.1-beta-1.

D runtime changes:

    - Import druntime v2.108.1-beta-1.

Phobos changes:

    - Import phobos v2.108.1-beta-1.

Bootstrapped and regression tested on x86_64-linux-gnu/-m32, committed
to mainline.

Regards,
Iain.

---
gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd f8bae04558.
	* dmd/VERSION: Bump version to v2.108.0-beta.1.
	* d-builtins.cc (build_frontend_type): Update for new front-end
	interface.
	* d-codegen.cc (build_assert_call): Likewise.
	* d-convert.cc (d_array_convert): Likewise.
	* decl.cc (get_vtable_decl): Likewise.
	* expr.cc (ExprVisitor::visit (EqualExp *)): Likewise.
	(ExprVisitor::visit (VarExp *)): Likewise.
	(ExprVisitor::visit (ArrayLiteralExp *)): Likewise.
	(ExprVisitor::visit (AssocArrayLiteralExp)): Likewise.
	* intrinsics.cc (build_shuffle_mask_type): Likewise.
	(maybe_warn_intrinsic_mismatch): Likewise.
	* runtime.cc (get_libcall_type): Likewise.
	* typeinfo.cc (TypeInfoVisitor::layout_string): Likewise.
	(TypeInfoVisitor::visit(TypeInfoTupleDeclaration *)): Likewise.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime 02d6d07a69.
	* src/MERGE: Merge upstream phobos a2ade9dec.
---
 gcc/d/d-builtins.cc                           |   6 +-
 gcc/d/d-codegen.cc                            |   2 +-
 gcc/d/d-convert.cc                            |   4 +-
 gcc/d/decl.cc                                 |   2 +-
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/dmd/VERSION                             |   2 +-
 gcc/d/dmd/constfold.d                         |   2 +-
 gcc/d/dmd/cparse.d                            |   8 +-
 gcc/d/dmd/cxxfrontend.d                       |  36 ++
 gcc/d/dmd/denum.d                             |   1 -
 gcc/d/dmd/dinterpret.d                        |   4 +-
 gcc/d/dmd/dmodule.d                           |  16 +-
 gcc/d/dmd/expressionsem.d                     |  14 +-
 gcc/d/dmd/func.d                              | 176 ----------
 gcc/d/dmd/funcsem.d                           | 168 +++++++++
 gcc/d/dmd/location.d                          |  10 +-
 gcc/d/dmd/mtype.d                             | 321 +-----------------
 gcc/d/dmd/mtype.h                             |  14 +-
 gcc/d/dmd/optimize.d                          |   2 +-
 gcc/d/dmd/safe.d                              |   2 +-
 gcc/d/dmd/typesem.d                           | 317 ++++++++++++++++-
 gcc/d/expr.cc                                 |  21 +-
 gcc/d/intrinsics.cc                           |   6 +-
 gcc/d/runtime.cc                              |  20 +-
 gcc/d/typeinfo.cc                             |   4 +-
 .../gdc.test/compilable/issue24399.d          |   9 +
 .../gdc.test/compilable/issue24409.d          |  17 +
 gcc/testsuite/gdc.test/runnable/issue24401.d  |   6 +
 gcc/testsuite/gdc.test/runnable/test24371.d   |  15 +
 .../gdc.test/runnable_cxx/test7925.d          |   7 -
 libphobos/libdruntime/MERGE                   |   2 +-
 libphobos/libdruntime/core/exception.d        |  13 +
 .../libdruntime/core/sys/linux/ifaddrs.d      |  11 +-
 .../libdruntime/core/sys/posix/sys/select.d   |  44 +--
 libphobos/src/MERGE                           |   2 +-
 libphobos/src/etc/c/zlib.d                    |  49 +--
 36 files changed, 717 insertions(+), 618 deletions(-)
 create mode 100644 gcc/testsuite/gdc.test/compilable/issue24399.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/issue24409.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/issue24401.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/test24371.d
diff mbox series

Patch

diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc
index dc50df4252c..4546c0e9b56 100644
--- a/gcc/d/d-builtins.cc
+++ b/gcc/d/d-builtins.cc
@@ -197,8 +197,8 @@  build_frontend_type (tree type)
 	  length = size_binop (PLUS_EXPR, size_one_node,
 			       convert (sizetype, length));
 
-	  dtype =
-	    dmd::addMod (dtype->sarrayOf (TREE_INT_CST_LOW (length)), mod);
+	  dtype = dmd::sarrayOf (dtype, TREE_INT_CST_LOW (length));
+	  dtype = dmd::addMod (dtype, mod);
 	  builtin_converted_decls.safe_push (builtin_data (dtype, type));
 	  return dtype;
 	}
@@ -214,7 +214,7 @@  build_frontend_type (tree type)
       if (!dtype)
 	break;
 
-      dtype = dmd::addMod (dtype->sarrayOf (nunits), mod);
+      dtype = dmd::addMod (dmd::sarrayOf (dtype, nunits), mod);
       if (target.isVectorTypeSupported (dtype->size (), dtype->nextOf ()))
 	break;
 
diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index 43d7739f8fc..2b3089b5f6d 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -1906,7 +1906,7 @@  build_assert_call (const Loc &loc, libcall_fn libcall, tree msg)
 	  tree str = build_string (len, filename);
 	  TREE_TYPE (str) = make_array_type (Type::tchar, len);
 
-	  file = d_array_value (build_ctype (Type::tchar->arrayOf ()),
+	  file = d_array_value (build_ctype (dmd::arrayOf (Type::tchar)),
 				size_int (len), build_address (str));
 	}
       else
diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc
index 4ccbf0908b5..5c79cdf6e1e 100644
--- a/gcc/d/d-convert.cc
+++ b/gcc/d/d-convert.cc
@@ -957,7 +957,7 @@  d_array_convert (Expression *exp)
 
   if (tb->ty == TY::Tsarray)
     {
-      Type *totype = tb->nextOf ()->arrayOf ();
+      Type *totype = dmd::arrayOf (tb->nextOf ());
       return convert_expr (build_expr (exp), exp->type, totype);
     }
 
@@ -986,7 +986,7 @@  d_array_convert (Type *etype, Expression *exp)
 	  expr = compound_expr (modify_expr (var, expr), var);
 	}
 
-      return d_array_value (build_ctype (exp->type->arrayOf ()),
+      return d_array_value (build_ctype (dmd::arrayOf (exp->type)),
 			    size_int (1), build_address (expr));
     }
   else
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index 25398a32381..3b7627d3dfa 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -2211,7 +2211,7 @@  get_vtable_decl (ClassDeclaration *decl)
   tree ident = mangle_internal_decl (decl, "__vtbl", "Z");
   /* Note: Using a static array type for the VAR_DECL, the DECL_INITIAL value
      will have a different type.  However the back-end seems to accept this.  */
-  tree type = build_ctype (Type::tvoidptr->sarrayOf (decl->vtbl.length));
+  tree type = build_ctype (dmd::sarrayOf (Type::tvoidptr, decl->vtbl.length));
 
   Dsymbol *vtblsym = decl->vtblSymbol ();
   vtblsym->csym = declare_extern_var (ident, type);
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index f11c5fbfb0b..4c0a0bc2aac 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-ceff48bf7db05503117f54fdc0cefcb89b711136
+f8bae0455851a1dfc8113d69323415f6de549e39
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION
index 1880c9808c5..416807683f5 100644
--- a/gcc/d/dmd/VERSION
+++ b/gcc/d/dmd/VERSION
@@ -1 +1 @@ 
-v2.107.1-rc.1
+v2.108.0-beta.1
diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d
index 0686e1b0825..6ec31d5f9bb 100644
--- a/gcc/d/dmd/constfold.d
+++ b/gcc/d/dmd/constfold.d
@@ -36,7 +36,7 @@  import dmd.root.utf;
 import dmd.sideeffect;
 import dmd.target;
 import dmd.tokens;
-import dmd.typesem : toDsymbol, equivalent;
+import dmd.typesem : toDsymbol, equivalent, sarrayOf;
 
 private enum LOG = false;
 
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index e917d2cb7e7..aeedb493efc 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -2155,7 +2155,7 @@  final class CParser(AST) : Parser!AST
                 error("function identifier-list cannot end with `...`");
             ft.parameterList.varargs = AST.VarArg.KRvariadic;   // but C11 allows extra arguments
             auto plLength = pl.length;
-            if (symbols.length != plLength)
+            if (symbols && symbols.length != plLength)
                 error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
 
             /* Transfer the types and storage classes from symbols[] to pl[]
@@ -2176,6 +2176,12 @@  final class CParser(AST) : Parser!AST
 
                 if (p.type || !(p.storageClass & STC.parameter))
                     error("storage class and type are not allowed in identifier-list");
+                if (!symbols)
+                {
+                    // Error already given in cparseDeclaration
+                    p.type = AST.Type.terror;
+                    continue;
+                }
                 foreach (s; (*symbols)[]) // yes, quadratic
                 {
                     auto ad = s.isAttribDeclaration();
diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d
index 1b94a69da23..8c046343468 100644
--- a/gcc/d/dmd/cxxfrontend.d
+++ b/gcc/d/dmd/cxxfrontend.d
@@ -475,6 +475,18 @@  bool equivalent(Type src, Type t)
     return dmd.typesem.equivalent(src, t);
 }
 
+Type sarrayOf(Type type, dinteger_t dim)
+{
+    import dmd.typesem;
+    return dmd.typesem.sarrayOf(type, dim);
+}
+
+Type arrayOf(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.arrayOf(type);
+}
+
 Type constOf(Type type)
 {
     import dmd.typesem;
@@ -535,6 +547,30 @@  Type sharedWildConstOf(Type type)
     return dmd.typesem.sharedWildConstOf(type);
 }
 
+Type substWildTo(Type type, uint mod)
+{
+    import dmd.typesem;
+    return dmd.typesem.substWildTo(type, mod);
+}
+
+Type unqualify(Type type, uint m)
+{
+    import dmd.typesem;
+    return dmd.typesem.unqualify(type, m);
+}
+
+Type toHeadMutable(const(Type) type)
+{
+    import dmd.typesem;
+    return dmd.typesem.toHeadMutable(type);
+}
+
+Type aliasthisOf(Type type)
+{
+    import dmd.typesem;
+    return dmd.typesem.aliasthisOf(type);
+}
+
 Type castMod(Type type, MOD mod)
 {
     import dmd.typesem;
diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d
index 36799766ece..5c739eee6ae 100644
--- a/gcc/d/dmd/denum.d
+++ b/gcc/d/dmd/denum.d
@@ -20,7 +20,6 @@  import dmd.astenums;
 import dmd.attrib;
 import dmd.gluelayer;
 import dmd.declaration;
-import dmd.dscope;
 import dmd.dsymbol;
 import dmd.expression;
 import dmd.id;
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
index 467e29f3c3c..c4924903f25 100644
--- a/gcc/d/dmd/dinterpret.d
+++ b/gcc/d/dmd/dinterpret.d
@@ -50,7 +50,7 @@  import dmd.rootobject;
 import dmd.root.utf;
 import dmd.statement;
 import dmd.tokens;
-import dmd.typesem : mutableOf, equivalent, pointerTo;
+import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf;
 import dmd.utils : arrayCastBigEndian;
 import dmd.visitor;
 
@@ -3787,7 +3787,7 @@  public:
             if (v is v2 || !v.isOverlappedWith(v2))
                 continue;
             auto e = (*sle.elements)[i];
-            if (e.op != EXP.void_)
+            if (e !is null && e.op != EXP.void_)
                 (*sle.elements)[i] = voidInitLiteral(e.type, v).copy();
         }
     }
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index a77e4f303cd..58bf3fd0757 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -1394,6 +1394,7 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
 {
     enum SourceEncoding { utf16, utf32}
     enum Endian { little, big}
+    immutable loc = mod.getLoc();
 
     /*
      * Convert a buffer from UTF32 to UTF8
@@ -1413,7 +1414,7 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
 
         if (buf.length & 3)
         {
-            .error(mod.loc, "%s `%s` odd length of UTF-32 char source %llu",
+            .error(loc, "%s `%s` odd length of UTF-32 char source %llu",
                 mod.kind, mod.toPrettyChars, cast(ulong) buf.length);
             return null;
         }
@@ -1430,7 +1431,7 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
             {
                 if (u > 0x10FFFF)
                 {
-                    .error(mod.loc, "%s `%s` UTF-32 value %08x greater than 0x10FFFF", mod.kind, mod.toPrettyChars, u);
+                    .error(loc, "%s `%s` UTF-32 value %08x greater than 0x10FFFF", mod.kind, mod.toPrettyChars, u);
                     return null;
                 }
                 dbuf.writeUTF8(u);
@@ -1460,7 +1461,7 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
 
         if (buf.length & 1)
         {
-            .error(mod.loc, "%s `%s` odd length of UTF-16 char source %llu", mod.kind, mod.toPrettyChars, cast(ulong) buf.length);
+            .error(loc, "%s `%s` odd length of UTF-16 char source %llu", mod.kind, mod.toPrettyChars, cast(ulong) buf.length);
             return null;
         }
 
@@ -1480,13 +1481,13 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
                     i++;
                     if (i >= eBuf.length)
                     {
-                        .error(mod.loc, "%s `%s` surrogate UTF-16 high value %04x at end of file", mod.kind, mod.toPrettyChars, u);
+                        .error(loc, "%s `%s` surrogate UTF-16 high value %04x at end of file", mod.kind, mod.toPrettyChars, u);
                         return null;
                     }
                     const u2 = readNext(&eBuf[i]);
                     if (u2 < 0xDC00 || 0xE000 <= u2)
                     {
-                        .error(mod.loc, "%s `%s` surrogate UTF-16 low value %04x out of range", mod.kind, mod.toPrettyChars, u2);
+                        .error(loc, "%s `%s` surrogate UTF-16 low value %04x out of range", mod.kind, mod.toPrettyChars, u2);
                         return null;
                     }
                     u = (u - 0xD7C0) << 10;
@@ -1494,12 +1495,12 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
                 }
                 else if (u >= 0xDC00 && u <= 0xDFFF)
                 {
-                    .error(mod.loc, "%s `%s` unpaired surrogate UTF-16 value %04x", mod.kind, mod.toPrettyChars, u);
+                    .error(loc, "%s `%s` unpaired surrogate UTF-16 value %04x", mod.kind, mod.toPrettyChars, u);
                     return null;
                 }
                 else if (u == 0xFFFE || u == 0xFFFF)
                 {
-                    .error(mod.loc, "%s `%s` illegal UTF-16 value %04x", mod.kind, mod.toPrettyChars, u);
+                    .error(loc, "%s `%s` illegal UTF-16 value %04x", mod.kind, mod.toPrettyChars, u);
                     return null;
                 }
                 dbuf.writeUTF8(u);
@@ -1558,7 +1559,6 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
     // It's UTF-8
     if (buf[0] >= 0x80)
     {
-        auto loc = mod.getLoc();
         .error(loc, "%s `%s` source file must start with BOM or ASCII character, not \\x%02X", mod.kind, mod.toPrettyChars, buf[0]);
         return null;
     }
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index b4d5274e7c3..db40ae01dec 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -12357,8 +12357,16 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return result;
         }
 
-        void handleCatArgument(Expressions *arguments, Expression e)
+        void handleCatArgument(Expressions *arguments, Expression e, Type catType, bool isRightArg)
         {
+            auto tb = e.type.toBasetype();
+
+            if ((isRightArg && e.parens) || (!isRightArg && !tb.equals(catType)))
+            {
+                arguments.push(e);
+                return;
+            }
+
             if (auto ce = e.isCatExp())
             {
                 Expression lowering = ce.lowering;
@@ -12388,8 +12396,8 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             arguments.push(new StringExp(exp.loc, funcname.toDString()));
         }
 
-        handleCatArgument(arguments, exp.e1);
-        handleCatArgument(arguments, exp.e2);
+        handleCatArgument(arguments, exp.e1, exp.type.toBasetype(), false);
+        handleCatArgument(arguments, exp.e2, exp.type.toBasetype(), true);
 
         Expression id = new IdentifierExp(exp.loc, Id.empty);
         id = new DotIdExp(exp.loc, id, Id.object);
diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d
index d890811115c..7003c2b1192 100644
--- a/gcc/d/dmd/func.d
+++ b/gcc/d/dmd/func.d
@@ -34,12 +34,10 @@  import dmd.dmodule;
 import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
-import dmd.dsymbolsem;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.escape;
 import dmd.expression;
-import dmd.funcsem;
 import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
@@ -57,7 +55,6 @@  import dmd.semantic2;
 import dmd.semantic3;
 import dmd.statement_rewrite_walker;
 import dmd.statement;
-import dmd.statementsem;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
@@ -115,90 +112,6 @@  enum BUILTIN : ubyte
     toPrecReal
 }
 
-/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
- */
-extern (C++) final class NrvoWalker : StatementRewriteWalker
-{
-    alias visit = typeof(super).visit;
-public:
-    FuncDeclaration fd;
-    Scope* sc;
-
-    override void visit(ReturnStatement s)
-    {
-        // See if all returns are instead to be replaced with a goto returnLabel;
-        if (fd.returnLabel)
-        {
-            /* Rewrite:
-             *  return exp;
-             * as:
-             *  vresult = exp; goto Lresult;
-             */
-            auto gs = new GotoStatement(s.loc, Id.returnLabel);
-            gs.label = fd.returnLabel;
-
-            Statement s1 = gs;
-            if (s.exp)
-                s1 = new CompoundStatement(s.loc, new ExpStatement(s.loc, s.exp), gs);
-
-            replaceCurrent(s1);
-        }
-    }
-
-    override void visit(TryFinallyStatement s)
-    {
-        DtorExpStatement des;
-        if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
-            fd.nrvo_var == des.var)
-        {
-            if (!(global.params.useExceptions && ClassDeclaration.throwable))
-            {
-                /* Don't need to call destructor at all, since it is nrvo
-                 */
-                replaceCurrent(s._body);
-                s._body.accept(this);
-                return;
-            }
-
-            /* Normally local variable dtors are called regardless exceptions.
-             * But for nrvo_var, its dtor should be called only when exception is thrown.
-             *
-             * Rewrite:
-             *      try { s.body; } finally { nrvo_var.edtor; }
-             *      // equivalent with:
-             *      //    s.body; scope(exit) nrvo_var.edtor;
-             * as:
-             *      try { s.body; } catch(Throwable __o) { nrvo_var.edtor; throw __o; }
-             *      // equivalent with:
-             *      //    s.body; scope(failure) nrvo_var.edtor;
-             */
-            Statement sexception = new DtorExpStatement(Loc.initial, fd.nrvo_var.edtor, fd.nrvo_var);
-            Identifier id = Identifier.generateId("__o");
-
-            Statement handler = new PeelStatement(sexception);
-            if (sexception.blockExit(fd, null) & BE.fallthru)
-            {
-                auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
-                ts.internalThrow = true;
-                handler = new CompoundStatement(Loc.initial, handler, ts);
-            }
-
-            auto catches = new Catches();
-            auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
-            ctch.internalCatch = true;
-            ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o'
-            catches.push(ctch);
-
-            Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches);
-            fd.hasNoEH = false;
-            replaceCurrent(s2);
-            s2.accept(this);
-        }
-        else
-            StatementRewriteWalker.visit(s);
-    }
-}
-
 private struct FUNCFLAG
 {
     bool purityInprocess;    /// working on determining purity
@@ -2021,44 +1934,6 @@  extern (C++) class FuncDeclaration : Declaration
         return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid;
     }
 
-    /****************************************************
-     * Declare result variable lazily.
-     */
-    extern (D) final void buildResultVar(Scope* sc, Type tret)
-    {
-        if (!vresult)
-        {
-            Loc loc = fensure ? fensure.loc : this.loc;
-
-            /* If inferRetType is true, tret may not be a correct return type yet.
-             * So, in here it may be a temporary type for vresult, and after
-             * fbody.dsymbolSemantic() running, vresult.type might be modified.
-             */
-            vresult = new VarDeclaration(loc, tret, Id.result, null);
-            vresult.storage_class |= STC.nodtor | STC.temp;
-            if (!isVirtual())
-                vresult.storage_class |= STC.const_;
-            vresult.storage_class |= STC.result;
-
-            // set before the semantic() for checkNestedReference()
-            vresult.parent = this;
-        }
-
-        if (sc && vresult.semanticRun == PASS.initial)
-        {
-            TypeFunction tf = type.toTypeFunction();
-            if (tf.isref)
-                vresult.storage_class |= STC.ref_;
-            vresult.type = tret;
-
-            vresult.dsymbolSemantic(sc);
-
-            if (!sc.insert(vresult))
-                .error(loc, "%s `%s` out result %s is already defined", kind, toPrettyChars, vresult.toChars());
-            assert(vresult.parent == this);
-        }
-    }
-
     /****************************************************
      * Merge into this function the 'in' contracts of all it overrides.
      * 'in's are OR'd together, i.e. only one of them needs to pass.
@@ -2680,57 +2555,6 @@  extern (C++) class FuncDeclaration : Declaration
     }
 }
 
-/********************************************************
- * Generate Expression to call the invariant.
- * Input:
- *      ad      aggregate with the invariant
- *      vthis   variable with 'this'
- * Returns:
- *      void expression that calls the invariant
- */
-Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis)
-{
-    Expression e = null;
-    // Call invariant directly only if it exists
-    FuncDeclaration inv = ad.inv;
-    ClassDeclaration cd = ad.isClassDeclaration();
-
-    while (!inv && cd)
-    {
-        cd = cd.baseClass;
-        if (!cd)
-            break;
-        inv = cd.inv;
-    }
-    if (inv)
-    {
-        version (all)
-        {
-            // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394
-            // For the correct mangling,
-            // run attribute inference on inv if needed.
-            functionSemantic(inv);
-        }
-
-        //e = new DsymbolExp(Loc.initial, inv);
-        //e = new CallExp(Loc.initial, e);
-        //e = e.semantic(sc2);
-
-        /* https://issues.dlang.org/show_bug.cgi?id=13113
-         * Currently virtual invariant calls completely
-         * bypass attribute enforcement.
-         * Change the behavior of pre-invariant call by following it.
-         */
-        e = new ThisExp(Loc.initial);
-        e.type = ad.type.addMod(vthis.type.mod);
-        e = new DotVarExp(Loc.initial, e, inv, false);
-        e.type = inv.type;
-        e = new CallExp(Loc.initial, e);
-        e.type = Type.tvoid;
-    }
-    return e;
-}
-
 /***************************************************
  * Visit each overloaded function/template in turn, and call dg(s) on it.
  * Exit when no more, or dg(s) returns nonzero.
diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d
index b8b185c94f3..2cadc401911 100644
--- a/gcc/d/dmd/funcsem.d
+++ b/gcc/d/dmd/funcsem.d
@@ -65,6 +65,90 @@  import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
 
+/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO.
+ */
+extern (C++) final class NrvoWalker : StatementRewriteWalker
+{
+    alias visit = typeof(super).visit;
+public:
+    FuncDeclaration fd;
+    Scope* sc;
+
+    override void visit(ReturnStatement s)
+    {
+        // See if all returns are instead to be replaced with a goto returnLabel;
+        if (fd.returnLabel)
+        {
+            /* Rewrite:
+             *  return exp;
+             * as:
+             *  vresult = exp; goto Lresult;
+             */
+            auto gs = new GotoStatement(s.loc, Id.returnLabel);
+            gs.label = fd.returnLabel;
+
+            Statement s1 = gs;
+            if (s.exp)
+                s1 = new CompoundStatement(s.loc, new ExpStatement(s.loc, s.exp), gs);
+
+            replaceCurrent(s1);
+        }
+    }
+
+    override void visit(TryFinallyStatement s)
+    {
+        DtorExpStatement des;
+        if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
+            fd.nrvo_var == des.var)
+        {
+            if (!(global.params.useExceptions && ClassDeclaration.throwable))
+            {
+                /* Don't need to call destructor at all, since it is nrvo
+                 */
+                replaceCurrent(s._body);
+                s._body.accept(this);
+                return;
+            }
+
+            /* Normally local variable dtors are called regardless exceptions.
+             * But for nrvo_var, its dtor should be called only when exception is thrown.
+             *
+             * Rewrite:
+             *      try { s.body; } finally { nrvo_var.edtor; }
+             *      // equivalent with:
+             *      //    s.body; scope(exit) nrvo_var.edtor;
+             * as:
+             *      try { s.body; } catch(Throwable __o) { nrvo_var.edtor; throw __o; }
+             *      // equivalent with:
+             *      //    s.body; scope(failure) nrvo_var.edtor;
+             */
+            Statement sexception = new DtorExpStatement(Loc.initial, fd.nrvo_var.edtor, fd.nrvo_var);
+            Identifier id = Identifier.generateId("__o");
+
+            Statement handler = new PeelStatement(sexception);
+            if (sexception.blockExit(fd, null) & BE.fallthru)
+            {
+                auto ts = new ThrowStatement(Loc.initial, new IdentifierExp(Loc.initial, id));
+                ts.internalThrow = true;
+                handler = new CompoundStatement(Loc.initial, handler, ts);
+            }
+
+            auto catches = new Catches();
+            auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
+            ctch.internalCatch = true;
+            ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o'
+            catches.push(ctch);
+
+            Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches);
+            fd.hasNoEH = false;
+            replaceCurrent(s2);
+            s2.accept(this);
+        }
+        else
+            StatementRewriteWalker.visit(s);
+    }
+}
+
 /**********************************
  * Main semantic routine for functions.
  */
@@ -1755,3 +1839,87 @@  if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
     if (constraintsTip)
         .tip(constraintsTip);
 }
+
+/********************************************************
+ * Generate Expression to call the invariant.
+ * Input:
+ *      ad      aggregate with the invariant
+ *      vthis   variable with 'this'
+ * Returns:
+ *      void expression that calls the invariant
+ */
+Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis)
+{
+    Expression e = null;
+    // Call invariant directly only if it exists
+    FuncDeclaration inv = ad.inv;
+    ClassDeclaration cd = ad.isClassDeclaration();
+
+    while (!inv && cd)
+    {
+        cd = cd.baseClass;
+        if (!cd)
+            break;
+        inv = cd.inv;
+    }
+    if (inv)
+    {
+        version (all)
+        {
+            // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394
+            // For the correct mangling,
+            // run attribute inference on inv if needed.
+            functionSemantic(inv);
+        }
+
+        //e = new DsymbolExp(Loc.initial, inv);
+        //e = new CallExp(Loc.initial, e);
+        //e = e.semantic(sc2);
+
+        /* https://issues.dlang.org/show_bug.cgi?id=13113
+         * Currently virtual invariant calls completely
+         * bypass attribute enforcement.
+         * Change the behavior of pre-invariant call by following it.
+         */
+        e = new ThisExp(Loc.initial);
+        e.type = ad.type.addMod(vthis.type.mod);
+        e = new DotVarExp(Loc.initial, e, inv, false);
+        e.type = inv.type;
+        e = new CallExp(Loc.initial, e);
+        e.type = Type.tvoid;
+    }
+    return e;
+}
+
+/****************************************************
+ * Declare result variable lazily.
+ */
+void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret)
+{
+    if (!fd.vresult)
+    {
+        Loc loc = fd.fensure ? fd.fensure.loc : fd.loc;
+        /* If inferRetType is true, tret may not be a correct return type yet.
+         * So, in here it may be a temporary type for vresult, and after
+         * fbody.dsymbolSemantic() running, vresult.type might be modified.
+         */
+        fd.vresult = new VarDeclaration(loc, tret, Id.result, null);
+        fd.vresult.storage_class |= STC.nodtor | STC.temp;
+        if (!fd.isVirtual())
+            fd.vresult.storage_class |= STC.const_;
+        fd.vresult.storage_class |= STC.result;
+        // set before the semantic() for checkNestedReference()
+        fd.vresult.parent = fd;
+    }
+    if (sc && fd.vresult.semanticRun == PASS.initial)
+    {
+        TypeFunction tf = fd.type.toTypeFunction();
+        if (tf.isref)
+            fd.vresult.storage_class |= STC.ref_;
+        fd.vresult.type = tret;
+        fd.vresult.dsymbolSemantic(sc);
+        if (!sc.insert(fd.vresult))
+            .error(fd.loc, "%s `%s` out result %s is already defined", fd.kind, fd.toPrettyChars, fd.vresult.toChars());
+        assert(fd.vresult.parent == fd);
+    }
+}
diff --git a/gcc/d/dmd/location.d b/gcc/d/dmd/location.d
index d71ea588c91..ca6805ee0b6 100644
--- a/gcc/d/dmd/location.d
+++ b/gcc/d/dmd/location.d
@@ -115,15 +115,7 @@  nothrow:
             //printf("setting %s\n", name);
             filenames.push(name);
             fileIndex = cast(uint)filenames.length;
-            if (!fileIndex)
-            {
-                import dmd.globals : global;
-                import dmd.errors : error, fatal;
-
-                global.gag = 0; // ensure error message gets printed
-                error(Loc.initial, "internal compiler error: file name index overflow!");
-                fatal();
-            }
+            assert(fileIndex, "internal compiler error: file name index overflow");
         }
         else
             fileIndex = 0;
diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d
index 843c402031d..2c9e058bf32 100644
--- a/gcc/d/dmd/mtype.d
+++ b/gcc/d/dmd/mtype.d
@@ -1185,110 +1185,12 @@  extern (C++) abstract class Type : ASTNode
         return t;
     }
 
-    final Type arrayOf()
-    {
-        if (ty == Terror)
-            return this;
-        if (!arrayof)
-        {
-            Type t = new TypeDArray(this);
-            arrayof = t.merge();
-        }
-        return arrayof;
-    }
-
-    // Make corresponding static array type without semantic
-    final Type sarrayOf(dinteger_t dim)
-    {
-        assert(deco);
-        Type t = new TypeSArray(this, new IntegerExp(Loc.initial, dim, Type.tsize_t));
-        // according to TypeSArray::semantic()
-        t = t.addMod(mod);
-        t = t.merge();
-        return t;
-    }
-
     final bool hasDeprecatedAliasThis()
     {
         auto ad = isAggregate(this);
         return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated);
     }
 
-    final Type aliasthisOf()
-    {
-        auto ad = isAggregate(this);
-        if (!ad || !ad.aliasthis)
-            return null;
-
-        auto s = ad.aliasthis.sym;
-        if (s.isAliasDeclaration())
-            s = s.toAlias();
-
-        if (s.isTupleDeclaration())
-            return null;
-
-        if (auto vd = s.isVarDeclaration())
-        {
-            auto t = vd.type;
-            if (vd.needThis())
-                t = t.addMod(this.mod);
-            return t;
-        }
-        Dsymbol callable = s.isFuncDeclaration();
-        callable = callable ? callable : s.isTemplateDeclaration();
-        if (callable)
-        {
-            auto fd = resolveFuncCall(Loc.initial, null, callable, null, this, ArgumentList(), FuncResolveFlag.quiet);
-            if (!fd || fd.errors || !functionSemantic(fd))
-                return Type.terror;
-
-            auto t = fd.type.nextOf();
-            if (!t) // https://issues.dlang.org/show_bug.cgi?id=14185
-                return Type.terror;
-            t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod);
-            return t;
-        }
-        if (auto d = s.isDeclaration())
-        {
-            assert(d.type);
-            return d.type;
-        }
-        if (auto ed = s.isEnumDeclaration())
-        {
-            return ed.type;
-        }
-
-        //printf("%s\n", s.kind());
-        return null;
-    }
-
-    /**
-     * Check whether this type has endless `alias this` recursion.
-     * Returns:
-     *   `true` if this type has an `alias this` that can be implicitly
-     *    converted back to this type itself.
-     */
-    extern (D) final bool checkAliasThisRec()
-    {
-        Type tb = toBasetype();
-        AliasThisRec* pflag;
-        if (tb.ty == Tstruct)
-            pflag = &(cast(TypeStruct)tb).att;
-        else if (tb.ty == Tclass)
-            pflag = &(cast(TypeClass)tb).att;
-        else
-            return false;
-
-        AliasThisRec flag = cast(AliasThisRec)(*pflag & AliasThisRec.typeMask);
-        if (flag == AliasThisRec.fwdref)
-        {
-            Type att = aliasthisOf();
-            flag = att && att.implicitConvTo(this) ? AliasThisRec.yes : AliasThisRec.no;
-        }
-        *pflag = cast(AliasThisRec)(flag | (*pflag & ~AliasThisRec.typeMask));
-        return flag == AliasThisRec.yes;
-    }
-
     Type makeConst()
     {
         //printf("Type::makeConst() %p, %s\n", this, toChars());
@@ -1451,129 +1353,6 @@  extern (C++) abstract class Type : ASTNode
         return 0;
     }
 
-    Type substWildTo(uint mod)
-    {
-        //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
-        Type t;
-
-        if (Type tn = nextOf())
-        {
-            // substitution has no effect on function pointer type.
-            if (ty == Tpointer && tn.ty == Tfunction)
-            {
-                t = this;
-                goto L1;
-            }
-
-            t = tn.substWildTo(mod);
-            if (t == tn)
-                t = this;
-            else
-            {
-                if (ty == Tpointer)
-                    t = t.pointerTo();
-                else if (ty == Tarray)
-                    t = t.arrayOf();
-                else if (ty == Tsarray)
-                    t = new TypeSArray(t, (cast(TypeSArray)this).dim.syntaxCopy());
-                else if (ty == Taarray)
-                {
-                    t = new TypeAArray(t, (cast(TypeAArray)this).index.syntaxCopy());
-                }
-                else if (ty == Tdelegate)
-                {
-                    t = new TypeDelegate(t.isTypeFunction());
-                }
-                else
-                    assert(0);
-
-                t = t.merge();
-            }
-        }
-        else
-            t = this;
-
-    L1:
-        if (isWild())
-        {
-            if (mod == MODFlags.immutable_)
-            {
-                t = t.immutableOf();
-            }
-            else if (mod == MODFlags.wildconst)
-            {
-                t = t.wildConstOf();
-            }
-            else if (mod == MODFlags.wild)
-            {
-                if (isWildConst())
-                    t = t.wildConstOf();
-                else
-                    t = t.wildOf();
-            }
-            else if (mod == MODFlags.const_)
-            {
-                t = t.constOf();
-            }
-            else
-            {
-                if (isWildConst())
-                    t = t.constOf();
-                else
-                    t = t.mutableOf();
-            }
-        }
-        if (isConst())
-            t = t.addMod(MODFlags.const_);
-        if (isShared())
-            t = t.addMod(MODFlags.shared_);
-
-        //printf("-Type::substWildTo t = %s\n", t.toChars());
-        return t;
-    }
-
-    final Type unqualify(uint m)
-    {
-        Type t = this.mutableOf().unSharedOf();
-
-        Type tn = ty == Tenum ? null : nextOf();
-        if (tn && tn.ty != Tfunction)
-        {
-            Type utn = tn.unqualify(m);
-            if (utn != tn)
-            {
-                if (ty == Tpointer)
-                    t = utn.pointerTo();
-                else if (ty == Tarray)
-                    t = utn.arrayOf();
-                else if (ty == Tsarray)
-                    t = new TypeSArray(utn, (cast(TypeSArray)this).dim);
-                else if (ty == Taarray)
-                {
-                    t = new TypeAArray(utn, (cast(TypeAArray)this).index);
-                }
-                else
-                    assert(0);
-
-                t = t.merge();
-            }
-        }
-        t = t.addMod(mod & ~m);
-        return t;
-    }
-
-    /**************************
-     * Return type with the top level of it being mutable.
-     */
-    inout(Type) toHeadMutable() inout
-    {
-        if (!mod)
-            return this;
-        Type unqualThis = cast(Type) this;
-        // `mutableOf` needs a mutable `this` only for caching
-        return cast(inout(Type)) unqualThis.mutableOf();
-    }
-
     inout(ClassDeclaration) isClassHandle() inout
     {
         return null;
@@ -3396,55 +3175,6 @@  extern (C++) final class TypeFunction : TypeNext
         return linkage == LINK.d && parameterList.varargs == VarArg.variadic;
     }
 
-    override Type substWildTo(uint)
-    {
-        if (!iswild && !(mod & MODFlags.wild))
-            return this;
-
-        // Substitude inout qualifier of function type to mutable or immutable
-        // would break type system. Instead substitude inout to the most weak
-        // qualifer - const.
-        uint m = MODFlags.const_;
-
-        assert(next);
-        Type tret = next.substWildTo(m);
-        Parameters* params = parameterList.parameters;
-        if (mod & MODFlags.wild)
-            params = parameterList.parameters.copy();
-        for (size_t i = 0; i < params.length; i++)
-        {
-            Parameter p = (*params)[i];
-            Type t = p.type.substWildTo(m);
-            if (t == p.type)
-                continue;
-            if (params == parameterList.parameters)
-                params = parameterList.parameters.copy();
-            (*params)[i] = new Parameter(p.loc, p.storageClass, t, null, null, null);
-        }
-        if (next == tret && params == parameterList.parameters)
-            return this;
-
-        // Similar to TypeFunction::syntaxCopy;
-        auto t = new TypeFunction(ParameterList(params, parameterList.varargs), tret, linkage);
-        t.mod = ((mod & MODFlags.wild) ? (mod & ~MODFlags.wild) | MODFlags.const_ : mod);
-        t.isnothrow = isnothrow;
-        t.isnogc = isnogc;
-        t.purity = purity;
-        t.isproperty = isproperty;
-        t.isref = isref;
-        t.isreturn = isreturn;
-        t.isreturnscope = isreturnscope;
-        t.isScopeQual = isScopeQual;
-        t.isreturninferred = isreturninferred;
-        t.isscopeinferred = isscopeinferred;
-        t.isInOutParam = false;
-        t.isInOutQual = false;
-        t.trust = trust;
-        t.fargs = fargs;
-        t.isctor = isctor;
-        return t.merge();
-    }
-
     extern(D) static const(char)* getMatchError(A...)(const(char)* format, A args)
     {
         if (global.gag && !global.params.v.showGaggedErrors)
@@ -4316,7 +4046,7 @@  extern (C++) final class TypeStruct : Type
         MATCH m;
         if (!(ty == to.ty && sym == (cast(TypeStruct)to).sym) && sym.aliasthis && !(att & AliasThisRec.tracing))
         {
-            if (auto ato = aliasthisOf())
+            if (auto ato = aliasthisOf(this))
             {
                 att = cast(AliasThisRec)(att | AliasThisRec.tracing);
                 m = ato.implicitConvTo(to);
@@ -4353,7 +4083,7 @@  extern (C++) final class TypeStruct : Type
 
         if (t.hasWild() && sym.aliasthis && !(att & AliasThisRec.tracing))
         {
-            if (auto ato = aliasthisOf())
+            if (auto ato = aliasthisOf(this))
             {
                 att = cast(AliasThisRec)(att | AliasThisRec.tracing);
                 wm = ato.deduceWild(t, isRef);
@@ -4364,11 +4094,6 @@  extern (C++) final class TypeStruct : Type
         return wm;
     }
 
-    override inout(Type) toHeadMutable() inout
-    {
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4595,7 +4320,7 @@  extern (C++) final class TypeClass : Type
         MATCH m;
         if (sym.aliasthis && !(att & AliasThisRec.tracing))
         {
-            if (auto ato = aliasthisOf())
+            if (auto ato = aliasthisOf(this))
             {
                 att = cast(AliasThisRec)(att | AliasThisRec.tracing);
                 m = ato.implicitConvTo(to);
@@ -4644,7 +4369,7 @@  extern (C++) final class TypeClass : Type
 
         if (t.hasWild() && sym.aliasthis && !(att & AliasThisRec.tracing))
         {
-            if (auto ato = aliasthisOf())
+            if (auto ato = aliasthisOf(this))
             {
                 att = cast(AliasThisRec)(att | AliasThisRec.tracing);
                 wm = ato.deduceWild(t, isRef);
@@ -4655,11 +4380,6 @@  extern (C++) final class TypeClass : Type
         return wm;
     }
 
-    override inout(Type) toHeadMutable() inout
-    {
-        return this;
-    }
-
     override bool isZeroInit(const ref Loc loc)
     {
         return true;
@@ -5852,36 +5572,3 @@  TypeIdentifier getException()
     tid.addIdent(Id.Exception);
     return tid;
 }
-
-/**************************************
- * Check and set 'att' if 't' is a recursive 'alias this' type
- *
- * The goal is to prevent endless loops when there is a cycle in the alias this chain.
- * Since there is no multiple `alias this`, the chain either ends in a leaf,
- * or it loops back on itself as some point.
- *
- * Example: S0 -> (S1 -> S2 -> S3 -> S1)
- *
- * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried.
- * `S1` is a recursive alias this type, but since `att` is initialized to `null`,
- * this still returns `false`, but `att1` is set to `S1`.
- * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again,
- * we notice `att == t`, so we're back at the start of the loop, and this returns `true`.
- *
- * Params:
- *   att = type reference used to detect recursion. Should be initialized to `null`.
- *   t   = type of 'alias this' rewrite to attempt
- *
- * Returns:
- *   `false` if the rewrite is safe, `true` if it would loop back around
- */
-bool isRecursiveAliasThis(ref Type att, Type t)
-{
-    //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars());
-    auto tb = t.toBasetype();
-    if (att && tb.equivalent(att))
-        return true;
-    else if (!att && tb.checkAliasThisRec())
-        att = tb;
-    return false;
-}
diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h
index 57f4ec6e0d2..2f8bfa68d22 100644
--- a/gcc/d/dmd/mtype.h
+++ b/gcc/d/dmd/mtype.h
@@ -257,7 +257,6 @@  public:
     Type *arrayOf();
     Type *sarrayOf(dinteger_t dim);
     bool hasDeprecatedAliasThis();
-    Type *aliasthisOf();
     virtual Type *makeConst();
     virtual Type *makeImmutable();
     virtual Type *makeShared();
@@ -271,11 +270,7 @@  public:
     virtual MATCH implicitConvTo(Type *to);
     virtual MATCH constConv(Type *to);
     virtual unsigned char deduceWild(Type *t, bool isRef);
-    virtual Type *substWildTo(unsigned mod);
 
-    Type *unqualify(unsigned m);
-
-    virtual Type *toHeadMutable();
     virtual ClassDeclaration *isClassHandle();
     virtual structalign_t alignment();
     virtual Expression *defaultInitLiteral(const Loc &loc);
@@ -580,7 +575,6 @@  public:
     bool hasLazyParameters();
     bool isDstyleVariadic() const;
 
-    Type *substWildTo(unsigned mod) override;
     MATCH constConv(Type *to) override;
 
     bool isnothrow() const;
@@ -751,7 +745,6 @@  public:
     MATCH implicitConvTo(Type *to) override;
     MATCH constConv(Type *to) override;
     unsigned char deduceWild(Type *t, bool isRef) override;
-    Type *toHeadMutable() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -804,7 +797,6 @@  public:
     MATCH implicitConvTo(Type *to) override;
     MATCH constConv(Type *to) override;
     unsigned char deduceWild(Type *t, bool isRef) override;
-    Type *toHeadMutable() override;
     bool isZeroInit(const Loc &loc) override;
     bool isscope() override;
     bool isBoolean() override;
@@ -892,6 +884,8 @@  namespace dmd
     Type *pointerTo(Type *type);
     Type *referenceTo(Type *type);
     Type *merge2(Type *type);
+    Type *sarrayOf(Type *type, dinteger_t dim);
+    Type *arrayOf(Type *type);
     Type *constOf(Type *type);
     Type *immutableOf(Type *type);
     Type *mutableOf(Type *type);
@@ -902,7 +896,11 @@  namespace dmd
     Type *wildConstOf(Type *type);
     Type *sharedWildOf(Type *type);
     Type *sharedWildConstOf(Type *type);
+    Type *unqualify(Type *type, unsigned m);
+    Type *toHeadMutable(Type *type);
+    Type *aliasthisOf(Type *type);
     Type *castMod(Type *type, MOD mod);
     Type *addMod(Type *type, MOD mod);
     Type *addStorageClass(Type *type, StorageClass stc);
+    Type *substWildTo(Type *type, unsigned mod);
 }
diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d
index dd6b117e0ac..2c89a58d5d2 100644
--- a/gcc/d/dmd/optimize.d
+++ b/gcc/d/dmd/optimize.d
@@ -1349,7 +1349,7 @@  Expression optimize(Expression e, int result, bool keepLvalue = false)
         if (b++ == global.recursionLimit)
         {
             error(e.loc, "infinite loop while optimizing expression");
-            fatal();
+            return ErrorExp.get();
         }
 
         auto ex = ret;
diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d
index 8b57f7fe249..1e5fb47a3dc 100644
--- a/gcc/d/dmd/safe.d
+++ b/gcc/d/dmd/safe.d
@@ -26,7 +26,7 @@  import dmd.identifier;
 import dmd.mtype;
 import dmd.target;
 import dmd.tokens;
-import dmd.typesem : hasPointers;
+import dmd.typesem : hasPointers, arrayOf;
 import dmd.func : setUnsafe, setUnsafePreview;
 
 /*************************************************************
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index 61272ea344c..ad87ea0baeb 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -1687,7 +1687,7 @@  Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             if (!ClassDeclaration.object)
             {
                 .error(Loc.initial, "missing or corrupt object.d");
-                fatal();
+                return error();
             }
 
             __gshared FuncDeclaration feq = null;
@@ -6229,6 +6229,29 @@  Type referenceTo(Type type)
     return type.rto;
 }
 
+// Make corresponding static array type without semantic
+Type sarrayOf(Type type, dinteger_t dim)
+{
+    assert(type.deco);
+    Type t = new TypeSArray(type, new IntegerExp(Loc.initial, dim, Type.tsize_t));
+    // according to TypeSArray.semantic()
+    t = t.addMod(type.mod);
+    t = t.merge();
+    return t;
+}
+
+Type arrayOf(Type type)
+{
+    if (type.ty == Terror)
+        return type;
+    if (!type.arrayof)
+    {
+        Type t = new TypeDArray(type);
+        type.arrayof = t.merge();
+    }
+    return type.arrayof;
+}
+
 /********************************
  * Convert to 'const'.
  */
@@ -6478,6 +6501,104 @@  Type sharedWildConstOf(Type type)
     return t;
 }
 
+Type unqualify(Type type, uint m)
+{
+    Type t = type.mutableOf().unSharedOf();
+
+    Type tn = type.ty == Tenum ? null : type.nextOf();
+    if (tn && tn.ty != Tfunction)
+    {
+        Type utn = tn.unqualify(m);
+        if (utn != tn)
+        {
+            if (type.ty == Tpointer)
+                t = utn.pointerTo();
+            else if (type.ty == Tarray)
+                t = utn.arrayOf();
+            else if (type.ty == Tsarray)
+                t = new TypeSArray(utn, (cast(TypeSArray)type).dim);
+            else if (type.ty == Taarray)
+            {
+                t = new TypeAArray(utn, (cast(TypeAArray)type).index);
+            }
+            else
+                assert(0);
+
+            t = t.merge();
+        }
+    }
+    t = t.addMod(type.mod & ~m);
+    return t;
+}
+
+/**************************
+ * Return type with the top level of it being mutable.
+ *
+ * Params:
+ *  t = type for which the top level mutable version is being returned
+ *
+ * Returns:
+ *  type version with mutable top level
+ */
+Type toHeadMutable(const Type t)
+{
+    Type unqualType = cast(Type) t;
+    if (t.isTypeStruct() || t.isTypeClass())
+        return unqualType;
+
+    if (!t.mod)
+        return unqualType;
+    return unqualType.mutableOf();
+}
+
+Type aliasthisOf(Type type)
+{
+    auto ad = isAggregate(type);
+    if (!ad || !ad.aliasthis)
+        return null;
+
+    auto s = ad.aliasthis.sym;
+    if (s.isAliasDeclaration())
+        s = s.toAlias();
+
+    if (s.isTupleDeclaration())
+        return null;
+
+    if (auto vd = s.isVarDeclaration())
+    {
+        auto t = vd.type;
+        if (vd.needThis())
+            t = t.addMod(type.mod);
+        return t;
+    }
+    Dsymbol callable = s.isFuncDeclaration();
+    callable = callable ? callable : s.isTemplateDeclaration();
+    if (callable)
+    {
+        auto fd = resolveFuncCall(Loc.initial, null, callable, null, type, ArgumentList(), FuncResolveFlag.quiet);
+        if (!fd || fd.errors || !functionSemantic(fd))
+            return Type.terror;
+
+        auto t = fd.type.nextOf();
+        if (!t) // https://issues.dlang.org/show_bug.cgi?id=14185
+            return Type.terror;
+        t = t.substWildTo(type.mod == 0 ? MODFlags.mutable : type.mod);
+        return t;
+    }
+    if (auto d = s.isDeclaration())
+    {
+        assert(d.type);
+        return d.type;
+    }
+    if (auto ed = s.isEnumDeclaration())
+    {
+        return ed.type;
+    }
+
+    //printf("%s\n", s.kind());
+    return null;
+}
+
 /************************************
  * Apply MODxxxx bits to existing type.
  */
@@ -6528,6 +6649,137 @@  Type castMod(Type type, MOD mod)
     return t;
 }
 
+Type substWildTo(Type type, uint mod)
+{
+    auto tf = type.isTypeFunction();
+    if (!tf)
+    {
+        //printf("+Type.substWildTo this = %s, mod = x%x\n", toChars(), mod);
+        Type t;
+
+        if (Type tn = type.nextOf())
+        {
+            // substitution has no effect on function pointer type.
+            if (type.ty == Tpointer && tn.ty == Tfunction)
+            {
+                t = type;
+                goto L1;
+            }
+
+            t = tn.substWildTo(mod);
+            if (t == tn)
+                t = type;
+            else
+            {
+                if (type.ty == Tpointer)
+                    t = t.pointerTo();
+                else if (type.ty == Tarray)
+                    t = t.arrayOf();
+                else if (type.ty == Tsarray)
+                    t = new TypeSArray(t, (cast(TypeSArray)type).dim.syntaxCopy());
+                else if (type.ty == Taarray)
+                {
+                    t = new TypeAArray(t, (cast(TypeAArray)type).index.syntaxCopy());
+                }
+                else if (type.ty == Tdelegate)
+                {
+                    t = new TypeDelegate(t.isTypeFunction());
+                }
+                else
+                    assert(0);
+
+                t = t.merge();
+            }
+        }
+        else
+            t = type;
+
+    L1:
+        if (type.isWild())
+        {
+            if (mod == MODFlags.immutable_)
+            {
+                t = t.immutableOf();
+            }
+            else if (mod == MODFlags.wildconst)
+            {
+                t = t.wildConstOf();
+            }
+            else if (mod == MODFlags.wild)
+            {
+                if (type.isWildConst())
+                    t = t.wildConstOf();
+                else
+                    t = t.wildOf();
+            }
+            else if (mod == MODFlags.const_)
+            {
+                t = t.constOf();
+            }
+            else
+            {
+                if (type.isWildConst())
+                    t = t.constOf();
+                else
+                    t = t.mutableOf();
+            }
+        }
+        if (type.isConst())
+            t = t.addMod(MODFlags.const_);
+        if (type.isShared())
+            t = t.addMod(MODFlags.shared_);
+
+        //printf("-Type.substWildTo t = %s\n", t.toChars());
+        return t;
+    }
+
+    if (!tf.iswild && !(tf.mod & MODFlags.wild))
+        return tf;
+
+    // Substitude inout qualifier of function type to mutable or immutable
+    // would break type system. Instead substitude inout to the most weak
+    // qualifer - const.
+    uint m = MODFlags.const_;
+
+    assert(tf.next);
+    Type tret = tf.next.substWildTo(m);
+    Parameters* params = tf.parameterList.parameters;
+    if (tf.mod & MODFlags.wild)
+        params = tf.parameterList.parameters.copy();
+    for (size_t i = 0; i < params.length; i++)
+    {
+        Parameter p = (*params)[i];
+        Type t = p.type.substWildTo(m);
+        if (t == p.type)
+            continue;
+        if (params == tf.parameterList.parameters)
+            params = tf.parameterList.parameters.copy();
+        (*params)[i] = new Parameter(p.loc, p.storageClass, t, null, null, null);
+    }
+    if (tf.next == tret && params == tf.parameterList.parameters)
+        return tf;
+
+    // Similar to TypeFunction.syntaxCopy;
+    auto t = new TypeFunction(ParameterList(params, tf.parameterList.varargs), tret, tf.linkage);
+    t.mod = ((tf.mod & MODFlags.wild) ? (tf.mod & ~MODFlags.wild) | MODFlags.const_ : tf.mod);
+    t.isnothrow = tf.isnothrow;
+    t.isnogc = tf.isnogc;
+    t.purity = tf.purity;
+    t.isproperty = tf.isproperty;
+    t.isref = tf.isref;
+    t.isreturn = tf.isreturn;
+    t.isreturnscope = tf.isreturnscope;
+    t.isScopeQual = tf.isScopeQual;
+    t.isreturninferred = tf.isreturninferred;
+    t.isscopeinferred = tf.isscopeinferred;
+    t.isInOutParam = false;
+    t.isInOutQual = false;
+    t.trust = tf.trust;
+    t.fargs = tf.fargs;
+    t.isctor = tf.isctor;
+    return t.merge();
+}
+
 /************************************
  * Add MODxxxx bits to existing type.
  * We're adding, not replacing, so adding const to
@@ -6633,6 +6885,69 @@  Type addMod(Type type, MOD mod)
     return t;
 }
 
+/**
+ * Check whether this type has endless `alias this` recursion.
+ *
+ * Params:
+ *   t = type to check whether it has a recursive alias this
+ * Returns:
+ *   `true` if `t` has an `alias this` that can be implicitly
+ *    converted back to `t` itself.
+ */
+private bool checkAliasThisRec(Type t)
+{
+    Type tb = t.toBasetype();
+    AliasThisRec* pflag;
+    if (tb.ty == Tstruct)
+        pflag = &(cast(TypeStruct)tb).att;
+    else if (tb.ty == Tclass)
+        pflag = &(cast(TypeClass)tb).att;
+    else
+        return false;
+
+    AliasThisRec flag = cast(AliasThisRec)(*pflag & AliasThisRec.typeMask);
+    if (flag == AliasThisRec.fwdref)
+    {
+        Type att = aliasthisOf(t);
+        flag = att && att.implicitConvTo(t) ? AliasThisRec.yes : AliasThisRec.no;
+    }
+    *pflag = cast(AliasThisRec)(flag | (*pflag & ~AliasThisRec.typeMask));
+    return flag == AliasThisRec.yes;
+}
+
+/**************************************
+ * Check and set 'att' if 't' is a recursive 'alias this' type
+ *
+ * The goal is to prevent endless loops when there is a cycle in the alias this chain.
+ * Since there is no multiple `alias this`, the chain either ends in a leaf,
+ * or it loops back on itself as some point.
+ *
+ * Example: S0 -> (S1 -> S2 -> S3 -> S1)
+ *
+ * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried.
+ * `S1` is a recursive alias this type, but since `att` is initialized to `null`,
+ * this still returns `false`, but `att1` is set to `S1`.
+ * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again,
+ * we notice `att == t`, so we're back at the start of the loop, and this returns `true`.
+ *
+ * Params:
+ *   att = type reference used to detect recursion. Should be initialized to `null`.
+ *   t   = type of 'alias this' rewrite to attempt
+ *
+ * Returns:
+ *   `false` if the rewrite is safe, `true` if it would loop back around
+ */
+bool isRecursiveAliasThis(ref Type att, Type t)
+{
+    //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars());
+    auto tb = t.toBasetype();
+    if (att && tb.equivalent(att))
+        return true;
+    else if (!att && tb.checkAliasThisRec())
+        att = tb;
+    return false;
+}
+
 /******************************* Private *****************************************/
 
 private:
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
index 7fbabbe185f..d055e0b4025 100644
--- a/gcc/d/expr.cc
+++ b/gcc/d/expr.cc
@@ -464,7 +464,7 @@  public:
 	else
 	  {
 	    /* Use _adEq2() to compare each element.  */
-	    Type *t1array = t1elem->arrayOf ();
+	    Type *t1array = dmd::arrayOf (t1elem);
 	    tree result = build_libcall (LIBCALL_ADEQ2, e->type, 3,
 					 d_array_convert (e->e1),
 					 d_array_convert (e->e2),
@@ -2172,7 +2172,8 @@  public:
 	      {
 		/* Generate a slice for non-zero initialized aggregates,
 		   otherwise create an empty array.  */
-		gcc_assert (e->type == dmd::constOf (Type::tvoid->arrayOf ()));
+		gcc_assert (e->type->isConst ()
+			    && e->type->nextOf ()->ty == TY::Tvoid);
 
 		tree type = build_ctype (e->type);
 		tree length = size_int (sd->dsym->structsize);
@@ -2571,7 +2572,7 @@  public:
 
     /* Implicitly convert void[n] to ubyte[n].  */
     if (tb->ty == TY::Tsarray && tb->nextOf ()->toBasetype ()->ty == TY::Tvoid)
-      tb = Type::tuns8->sarrayOf (tb->isTypeSArray ()->dim->toUInteger ());
+      tb = dmd::sarrayOf (Type::tuns8, tb->isTypeSArray ()->dim->toUInteger ());
 
     gcc_assert (tb->ty == TY::Tarray || tb->ty == TY::Tsarray
 		|| tb->ty == TY::Tpointer);
@@ -2685,7 +2686,7 @@  public:
 	/* Allocate space on the memory managed heap.  */
 	tree mem = build_libcall (LIBCALL_ARRAYLITERALTX,
 				  dmd::pointerTo (etype), 2,
-				  build_typeinfo (e, etype->arrayOf ()),
+				  build_typeinfo (e, dmd::arrayOf (etype)),
 				  size_int (e->elements->length));
 	mem = d_save_expr (mem);
 
@@ -2732,20 +2733,20 @@  public:
 
     /* Build an expression that assigns all expressions in KEYS
        to a constructor.  */
-    tree akeys = build_array_from_exprs (ta->index->sarrayOf (e->keys->length),
-					 e->keys, this->constp_);
+    Type *tkarray = dmd::sarrayOf (ta->index, e->keys->length);
+    tree akeys = build_array_from_exprs (tkarray, e->keys, this->constp_);
     tree init = stabilize_expr (&akeys);
 
     /* Do the same with all expressions in VALUES.  */
-    tree avals = build_array_from_exprs (ta->next->sarrayOf (e->values->length),
-					 e->values, this->constp_);
+    Type *tvarray = dmd::sarrayOf (ta->next, e->values->length);
+    tree avals = build_array_from_exprs (tvarray, e->values, this->constp_);
     init = compound_expr (init, stabilize_expr (&avals));
 
     /* Generate: _d_assocarrayliteralTX (ti, keys, vals);  */
-    tree keys = d_array_value (build_ctype (ta->index->arrayOf ()),
+    tree keys = d_array_value (build_ctype (dmd::arrayOf (ta->index)),
 			       size_int (e->keys->length),
 			       build_address (akeys));
-    tree vals = d_array_value (build_ctype (ta->next->arrayOf ()),
+    tree vals = d_array_value (build_ctype (dmd::arrayOf (ta->next)),
 			       size_int (e->values->length),
 			       build_address (avals));
 
diff --git a/gcc/d/intrinsics.cc b/gcc/d/intrinsics.cc
index 8bbcdc164ee..c895c1a1a52 100644
--- a/gcc/d/intrinsics.cc
+++ b/gcc/d/intrinsics.cc
@@ -274,7 +274,7 @@  build_shuffle_mask_type (tree type)
   gcc_assert (t != NULL);
   unsigned HOST_WIDE_INT nunits = TYPE_VECTOR_SUBPARTS (type).to_constant ();
 
-  return build_ctype (TypeVector::create (t->sarrayOf (nunits)));
+  return build_ctype (TypeVector::create (dmd::sarrayOf (t, nunits)));
 }
 
 /* Checks if call to intrinsic FUNCTION in CALLEXP matches the internal
@@ -414,7 +414,7 @@  maybe_warn_intrinsic_mismatch (tree function, tree callexp)
 	      break;
 
 	    Type *inner = build_frontend_type (TREE_TYPE (vec0));
-	    Type *vector = TypeVector::create (inner->sarrayOf (nunits));
+	    Type *vector = TypeVector::create (dmd::sarrayOf (inner, nunits));
 	    return warn_mismatched_argument (callexp, 1,
 					     build_ctype (vector), true);
 	  }
@@ -479,7 +479,7 @@  maybe_warn_intrinsic_mismatch (tree function, tree callexp)
 	      break;
 
 	    Type *inner = build_frontend_type (TREE_TYPE (arg));
-	    Type *vector = TypeVector::create (inner->sarrayOf (nunits));
+	    Type *vector = TypeVector::create (dmd::sarrayOf (inner, nunits));
 	    return warn_mismatched_argument (callexp, 0,
 					     build_ctype (vector), true);
 	  }
diff --git a/gcc/d/runtime.cc b/gcc/d/runtime.cc
index e5988c721ca..8a64c5282d1 100644
--- a/gcc/d/runtime.cc
+++ b/gcc/d/runtime.cc
@@ -158,31 +158,31 @@  get_libcall_type (d_libcall_type type)
       break;
 
     case LCT_ARRAY_VOID:
-      libcall_types[type] = Type::tvoid->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tvoid);
       break;
 
     case LCT_ARRAY_SIZE_T:
-      libcall_types[type] = Type::tsize_t->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tsize_t);
       break;
 
     case LCT_ARRAY_BYTE:
-      libcall_types[type] = Type::tint8->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tint8);
       break;
 
     case LCT_ARRAY_STRING:
-      libcall_types[type] = Type::tstring->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tstring);
       break;
 
     case LCT_ARRAY_WSTRING:
-      libcall_types[type] = Type::twstring->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::twstring);
       break;
 
     case LCT_ARRAY_DSTRING:
-      libcall_types[type] = Type::tdstring->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tdstring);
       break;
 
     case LCT_ARRAYARRAY_BYTE:
-      libcall_types[type] = Type::tint8->arrayOf ()->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tint8);
       break;
 
     case LCT_POINTER_ASSOCARRAY:
@@ -190,15 +190,15 @@  get_libcall_type (d_libcall_type type)
       break;
 
     case LCT_POINTER_VOIDPTR:
-      libcall_types[type] = Type::tvoidptr->arrayOf ();
+      libcall_types[type] = dmd::arrayOf (Type::tvoidptr);
       break;
 
     case LCT_ARRAYPTR_VOID:
-      libcall_types[type] = dmd::pointerTo (Type::tvoid->arrayOf ());
+      libcall_types[type] = dmd::pointerTo (dmd::arrayOf (Type::tvoid));
       break;
 
     case LCT_ARRAYPTR_BYTE:
-      libcall_types[type] = dmd::pointerTo (Type::tint8->arrayOf ());
+      libcall_types[type] = dmd::pointerTo (dmd::arrayOf (Type::tint8));
       break;
 
     case LCT_IMMUTABLE_CHARPTR:
diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc
index 794737b3b0b..cadcbe8164e 100644
--- a/gcc/d/typeinfo.cc
+++ b/gcc/d/typeinfo.cc
@@ -415,7 +415,7 @@  class TypeInfoVisitor : public Visitor
     tree decl = this->internal_reference (value);
     TREE_READONLY (decl) = 1;
 
-    value = d_array_value (build_ctype (Type::tchar->arrayOf ()),
+    value = d_array_value (build_ctype (dmd::arrayOf (Type::tchar)),
 			   size_int (len), build_address (decl));
     this->layout_field (value);
   }
@@ -1137,7 +1137,7 @@  public:
     this->layout_base (Type::typeinfotypelist);
 
     /* TypeInfo[] elements;  */
-    Type *satype = Type::tvoidptr->sarrayOf (ti->arguments->length);
+    Type *satype = dmd::sarrayOf (Type::tvoidptr, ti->arguments->length);
     vec<constructor_elt, va_gc> *elms = NULL;
     for (size_t i = 0; i < ti->arguments->length; i++)
       {
diff --git a/gcc/testsuite/gdc.test/compilable/issue24399.d b/gcc/testsuite/gdc.test/compilable/issue24399.d
new file mode 100644
index 00000000000..ae3e7442f56
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue24399.d
@@ -0,0 +1,9 @@ 
+// REQUIRED_ARGS: -main
+// LINK:
+template rt_options()
+{
+    __gshared string[] rt_options = [];
+    string[] rt_options_tls = [];
+}
+
+alias _ = rt_options!();
diff --git a/gcc/testsuite/gdc.test/compilable/issue24409.d b/gcc/testsuite/gdc.test/compilable/issue24409.d
new file mode 100644
index 00000000000..5d298df2113
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/issue24409.d
@@ -0,0 +1,17 @@ 
+static struct S
+{
+    union
+    {
+        int i;
+        long l;
+    }
+}
+
+int f()
+{
+    S* r = new S();
+    r.i = 5;
+    return r.i;
+}
+
+enum X = f();
diff --git a/gcc/testsuite/gdc.test/runnable/issue24401.d b/gcc/testsuite/gdc.test/runnable/issue24401.d
new file mode 100644
index 00000000000..109d543a0b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/issue24401.d
@@ -0,0 +1,6 @@ 
+// PERMUTE_ARGS:
+// https://issues.dlang.org/show_bug.cgi?id=24401
+int main()
+{
+    return (() @trusted => 0)();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test24371.d b/gcc/testsuite/gdc.test/runnable/test24371.d
new file mode 100644
index 00000000000..885f9b8648b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test24371.d
@@ -0,0 +1,15 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=24371
+
+void main()
+{
+    assert("b" ~ "c" == "bc");
+    assert(["a"] ~ "b" == ["a", "b"]);
+    assert(["a"] ~ ("b" ~ "c") == ["a", "bc"]);
+
+    auto strArr = ["a"];
+    assert(strArr ~ ("b" ~ "c") == ["a", "bc"]);
+    auto str = "c";
+    assert(["a"] ~ ("b" ~ str) == ["a", "bc"]);
+
+    assert(strArr ~ ("b" ~ str) == ["a", "bc"]);
+}
diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test7925.d b/gcc/testsuite/gdc.test/runnable_cxx/test7925.d
index f05aac916e2..2d0b023fe55 100644
--- a/gcc/testsuite/gdc.test/runnable_cxx/test7925.d
+++ b/gcc/testsuite/gdc.test/runnable_cxx/test7925.d
@@ -1,12 +1,5 @@ 
 // EXTRA_CPP_SOURCES: cpp7925.cpp
 
-/*
-Exclude -O/-inline due to a codegen bug on OSX:
-https://issues.dlang.org/show_bug.cgi?id=22556
-
-PERMUTE_ARGS(osx): -release -g
-*/
-
 import core.vararg;
 
 extern(C++) class C1
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index f11c5fbfb0b..4c0a0bc2aac 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@ 
-ceff48bf7db05503117f54fdc0cefcb89b711136
+f8bae0455851a1dfc8113d69323415f6de549e39
 
 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/exception.d b/libphobos/libdruntime/core/exception.d
index 959ce83f02e..c7b302c4520 100644
--- a/libphobos/libdruntime/core/exception.d
+++ b/libphobos/libdruntime/core/exception.d
@@ -19,6 +19,19 @@  void __switch_errorT()(string file = __FILE__, size_t line = __LINE__) @trusted
         assert(0, "No appropriate switch clause found");
 }
 
+/*
+ * Make sure template __switch_errorT is always instantiated when building
+ * druntime. This works around https://issues.dlang.org/show_bug.cgi?id=20802.
+ * When druntime and phobos are compiled with -release, the instance for
+ * __switch_errorT is not needed. An application compiled with -release
+ * could need the instance for __switch_errorT, but the compiler would
+ * not generate code for it, because it assumes, that it was already
+ * generated for druntime. Always including the instance in a compiled
+ * druntime allows to use an application without -release with druntime
+ * with -release.
+ */
+private alias dummy__switch_errorT = __switch_errorT!();
+
 /**
  * Thrown on a range error.
  */
diff --git a/libphobos/libdruntime/core/sys/linux/ifaddrs.d b/libphobos/libdruntime/core/sys/linux/ifaddrs.d
index 479dfa8d39b..d8c52c148e5 100644
--- a/libphobos/libdruntime/core/sys/linux/ifaddrs.d
+++ b/libphobos/libdruntime/core/sys/linux/ifaddrs.d
@@ -39,11 +39,18 @@  struct ifaddrs
     union
     {
         /// Broadcast address of the interface
-        sockaddr* ifu_broadaddr;
+        sockaddr* ifa_broadaddr;
+
         /// Point-to-point destination addresss
-        sockaddr* if_dstaddr;
+        sockaddr* ifa_dstaddr;
     }
 
+    deprecated("druntime declared this incorrectly before. The correct name is ifa_broadaddr.")
+    alias ifu_broadaddr = ifa_broadaddr;
+
+    deprecated("druntime declared this incorrectly before. The correct name is ifa_dstaddr.")
+    alias if_dstaddr = ifa_dstaddr;
+
     /// Address specific data
     void* ifa_data;
 }
diff --git a/libphobos/libdruntime/core/sys/posix/sys/select.d b/libphobos/libdruntime/core/sys/posix/sys/select.d
index dd05d08dde6..bd342503252 100644
--- a/libphobos/libdruntime/core/sys/posix/sys/select.d
+++ b/libphobos/libdruntime/core/sys/posix/sys/select.d
@@ -183,7 +183,8 @@  else version (FreeBSD)
 
     struct fd_set
     {
-        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] __fds_bits;
+        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] fds_bits;
+        deprecated("druntime incorrectly named fds_bits __fds_bits") alias __fds_bits = fds_bits;
     }
 
     extern (D) __fd_mask __fdset_mask(uint n) pure
@@ -193,17 +194,17 @@  else version (FreeBSD)
 
     extern (D) void FD_CLR( int n, fd_set* p ) pure
     {
-        p.__fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
     }
 
     extern (D) bool FD_ISSET( int n, const(fd_set)* p ) pure
     {
-        return (p.__fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
+        return (p.fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
     }
 
     extern (D) void FD_SET( int n, fd_set* p ) pure
     {
-        p.__fds_bits[n / _NFDBITS] |= __fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] |= __fdset_mask(n);
     }
 
     extern (D) void FD_ZERO( fd_set* p ) pure
@@ -214,7 +215,7 @@  else version (FreeBSD)
         _p = p;
         _n = (FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS;
         while (_n > 0)
-            _p.__fds_bits[--_n] = 0;
+            _p.fds_bits[--_n] = 0;
     }
 
     int pselect(int, fd_set*, fd_set*, fd_set*, const scope timespec*, const scope sigset_t*);
@@ -232,7 +233,8 @@  else version (NetBSD)
 
     struct fd_set
     {
-        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] __fds_bits;
+        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] fds_bits;
+        deprecated("druntime incorrectly named fds_bits __fds_bits") alias __fds_bits = fds_bits;
     }
 
     extern (D) __fd_mask __fdset_mask(uint n) pure
@@ -242,17 +244,17 @@  else version (NetBSD)
 
     extern (D) void FD_CLR( int n, fd_set* p ) pure
     {
-        p.__fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
     }
 
     extern (D) bool FD_ISSET( int n, const(fd_set)* p ) pure
     {
-        return (p.__fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
+        return (p.fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
     }
 
     extern (D) void FD_SET( int n, fd_set* p ) pure
     {
-        p.__fds_bits[n / _NFDBITS] |= __fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] |= __fdset_mask(n);
     }
 
     extern (D) void FD_ZERO( fd_set* p ) pure
@@ -263,7 +265,7 @@  else version (NetBSD)
         _p = p;
         _n = (FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS;
         while (_n > 0)
-            _p.__fds_bits[--_n] = 0;
+            _p.fds_bits[--_n] = 0;
     }
 
     int pselect(int, fd_set*, fd_set*, fd_set*, const scope timespec*, const scope sigset_t*);
@@ -281,7 +283,8 @@  else version (OpenBSD)
 
     struct fd_set
     {
-        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] __fds_bits;
+        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] fds_bits;
+        deprecated("druntime incorrectly named fds_bits __fds_bits") alias __fds_bits = fds_bits;
     }
 
     extern (D) __fd_mask __fdset_mask(uint n) pure
@@ -291,17 +294,17 @@  else version (OpenBSD)
 
     extern (D) void FD_CLR(int n, fd_set* p) pure
     {
-        p.__fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
     }
 
     extern (D) bool FD_ISSET(int n, const(fd_set)* p) pure
     {
-        return (p.__fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
+        return (p.fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
     }
 
     extern (D) void FD_SET(int n, fd_set* p) pure
     {
-        p.__fds_bits[n / _NFDBITS] |= __fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] |= __fdset_mask(n);
     }
 
     extern (D) void FD_ZERO(fd_set* p) pure
@@ -310,7 +313,7 @@  else version (OpenBSD)
         size_t _n = (FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS;
 
         while (_n > 0)
-            _p.__fds_bits[--_n] = 0;
+            _p.fds_bits[--_n] = 0;
     }
 
     int pselect(int, fd_set*, fd_set*, fd_set*, const scope timespec*, const scope sigset_t*);
@@ -328,7 +331,8 @@  else version (DragonFlyBSD)
 
     struct fd_set
     {
-        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] __fds_bits;
+        __fd_mask[(FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS] fds_bits;
+        deprecated("druntime incorrectly named fds_bits __fds_bits") alias __fds_bits = fds_bits;
     }
 
     extern (D) __fd_mask __fdset_mask(uint n) pure
@@ -338,17 +342,17 @@  else version (DragonFlyBSD)
 
     extern (D) void FD_CLR( int n, fd_set* p ) pure
     {
-        p.__fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] &= ~__fdset_mask(n);
     }
 
     extern (D) bool FD_ISSET( int n, const(fd_set)* p ) pure
     {
-        return (p.__fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
+        return (p.fds_bits[n / _NFDBITS] & __fdset_mask(n)) != 0;
     }
 
     extern (D) void FD_SET( int n, fd_set* p ) pure
     {
-        p.__fds_bits[n / _NFDBITS] |= __fdset_mask(n);
+        p.fds_bits[n / _NFDBITS] |= __fdset_mask(n);
     }
 
     extern (D) void FD_ZERO( fd_set* p ) pure
@@ -359,7 +363,7 @@  else version (DragonFlyBSD)
         _p = p;
         _n = (FD_SETSIZE + (_NFDBITS - 1)) / _NFDBITS;
         while (_n > 0)
-            _p.__fds_bits[--_n] = 0;
+            _p.fds_bits[--_n] = 0;
     }
 
     int pselect(int, fd_set*, fd_set*, fd_set*, const scope timespec*, const scope sigset_t*);
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 50d71f59154..ff34bece2a3 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@ 
-dcbfbd43ac321e81af60afd795bd0f3c3f47cfa0
+a2ade9dec49e70c6acd447df52321988a4c2fb9f
 
 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/zlib.d b/libphobos/src/etc/c/zlib.d
index 73c6534d8da..0ff5f978fbc 100644
--- a/libphobos/src/etc/c/zlib.d
+++ b/libphobos/src/etc/c/zlib.d
@@ -9,9 +9,9 @@  module etc.c.zlib;
 import core.stdc.config;
 
 /* zlib.h -- interface of the 'zlib' general purpose compression library
-  version 1.2.12, March 11th, 2022
+  version 1.3.1, January 22nd, 2024
 
-  Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler
+  Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -43,8 +43,8 @@  nothrow:
 extern (C):
 
 // Those are extern(D) as they should be mangled
-extern(D) immutable string ZLIB_VERSION = "1.2.12";
-extern(D) immutable ZLIB_VERNUM = 0x12c0;
+extern(D) immutable string ZLIB_VERSION = "1.3.1";
+extern(D) immutable ZLIB_VERNUM = 0x1310;
 
 /*
     The 'zlib' compression library provides in-memory compression and
@@ -250,7 +250,7 @@  int deflateInit(z_streamp strm, int level)
      Initializes the internal stream state for compression.  The fields
    zalloc, zfree and opaque must be initialized before by the caller.  If
    zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
-   allocation functions.
+   allocation functions.  total_in, total_out, adler, and msg are initialized.
 
      The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
    1 gives best speed, 9 gives best compression, 0 gives no compression at all
@@ -296,7 +296,7 @@  int deflate(z_streamp strm, int flush);
   == 0), or after each call of deflate().  If deflate returns Z_OK and with
   zero avail_out, it must be called again after making room in the output
   buffer because there might be more output pending. See deflatePending(),
-  which can be used if desired to determine whether or not there is more ouput
+  which can be used if desired to determine whether or not there is more output
   in that case.
 
     Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
@@ -340,8 +340,8 @@  int deflate(z_streamp strm, int flush);
   with the same value of the flush parameter and more output space (updated
   avail_out), until the flush is complete (deflate returns with non-zero
   avail_out).  In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
-  avail_out is greater than six to avoid repeated flush markers due to
-  avail_out == 0 on return.
+  avail_out is greater than six when the flush marker begins, in order to avoid
+  repeated flush markers upon calling deflate() again when avail_out == 0.
 
     If the parameter flush is set to Z_FINISH, pending input is processed,
   pending output is flushed and deflate returns with Z_STREAM_END if there was
@@ -405,7 +405,8 @@  int inflateInit(z_streamp strm)
    read or consumed.  The allocation of a sliding window will be deferred to
    the first call of inflate (if the decompression does not complete on the
    first call).  If zalloc and zfree are set to Z_NULL, inflateInit updates
-   them to use default allocation functions.
+   them to use default allocation functions.  total_in, total_out, adler, and
+   msg are initialized.
 
      inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
    memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
@@ -680,7 +681,7 @@  int deflateGetDictionary(z_streamp strm, ubyte *dictionary, uint  dictLength);
    to dictionary.  dictionary must have enough space, where 32768 bytes is
    always enough.  If deflateGetDictionary() is called with dictionary equal to
    Z_NULL, then only the dictionary length is returned, and nothing is copied.
-   Similary, if dictLength is Z_NULL, then it is not set.
+   Similarly, if dictLength is Z_NULL, then it is not set.
 
      deflateGetDictionary() may return a length less than the window size, even
    when more than the window size in input has been provided. It may return up
@@ -715,7 +716,7 @@  int deflateReset(z_streamp strm);
      This function is equivalent to deflateEnd followed by deflateInit, but
    does not free and reallocate the internal compression state.  The stream
    will leave the compression level and any other attributes that may have been
-   set unchanged.
+   set unchanged.  total_in, total_out, adler, and msg are initialized.
 
      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
    stream state was inconsistent (such as zalloc or state being Z_NULL).
@@ -746,7 +747,7 @@  int deflateParams(z_streamp strm, int level, int strategy);
    Then no more input data should be provided before the deflateParams() call.
    If this is done, the old level and strategy will be applied to the data
    compressed before deflateParams(), and the new level and strategy will be
-   applied to the the data compressed after deflateParams().
+   applied to the data compressed after deflateParams().
 
      deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream
    state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if
@@ -829,8 +830,9 @@  int deflateSetHeader(z_streamp strm, gz_headerp head);
    gzip file" and give up.
 
      If deflateSetHeader is not used, the default gzip header has text false,
-   the time set to zero, and os set to 255, with no extra, name, or comment
-   fields.  The gzip header is returned to the default state by deflateReset().
+   the time set to zero, and os set to the current operating system, with no
+   extra, name, or comment fields.  The gzip header is returned to the default
+   state by deflateReset().
 
      deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
    stream state was inconsistent.
@@ -920,7 +922,7 @@  int inflateGetDictionary(z_streamp strm, ubyte* dictionary, uint* dictLength);
    to dictionary.  dictionary must have enough space, where 32768 bytes is
    always enough.  If inflateGetDictionary() is called with dictionary equal to
    Z_NULL, then only the dictionary length is returned, and nothing is copied.
-   Similary, if dictLength is Z_NULL, then it is not set.
+   Similarly, if dictLength is Z_NULL, then it is not set.
 
      inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
    stream state is inconsistent.
@@ -939,10 +941,10 @@  int inflateSync(z_streamp strm);
      inflateSync returns Z_OK if a possible full flush point has been found,
    Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
    has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
-   In the success case, the application may save the current current value of
-   total_in which indicates where valid compressed data was found.  In the
-   error case, the application may repeatedly call inflateSync, providing more
-   input each time, until success or end of the input data.
+   In the success case, the application may save the current value of total_in
+   which indicates where valid compressed data was found.  In the error case,
+   the application may repeatedly call inflateSync, providing more input each
+   time, until success or end of the input data.
 */
 
 int inflateCopy(z_streamp dest, z_streamp source);
@@ -1446,12 +1448,12 @@  z_size_t gzfread(void* buf, z_size_t size, z_size_t nitems, gzFile file);
 
      In the event that the end of file is reached and only a partial item is
    available at the end, i.e. the remaining uncompressed data length is not a
-   multiple of size, then the final partial item is nevetheless read into buf
+   multiple of size, then the final partial item is nevertheless read into buf
    and the end-of-file flag is set.  The length of the partial item read is not
    provided, but could be inferred from the result of gztell().  This behavior
    is the same as the behavior of fread() implementations in common libraries,
    but it prevents the direct use of gzfread() to read a concurrently written
-   file, reseting and retrying on end-of-file, when size is not 1.
+   file, resetting and retrying on end-of-file, when size is not 1.
 */
 
 int gzwrite(gzFile file, void* buf, uint len);
@@ -1747,19 +1749,18 @@  uint crc32_z(uint crc, const(ubyte)* buf, z_size_t len);
 */
 
 uint crc32_combine(uint crc1, uint crc2, z_off_t len2);
-
 /*
      Combine two CRC-32 check values into one.  For two sequences of bytes,
    seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
    calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
    check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
-   len2.
+   len2. len2 must be non-negative.
 */
 
 uint crc32_combine_gen(z_off_t len2);
 /*
      Return the operator corresponding to length len2, to be used with
-   crc32_combine_op().
+   crc32_combine_op(). len2 must be non-negative.
 */
 
 uint crc32_combine_op(uint crc1, uint crc2, uint op);