diff mbox series

[committed] d: Merge upstream dmd ad8412530, druntime fd9a4544, phobos 495e835c2.

Message ID 20211220183412.2392609-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd ad8412530, druntime fd9a4544, phobos 495e835c2. | expand

Commit Message

Iain Buclaw Dec. 20, 2021, 6:34 p.m. UTC
Hi,

This patch merges the D front-end with upstream dmd ad8412530, and the
run-time libraries with upstream druntime fd9a4544 and phobos 495e835c2.

D front-end changes:

    - Import dmd v2.098.1
    - Remove calling of _d_delstruct from code generator.

Druntime changes:

    - Import druntime v2.098.1

Phobos changes:

    - Import phobos v2.098.1

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

Regards,
Iain.

---
gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd ad8412530.
	* expr.cc (ExprVisitor::visit (DeleteExp *)): Remove code generation
	of _d_delstruct.
	* runtime.def (DELSTRUCT): Remove.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime fd9a4544.
	* src/MERGE: Merge upstream phobos 495e835c2.
---
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/dmd/canthrow.d                          |  16 ++
 gcc/d/dmd/dcast.d                             |  73 +++----
 gcc/d/dmd/dinterpret.d                        |  41 ++++
 gcc/d/dmd/dsymbol.d                           |  16 +-
 gcc/d/dmd/dsymbolsem.d                        |  24 ++-
 gcc/d/dmd/expressionsem.d                     |  26 ++-
 gcc/d/dmd/id.d                                |   3 +
 gcc/d/dmd/initsem.d                           | 106 ++++++-----
 gcc/d/dmd/nogc.d                              |  14 ++
 gcc/d/dmd/semantic3.d                         |   3 +-
 gcc/d/dmd/tokens.d                            |  26 +--
 gcc/d/dmd/tokens.h                            |  11 +-
 gcc/d/dmd/typesem.d                           | 178 +++++++++---------
 gcc/d/expr.cc                                 |  12 +-
 gcc/d/runtime.def                             |   2 -
 gcc/testsuite/gdc.test/compilable/test22593.d |  13 ++
 .../gdc.test/fail_compilation/ice17074.d      |  12 +-
 .../gdc.test/fail_compilation/test22593.d     |  23 +++
 libphobos/libdruntime/MERGE                   |   2 +-
 libphobos/libdruntime/core/builtins.d         |  48 ++++-
 libphobos/libdruntime/core/lifetime.d         |  20 +-
 libphobos/libdruntime/core/sys/linux/sched.d  |   3 +
 libphobos/libdruntime/object.d                |  30 ++-
 libphobos/src/MERGE                           |   2 +-
 libphobos/src/std/format/write.d              |  23 +++
 libphobos/src/std/range/interfaces.d          |   9 +
 libphobos/src/std/typecons.d                  |   2 +-
 28 files changed, 468 insertions(+), 272 deletions(-)
 create mode 100644 gcc/testsuite/gdc.test/compilable/test22593.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test22593.d
diff mbox series

Patch

diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index d7eff4ffd2f..b42576c2ce6 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-93108bb9ea6216d67fa97bb4842fb59f26f6bfc7
+ad8412530e607ffebec36f2dbdff1a6f2798faf7
 
 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/canthrow.d b/gcc/d/dmd/canthrow.d
index b67a9d14dd4..b1877151c88 100644
--- a/gcc/d/dmd/canthrow.d
+++ b/gcc/d/dmd/canthrow.d
@@ -82,6 +82,22 @@  extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow
 
             if (global.errors && !ce.e1.type)
                 return; // error recovery
+
+            import dmd.id : Id;
+
+            if (ce.f && ce.f.ident == Id._d_delstruct)
+            {
+                // Only check if the dtor throws.
+                Type tb = (*ce.arguments)[0].type.toBasetype();
+                auto ts = tb.nextOf().baseElemOf().isTypeStruct();
+                if (ts)
+                {
+                    auto sd = ts.sym;
+                    if (sd.dtor)
+                        checkFuncThrows(ce, sd.dtor);
+                }
+            }
+
             /* If calling a function or delegate that is typed as nothrow,
              * then this expression cannot throw.
              * Note that pure functions can throw.
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
index 2e5a79d0feb..a572a1ff6e0 100644
--- a/gcc/d/dmd/dcast.d
+++ b/gcc/d/dmd/dcast.d
@@ -1565,9 +1565,9 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 result = e;
                 return;
             }
-            if (e.op == EXP.variable)
+            if (auto ve = e.isVarExp())
             {
-                VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+                VarDeclaration v = ve.var.isVarDeclaration();
                 if (v && v.storage_class & STC.manifest)
                 {
                     result = e.ctfeInterpret();
@@ -1852,8 +1852,8 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         override void visit(StructLiteralExp e)
         {
             visit(cast(Expression)e);
-            if (result.op == EXP.structLiteral)
-                (cast(StructLiteralExp)result).stype = t; // commit type
+            if (auto sle = result.isStructLiteralExp())
+                sle.stype = t; // commit type
         }
 
         override void visit(StringExp e)
@@ -1866,7 +1866,8 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
 
             //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed);
 
-            if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid)
+            if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid &&
+                (!sc || !(sc.flags & SCOPE.Cfile)))
             {
                 e.error("cannot convert string literal to `void*`");
                 result = ErrorExp.get();
@@ -1883,7 +1884,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
 
             if (!e.committed)
             {
-                se = cast(StringExp)e.copy();
+                se = e.copy().isStringExp();
                 se.committed = 1;
                 copied = 1;
             }
@@ -1908,7 +1909,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 if (!copied)
                 {
-                    se = cast(StringExp)e.copy();
+                    se = e.copy().isStringExp();
                     copied = 1;
                 }
                 se.type = t;
@@ -1924,7 +1925,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
              */
             if (e.committed && tb.ty == Tsarray && typeb.ty == Tarray)
             {
-                se = cast(StringExp)e.copy();
+                se = e.copy().isStringExp();
                 d_uns64 szx = tb.nextOf().size();
                 assert(szx <= 255);
                 se.sz = cast(ubyte)szx;
@@ -1952,7 +1953,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 if (!copied)
                 {
-                    se = cast(StringExp)e.copy();
+                    se = e.copy().isStringExp();
                     copied = 1;
                 }
                 return lcast();
@@ -1961,7 +1962,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 if (!copied)
                 {
-                    se = cast(StringExp)e.copy();
+                    se = e.copy().isStringExp();
                     copied = 1;
                 }
                 return lcast();
@@ -1977,7 +1978,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 if (!copied)
                 {
-                    se = cast(StringExp)e.copy();
+                    se = e.copy().isStringExp();
                     copied = 1;
                 }
                 if (tb.ty == Tsarray)
@@ -2088,7 +2089,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 L1:
                     if (!copied)
                     {
-                        se = cast(StringExp)e.copy();
+                        se = e.copy().isStringExp();
                         copied = 1;
                     }
 
@@ -2154,10 +2155,10 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             }
 
             // Look for pointers to functions where the functions are overloaded.
