diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index 264ae03a89e..b3007a96bd0 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -174,11 +174,11 @@ D_FRONTEND_OBJS = \
 	d/root-port.o \
 	d/root-region.o \
 	d/root-rmem.o \
-	d/root-rootobject.o \
 	d/root-speller.o \
 	d/root-string.o \
 	d/root-stringtable.o \
 	d/root-utf.o \
+	d/rootobject.o \
 	d/safe.o \
 	d/sapply.o \
 	d/semantic2.o \
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 794600274a3..bfadeaa0c68 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-4c18eed9674e04c1ca89fbc8bd5c4e483eb5477c
+f4be7f6f7bae75f1613b862940cdd533b5ae99b2
 
 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/README.md b/gcc/d/dmd/README.md
index d0c75a5b14a..f8ac00117eb 100644
--- a/gcc/d/dmd/README.md
+++ b/gcc/d/dmd/README.md
@@ -84,6 +84,7 @@
 | [astcodegen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astcodegen.d)     | Namespace of AST nodes of a AST ready for code generation   |
 | [astenums.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astenums.d)         | Enums common to DMD and AST                                 |
 | [expression.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expression.d)     | Define expression AST nodes                                 |
+| [rootobject.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/rootobject.d)     | Define an abstract root class                           |
 | [statement.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement.d)       | Define statement AST nodes                                  |
 | [staticassert.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticassert.d) | Define a `static assert` AST node                           |
 | [aggregate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aggregate.d)       | Define an aggregate (`struct`, `union` or `class`) AST node |
diff --git a/gcc/d/dmd/arraytypes.d b/gcc/d/dmd/arraytypes.d
index 34ffa6eb8fd..6634a6af1fc 100644
--- a/gcc/d/dmd/arraytypes.d
+++ b/gcc/d/dmd/arraytypes.d
@@ -22,7 +22,7 @@ import dmd.identifier;
 import dmd.init;
 import dmd.mtype;
 import dmd.root.array;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.statement;
 
 alias Strings = Array!(const(char)*);
diff --git a/gcc/d/dmd/ast_node.d b/gcc/d/dmd/ast_node.d
index c0dd186628a..313c2bd3691 100644
--- a/gcc/d/dmd/ast_node.d
+++ b/gcc/d/dmd/ast_node.d
@@ -10,7 +10,7 @@
  */
 module dmd.ast_node;
 
-import dmd.root.rootobject : RootObject;
+import dmd.rootobject : RootObject;
 import dmd.visitor : Visitor;
 
 /// The base class of all AST nodes.
diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d
index 31a32cf258c..5108ecf7ba7 100644
--- a/gcc/d/dmd/blockexit.d
+++ b/gcc/d/dmd/blockexit.d
@@ -151,14 +151,7 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink)
                         }
                     }
 
-                    if (!(result & BE.fallthru) && !s.comeFrom())
-                    {
-                        version (none) // this warning is completely useless due to insane false positive rate in real life template code
-                        if (blockExit(s, func, eSink) != BE.halt && s.hasCode() &&
-                            s.loc != Loc.initial) // don't emit warning for generated code
-                            global.errorSink.warning(s.loc, "statement is not reachable");
-                    }
-                    else
+                    if ((result & BE.fallthru) || s.comeFrom())
                     {
                         result &= ~BE.fallthru;
                         result |= blockExit(s, func, eSink);
@@ -447,17 +440,6 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink)
                     blockExit(s.finalbody, func, eSink);
             }
 
-            version (none)
-            {
-                // https://issues.dlang.org/show_bug.cgi?id=13201
-                // Mask to prevent spurious warnings for
-                // destructor call, exit of synchronized statement, etc.
-                if (result == BE.halt && finalresult != BE.halt && s.finalbody && s.finalbody.hasCode())
-                {
-                    eSink.warning(s.finalbody.loc, "statement is not reachable");
-                }
-            }
-
             if (!(finalresult & BE.fallthru))
                 result &= ~BE.fallthru;
             result |= finalresult & ~BE.fallthru;
diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d
index 70a7c882eb3..e4d1096db48 100644
--- a/gcc/d/dmd/cond.d
+++ b/gcc/d/dmd/cond.d
@@ -30,7 +30,7 @@ import dmd.location;
 import dmd.mtype;
 import dmd.typesem;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.tokens;
 import dmd.utils;
diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d
index 230bfec17ea..55844ddf606 100644
--- a/gcc/d/dmd/cppmangle.d
+++ b/gcc/d/dmd/cppmangle.d
@@ -42,7 +42,7 @@ import dmd.mtype;
 import dmd.nspace;
 import dmd.root.array;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.target;
 import dmd.typesem;
diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d
index b65e7e833d4..40463b4d970 100644
--- a/gcc/d/dmd/declaration.d
+++ b/gcc/d/dmd/declaration.d
@@ -37,7 +37,7 @@ import dmd.intrange;
 import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.target;
 import dmd.tokens;
 import dmd.typesem;
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
index a43be7d2308..fc5a3aaf62c 100644
--- a/gcc/d/dmd/dinterpret.d
+++ b/gcc/d/dmd/dinterpret.d
@@ -44,7 +44,7 @@ import dmd.root.rmem;
 import dmd.root.array;
 import dmd.root.ctfloat;
 import dmd.root.region;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.utf;
 import dmd.statement;
 import dmd.tokens;
@@ -4965,6 +4965,22 @@ public:
 
     override void visit(CommaExp e)
     {
+        /****************************************
+         * Find the first non-comma expression.
+         * Params:
+         *      e = Expressions connected by commas
+         * Returns:
+         *      left-most non-comma expression
+         */
+        static inout(Expression) firstComma(inout Expression e)
+        {
+            Expression ex = cast()e;
+            while (ex.op == EXP.comma)
+                ex = (cast(CommaExp)ex).e1;
+            return cast(inout)ex;
+
+        }
+
         debug (LOG)
         {
             printf("%s CommaExp::interpret() %s\n", e.loc.toChars(), e.toChars());
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index 202630304be..9031b157000 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -45,7 +45,7 @@ import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.port;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.semantic2;
 import dmd.semantic3;
diff --git a/gcc/d/dmd/doc.h b/gcc/d/dmd/doc.h
index 562427f1a4d..ebd3094348d 100644
--- a/gcc/d/dmd/doc.h
+++ b/gcc/d/dmd/doc.h
@@ -10,8 +10,10 @@
 
 #pragma once
 
+#include "root/dcompat.h" // for d_size_t
+
 class Module;
 class ErrorSink;
 
-void gendocfile(Module *m, const char *ddoctext_ptr, size_t ddoctext_length,
+void gendocfile(Module *m, const char *ddoctext_ptr, d_size_t ddoctext_length,
                 const char *datetime, ErrorSink *eSink, OutBuffer &outbuf);
diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d
index 981e093934c..14dbe9c6149 100644
--- a/gcc/d/dmd/dscope.d
+++ b/gcc/d/dmd/dscope.d
@@ -479,12 +479,6 @@ extern (C++) struct Scope
                             s = *ps;
                         }
                     }
-                    if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
-                        ident == Id.length && sc.scopesym.isArrayScopeSymbol() &&
-                        sc.enclosing && sc.enclosing.search(loc, ident, null, flags))
-                    {
-                        warning(s.loc, "array `length` hides other `length` name in outer scope");
-                    }
                     //printMsg("\tfound local", s);
                     if (pscopesym)
                         *pscopesym = sc.scopesym;
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index 1f4a4664c3e..6538664eace 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -48,7 +48,7 @@ import dmd.nspace;
 import dmd.opover;
 import dmd.root.aav;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.speller;
 import dmd.root.string;
 import dmd.statement;
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 65c07952e00..4de037c8f68 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -62,7 +62,7 @@ import dmd.root.array;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.utf;
 import dmd.semantic2;
 import dmd.semantic3;
@@ -3854,12 +3854,6 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                              */
                             funcdecl.foverrides.push(fdv);
 
-                            /* Should we really require 'override' when implementing
-                             * an interface function?
-                             */
-                            //if (!isOverride())
-                            //    warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv.toPrettyChars());
-
                             if (fdv.tintro)
                                 ti = fdv.tintro;
                             else if (!funcdecl.type.equals(fdv.type))
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index 23d1140fe38..ae49dda9315 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -70,7 +70,7 @@ import dmd.mtype;
 import dmd.opover;
 import dmd.root.array;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.semantic2;
 import dmd.semantic3;
 import dmd.tokens;
diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d
index f906ee12789..7c76da99d1b 100644
--- a/gcc/d/dmd/dtoh.d
+++ b/gcc/d/dmd/dtoh.d
@@ -1813,7 +1813,7 @@ public:
         {
             buf.writestring("::");
 
-            import dmd.root.rootobject;
+            import dmd.rootobject;
             // Is this even possible?
             if (arg.dyncast != DYNCAST.identifier)
             {
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index 8562e2eabd6..3f85ea08320 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -32,7 +32,7 @@ import dmd.init;
 import dmd.location;
 import dmd.mtype;
 import dmd.printast;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.tokens;
 import dmd.visitor;
 import dmd.arraytypes;
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index 720523174b7..2c55f0ebc94 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -56,14 +56,13 @@ import dmd.nspace;
 import dmd.objc;
 import dmd.opover;
 import dmd.optimize;
-import dmd.postordervisitor;
 import dmd.root.complex;
 import dmd.root.ctfloat;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.optional;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.utf;
 import dmd.safe;
@@ -113,22 +112,6 @@ enum ModifyFlags
     fieldAssign = 0x2,
 }
 
-/****************************************
- * Find the first non-comma expression.
- * Params:
- *      e = Expressions connected by commas
- * Returns:
- *      left-most non-comma expression
- */
-inout(Expression) firstComma(inout Expression e)
-{
-    Expression ex = cast()e;
-    while (ex.op == EXP.comma)
-        ex = (cast(CommaExp)ex).e1;
-    return cast(inout)ex;
-
-}
-
 /****************************************
  * Find the last non-comma expression.
  * Params:
@@ -146,59 +129,6 @@ inout(Expression) lastComma(inout Expression e)
 
 }
 
-/*****************************************
- * Determine if `this` is available by walking up the enclosing
- * scopes until a function is found.
- *
- * Params:
- *      sc = where to start looking for the enclosing function
- * Returns:
- *      Found function if it satisfies `isThis()`, otherwise `null`
- */
-FuncDeclaration hasThis(Scope* sc)
-{
-    //printf("hasThis()\n");
-    Dsymbol p = sc.parent;
-    while (p && p.isTemplateMixin())
-        p = p.parent;
-    FuncDeclaration fdthis = p ? p.isFuncDeclaration() : null;
-    //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis.toChars() : "");
-
-    // Go upwards until we find the enclosing member function
-    FuncDeclaration fd = fdthis;
-    while (1)
-    {
-        if (!fd)
-        {
-            return null;
-        }
-        if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2()))
-            break;
-
-        Dsymbol parent = fd.parent;
-        while (1)
-        {
-            if (!parent)
-                return null;
-            TemplateInstance ti = parent.isTemplateInstance();
-            if (ti)
-                parent = ti.parent;
-            else
-                break;
-        }
-        fd = parent.isFuncDeclaration();
-    }
-
-    if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2()))
-    {
-        return null;
-    }
-
-    assert(fd.vthis);
-    return fd;
-
-}
-
 /***********************************
  * Determine if a `this` is needed to access `d`.
  * Params:
@@ -396,126 +326,6 @@ TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe
     return null;
 }
 
-/************************************************
- * If we want the value of this expression, but do not want to call
- * the destructor on it.
- */
-Expression valueNoDtor(Expression e)
-{
-    auto ex = lastComma(e);
-
-    if (auto ce = ex.isCallExp())
-    {
-        /* The struct value returned from the function is transferred
-         * so do not call the destructor on it.
-         * Recognize:
-         *       ((S _ctmp = S.init), _ctmp).this(...)
-         * and make sure the destructor is not called on _ctmp
-         * BUG: if ex is a CommaExp, we should go down the right side.
-         */
-        if (auto dve = ce.e1.isDotVarExp())
-        {
-            if (dve.var.isCtorDeclaration())
-            {
-                // It's a constructor call
-                if (auto comma = dve.e1.isCommaExp())
-                {
-                    if (auto ve = comma.e2.isVarExp())
-                    {
-                        VarDeclaration ctmp = ve.var.isVarDeclaration();
-                        if (ctmp)
-                        {
-                            ctmp.storage_class |= STC.nodtor;
-                            assert(!ce.isLvalue());
-                        }
-                    }
-                }
-            }
-        }
-    }
-    else if (auto ve = ex.isVarExp())
-    {
-        auto vtmp = ve.var.isVarDeclaration();
-        if (vtmp && (vtmp.storage_class & STC.rvalue))
-        {
-            vtmp.storage_class |= STC.nodtor;
-        }
-    }
-    return e;
-}
-
-/*********************************************
- * If e is an instance of a struct, and that struct has a copy constructor,
- * rewrite e as:
- *    (tmp = e),tmp
- * Input:
- *      sc = just used to specify the scope of created temporary variable
- *      destinationType = the type of the object on which the copy constructor is called;
- *                        may be null if the struct defines a postblit
- */
-private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
-{
-    if (auto ts = e.type.baseElemOf().isTypeStruct())
-    {
-        StructDeclaration sd = ts.sym;
-        if (sd.postblit || sd.hasCopyCtor)
-        {
-            /* Create a variable tmp, and replace the argument e with:
-             *      (tmp = e),tmp
-             * and let AssignExp() handle the construction.
-             * This is not the most efficient, ideally tmp would be constructed
-             * directly onto the stack.
-             */
-            auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
-            if (sd.hasCopyCtor && destinationType)
-            {
-                // https://issues.dlang.org/show_bug.cgi?id=22619
-                // If the destination type is inout we can preserve it
-                // only if inside an inout function; if we are not inside
-                // an inout function, then we will preserve the type of
-                // the source
-                if (destinationType.hasWild && !(sc.func.storage_class & STC.wild))
-                    tmp.type = e.type;
-                else
-                    tmp.type = destinationType;
-            }
-            tmp.storage_class |= STC.nodtor;
-            tmp.dsymbolSemantic(sc);
-            Expression de = new DeclarationExp(e.loc, tmp);
-            Expression ve = new VarExp(e.loc, tmp);
-            de.type = Type.tvoid;
-            ve.type = e.type;
-            return Expression.combine(de, ve);
-        }
-    }
-    return e;
-}
-
-/************************************************
- * Handle the postblit call on lvalue, or the move of rvalue.
- *
- * Params:
- *   sc = the scope where the expression is encountered
- *   e = the expression the needs to be moved or copied (source)
- *   t = if the struct defines a copy constructor, the type of the destination
- *
- * Returns:
- *  The expression that copy constructs or moves the value.
- */
-extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
-{
-    if (auto ce = e.isCondExp())
-    {
-        ce.e1 = doCopyOrMove(sc, ce.e1);
-        ce.e2 = doCopyOrMove(sc, ce.e2);
-    }
-    else
-    {
-        e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e);
-    }
-    return e;
-}
-
 /****************************************************************/
 /* A type meant as a union of all the Expression types,
  * to serve essentially as a Variant that will sit on the stack
@@ -1461,26 +1271,6 @@ extern (C++) abstract class Expression : ASTNode
         return false;
     }
 
-    extern (D) final bool checkRightThis(Scope* sc)
-    {
-        if (op == EXP.error)
-            return true;
-        if (op == EXP.variable && type.ty != Terror)
-        {
-            VarExp ve = cast(VarExp)this;
-            if (isNeedThisScope(sc, ve.var))
-            {
-                //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
-                //        sc.intypeof, sc.getStructClassScope(), func, fdthis);
-                auto t = ve.var.isThis();
-                assert(t);
-                error(loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars());
-                return true;
-            }
-        }
-        return false;
-    }
-
     /*******************************
      * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
      * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
@@ -6847,78 +6637,6 @@ extern (C++) final class CondExp : BinExp
         return toLvalue(sc, this);
     }
 
-    void hookDtors(Scope* sc)
-    {
-        extern (C++) final class DtorVisitor : StoppableVisitor
-        {
-            alias visit = typeof(super).visit;
-        public:
-            Scope* sc;
-            CondExp ce;
-            VarDeclaration vcond;
-            bool isThen;
-
-            extern (D) this(Scope* sc, CondExp ce) @safe
-            {
-                this.sc = sc;
-                this.ce = ce;
-            }
-
-            override void visit(Expression e)
-            {
-                //printf("(e = %s)\n", e.toChars());
-            }
-
-            override void visit(DeclarationExp e)
-            {
-                auto v = e.declaration.isVarDeclaration();
-                if (v && !v.isDataseg())
-                {
-                    if (v._init)
-                    {
-                        if (auto ei = v._init.isExpInitializer())
-                            walkPostorder(ei.exp, this);
-                    }
-
-                    if (v.edtor)
-                        walkPostorder(v.edtor, this);
-
-                    if (v.needsScopeDtor())
-                    {
-                        if (!vcond)
-                        {
-                            vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd);
-                            vcond.dsymbolSemantic(sc);
-
-                            Expression de = new DeclarationExp(ce.econd.loc, vcond);
-                            de = de.expressionSemantic(sc);
-
-                            Expression ve = new VarExp(ce.econd.loc, vcond);
-                            ce.econd = Expression.combine(de, ve);
-                        }
-
-                        //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
-                        Expression ve = new VarExp(vcond.loc, vcond);
-                        if (isThen)
-                            v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor);
-                        else
-                            v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor);
-                        v.edtor = v.edtor.expressionSemantic(sc);
-                        //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
-                    }
-                }
-            }
-        }
-
-        scope DtorVisitor v = new DtorVisitor(sc, this);
-        //printf("+%s\n", toChars());
-        v.isThen = true;
-        walkPostorder(e1, v);
-        v.isThen = false;
-        walkPostorder(e2, v);
-        //printf("-%s\n", toChars());
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index 5c656ee9353..a92d9ada6f5 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -1326,7 +1326,6 @@ public:
     bool isLvalue() override;
     Expression *toLvalue(Scope *sc, Expression *e) override;
     Expression *modifiableLvalue(Scope *sc, Expression *e) override;
-    void hookDtors(Scope *sc);
 
     void accept(Visitor *v) override { v->visit(this); }
 };
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 0bdcda9fe6e..3472f9254cb 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -63,11 +63,12 @@ import dmd.opover;
 import dmd.optimize;
 import dmd.parse;
 import dmd.printast;
+import dmd.postordervisitor;
 import dmd.root.array;
 import dmd.root.ctfloat;
 import dmd.root.filename;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.utf;
 import dmd.semantic2;
@@ -140,6 +141,58 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps)
     return false;
 }
 
+/*****************************************
+ * Determine if `this` is available by walking up the enclosing
+ * scopes until a function is found.
+ *
+ * Params:
+ *      sc = where to start looking for the enclosing function
+ * Returns:
+ *      Found function if it satisfies `isThis()`, otherwise `null`
+ */
+FuncDeclaration hasThis(Scope* sc)
+{
+    //printf("hasThis()\n");
+    Dsymbol p = sc.parent;
+    while (p && p.isTemplateMixin())
+        p = p.parent;
+    FuncDeclaration fdthis = p ? p.isFuncDeclaration() : null;
+    //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis.toChars() : "");
+
+    // Go upwards until we find the enclosing member function
+    FuncDeclaration fd = fdthis;
+    while (1)
+    {
+        if (!fd)
+        {
+            return null;
+        }
+        if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2()))
+            break;
+
+        Dsymbol parent = fd.parent;
+        while (1)
+        {
+            if (!parent)
+                return null;
+            TemplateInstance ti = parent.isTemplateInstance();
+            if (ti)
+                parent = ti.parent;
+            else
+                break;
+        }
+        fd = parent.isFuncDeclaration();
+    }
+
+    if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2()))
+    {
+        return null;
+    }
+
+    assert(fd.vthis);
+    return fd;
+
+}
 
 /***********************************************************
  * Resolve `exp` as a compile-time known string.
@@ -355,6 +408,126 @@ extern(D) bool arrayExpressionSemantic(
     return err;
 }
 
+/************************************************
+ * Handle the postblit call on lvalue, or the move of rvalue.
+ *
+ * Params:
+ *   sc = the scope where the expression is encountered
+ *   e = the expression the needs to be moved or copied (source)
+ *   t = if the struct defines a copy constructor, the type of the destination
+ *
+ * Returns:
+ *  The expression that copy constructs or moves the value.
+ */
+extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
+{
+    if (auto ce = e.isCondExp())
+    {
+        ce.e1 = doCopyOrMove(sc, ce.e1);
+        ce.e2 = doCopyOrMove(sc, ce.e2);
+    }
+    else
+    {
+        e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e);
+    }
+    return e;
+}
+
+/*********************************************
+ * If e is an instance of a struct, and that struct has a copy constructor,
+ * rewrite e as:
+ *    (tmp = e),tmp
+ * Input:
+ *      sc = just used to specify the scope of created temporary variable
+ *      destinationType = the type of the object on which the copy constructor is called;
+ *                        may be null if the struct defines a postblit
+ */
+private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
+{
+    if (auto ts = e.type.baseElemOf().isTypeStruct())
+    {
+        StructDeclaration sd = ts.sym;
+        if (sd.postblit || sd.hasCopyCtor)
+        {
+            /* Create a variable tmp, and replace the argument e with:
+             *      (tmp = e),tmp
+             * and let AssignExp() handle the construction.
+             * This is not the most efficient, ideally tmp would be constructed
+             * directly onto the stack.
+             */
+            auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+            if (sd.hasCopyCtor && destinationType)
+            {
+                // https://issues.dlang.org/show_bug.cgi?id=22619
+                // If the destination type is inout we can preserve it
+                // only if inside an inout function; if we are not inside
+                // an inout function, then we will preserve the type of
+                // the source
+                if (destinationType.hasWild && !(sc.func.storage_class & STC.wild))
+                    tmp.type = e.type;
+                else
+                    tmp.type = destinationType;
+            }
+            tmp.storage_class |= STC.nodtor;
+            tmp.dsymbolSemantic(sc);
+            Expression de = new DeclarationExp(e.loc, tmp);
+            Expression ve = new VarExp(e.loc, tmp);
+            de.type = Type.tvoid;
+            ve.type = e.type;
+            return Expression.combine(de, ve);
+        }
+    }
+    return e;
+}
+
+/************************************************
+ * If we want the value of this expression, but do not want to call
+ * the destructor on it.
+ */
+Expression valueNoDtor(Expression e)
+{
+    auto ex = lastComma(e);
+
+    if (auto ce = ex.isCallExp())
+    {
+        /* The struct value returned from the function is transferred
+         * so do not call the destructor on it.
+         * Recognize:
+         *       ((S _ctmp = S.init), _ctmp).this(...)
+         * and make sure the destructor is not called on _ctmp
+         * BUG: if ex is a CommaExp, we should go down the right side.
+         */
+        if (auto dve = ce.e1.isDotVarExp())
+        {
+            if (dve.var.isCtorDeclaration())
+            {
+                // It's a constructor call
+                if (auto comma = dve.e1.isCommaExp())
+                {
+                    if (auto ve = comma.e2.isVarExp())
+                    {
+                        VarDeclaration ctmp = ve.var.isVarDeclaration();
+                        if (ctmp)
+                        {
+                            ctmp.storage_class |= STC.nodtor;
+                            assert(!ce.isLvalue());
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else if (auto ve = ex.isVarExp())
+    {
+        auto vtmp = ve.var.isVarDeclaration();
+        if (vtmp && (vtmp.storage_class & STC.rvalue))
+        {
+            vtmp.storage_class |= STC.nodtor;
+        }
+    }
+    return e;
+}
+
 /*
 Checks if `exp` contains a direct access to a `noreturn`
 variable. If that is the case, an `assert(0)` expression
@@ -493,6 +666,78 @@ private bool isDotOpDispatch(Expression e)
     return false;
 }
 
+private void hookDtors(CondExp ce, Scope* sc)
+{
+    extern (C++) final class DtorVisitor : StoppableVisitor
+    {
+        alias visit = typeof(super).visit;
+    public:
+        Scope* sc;
+        CondExp ce;
+        VarDeclaration vcond;
+        bool isThen;
+
+        extern (D) this(Scope* sc, CondExp ce) @safe
+        {
+            this.sc = sc;
+            this.ce = ce;
+        }
+
+        override void visit(Expression e)
+        {
+            //printf("(e = %s)\n", e.toChars());
+        }
+
+        override void visit(DeclarationExp e)
+        {
+            auto v = e.declaration.isVarDeclaration();
+            if (v && !v.isDataseg())
+            {
+                if (v._init)
+                {
+                    if (auto ei = v._init.isExpInitializer())
+                        walkPostorder(ei.exp, this);
+                }
+
+                if (v.edtor)
+                    walkPostorder(v.edtor, this);
+
+                if (v.needsScopeDtor())
+                {
+                    if (!vcond)
+                    {
+                        vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd);
+                        vcond.dsymbolSemantic(sc);
+
+                        Expression de = new DeclarationExp(ce.econd.loc, vcond);
+                        de = de.expressionSemantic(sc);
+
+                        Expression ve = new VarExp(ce.econd.loc, vcond);
+                        ce.econd = Expression.combine(de, ve);
+                    }
+
+                    //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
+                    Expression ve = new VarExp(vcond.loc, vcond);
+                    if (isThen)
+                        v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor);
+                    else
+                        v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor);
+                    v.edtor = v.edtor.expressionSemantic(sc);
+                    //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
+                }
+            }
+        }
+    }
+
+    scope DtorVisitor v = new DtorVisitor(sc, ce);
+    //printf("+%s\n", toChars());
+    v.isThen = true;
+    walkPostorder(ce.e1, v);
+    v.isThen = false;
+    walkPostorder(ce.e2, v);
+    //printf("-%s\n", toChars());
+}
+
 
 /******************************
  * Pull out callable entity with UFCS.
@@ -1516,6 +1761,26 @@ Leprop:
     return ErrorExp.get();
 }
 
+private bool checkRightThis(Expression e, Scope* sc)
+{
+    if (e.op == EXP.error)
+        return true;
+    if (e.op == EXP.variable && e.type.ty != Terror)
+    {
+        VarExp ve = cast(VarExp)e;
+        if (isNeedThisScope(sc, ve.var))
+        {
+            //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
+            //        sc.intypeof, sc.getStructClassScope(), func, fdthis);
+            auto t = ve.var.isThis();
+            assert(t);
+            error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars());
+            return true;
+        }
+    }
+    return false;
+}
+
 extern (C++) Expression resolveProperties(Scope* sc, Expression e)
 {
     //printf("resolveProperties(%s)\n", e.toChars());
@@ -10085,24 +10350,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return setError();
             }
 
-            version (none)
-            {
-                if (global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == EXP.assign &&
-                    e2x.op != EXP.slice && e2x.op != EXP.assign &&
-                    e2x.op != EXP.arrayLiteral && e2x.op != EXP.string_ &&
-                    !(e2x.op == EXP.add || e2x.op == EXP.min ||
-                      e2x.op == EXP.mul || e2x.op == EXP.div ||
-                      e2x.op == EXP.mod || e2x.op == EXP.xor ||
-                      e2x.op == EXP.and || e2x.op == EXP.or ||
-                      e2x.op == EXP.pow ||
-                      e2x.op == EXP.tilde || e2x.op == EXP.negate))
-                {
-                    const(char)* e1str = exp.e1.toChars();
-                    const(char)* e2str = e2x.toChars();
-                    exp.warning("explicit element-wise assignment `%s = (%s)[]` is better than `%s = %s`", e1str, e2str, e1str, e2str);
-                }
-            }
-
             Type t2n = t2.nextOf();
             Type t1n = t1.nextOf();
             int offset;
@@ -10149,21 +10396,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         else
         {
-            version (none)
-            {
-                if (global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == EXP.assign &&
-                    t1.ty == Tarray && t2.ty == Tsarray &&
-                    e2x.op != EXP.slice &&
-                    t2.implicitConvTo(t1))
-                {
-                    // Disallow ar[] = sa (Converted to ar[] = sa[])
-                    // Disallow da   = sa (Converted to da   = sa[])
-                    const(char)* e1str = exp.e1.toChars();
-                    const(char)* e2str = e2x.toChars();
-                    const(char)* atypestr = exp.e1.op == EXP.slice ? "element-wise" : "slice";
-                    exp.warning("explicit %s assignment `%s = (%s)[]` is better than `%s = %s`", atypestr, e1str, e2str, e1str, e2str);
-                }
-            }
             if (exp.op == EXP.blit)
                 e2x = e2x.castTo(sc, exp.e1.type);
             else
diff --git a/gcc/d/dmd/foreachvar.d b/gcc/d/dmd/foreachvar.d
index 8af2a95252e..dc4b20bf957 100644
--- a/gcc/d/dmd/foreachvar.d
+++ b/gcc/d/dmd/foreachvar.d
@@ -35,7 +35,7 @@ import dmd.mtype;
 import dmd.postordervisitor;
 import dmd.printast;
 import dmd.root.array;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.statement;
 import dmd.tokens;
 import dmd.visitor;
diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d
index 81bb028964f..ac73e70db34 100644
--- a/gcc/d/dmd/func.d
+++ b/gcc/d/dmd/func.d
@@ -48,7 +48,7 @@ import dmd.mtype;
 import dmd.objc;
 import dmd.root.aav;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.stringtable;
 import dmd.semantic2;
@@ -4620,15 +4620,7 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)
       case default_:
         if (!sc.func)
             return false;
-        if (sc.func.isSafeBypassingInference())
-        {
-            if (!gag)
-            {
-                version (none) // disable obsolete warning
-                    warning(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : "");
-            }
-        }
-        else if (!sc.func.safetyViolation)
+        if (!sc.func.isSafeBypassingInference() && !sc.func.safetyViolation)
         {
             import dmd.func : AttributeViolation;
             sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2);
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index 056e486e927..9d585cb898b 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -45,7 +45,7 @@ import dmd.parse;
 import dmd.root.complex;
 import dmd.root.ctfloat;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.statement;
 import dmd.staticassert;
diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d
index 3173445fddc..c2b2fbafa25 100644
--- a/gcc/d/dmd/identifier.d
+++ b/gcc/d/dmd/identifier.d
@@ -17,7 +17,7 @@ import core.stdc.string;
 import dmd.id;
 import dmd.location;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.stringtable;
 import dmd.root.utf;
diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d
index ecca5528352..ebcd011f8a1 100644
--- a/gcc/d/dmd/init.d
+++ b/gcc/d/dmd/init.d
@@ -25,7 +25,7 @@ import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.tokens;
 import dmd.visitor;
 
diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d
index f1999fd458d..11ab8169099 100644
--- a/gcc/d/dmd/json.d
+++ b/gcc/d/dmd/json.d
@@ -35,7 +35,7 @@ import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.target;
 import dmd.visitor;
diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d
index 01f94a7b04d..70a0c032e65 100644
--- a/gcc/d/dmd/mtype.d
+++ b/gcc/d/dmd/mtype.d
@@ -46,7 +46,7 @@ import dmd.opover;
 import dmd.root.ctfloat;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.stringtable;
 import dmd.target;
 import dmd.tokens;
diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d
index 1d831bbed6b..693464919e0 100644
--- a/gcc/d/dmd/mustuse.d
+++ b/gcc/d/dmd/mustuse.d
@@ -18,17 +18,6 @@ import dmd.globals;
 import dmd.identifier;
 import dmd.location;
 
-// Used in isIncrementOrDecrement
-private const StringExp plusPlus, minusMinus;
-
-// Loc.initial cannot be used in static initializers, so
-// these need a static constructor.
-shared static this()
-{
-    plusPlus = new StringExp(Loc.initial, "++");
-    minusMinus = new StringExp(Loc.initial, "--");
-}
-
 /**
  * Check whether discarding an expression would violate the requirements of
  * @mustuse. If so, emit an error.
@@ -173,9 +162,15 @@ private bool isIncrementOrDecrement(Expression e)
                     {
                         if (auto argExp = (*tiargs)[0].isExpression())
                         {
-                            auto op = argExp.isStringExp();
-                            if (op && (op.compare(plusPlus) == 0 || op.compare(minusMinus) == 0))
-                                return true;
+                            if (auto op = argExp.isStringExp())
+                            {
+                                if (op.len == 2 && op.sz == 1)
+                                {
+                                    const s = op.peekString();
+                                    if (s == "++" || s == "--")
+                                        return true;
+                                }
+                            }
                         }
                     }
                 }
diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d
index 560606190e0..59bf1d5d1d5 100644
--- a/gcc/d/dmd/nogc.d
+++ b/gcc/d/dmd/nogc.d
@@ -256,7 +256,7 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
 {
     import dmd.id : Id;
     import dmd.dsymbol : Dsymbol;
-    import dmd.root.rootobject : RootObject, DYNCAST;
+    import dmd.rootobject : RootObject, DYNCAST;
 
     if (fd.ident != Id._d_HookTraceImpl)
         return fd;
diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d
index 8b30681afc0..dc94aecc61b 100644
--- a/gcc/d/dmd/ob.d
+++ b/gcc/d/dmd/ob.d
@@ -16,7 +16,7 @@ import core.stdc.stdlib;
 import core.stdc.string;
 
 import dmd.root.array;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.rmem;
 
 import dmd.aggregate;
diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d
index 3821f947a97..9a13d5c254e 100644
--- a/gcc/d/dmd/parse.d
+++ b/gcc/d/dmd/parse.d
@@ -25,7 +25,7 @@ import dmd.location;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.tokens;
 
diff --git a/gcc/d/dmd/root/rootobject.d b/gcc/d/dmd/rootobject.d
similarity index 88%
rename from gcc/d/dmd/root/rootobject.d
rename to gcc/d/dmd/rootobject.d
index 65c499ddf4e..7867ad5de31 100644
--- a/gcc/d/dmd/root/rootobject.d
+++ b/gcc/d/dmd/rootobject.d
@@ -4,12 +4,12 @@
  * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
  * Authors:   Walter Bright, https://www.digitalmars.com
  * License:   $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/rootobject.d, root/_rootobject.d)
- * Documentation:  https://dlang.org/phobos/dmd_root_rootobject.html
- * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/rootobject.d
+ * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/rootobject.d, _rootobject.d)
+ * Documentation:  https://dlang.org/phobos/dmd_rootobject.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/rootobject.d
  */
 
-module dmd.root.rootobject;
+module dmd.rootobject;
 
 /***********************************************************
  */
diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d
index 6a3795178d3..036560b5407 100644
--- a/gcc/d/dmd/semantic2.d
+++ b/gcc/d/dmd/semantic2.d
@@ -55,7 +55,7 @@ import dmd.parse;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.utf;
 import dmd.sideeffect;
 import dmd.statementsem;
diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d
index e0931409df8..2f1839cdac1 100644
--- a/gcc/d/dmd/semantic3.d
+++ b/gcc/d/dmd/semantic3.d
@@ -58,7 +58,7 @@ import dmd.parse;
 import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.utf;
 import dmd.sideeffect;
 import dmd.statementsem;
diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d
index de92b294c14..465bffe4da0 100644
--- a/gcc/d/dmd/sideeffect.d
+++ b/gcc/d/dmd/sideeffect.d
@@ -269,41 +269,6 @@ bool discardValue(Expression e)
             break;
         }
     case EXP.call:
-        /* Issue 3882: */
-        if (global.params.warnings != DiagnosticReporting.off && !global.gag)
-        {
-            CallExp ce = cast(CallExp)e;
-            if (e.type.ty == Tvoid)
-            {
-                /* Don't complain about calling void-returning functions with no side-effect,
-                 * because purity and nothrow are inferred, and because some of the
-                 * runtime library depends on it. Needs more investigation.
-                 *
-                 * One possible solution is to restrict this message to only be called in hierarchies that
-                 * never call assert (and or not called from inside unittest blocks)
-                 */
-            }
-            else if (ce.e1.type)
-            {
-                Type t = ce.e1.type.toBasetype();
-                if (t.ty == Tdelegate)
-                    t = (cast(TypeDelegate)t).next;
-                if (t.ty == Tfunction && (ce.f ? callSideEffectLevel(ce.f) : callSideEffectLevel(ce.e1.type)) > 0)
-                {
-                    const(char)* s;
-                    if (ce.f)
-                        s = ce.f.toPrettyChars();
-                    else if (ce.e1.op == EXP.star)
-                    {
-                        // print 'fp' if ce.e1 is (*fp)
-                        s = (cast(PtrExp)ce.e1).e1.toChars();
-                    }
-                    else
-                        s = ce.e1.toChars();
-                    warning(e.loc, "calling `%s` without side effects discards return value of type `%s`; prepend a `cast(void)` if intentional", s, e.type.toChars());
-                }
-            }
-        }
         return false;
     case EXP.andAnd:
     case EXP.orOr:
diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d
index da26bc980e8..5a31e07dc0f 100644
--- a/gcc/d/dmd/statement.d
+++ b/gcc/d/dmd/statement.d
@@ -33,7 +33,7 @@ import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.sapply;
 import dmd.staticassert;
 import dmd.tokens;
diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h
index fe899c6a496..73feb3f912b 100644
--- a/gcc/d/dmd/statement.h
+++ b/gcc/d/dmd/statement.h
@@ -113,9 +113,6 @@ public:
 
     virtual Statement *syntaxCopy();
 
-    void error(const char *format, ...);
-    void warning(unsigned flag, const char *format, ...);
-    void deprecation(const char *format, ...);
     virtual Statement *getRelatedLabeled() { return this; }
     virtual bool hasBreak() const;
     virtual bool hasContinue() const;
diff --git a/gcc/d/dmd/templateparamsem.d b/gcc/d/dmd/templateparamsem.d
index 6b8f949f526..de40c1f2bb2 100644
--- a/gcc/d/dmd/templateparamsem.d
+++ b/gcc/d/dmd/templateparamsem.d
@@ -19,7 +19,7 @@ import dmd.globals;
 import dmd.location;
 import dmd.expression;
 import dmd.expressionsem;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.mtype;
 import dmd.typesem;
 import dmd.visitor;
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
index bdc569c8725..254900e6030 100644
--- a/gcc/d/dmd/traits.d
+++ b/gcc/d/dmd/traits.d
@@ -49,7 +49,7 @@ import dmd.target;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.common.outbuffer;
 import dmd.root.string;
 
diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/transitivevisitor.d
index 376c12fa808..bf1d38e61ca 100644
--- a/gcc/d/dmd/transitivevisitor.d
+++ b/gcc/d/dmd/transitivevisitor.d
@@ -8,7 +8,7 @@ module dmd.transitivevisitor;
 import dmd.astenums;
 import dmd.permissivevisitor;
 import dmd.tokens;
-import dmd.root.rootobject;
+import dmd.rootobject;
 
 import core.stdc.stdio;
 
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index c69268a80ed..fe54e293750 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -57,7 +57,7 @@ import dmd.root.complex;
 import dmd.root.ctfloat;
 import dmd.root.rmem;
 import dmd.common.outbuffer;
-import dmd.root.rootobject;
+import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.stringtable;
 import dmd.safe;
diff --git a/gcc/d/dmd/visitor.d b/gcc/d/dmd/visitor.d
index 591d3c0c3b4..5722e10d256 100644
--- a/gcc/d/dmd/visitor.d
+++ b/gcc/d/dmd/visitor.d
@@ -17,7 +17,7 @@ import dmd.parsetimevisitor;
 import dmd.tokens;
 import dmd.transitivevisitor;
 import dmd.expression;
-import dmd.root.rootobject;
+import dmd.rootobject;
 
 /**
  * Classic Visitor class which implements visit methods for all the AST
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3882.d b/gcc/testsuite/gdc.test/fail_compilation/fail3882.d
index 27ddad41604..9647f08805b 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail3882.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail3882.d
@@ -1,18 +1,17 @@
-// REQUIRED_ARGS: -w
-// PERMUTE_ARGS: -debug
-
-/******************************************/
-// https://issues.dlang.org/show_bug.cgi?id=3882
-
 /*
+PERMUTE_ARGS: -debug
 TEST_OUTPUT:
 ---
-fail_compilation/fail3882.d(23): Warning: calling `fail3882.strictlyPure!int.strictlyPure` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
-fail_compilation/fail3882.d(27): Warning: calling `fp` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
+fail_compilation/fail3882.d(32): Error: `@mustuse` on functions is reserved for future use
+fail_compilation/fail3882.d(33): Error: `@mustuse` on functions is reserved for future use
 ---
 */
+import core.attribute;
+
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=3882
 
-@safe pure nothrow T strictlyPure(T)(T x)
+@mustuse @safe pure nothrow T strictlyPure(T)(T x)
 {
     return x*x;
 }
@@ -30,18 +29,8 @@ void main()
 /******************************************/
 // bugfix in TypeFunction::purityLevel
 
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail3882.d(48): Warning: calling `fail3882.f1` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
-fail_compilation/fail3882.d(49): Warning: calling `fail3882.f2` without side effects discards return value of type `int`; prepend a `cast(void)` if intentional
-Error: warnings are treated as errors
-       Use -wi if you wish to treat warnings only as informational.
----
-*/
-
-nothrow pure int f1(immutable(int)[] a) { return 0; }
-nothrow pure int f2(immutable(int)*  p) { return 0; }
+@mustuse nothrow pure int f1(immutable(int)[] a) { return 0; }
+@mustuse nothrow pure int f2(immutable(int)*  p) { return 0; }
 
 void test_bug()
 {
diff --git a/gcc/testsuite/gdc.test/runnable/issue24168.d b/gcc/testsuite/gdc.test/runnable/issue24168.d
new file mode 100644
index 00000000000..c21a35bc9d4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/issue24168.d
@@ -0,0 +1,31 @@
+/*
+REQUIRED_ARGS: -fPIE
+DISABLED: win32 win64
+*/
+
+//https://issues.dlang.org/show_bug.cgi?id=24168
+
+int i = 42;
+
+bool foo(ref int a)
+{
+    return a == 42;
+}
+
+ref int bar()
+{
+    return i;
+}
+
+bool baz()
+{
+    static int i = 42;
+    return foo(i);
+}
+
+void main()
+{
+    assert(foo(i));
+    assert(bar() == 42);
+    assert(baz());
+}