-            if (e.e1.op == EXP.overloadSet &&
+            if (e.e1.isOverExp() &&
                 (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
             {
-                OverExp eo = cast(OverExp)e.e1;
+                OverExp eo = e.e1.isOverExp();
                 FuncDeclaration f = null;
                 for (size_t i = 0; i < eo.vars.a.dim; i++)
                 {
@@ -2188,11 +2189,11 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 }
             }
 
-            if (e.e1.op == EXP.variable &&
+            if (e.e1.isVarExp() &&
                 typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
                 tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
             {
-                auto ve = cast(VarExp)e.e1;
+                auto ve = e.e1.isVarExp();
                 auto f = ve.var.isFuncDeclaration();
                 if (f)
                 {
@@ -2303,7 +2304,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                             goto L1;
                     }
 
-                    ae = cast(ArrayLiteralExp)e.copy();
+                    ae = e.copy().isArrayLiteralExp();
                     if (e.basis)
                         ae.basis = e.basis.castTo(sc, tb.nextOf());
                     ae.elements = e.elements.copy();
@@ -2325,7 +2326,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 Type tp = typeb.nextOf().pointerTo();
                 if (!tp.equals(ae.type))
                 {
-                    ae = cast(ArrayLiteralExp)e.copy();
+                    ae = e.copy().isArrayLiteralExp();
                     ae.type = tp;
                 }
             }
@@ -2382,7 +2383,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             if (tb.ty == Taarray && typeb.ty == Taarray &&
                 tb.nextOf().toBasetype().ty != Tvoid)
             {
-                AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e.copy();
+                AssocArrayLiteralExp ae = e.copy().isAssocArrayLiteralExp();
                 ae.keys = e.keys.copy();
                 ae.values = e.values.copy();
                 assert(e.keys.dim == e.values.dim);
@@ -2422,7 +2423,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             {
                 result = e.copy();
                 result.type = t;
-                (cast(SymOffExp)result).hasOverloads = false;
+                result.isSymOffExp().hasOverloads = false;
                 return;
             }
 
@@ -2641,7 +2642,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 {
                     Expression e1x = e.e1.implicitCastTo(sc, t1b);
                     assert(e1x.op != EXP.error);
-                    e = cast(SliceExp)e.copy();
+                    e = e.copy().isSliceExp();
                     e.e1 = e1x;
                     e.type = t;
                     result = e;
@@ -2751,10 +2752,10 @@  Expression inferType(Expression e, Type t, int flag = 0)
 
     if (t) switch (e.op)
     {
-        case EXP.arrayLiteral:      return visitAle(cast(ArrayLiteralExp) e);
-        case EXP.assocArrayLiteral: return visitAar(cast(AssocArrayLiteralExp) e);
-        case EXP.function_:         return visitFun(cast(FuncExp) e);
-        case EXP.question:          return visitTer(cast(CondExp) e);
+        case EXP.arrayLiteral:      return visitAle(e.isArrayLiteralExp());
+        case EXP.assocArrayLiteral: return visitAar(e.isAssocArrayLiteralExp());
+        case EXP.function_:         return visitFun(e.isFuncExp());
+        case EXP.question:          return visitTer(e.isCondExp());
         default:
     }
     return e;
@@ -2830,9 +2831,9 @@  Expression scaleFactor(BinExp be, Scope* sc)
  */
 private bool isVoidArrayLiteral(Expression e, Type other)
 {
-    while (e.op == EXP.arrayLiteral && e.type.ty == Tarray && ((cast(ArrayLiteralExp)e).elements.dim == 1))
+    while (e.op == EXP.arrayLiteral && e.type.ty == Tarray && (e.isArrayLiteralExp().elements.dim == 1))
     {
-        auto ale = cast(ArrayLiteralExp)e;
+        auto ale = e.isArrayLiteralExp();
         e = ale[0];
         if (other.ty == Tsarray || other.ty == Tarray)
             other = other.nextOf();
@@ -2842,7 +2843,7 @@  private bool isVoidArrayLiteral(Expression e, Type other)
     if (other.ty != Tsarray && other.ty != Tarray)
         return false;
     Type t = e.type;
-    return (e.op == EXP.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && (cast(ArrayLiteralExp)e).elements.dim == 0);
+    return (e.op == EXP.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && e.isArrayLiteralExp().elements.dim == 0);
 }
 
 /**
@@ -3463,20 +3464,20 @@  LmodCompare:
         Expression rhs = e2;
 
         // T[x .. y] op ?
-        if (e1.isSliceExp())
-            lhs = new IndexExp(Loc.initial, (cast(UnaExp)e1).e1, IntegerExp.literal!0);
+        if (auto se1 = e1.isSliceExp())
+            lhs = new IndexExp(Loc.initial, se1.e1, IntegerExp.literal!0);
 
         // [t1, t2, .. t3] op ?
-        if (e1.isArrayLiteralExp())
-            lhs = (cast(ArrayLiteralExp)e1).opIndex(0);
+        if (auto ale1 = e1.isArrayLiteralExp())
+            lhs = ale1.opIndex(0);
 
         // ? op U[z .. t]
-        if (e2.isSliceExp())
-            rhs = new IndexExp(Loc.initial, (cast(UnaExp)e2).e1, IntegerExp.literal!0);
+        if (auto se2 = e2.isSliceExp())
+            rhs = new IndexExp(Loc.initial, se2.e1, IntegerExp.literal!0);
 
         // ? op [u1, u2, .. u3]
-        if (e2.isArrayLiteralExp())
-            rhs = (cast(ArrayLiteralExp)e2).opIndex(0);
+        if (auto ale2 = e2.isArrayLiteralExp())
+            rhs = ale2.opIndex(0);
 
         // create a new binary expression with the new lhs and rhs (at this stage, at least
         // one of lhs/rhs has been replaced with the 0'th element of the array it was before)
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
index 8f20c38f6f1..fc5b9a8843b 100644
--- a/gcc/d/dmd/dinterpret.d
+++ b/gcc/d/dmd/dinterpret.d
@@ -4837,6 +4837,47 @@  public:
                 result = interpret(ce, istate);
                 return;
             }
+            else if (fd.ident == Id._d_delstruct)
+            {
+                // Only interpret the dtor and the argument.
+                assert(e.arguments.dim == 1);
+
+                Type tb = (*e.arguments)[0].type.toBasetype();
+                auto ts = tb.nextOf().baseElemOf().isTypeStruct();
+                if (ts)
+                {
+                    result = interpretRegion((*e.arguments)[0], istate);
+                    if (exceptionOrCant(result))
+                        return;
+
+                    if (result.op == EXP.null_)
+                    {
+                        result = CTFEExp.voidexp;
+                        return;
+                    }
+
+                    if (result.op != EXP.address ||
+                        (cast(AddrExp)result).e1.op != EXP.structLiteral)
+                    {
+                        e.error("`delete` on invalid struct pointer `%s`", result.toChars());
+                        result = CTFEExp.cantexp;
+                        return;
+                    }
+
+                    auto sd = ts.sym;
+                    if (sd.dtor)
+                    {
+                        auto sle = cast(StructLiteralExp)(cast(AddrExp)result).e1;
+                        result = interpretFunction(pue, sd.dtor, istate, null, sle);
+                        if (exceptionOrCant(result))
+                            return;
+
+                        result = CTFEExp.voidexp;
+                    }
+                }
+
+                return;
+            }
         }
         else if (auto soe = ecall.isSymOffExp())
         {
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index 0f75157f874..e34a94ad44b 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -2453,10 +2453,10 @@  Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
         if (i1 && i2)
             return collision();         // can't both have initializers
 
-        if (i1)
+        if (i1)                         // vd is the definition
         {
-            vd2._init = vd._init;
-            vd._init = null;
+            sds.symtab.update(vd);      // replace vd2 with the definition
+            return vd;
         }
 
         /* BUG: the types should match, which needs semantic() to be run on it
@@ -2497,14 +2497,10 @@  Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
         if (fd.fbody && fd2.fbody)
             return collision();         // can't both have bodies
 
-        if (fd.fbody)
+        if (fd.fbody)                   // fd is the definition
         {
-            fd2.fbody = fd.fbody;       // transfer body to existing declaration
-            fd.fbody = null;
-
-            auto tf = fd.type.toTypeFunction();
-            auto tf2 = fd2.type.toTypeFunction();
-            tf2.parameterList = tf.parameterList;   // transfer parameter list.
+            sds.symtab.update(fd);      // replace fd2 in symbol table with fd
+            return fd;
         }
 
         /* BUG: just like with VarDeclaration, the types should match, which needs semantic() to be run on it.
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 3a9abd2d87e..118b861d059 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -3981,15 +3981,15 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
             return;
 
         TypeFunction tf = ctd.type.toTypeFunction();
+        immutable dim = tf.parameterList.length;
+        auto sd = ad.isStructDeclaration();
 
         /* See if it's the default constructor
          * But, template constructor should not become a default constructor.
          */
         if (ad && (!ctd.parent.isTemplateInstance() || ctd.parent.isTemplateMixin()))
         {
-            immutable dim = tf.parameterList.length;
-
-            if (auto sd = ad.isStructDeclaration())
+            if (sd)
             {
                 if (dim == 0 && tf.parameterList.varargs == VarArg.none) // empty default ctor w/o any varargs
                 {
@@ -4034,6 +4034,24 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 ad.defaultCtor = ctd;
             }
         }
+        // https://issues.dlang.org/show_bug.cgi?id=22593
+        else if (auto ti = ctd.parent.isTemplateInstance())
+        {
+            if (sd && sd.hasCopyCtor && (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+            {
+                auto param = tf.parameterList[0];
+
+                // if the template instance introduces an rvalue constructor
+                // between the members of a struct declaration, we should check if a
+                // copy constructor exists and issue an error in that case.
+                if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
+                {
+                    .error(ctd.loc, "Cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars);
+                    .errorSupplemental(ti.loc, "Template instance `%s` creates a rvalue constructor for `struct %s`",
+                            ti.toChars(), sd.toChars());
+                }
+            }
+        }
     }
 
     override void visit(PostBlitDeclaration pbd)
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 48e47cef250..2f1cd4767c5 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -7316,6 +7316,8 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 deprecation(exp.loc, "The `delete` keyword has been deprecated.  Use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead.");
         }
 
+        Expression e = exp;
+
         if (Expression ex = unaSemantic(exp, sc))
         {
             result = ex;
@@ -7352,7 +7354,27 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (tb.ty == Tstruct)
             {
                 ad = (cast(TypeStruct)tb).sym;
-                semanticTypeInfo(sc, tb);
+
+                Identifier hook = global.params.tracegc ? Id._d_delstructTrace : Id._d_delstruct;
+                if (!verifyHookExist(exp.loc, *sc, Id._d_delstructImpl, "deleting struct with dtor", Id.object))
+                    return setError();
+
+                // Lower to .object._d_delstruct{,Trace}(exp.e1)
+                Expression id = new IdentifierExp(exp.loc, Id.empty);
+                id = new DotIdExp(exp.loc, id, Id.object);
+
+                auto tiargs = new Objects();
+                tiargs.push(exp.e1.type);
+                id = new DotTemplateInstanceExp(exp.loc, id, Id._d_delstructImpl, tiargs);
+                id = new DotIdExp(exp.loc, id, hook);
+
+                e = new CallExp(exp.loc, id, exp.e1);
+                /* Gag errors generated by calls to `_d_delstruct`, because they display
+                 * internal compiler information, which is unnecessary to the user.
+                 */
+                uint errors = global.startGagging();
+                e = e.expressionSemantic(sc);
+                global.endGagging(errors);
             }
             break;
 
@@ -7397,7 +7419,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (err)
             return setError();
 
-        result = exp;
+        result = e;
     }
 
     override void visit(CastExp exp)
diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d
index 83c89c05fda..d33b676a032 100644
--- a/gcc/d/dmd/id.d
+++ b/gcc/d/dmd/id.d
@@ -311,6 +311,9 @@  immutable Msgtable[] msgtable =
     { "__ArrayPostblit" },
     { "__ArrayDtor" },
     { "_d_delThrowable" },
+    { "_d_delstructImpl" },
+    { "_d_delstruct" },
+    { "_d_delstructTrace" },
     { "_d_assert_fail" },
     { "dup" },
     { "_aaApply" },
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index 51ee27d00fe..826b0adebed 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -284,7 +284,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         case Tarray:
             break;
         case Tvector:
-            t = (cast(TypeVector)t).basetype;
+            t = t.isTypeVector().basetype;
             break;
         case Taarray:
         case Tstruct: // consider implicit constructor call
@@ -346,7 +346,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             // found a tuple, expand it
             if (ei && ei.exp.op == EXP.tuple)
             {
-                TupleExp te = cast(TupleExp)ei.exp;
+                TupleExp te = ei.exp.isTupleExp();
                 i.index.remove(j);
                 i.value.remove(j);
                 for (size_t k = 0; k < te.exps.dim; ++k)
@@ -462,7 +462,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         {
             return i; // Failed, suppress duplicate error messages
         }
-        if (i.exp.type.ty == Ttuple && (cast(TypeTuple)i.exp.type).arguments.dim == 0)
+        if (i.exp.type.isTypeTuple() && i.exp.type.isTypeTuple().arguments.dim == 0)
         {
             Type et = i.exp.type;
             i.exp = new TupleExp(i.exp.loc, new Expressions());
@@ -492,12 +492,12 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
          */
         if (i.exp.op == EXP.string_ && tb.ty == Tsarray)
         {
-            StringExp se = cast(StringExp)i.exp;
+            StringExp se = i.exp.isStringExp();
             Type typeb = se.type.toBasetype();
             TY tynto = tb.nextOf().ty;
             if (!se.committed &&
                 (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar &&
-                se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger())
+                se.numberOfCodeUnits(tynto) < tb.isTypeSArray().dim.toInteger())
             {
                 i.exp = se.castTo(sc, t);
                 goto L1;
@@ -520,7 +520,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         // Look for implicit constructor call
         if (tb.ty == Tstruct && !(ti.ty == Tstruct && tb.toDsymbol(sc) == ti.toDsymbol(sc)) && !i.exp.implicitConvTo(t))
         {
-            StructDeclaration sd = (cast(TypeStruct)tb).sym;
+            StructDeclaration sd = tb.isTypeStruct().sym;
             if (sd.ctor)
             {
                 // Rewrite as S().ctor(exp)
@@ -573,18 +573,16 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             // better diagnostic message, as same as AssignExp::semantic.
             if (tb.ty == Tsarray && i.exp.implicitConvTo(tb.nextOf().arrayOf()) > MATCH.nomatch)
             {
-                uinteger_t dim1 = (cast(TypeSArray)tb).dim.toInteger();
+                uinteger_t dim1 = tb.isTypeSArray().dim.toInteger();
                 uinteger_t dim2 = dim1;
-                if (i.exp.op == EXP.arrayLiteral)
+                if (auto ale = i.exp.isArrayLiteralExp())
                 {
-                    ArrayLiteralExp ale = cast(ArrayLiteralExp)i.exp;
                     dim2 = ale.elements ? ale.elements.dim : 0;
                 }
-                else if (i.exp.op == EXP.slice)
+                else if (auto se = i.exp.isSliceExp())
                 {
-                    Type tx = toStaticArrayType(cast(SliceExp)i.exp);
-                    if (tx)
-                        dim2 = (cast(TypeSArray)tx).dim.toInteger();
+                    if (Type tx = toStaticArrayType(se))
+                        dim2 = tx.isTypeSArray().dim.toInteger();
                 }
                 if (dim1 != dim2)
                 {
@@ -746,10 +744,11 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
          * Params:
          *    t = element type
          *    dim = max number of elements
+         *    simple = true if array of simple elements
          * Returns:
          *    # of elements in array
          */
-        size_t array(Type t, size_t dim)
+        size_t array(Type t, size_t dim, ref bool simple)
         {
             //printf(" type %s i %d dim %d dil.length = %d\n", t.toChars(), cast(int)i, cast(int)dim, cast(int)dil.length);
             auto tn = t.nextOf().toBasetype();
@@ -791,14 +790,30 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                 if (tnsa && di.initializer.isExpInitializer())
                 {
                     // no braces enclosing array initializer, so recurse
-                    array(tnsa, nelems);
+                    array(tnsa, nelems, simple);
                 }
                 else if (auto tns = tn.isTypeStruct())
                 {
-                    if (di.initializer.isExpInitializer())
+                    if (auto ei = di.initializer.isExpInitializer())
                     {
                         // no braces enclosing struct initializer
-                        dil[n].initializer = structs(tns);
+
+                        /* Disambiguate between an exp representing the entire
+                         * struct, and an exp representing the first field of the struct
+                        */
+                        if (needInterpret)
+                            sc = sc.startCTFE();
+                        ei.exp = ei.exp.expressionSemantic(sc);
+                        ei.exp = resolveProperties(sc, ei.exp);
+                        if (needInterpret)
+                            sc = sc.endCTFE();
+                        if (ei.exp.implicitConvTo(tn))
+                            di.initializer = elem(di.initializer); // the whole struct
+                        else
+                        {
+                            simple = false;
+                            dil[n].initializer = structs(tns); // the first field
+                        }
                     }
                     else
                         dil[n].initializer = elem(di.initializer);
@@ -816,7 +831,8 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         }
 
         size_t dim = tsa.isIncomplete() ? dil.length : cast(size_t)tsa.dim.toInteger();
-        auto newdim = array(t, dim);
+        bool simple = true;
+        auto newdim = array(t, dim, simple);
 
         if (errors)
             return err();
@@ -849,7 +865,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         /* If an array of simple elements, replace with an ArrayInitializer
          */
         auto tnb = tn.toBasetype();
-        if (!(tnb.isTypeSArray() || tnb.isTypeStruct()))
+        if (!tnb.isTypeSArray() && (!tnb.isTypeStruct() || simple))
         {
             auto ai = new ArrayInitializer(ci.loc);
             ai.dim = cast(uint) dil.length;
@@ -884,12 +900,12 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
 
     final switch (init.kind)
     {
-        case InitKind.void_:   return visitVoid  (cast(  VoidInitializer)init);
-        case InitKind.error:   return visitError (cast( ErrorInitializer)init);
-        case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
-        case InitKind.array:   return visitArray (cast( ArrayInitializer)init);
-        case InitKind.exp:     return visitExp   (cast(   ExpInitializer)init);
-        case InitKind.C_:      return visitC     (cast(     CInitializer)init);
+        case InitKind.void_:   return visitVoid  (init.isVoidInitializer());
+        case InitKind.error:   return visitError (init.isErrorInitializer());
+        case InitKind.struct_: return visitStruct(init.isStructInitializer());
+        case InitKind.array:   return visitArray (init.isArrayInitializer());
+        case InitKind.exp:     return visitExp   (init.isExpInitializer());
+        case InitKind.C_:      return visitC     (init.isCInitializer());
     }
 }
 
@@ -943,8 +959,7 @@  Initializer inferType(Initializer init, Scope* sc)
                 {
                     return iz;
                 }
-                assert(iz.isExpInitializer());
-                (*values)[i] = (cast(ExpInitializer)iz).exp;
+                (*values)[i] = iz.isExpInitializer().exp;
                 assert(!(*values)[i].isErrorExp());
             }
             Expression e = new AssocArrayLiteralExp(init.loc, keys, values);
@@ -966,8 +981,7 @@  Initializer inferType(Initializer init, Scope* sc)
                 {
                     return iz;
                 }
-                assert(iz.isExpInitializer());
-                (*elements)[i] = (cast(ExpInitializer)iz).exp;
+                (*elements)[i] = iz.isExpInitializer().exp;
                 assert(!(*elements)[i].isErrorExp());
             }
             Expression e = new ArrayLiteralExp(init.loc, null, elements);
@@ -996,9 +1010,8 @@  Initializer inferType(Initializer init, Scope* sc)
             init.exp = resolveAliasThis(sc, init.exp);
 
         init.exp = resolveProperties(sc, init.exp);
-        if (init.exp.op == EXP.scope_)
+        if (auto se = init.exp.isScopeExp())
         {
-            ScopeExp se = cast(ScopeExp)init.exp;
             TemplateInstance ti = se.sds.isTemplateInstance();
             if (ti && ti.semanticRun == PASS.semantic && !ti.aliasdecl)
                 se.error("cannot infer type from %s `%s`, possible circular dependency", se.sds.kind(), se.toChars());
@@ -1021,16 +1034,15 @@  Initializer inferType(Initializer init, Scope* sc)
                 return new ErrorInitializer();
             }
         }
-        if (init.exp.op == EXP.address)
+        if (auto ae = init.exp.isAddrExp())
         {
-            AddrExp ae = cast(AddrExp)init.exp;
             if (ae.e1.op == EXP.overloadSet)
             {
                 init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars());
                 return new ErrorInitializer();
             }
         }
-        if (init.exp.op == EXP.error)
+        if (init.exp.isErrorExp())
         {
             return new ErrorInitializer();
         }
@@ -1050,12 +1062,12 @@  Initializer inferType(Initializer init, Scope* sc)
 
     final switch (init.kind)
     {
-        case InitKind.void_:   return visitVoid  (cast(  VoidInitializer)init);
-        case InitKind.error:   return visitError (cast( ErrorInitializer)init);
-        case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
-        case InitKind.array:   return visitArray (cast( ArrayInitializer)init);
-        case InitKind.exp:     return visitExp   (cast(   ExpInitializer)init);
-        case InitKind.C_:      return visitC     (cast(     CInitializer)init);
+        case InitKind.void_:   return visitVoid  (init.isVoidInitializer());
+        case InitKind.error:   return visitError (init.isErrorInitializer());
+        case InitKind.struct_: return visitStruct(init.isStructInitializer());
+        case InitKind.array:   return visitArray (init.isArrayInitializer());
+        case InitKind.exp:     return visitExp   (init.isExpInitializer());
+        case InitKind.C_:      return visitC     (init.isCInitializer());
     }
 }
 
@@ -1260,12 +1272,12 @@  extern (C++) Expression initializerToExpression(Initializer init, Type itype = n
 
     final switch (init.kind)
     {
-        case InitKind.void_:   return visitVoid  (cast(  VoidInitializer)init);
-        case InitKind.error:   return visitError (cast( ErrorInitializer)init);
-        case InitKind.struct_: return visitStruct(cast(StructInitializer)init);
-        case InitKind.array:   return visitArray (cast( ArrayInitializer)init);
-        case InitKind.exp:     return visitExp   (cast(   ExpInitializer)init);
-        case InitKind.C_:      return visitC     (cast(     CInitializer)init);
+        case InitKind.void_:   return visitVoid  (init.isVoidInitializer());
+        case InitKind.error:   return visitError (init.isErrorInitializer());
+        case InitKind.struct_: return visitStruct(init.isStructInitializer());
+        case InitKind.array:   return visitArray (init.isArrayInitializer());
+        case InitKind.exp:     return visitExp   (init.isExpInitializer());
+        case InitKind.C_:      return visitC     (init.isCInitializer());
     }
 }
 
@@ -1308,7 +1320,7 @@  private bool hasNonConstPointers(Expression e)
     {
         if (ae.type.nextOf().hasPointers() && checkArray(ae.values))
             return true;
-        if ((cast(TypeAArray)ae.type).index.hasPointers())
+        if (ae.type.isTypeAArray().index.hasPointers())
             return checkArray(ae.keys);
         return false;
     }
diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d
index 31a25a71417..8bdddb8f9ea 100644
--- a/gcc/d/dmd/nogc.d
+++ b/gcc/d/dmd/nogc.d
@@ -83,6 +83,20 @@  public:
             }
             f.printGCUsage(e.loc, "setting `length` may cause a GC allocation");
         }
+        else if (fd.ident == Id._d_delstruct)
+        {
+            // In expressionsem.d, `delete s` was lowererd to `_d_delstruct(s)`.
+            // The following code handles the call like the original expression,
+            // so the error is menaningful to the user.
+            if (f.setGC())
+            {
+                e.error("cannot use `delete` in `@nogc` %s `%s`", f.kind(),
+                    f.toPrettyChars());
+                err = true;
+                return;
+            }
+            f.printGCUsage(e.loc, "`delete` requires the GC");
+        }
     }
 
     override void visit(ArrayLiteralExp e)
diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d
index da328fdcaf9..49b87d6bf47 100644
--- a/gcc/d/dmd/semantic3.d
+++ b/gcc/d/dmd/semantic3.d
@@ -419,8 +419,7 @@  private extern(C++) final class Semantic3Visitor : Visitor
                     sc2.insert(_arguments);
                     _arguments.parent = funcdecl;
                 }
-                if ((f.linkage == LINK.d || f.parameterList.length) &&
-                    !(sc.flags & SCOPE.Cfile))  // don't want to require importing stdarg for C files
+                if (f.linkage == LINK.d || f.parameterList.length)
                 {
                     // Declare _argptr
                     Type t = target.va_listType(funcdecl.loc, sc);
diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d
index 2d98d5eeed8..e5b05d38a45 100644
--- a/gcc/d/dmd/tokens.d
+++ b/gcc/d/dmd/tokens.d
@@ -23,7 +23,7 @@  import dmd.common.outbuffer;
 import dmd.root.rmem;
 import dmd.utf;
 
-enum TOK : ushort
+enum TOK : ubyte
 {
     reserved,
 
@@ -84,10 +84,7 @@  enum TOK : ushort
     rightShiftAssign,
     unsignedRightShift,
     unsignedRightShiftAssign,
-    concatenate,
     concatenateAssign, // ~=
-    concatenateElemAssign,
-    concatenateDcharAssign,
     add,
     min,
     addAssign,
@@ -109,15 +106,11 @@  enum TOK : ushort
     tilde,
     plusPlus,
     minusMinus,
-    construct,
-    blit,
     dot,
     comma,
     question,
     andAnd,
     orOr,
-    prePlusPlus,
-    preMinusMinus,
 
     // Numeric literals
     int32Literal,
@@ -144,7 +137,6 @@  enum TOK : ushort
     hexadecimalString,
     this_,
     super_,
-    tuple,
     error,
 
     // Basic types
@@ -244,7 +236,6 @@  enum TOK : ushort
 
     parameters,
     traits,
-    overloadSet,
     pure_,
     nothrow_,
     gshared,
@@ -564,7 +555,6 @@  private immutable TOK[] keywords =
     TOK.gshared,
     TOK.traits,
     TOK.vector,
-    TOK.overloadSet,
     TOK.file,
     TOK.fileFullPath,
     TOK.line,
@@ -769,7 +759,6 @@  extern (C++) struct Token
         TOK.gshared: "__gshared",
         TOK.traits: "__traits",
         TOK.vector: "__vector",
-        TOK.overloadSet: "__overloadset",
         TOK.file: "__FILE__",
         TOK.fileFullPath: "__FILE_FULL_PATH__",
         TOK.line: "__LINE__",
@@ -793,8 +782,6 @@  extern (C++) struct Token
         TOK.xor: "^",
         TOK.xorAssign: "^=",
         TOK.assign: "=",
-        TOK.construct: "=",
-        TOK.blit: "=",
         TOK.lessThan: "<",
         TOK.greaterThan: ">",
         TOK.lessOrEqual: "<=",
@@ -824,8 +811,6 @@  extern (C++) struct Token
         TOK.dollar: "$",
         TOK.plusPlus: "++",
         TOK.minusMinus: "--",
-        TOK.prePlusPlus: "++",
-        TOK.preMinusMinus: "--",
         TOK.type: "type",
         TOK.question: "?",
         TOK.negate: "-",
@@ -842,9 +827,6 @@  extern (C++) struct Token
         TOK.andAssign: "&=",
         TOK.orAssign: "|=",
         TOK.concatenateAssign: "~=",
-        TOK.concatenateElemAssign: "~=",
-        TOK.concatenateDcharAssign: "~=",
-        TOK.concatenate: "~",
         TOK.call: "call",
         TOK.identity: "is",
         TOK.notIdentity: "!is",
@@ -860,7 +842,6 @@  extern (C++) struct Token
         // For debugging
         TOK.error: "error",
         TOK.string_: "string",
-        TOK.tuple: "tuple",
         TOK.declaration: "declaration",
         TOK.onScopeExit: "scope(exit)",
         TOK.onScopeSuccess: "scope(success)",
@@ -1113,11 +1094,6 @@  nothrow:
         return toString(value).ptr;
     }
 
-    static const(char)* toChars(ushort value)
-    {
-        return toString(cast(TOK)value).ptr;
-    }
-
     extern (D) static string toString(TOK value) pure nothrow @nogc @safe
     {
         return tochars[value];
diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h
index 2e1d1f44492..e095aa6b91a 100644
--- a/gcc/d/dmd/tokens.h
+++ b/gcc/d/dmd/tokens.h
@@ -32,7 +32,7 @@  class Identifier;
         ?       &&      ||
  */
 
-enum class TOK : unsigned short
+enum class TOK : unsigned char
 {
     reserved,
 
@@ -93,10 +93,7 @@  enum class TOK : unsigned short
     rightShiftAssign,
     unsignedRightShift,
     unsignedRightShiftAssign,
-    concatenate,
     concatenateAssign, // ~=
-    concatenateElemAssign,
-    concatenateDcharAssign,
     add,
     min,
     addAssign,
@@ -118,15 +115,11 @@  enum class TOK : unsigned short
     tilde,
     plusPlus,
     minusMinus,
-    construct,
-    blit,
     dot,
     comma,
     question,
     andAnd,
     orOr,
-    prePlusPlus,
-    preMinusMinus,
 
     // Numeric literals
     int32Literal,   // 104,
@@ -153,7 +146,6 @@  enum class TOK : unsigned short
     hexadecimalString,
     this_,
     super_,
-    tuple,
     error,
 
     // Basic types
@@ -253,7 +245,6 @@  enum class TOK : unsigned short
 
     parameters,     // 210
     traits,
-    overloadSet,
     pure_,
     nothrow_,
     gshared,
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index 2a864161ff6..b8c43174f4a 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -271,7 +271,7 @@  private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb
                 sm = null;
             }
             // Same check as in Expression.semanticY(DotIdExp)
-            else if (sm.isPackage() && checkAccess(sc, cast(Package)sm))
+            else if (sm.isPackage() && checkAccess(sc, sm.isPackage()))
             {
                 // @@@DEPRECATED_2.096@@@
                 // Should be an error in 2.106. Just remove the deprecation call
@@ -509,7 +509,7 @@  private Type stripDefaultArgs(Type t)
         Parameters* params = stripParams(tf.parameterList.parameters);
         if (tret == tf.next && params == tf.parameterList.parameters)
             return t;
-        TypeFunction tr = cast(TypeFunction)tf.copy();
+        TypeFunction tr = tf.copy().isTypeFunction();
         tr.parameterList.parameters = params;
         tr.next = tret;
         //printf("strip %s\n   <- %s\n", tr.toChars(), t.toChars());
@@ -520,7 +520,7 @@  private Type stripDefaultArgs(Type t)
         Parameters* args = stripParams(tt.arguments);
         if (args == tt.arguments)
             return t;
-        TypeTuple tr = cast(TypeTuple)t.copy();
+        TypeTuple tr = t.copy().isTypeTuple();
         tr.arguments = args;
         return tr;
     }
@@ -588,11 +588,11 @@  Expression typeToExpression(Type t)
         return null;
     switch (t.ty)
     {
-        case Tsarray:   return visitSArray(cast(TypeSArray) t);
-        case Taarray:   return visitAArray(cast(TypeAArray) t);
-        case Tident:    return visitIdentifier(cast(TypeIdentifier) t);
-        case Tinstance: return visitInstance(cast(TypeInstance) t);
-        case Tmixin:    return visitMixin(cast(TypeMixin) t);
+        case Tsarray:   return visitSArray(t.isTypeSArray());
+        case Taarray:   return visitAArray(t.isTypeAArray());
+        case Tident:    return visitIdentifier(t.isTypeIdentifier());
+        case Tinstance: return visitInstance(t.isTypeInstance());
+        case Tmixin:    return visitMixin(t.isTypeMixin());
         default:        return null;
     }
 }
@@ -684,7 +684,7 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             .error(loc, "T in __vector(T) must be a static array, not `%s`", mtype.basetype.toChars());
             return error();
         }
-        TypeSArray t = cast(TypeSArray)mtype.basetype;
+        TypeSArray t = mtype.basetype.isTypeSArray();
         const sz = cast(int)t.size(loc);
         final switch (target.isVectorTypeSupported(sz, t.nextOf()))
         {
@@ -790,8 +790,8 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                 return overflowError();
 
             Type tbx = tbn.baseElemOf();
-            if (tbx.ty == Tstruct && !(cast(TypeStruct)tbx).sym.members ||
-                tbx.ty == Tenum && !(cast(TypeEnum)tbx).sym.members)
+            if (tbx.ty == Tstruct && !tbx.isTypeStruct().sym.members ||
+                tbx.ty == Tenum && !tbx.isTypeEnum().sym.members)
             {
                 /* To avoid meaningless error message, skip the total size limit check
                  * when the bottom of element type is opaque.
@@ -802,7 +802,7 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                      tbn.ty == Tarray ||
                      tbn.ty == Tsarray ||
                      tbn.ty == Taarray ||
-                     (tbn.ty == Tstruct && ((cast(TypeStruct)tbn).sym.sizeok == Sizeok.done)) ||
+                     (tbn.ty == Tstruct && tbn.isTypeStruct().sym.sizeok == Sizeok.done) ||
                      tbn.ty == Tclass)
             {
                 /* Only do this for types that don't need to have semantic()
@@ -819,7 +819,7 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             {
                 // Index the tuple to get the type
                 assert(mtype.dim);
-                TypeTuple tt = cast(TypeTuple)tbn;
+                TypeTuple tt = tbn.isTypeTuple();
                 uinteger_t d = mtype.dim.toUInteger();
                 if (d >= tt.arguments.dim)
                 {
@@ -1026,9 +1026,9 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                  */
             }
         }
-        else if (tbase.ty == Tclass && !(cast(TypeClass)tbase).sym.isInterfaceDeclaration())
+        else if (tbase.ty == Tclass && !tbase.isTypeClass().sym.isInterfaceDeclaration())
         {
-            ClassDeclaration cd = (cast(TypeClass)tbase).sym;
+            ClassDeclaration cd = tbase.isTypeClass().sym;
             if (cd.semanticRun < PASS.semanticdone)
                 cd.dsymbolSemantic(null);
 
@@ -1275,7 +1275,7 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             }
             if (e.op == EXP.function_) // https://issues.dlang.org/show_bug.cgi?id=4820
             {
-                FuncExp fe = cast(FuncExp)e;
+                FuncExp fe = e.isFuncExp();
                 // Replace function literal with a function symbol,
                 // since default arg expression must be copied when used
                 // and copying the literal itself is wrong.
@@ -1402,8 +1402,8 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                          (t.ty == Tstruct || t.ty == Tsarray || t.ty == Tenum))
                 {
                     Type tb2 = t.baseElemOf();
-                    if (tb2.ty == Tstruct && !(cast(TypeStruct)tb2).sym.members ||
-                        tb2.ty == Tenum && !(cast(TypeEnum)tb2).sym.memtype)
+                    if (tb2.ty == Tstruct && !tb2.isTypeStruct().sym.members ||
+                        tb2.ty == Tenum   && !tb2.isTypeEnum().sym.memtype)
                     {
                         if (global.params.previewIn && (fparam.storageClass & STC.in_))
                         {
@@ -1467,7 +1467,7 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                     else
                     {
                         Type tv = t.baseElemOf();
-                        if (tv.ty == Tstruct && (cast(TypeStruct)tv).sym.noDefaultCtor)
+                        if (tv.ty == Tstruct && tv.isTypeStruct().sym.noDefaultCtor)
                         {
                             .error(loc, "cannot have `out` parameter of type `%s` because the default construction is disabled", fparam.type.toChars());
                             errors = true;
@@ -1824,26 +1824,26 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             switch (e.op)
             {
             case EXP.dotVariable:
-                mtype.sym = (cast(DotVarExp)e).var;
+                mtype.sym = e.isDotVarExp().var;
                 break;
             case EXP.variable:
-                mtype.sym = (cast(VarExp)e).var;
+                mtype.sym = e.isVarExp().var;
                 break;
             case EXP.function_:
-                auto fe = cast(FuncExp)e;
+                auto fe = e.isFuncExp();
                 mtype.sym = fe.td ? fe.td : fe.fd;
                 break;
             case EXP.dotTemplateDeclaration:
-                mtype.sym = (cast(DotTemplateExp)e).td;
+                mtype.sym = e.isDotTemplateExp().td;
                 break;
             case EXP.dSymbol:
-                mtype.sym = (cast(DsymbolExp)e).s;
+                mtype.sym = e.isDsymbolExp().s;
                 break;
             case EXP.template_:
-                mtype.sym = (cast(TemplateExp)e).td;
+                mtype.sym = e.isTemplateExp().td;
                 break;
             case EXP.scope_:
-                mtype.sym = (cast(ScopeExp)e).sds;
+                mtype.sym = e.isScopeExp().sds;
                 break;
             case EXP.tuple:
                 TupleExp te = e.toTupleExp();
@@ -1854,13 +1854,13 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                     switch (src.op)
                     {
                     case EXP.type:
-                        (*elems)[i] = (cast(TypeExp)src).type;
+                        (*elems)[i] = src.isTypeExp().type;
                         break;
                     case EXP.dotType:
-                        (*elems)[i] = (cast(DotTypeExp)src).sym.isType();
+                        (*elems)[i] = src.isDotTypeExp().sym.isType();
                         break;
                     case EXP.overloadSet:
-                        (*elems)[i] = (cast(OverExp)src).type;
+                        (*elems)[i] = src.isOverExp().type;
                         break;
                     default:
                         if (auto sym = isDsymbol(src))
@@ -1873,13 +1873,13 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
                 mtype.sym = td;
                 break;
             case EXP.dotType:
-                result = (cast(DotTypeExp)e).sym.isType();
+                result = e.isDotTypeExp().sym.isType();
                 break;
             case EXP.type:
-                result = (cast(TypeExp)e).type;
+                result = e.isTypeExp().type;
                 break;
             case EXP.overloadSet:
-                result = (cast(OverExp)e).type;
+                result = e.isOverExp().type;
                 break;
             default:
                 break;
@@ -2211,26 +2211,26 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
     switch (type.ty)
     {
         default:         return visitType(type);
-        case Tvector:    return visitVector(cast(TypeVector)type);
-        case Tsarray:    return visitSArray(cast(TypeSArray)type);
-        case Tarray:     return visitDArray(cast(TypeDArray)type);
-        case Taarray:    return visitAArray(cast(TypeAArray)type);
-        case Tpointer:   return visitPointer(cast(TypePointer)type);
-        case Treference: return visitReference(cast(TypeReference)type);
-        case Tfunction:  return visitFunction(cast(TypeFunction)type);
-        case Tdelegate:  return visitDelegate(cast(TypeDelegate)type);
-        case Tident:     return visitIdentifier(cast(TypeIdentifier)type);
-        case Tinstance:  return visitInstance(cast(TypeInstance)type);
-        case Ttypeof:    return visitTypeof(cast(TypeTypeof)type);
-        case Ttraits:    return visitTraits(cast(TypeTraits)type);
-        case Treturn:    return visitReturn(cast(TypeReturn)type);
-        case Tstruct:    return visitStruct(cast(TypeStruct)type);
-        case Tenum:      return visitEnum(cast(TypeEnum)type);
-        case Tclass:     return visitClass(cast(TypeClass)type);
-        case Ttuple:     return visitTuple (cast(TypeTuple)type);
-        case Tslice:     return visitSlice(cast(TypeSlice)type);
-        case Tmixin:     return visitMixin(cast(TypeMixin)type);
-        case Ttag:       return visitTag(cast(TypeTag)type);
+        case Tvector:    return visitVector(type.isTypeVector());
+        case Tsarray:    return visitSArray(type.isTypeSArray());
+        case Tarray:     return visitDArray(type.isTypeDArray());
+        case Taarray:    return visitAArray(type.isTypeAArray());
+        case Tpointer:   return visitPointer(type.isTypePointer());
+        case Treference: return visitReference(type.isTypeReference());
+        case Tfunction:  return visitFunction(type.isTypeFunction());
+        case Tdelegate:  return visitDelegate(type.isTypeDelegate());
+        case Tident:     return visitIdentifier(type.isTypeIdentifier());
+        case Tinstance:  return visitInstance(type.isTypeInstance());
+        case Ttypeof:    return visitTypeof(type.isTypeTypeof());
+        case Ttraits:    return visitTraits(type.isTypeTraits());
+        case Treturn:    return visitReturn(type.isTypeReturn());
+        case Tstruct:    return visitStruct(type.isTypeStruct());
+        case Tenum:      return visitEnum(type.isTypeEnum());
+        case Tclass:     return visitClass(type.isTypeClass());
+        case Ttuple:     return visitTuple(type.isTypeTuple());
+        case Tslice:     return visitSlice(type.isTypeSlice());
+        case Tmixin:     return visitMixin(type.isTypeMixin());
+        case Ttag:       return visitTag(type.isTypeTag());
     }
 }
 
@@ -2300,7 +2300,7 @@  extern (C++) Type merge(Type type)
 
         case Tsarray:
             // prevents generating the mangle if the array dim is not yet known
-            if (!(cast(TypeSArray) type).dim.isIntegerExp())
+            if (!type.isTypeSArray().dim.isIntegerExp())
                 return type;
             goto default;
 
@@ -2308,7 +2308,7 @@  extern (C++) Type merge(Type type)
             break;
 
         case Taarray:
-            if (!(cast(TypeAArray)type).index.merge().deco)
+            if (!type.isTypeAArray().index.merge().deco)
                 return type;
             goto default;
 
@@ -2761,10 +2761,10 @@  Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
                                 visitBasic(cast(TypeBasic)t) :
                                 visitType(t);
 
-        case Terror:    return visitError (cast(TypeError)t);
-        case Tvector:   return visitVector(cast(TypeVector)t);
-        case Tenum:     return visitEnum  (cast(TypeEnum)t);
-        case Ttuple:    return visitTuple (cast(TypeTuple)t);
+        case Terror:    return visitError (t.isTypeError());
+        case Tvector:   return visitVector(t.isTypeVector());
+        case Tenum:     return visitEnum  (t.isTypeEnum());
+        case Ttuple:    return visitTuple (t.isTypeTuple());
     }
 }
 
@@ -2889,7 +2889,7 @@  void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
                 {
                     Expression e = cast(Expression)o;
                     if (e.op == EXP.dSymbol)
-                        return returnSymbol((cast(DsymbolExp)e).s);
+                        return returnSymbol(e.isDsymbolExp().s);
                     else
                         return returnExp(e);
                 }
@@ -3154,8 +3154,8 @@  void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
              * template functions.
              */
         }
-        if (auto f = mt.exp.op == EXP.variable    ? (cast(   VarExp)mt.exp).var.isFuncDeclaration()
-                   : mt.exp.op == EXP.dotVariable ? (cast(DotVarExp)mt.exp).var.isFuncDeclaration() : null)
+        if (auto f = mt.exp.op == EXP.variable    ? mt.exp.isVarExp().var.isFuncDeclaration()
+                   : mt.exp.op == EXP.dotVariable ? mt.exp.isDotVarExp().var.isFuncDeclaration() : null)
         {
             // f might be a unittest declaration which is incomplete when compiled
             // without -unittest. That causes a segfault in checkForwardRef, see
@@ -3350,17 +3350,17 @@  void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type
 
     switch (mt.ty)
     {
-        default:        visitType      (mt);                     break;
-        case Tsarray:   visitSArray    (cast(TypeSArray)mt);     break;
-        case Tarray:    visitDArray    (cast(TypeDArray)mt);     break;
-        case Taarray:   visitAArray    (cast(TypeAArray)mt);     break;
-        case Tident:    visitIdentifier(cast(TypeIdentifier)mt); break;
-        case Tinstance: visitInstance  (cast(TypeInstance)mt);   break;
-        case Ttypeof:   visitTypeof    (cast(TypeTypeof)mt);     break;
-        case Treturn:   visitReturn    (cast(TypeReturn)mt);     break;
-        case Tslice:    visitSlice     (cast(TypeSlice)mt);      break;
-        case Tmixin:    visitMixin     (cast(TypeMixin)mt);      break;
-        case Ttraits:   visitTraits    (cast(TypeTraits)mt);     break;
+        default:        visitType      (mt);                    break;
+        case Tsarray:   visitSArray    (mt.isTypeSArray());     break;
+        case Tarray:    visitDArray    (mt.isTypeDArray());     break;
+        case Taarray:   visitAArray    (mt.isTypeAArray());     break;
+        case Tident:    visitIdentifier(mt.isTypeIdentifier()); break;
+        case Tinstance: visitInstance  (mt.isTypeInstance());   break;
+        case Ttypeof:   visitTypeof    (mt.isTypeTypeof());     break;
+        case Treturn:   visitReturn    (mt.isTypeReturn());     break;
+        case Tslice:    visitSlice     (mt.isTypeSlice());      break;
+        case Tmixin:    visitMixin     (mt.isTypeMixin());      break;
+        case Ttraits:   visitTraits    (mt.isTypeTraits());     break;
     }
 }
 
@@ -4616,16 +4616,16 @@  Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
 
     switch (mt.ty)
     {
-        case Tvector:    return visitVector   (cast(TypeVector)mt);
-        case Tsarray:    return visitSArray   (cast(TypeSArray)mt);
-        case Tstruct:    return visitStruct   (cast(TypeStruct)mt);
-        case Tenum:      return visitEnum     (cast(TypeEnum)mt);
-        case Terror:     return visitError    (cast(TypeError)mt);
-        case Tarray:     return visitDArray   (cast(TypeDArray)mt);
-        case Taarray:    return visitAArray   (cast(TypeAArray)mt);
-        case Treference: return visitReference(cast(TypeReference)mt);
-        case Tdelegate:  return visitDelegate (cast(TypeDelegate)mt);
-        case Tclass:     return visitClass    (cast(TypeClass)mt);
+        case Tvector:    return visitVector   (mt.isTypeVector());
+        case Tsarray:    return visitSArray   (mt.isTypeSArray());
+        case Tstruct:    return visitStruct   (mt.isTypeStruct());
+        case Tenum:      return visitEnum     (mt.isTypeEnum());
+        case Terror:     return visitError    (mt.isTypeError());
+        case Tarray:     return visitDArray   (mt.isTypeDArray());
+        case Taarray:    return visitAArray   (mt.isTypeAArray());
+        case Treference: return visitReference(mt.isTypeReference());
+        case Tdelegate:  return visitDelegate (mt.isTypeDelegate());
+        case Tclass:     return visitClass    (mt.isTypeClass());
 
         default:         return mt.isTypeBasic()
                                 ? visitBasic(cast(TypeBasic)mt)
@@ -4786,12 +4786,12 @@  extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi
 
     switch (mt.ty)
     {
-        case Tvector:   return visitVector  (cast(TypeVector)mt);
-        case Tsarray:   return visitSArray  (cast(TypeSArray)mt);
-        case Tfunction: return visitFunction(cast(TypeFunction)mt);
-        case Tstruct:   return visitStruct  (cast(TypeStruct)mt);
-        case Tenum:     return visitEnum    (cast(TypeEnum)mt);
-        case Ttuple:    return visitTuple   (cast(TypeTuple)mt);
+        case Tvector:   return visitVector  (mt.isTypeVector());
+        case Tsarray:   return visitSArray  (mt.isTypeSArray());
+        case Tfunction: return visitFunction(mt.isTypeFunction());
+        case Tstruct:   return visitStruct  (mt.isTypeStruct());
+        case Tenum:     return visitEnum    (mt.isTypeEnum());
+        case Ttuple:    return visitTuple   (mt.isTypeTuple());
 
         case Tnull:     return new NullExp(Loc.initial, Type.tnull);
 
@@ -4803,7 +4803,7 @@  extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi
         case Treference:
         case Tdelegate:
         case Tclass:    return new NullExp(loc, mt);
-        case Tnoreturn: return visitNoreturn(cast(TypeNoreturn) mt);
+        case Tnoreturn: return visitNoreturn(mt.isTypeNoreturn());
 
         default:        return mt.isTypeBasic() ?
                                 visitBasic(cast(TypeBasic)mt) :
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
index f1c014dbc16..665d12205b3 100644
--- a/gcc/d/expr.cc
+++ b/gcc/d/expr.cc
@@ -1477,16 +1477,10 @@  public:
 	t1 = build_address (t1);
 	Type *tnext = tb1->isTypePointer ()->next->toBasetype ();
 
+	/* This case should have been rewritten to `_d_delstruct` in the
+	   semantic phase.  */
 	if (TypeStruct *ts = tnext->isTypeStruct ())
-	  {
-	    if (ts->sym->dtor)
-	      {
-		tree ti = build_typeinfo (e->loc, tnext);
-		this->result_ = build_libcall (LIBCALL_DELSTRUCT, Type::tvoid,
-					       2, t1, ti);
-		return;
-	      }
-	  }
+	  gcc_assert (!ts->sym->dtor);
 
 	/* Otherwise, the garbage collector is called to immediately free the
 	   memory allocated for the pointer.  */
diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def
index 3961a1d9bed..ef54a37d9bc 100644
--- a/gcc/d/runtime.def
+++ b/gcc/d/runtime.def
@@ -85,8 +85,6 @@  DEF_D_RUNTIME (NEWITEMIT, "_d_newitemiT", RT(VOIDPTR), P1(CONST_TYPEINFO), 0)
 
 /* Used when calling delete on a pointer.  */
 DEF_D_RUNTIME (DELMEMORY, "_d_delmemory", RT(VOID), P1(POINTER_VOIDPTR), 0)
-DEF_D_RUNTIME (DELSTRUCT, "_d_delstruct", RT(VOID),
-	       P2(POINTER_VOIDPTR, TYPEINFO), 0)
 
 /* Used when calling new on an array.  The `i' variant is for when the
    initializer is nonzero, and the `m' variant is when initializing a
diff --git a/gcc/testsuite/gdc.test/compilable/test22593.d b/gcc/testsuite/gdc.test/compilable/test22593.d
new file mode 100644
index 00000000000..20912941620
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test22593.d
@@ -0,0 +1,13 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=22593
+
+struct Foo(T){
+    this(Rhs, this This)(scope Rhs rhs){
+    }
+
+    this(ref scope typeof(this) rhs){
+    }
+}
+
+struct Bar{
+    Foo!int foo;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice17074.d b/gcc/testsuite/gdc.test/fail_compilation/ice17074.d
index 53e75e40934..84c4d8533e2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice17074.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice17074.d
@@ -1,19 +1,13 @@ 
 /*
-TEST_OUTPUT:
----
-fail_compilation/ice17074.d(9): Error: identifier expected for C++ namespace
-fail_compilation/ice17074.d(9): Error: found `__overloadset` when expecting `)`
-fail_compilation/ice17074.d(9): Error: declaration expected, not `)`
----
 */
 extern(C++, std.__overloadset) void ice_std_keyword();
 
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice17074.d(19): Error: identifier expected for C++ namespace
-fail_compilation/ice17074.d(19): Error: found `*` when expecting `)`
-fail_compilation/ice17074.d(19): Error: declaration expected, not `)`
+fail_compilation/ice17074.d(13): Error: identifier expected for C++ namespace
+fail_compilation/ice17074.d(13): Error: found `*` when expecting `)`
+fail_compilation/ice17074.d(13): Error: declaration expected, not `)`
 ---
 */
 extern(C++, std.*) void ice_std_token();
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22593.d b/gcc/testsuite/gdc.test/fail_compilation/test22593.d
new file mode 100644
index 00000000000..f90287e9e06
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test22593.d
@@ -0,0 +1,23 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=22593
+
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test22593.d(14): Error: Cannot define both an rvalue constructor and a copy constructor for `struct Foo`
+fail_compilation/test22593.d(22):        Template instance `__ctor!(immutable(Foo!int), immutable(Foo!int))` creates a rvalue constructor for `struct Foo`
+fail_compilation/test22593.d(22): Error: template instance `test22593.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` error instantiating
+---
+*/
+
+struct Foo(T)
+{
+    this(Rhs, this This)(scope Rhs rhs){}
+
+    this(ref scope typeof(this) rhs){}
+}
+
+void main()
+{
+    immutable Foo!int a;
+    a.__ctor(a);
+}
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index b3da9063ce3..70f7ff596a8 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@ 
-6364e010bc87f3621028c8ac648133535c126fb3
+fd9a45448244fb9dd4326520ad8526c540895eb0
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/druntime repository.
diff --git a/libphobos/libdruntime/core/builtins.d b/libphobos/libdruntime/core/builtins.d
index f2ca5038c59..1ed80f7d94a 100644
--- a/libphobos/libdruntime/core/builtins.d
+++ b/libphobos/libdruntime/core/builtins.d
@@ -1,11 +1,45 @@ 
 /**********************************************
- * This module implements common builtins for the D frontend.
- *
- * Copyright: Copyright © 2019, The D Language Foundation
- * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Authors:   Walter Bright
- * Source:    $(DRUNTIMESRC core/builtins.d)
- */
+To provide access to features that would be otherwise counterproductive or
+difficult to implement, compilers provide an interface consisting of a set
+of builtins (also called intrinsics) which can be called like normal functions.
+
+This module exposes builtins both common to all D compilers
+(those provided by the frontend) and specific to the host compiler i.e. those
+specific to either LLVM or GCC (`ldc.intrinsics` and `gcc.builtins` are publicly imported, respectively).
+Host-specific intrinsics cannot be reliably listed here, however listings can be found
+at the documentation for the relevant backends, i.e.
+$(LINK2 https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html, GCC) and
+$(LINK2 https://llvm.org/docs/LangRef.html, LLVM). It should be noted that not all
+builtins listed are necessarily supported by the host compiler, please file a bug
+if this is the case for your workload.
+
+Use of this module reduces the amount of conditional compilation needed
+to use a given builtin. For example, to write a target independent function
+that uses prefetching we can write the following:
+---
+float usePrefetch(float[] x)
+{
+    // There is only one import statement required rather than two (versioned) imports
+    import core.builtins;
+    version (GNU)
+        __builtin_prefetch(x.ptr);
+    version (LDC)
+        /+
+            For the curious: 0, 3, 1 mean `x` will only be read-from (0), it will be used
+            very often (3), and it should be fetched to the data-cache (1).
+        +/
+        llvm_prefetch(x.ptr, 0, 3, 1);
+    const doMath = blahBlahBlah;
+    return doMath;
+}
+---
+
+
+Copyright: Copyright © 2021, The D Language Foundation
+License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+Authors:   Walter Bright
+Source:    $(DRUNTIMESRC core/builtins.d)
+*/
 
 module core.builtins;
 
diff --git a/libphobos/libdruntime/core/lifetime.d b/libphobos/libdruntime/core/lifetime.d
index 8fb61a547d5..9a99f2da02a 100644
--- a/libphobos/libdruntime/core/lifetime.d
+++ b/libphobos/libdruntime/core/lifetime.d
@@ -1545,9 +1545,8 @@  template forward(args...)
 {
     import core.internal.traits : AliasSeq;
 
-    static if (args.length)
+    template fwd(alias arg)
     {
-        alias arg = args[0];
         // by ref || lazy || const/immutable
         static if (__traits(isRef,  arg) ||
                    __traits(isOut,  arg) ||
@@ -1556,15 +1555,16 @@  template forward(args...)
             alias fwd = arg;
         // (r)value
         else
-            @property auto fwd(){ return move(arg); }
-
-        static if (args.length == 1)
-            alias forward = fwd;
-        else
-            alias forward = AliasSeq!(fwd, forward!(args[1..$]));
+            @property auto fwd(){ pragma(inline, true); return move(arg); }
     }
+
+    alias Result = AliasSeq!();
+    static foreach (arg; args)
+        Result = AliasSeq!(Result, fwd!arg);
+    static if (Result.length == 1)
+        alias forward = Result[0];
     else
-        alias forward = AliasSeq!();
+        alias forward = Result;
 }
 
 ///
@@ -2316,7 +2316,7 @@  template _d_delstructImpl(T)
 @system pure nothrow unittest
 {
     int dtors = 0;
-    struct S { ~this() { ++dtors; } }
+    struct S { ~this() nothrow { ++dtors; } }
 
     S *s = new S();
     _d_delstructImpl!(typeof(s))._d_delstruct(s);
diff --git a/libphobos/libdruntime/core/sys/linux/sched.d b/libphobos/libdruntime/core/sys/linux/sched.d
index dc815a0fc3c..e828b7447d1 100644
--- a/libphobos/libdruntime/core/sys/linux/sched.d
+++ b/libphobos/libdruntime/core/sys/linux/sched.d
@@ -153,6 +153,9 @@  version (CRuntime_Glibc)
     int sched_getcpu();
 }
 
+/* Reassociate the calling thread with namespace referred to by fd */
+int setns(int fd, int nstype);
+
 enum CLONE_FILES = 0x400;
 enum CLONE_FS = 0x200;
 enum CLONE_NEWCGROUP = 0x2000000;
diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d
index 29b5d58de5b..c989caa64bf 100644
--- a/libphobos/libdruntime/object.d
+++ b/libphobos/libdruntime/object.d
@@ -4667,17 +4667,33 @@  public import core.internal.switch_: __switch_error;
 public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable);
 
 // Compare class and interface objects for ordering.
-private int __cmp(Obj)(Obj lhs, Obj rhs)
-if (is(Obj : Object))
+int __cmp(C1, C2)(C1 lhs, C2 rhs)
+if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) &&
+    (is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D"))))
 {
-    if (lhs is rhs)
+    static if (is(C1 == typeof(null)) && is(C2 == typeof(null)))
+    {
         return 0;
-    // Regard null references as always being "less than"
-    if (!lhs)
+    }
+    else static if (is(C1 == typeof(null)))
+    {
+        // Regard null references as always being "less than"
         return -1;
-    if (!rhs)
+    }
+    else static if (is(C2 == typeof(null)))
+    {
         return 1;
-    return lhs.opCmp(rhs);
+    }
+    else
+    {
+        if (lhs is rhs)
+            return 0;
+        if (lhs is null)
+            return -1;
+        if (rhs is null)
+            return 1;
+        return lhs.opCmp(rhs);
+    }
 }
 
 // objects
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index c9d166b03a7..b517749b542 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@ 
-575b67a9b4f78415f96ca77ad50b2de4c667cc74
+495e835c2da47606142ff24c85de707e3b955a9a
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/format/write.d b/libphobos/src/std/format/write.d
index c7587688fc0..e67d95ccc23 100644
--- a/libphobos/src/std/format/write.d
+++ b/libphobos/src/std/format/write.d
@@ -1287,3 +1287,26 @@  void formatValue(Writer, T, Char)(auto ref Writer w, auto ref T val, scope const
 
     assertThrown!FormatException(formattedWrite(w, "%(%0*d%)", new int[1]));
 }
+
+// https://issues.dlang.org/show_bug.cgi?id=22609
+@safe pure unittest
+{
+    static enum State: ubyte { INACTIVE }
+    static struct S {
+        State state = State.INACTIVE;
+        int generation = 1;
+        alias state this;
+        // DMDBUG: https://issues.dlang.org/show_bug.cgi?id=16657
+        auto opEquals(S other) const { return state == other.state && generation == other.generation; }
+        auto opEquals(State other) const { return state == other; }
+    }
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto writer = appender!string();
+    const spec = singleSpec("%s");
+    S a;
+    writer.formatValue(a, spec);
+    assert(writer.data == "0");
+}
diff --git a/libphobos/src/std/range/interfaces.d b/libphobos/src/std/range/interfaces.d
index 475f35b51da..6d55d4149c7 100644
--- a/libphobos/src/std/range/interfaces.d
+++ b/libphobos/src/std/range/interfaces.d
@@ -201,6 +201,9 @@  interface RandomAccessFinite(E) : BidirectionalRange!(E) {
 
 /**Interface for an infinite random access range of type `E`.*/
 interface RandomAccessInfinite(E) : ForwardRange!E {
+    ///
+    enum bool empty = false;
+
     /**Calls $(REF moveAt, std, range, primitives) on the wrapped range, if
      * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception.
      */
@@ -213,6 +216,12 @@  interface RandomAccessInfinite(E) : ForwardRange!E {
     E opIndex(size_t);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=22608
+@safe unittest
+{
+    static assert(isRandomAccessRange!(RandomAccessInfinite!int));
+}
+
 /**Adds assignable elements to InputRange.*/
 interface InputAssignable(E) : InputRange!E {
     ///
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
index 6dee863521d..cde2b9da055 100644
--- a/libphobos/src/std/typecons.d
+++ b/libphobos/src/std/typecons.d
@@ -6971,7 +6971,7 @@  mixin template Proxy(alias a)
             static if (is(typeof(a.opCmp(b))))
                 return a.opCmp(b);
             else static if (is(typeof(b.opCmp(a))))
-                return -b.opCmp(b);
+                return -b.opCmp(a);
             else
                 return a < b ? -1 : a > b ? +1 : 0;
         }