diff mbox series

[committed] d: Merge upstream dmd 65a3da148c, phobos fc06c514a.

Message ID 20231121133224.105698-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd 65a3da148c, phobos fc06c514a. | expand

Commit Message

Iain Buclaw Nov. 21, 2023, 1:32 p.m. UTC
Hi,

This patch merges the D front-end with upstream dmd 65a3da148c, and
the standard library with phobos fc06c514a.

Synchronizing with the upstream development branch as of 2023-11-12.

D front-end changes:

    - Import latest bug fixes from dmd v2.106.0-beta.1.

Phobos changes:

    - Import latest bug fixes from phobos v2.106.0-beta.1.
    - `std.range.primitives.isForwardRange' now takes an optional
      element type.

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

Regards,
Iain.

---
gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd 65a3da148c.

libphobos/ChangeLog:

	* src/MERGE: Merge upstream phobos fc06c514a.
---
 gcc/d/dmd/MERGE                               |    2 +-
 gcc/d/dmd/arrayop.d                           |    2 +-
 gcc/d/dmd/canthrow.d                          |    3 +-
 gcc/d/dmd/compiler.d                          |    1 +
 gcc/d/dmd/cond.d                              |    1 +
 gcc/d/dmd/cparse.d                            |   24 +-
 gcc/d/dmd/ctfeexpr.d                          |  227 +--
 gcc/d/dmd/dcast.d                             |    1 +
 gcc/d/dmd/dmangle.d                           |    1 +
 gcc/d/dmd/dmodule.d                           |    2 +-
 gcc/d/dmd/dsymbolsem.d                        |   13 +-
 gcc/d/dmd/dtemplate.d                         |    1 +
 gcc/d/dmd/expression.d                        | 1424 ++---------------
 gcc/d/dmd/expression.h                        |   49 +-
 gcc/d/dmd/expressionsem.d                     | 1313 ++++++++++++++-
 gcc/d/dmd/globals.d                           |    6 +
 gcc/d/dmd/hdrgen.d                            |    1 +
 gcc/d/dmd/iasmgcc.d                           |    4 +-
 gcc/d/dmd/initsem.d                           |    1 +
 gcc/d/dmd/lexer.d                             |   18 +
 gcc/d/dmd/opover.d                            |    1 +
 gcc/d/dmd/optimize.d                          |   16 +-
 gcc/d/dmd/semantic3.d                         |    3 +-
 gcc/d/dmd/statementsem.d                      |    3 +-
 gcc/d/dmd/staticcond.d                        |    1 +
 gcc/d/dmd/traits.d                            |   17 +-
 gcc/d/dmd/typesem.d                           |    7 +-
 gcc/testsuite/gdc.test/compilable/previewin.d |    6 -
 .../gdc.test/fail_compilation/fail212.d       |    6 +-
 gcc/testsuite/gdc.test/runnable/previewin.d   |   20 +-
 libphobos/src/MERGE                           |    2 +-
 libphobos/src/std/container/array.d           |   11 +
 libphobos/src/std/logger/package.d            |    1 +
 libphobos/src/std/range/primitives.d          |   15 +
 34 files changed, 1644 insertions(+), 1559 deletions(-)
diff mbox series

Patch

diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 235db4b2ef1..0cf9b5fd4a8 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-643b1261bba0757d97efa3ff1f63e461271eb000
+65a3da148c0c700a6c928f0e13799b2a7d34fcbe
 
 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/arrayop.d b/gcc/d/dmd/arrayop.d
index 25bbb3f32ad..c3b8526a9a4 100644
--- a/gcc/d/dmd/arrayop.d
+++ b/gcc/d/dmd/arrayop.d
@@ -172,7 +172,7 @@  Expression arrayOp(BinAssignExp e, Scope* sc)
     }
     if (e.e1.op == EXP.arrayLiteral)
     {
-        return e.e1.modifiableLvalue(sc, e.e1);
+        return e.e1.modifiableLvalue(sc);
     }
 
     return arrayOp(e.isBinExp(), sc);
diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
index 8aece3bdd71..67305922df6 100644
--- a/gcc/d/dmd/canthrow.d
+++ b/gcc/d/dmd/canthrow.d
@@ -22,6 +22,7 @@  import dmd.declaration;
 import dmd.dsymbol;
 import dmd.errorsink;
 import dmd.expression;
+import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
 import dmd.init;
@@ -80,7 +81,7 @@  CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink)
                     if (!f.isDtorDeclaration())
                         errorSupplementalInferredAttr(f, 10, false, STC.nothrow_);
 
-                    e.checkOverriddenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
+                    f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
                 }
                 else if (func)
                 {
diff --git a/gcc/d/dmd/compiler.d b/gcc/d/dmd/compiler.d
index e85cc202281..8b8a453d45e 100644
--- a/gcc/d/dmd/compiler.d
+++ b/gcc/d/dmd/compiler.d
@@ -12,6 +12,7 @@ 
 module dmd.compiler;
 
 import dmd.arraytypes;
+import dmd.ctfeexpr;
 import dmd.dmodule;
 import dmd.expression;
 import dmd.mtype;
diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d
index a8d099433a2..568b639e0b6 100644
--- a/gcc/d/dmd/cond.d
+++ b/gcc/d/dmd/cond.d
@@ -29,6 +29,7 @@  import dmd.globals;
 import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
+import dmd.optimize;
 import dmd.typesem;
 import dmd.common.outbuffer;
 import dmd.rootobject;
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index b8e80527ec7..f0c834972d6 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -2023,6 +2023,9 @@  final class CParser(AST) : Parser!AST
                 }
                 symbols.push(s);
             }
+            if (level == LVL.global && !id)
+                error("expected identifier for declaration");
+
             first = false;
 
             switch (token.value)
@@ -2736,7 +2739,7 @@  final class CParser(AST) : Parser!AST
     private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase,
         out Identifier pident, ref Specifier specifier)
     {
-        //printf("cparseDeclarator(%d, %p)\n", declarator, t);
+        //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars());
         AST.Types constTypes; // all the Types that will need `const` applied to them
 
         /* Insert tx -> t into
@@ -2755,6 +2758,7 @@  final class CParser(AST) : Parser!AST
 
         AST.Type parseDecl(AST.Type t)
         {
+            //printf("parseDecl() t: %s\n", t.toChars());
             AST.Type ts;
             while (1)
             {
@@ -2770,9 +2774,18 @@  final class CParser(AST) : Parser!AST
                     break;
 
                 case TOK.leftParenthesis:   // ( declarator )
+                    //printf("leftParen\n");
                     /* like: T (*fp)();
                      *       T ((*fp))();
                      */
+                    auto tk = &token;
+                    if (!isCDeclarator(tk, declarator))
+                    {
+                        /* Not ( declarator ), might be parameter-list
+                         */
+                        ts = t;
+                        break;
+                    }
                     nextToken();
 
                     if (token.value == TOK.__stdcall) // T (__stdcall*fp)();
@@ -2786,6 +2799,7 @@  final class CParser(AST) : Parser!AST
                     break;
 
                 case TOK.mul:               // pointer
+                    //printf("star\n");
                     t = new AST.TypePointer(t);
                     nextToken();
                     // add post fixes const/volatile/restrict/_Atomic
@@ -2797,6 +2811,7 @@  final class CParser(AST) : Parser!AST
                     continue;
 
                 default:
+                    //printf("default %s\n", token.toChars());
                     if (declarator == DTR.xdirect)
                     {
                         if (!t || t.isTypeIdentifier())
@@ -2914,7 +2929,7 @@  final class CParser(AST) : Parser!AST
                         if (specifier._pure)
                             stc |= STC.pure_;
                         AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
-    //                  tf = tf.addSTC(storageClass);  // TODO
+                        //tf = tf.addSTC(storageClass);  // TODO
                         insertTx(ts, tf, t);  // ts -> ... -> tf -> t
 
                         if (ts != tf)
@@ -2927,6 +2942,8 @@  final class CParser(AST) : Parser!AST
                 }
                 break;
             }
+            if (declarator == DTR.xdirect && !pident)
+                error("expected identifier for declarator");
             return ts;
         }
 
@@ -4556,6 +4573,7 @@  final class CParser(AST) : Parser!AST
      */
     private bool isCDeclarator(ref Token* pt, DTR declarator)
     {
+        //printf("isCDeclarator()\n");
         auto t = pt;
         while (1)
         {
@@ -4578,6 +4596,8 @@  final class CParser(AST) : Parser!AST
         else if (t.value == TOK.leftParenthesis)
         {
             t = peek(t);
+            if (t.value == TOK.__stdcall)
+                t = peek(t);
             if (!isCDeclarator(t, declarator))
                 return false;
             if (t.value != TOK.rightParenthesis)
diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d
index ddfb57d22d7..c93269fb321 100644
--- a/gcc/d/dmd/ctfeexpr.d
+++ b/gcc/d/dmd/ctfeexpr.d
@@ -35,75 +35,98 @@  import dmd.root.rmem;
 import dmd.tokens;
 import dmd.visitor;
 
-
-/***********************************************************
- * A reference to a class, or an interface. We need this when we
- * point to a base class (we must record what the type is).
+/****************************************************************/
+/* A type meant as a union of all the Expression types,
+ * to serve essentially as a Variant that will sit on the stack
+ * during CTFE to reduce memory consumption.
  */
-extern (C++) final class ClassReferenceExp : Expression
+extern (D) struct UnionExp
 {
-    StructLiteralExp value;
-
-    extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe
+    // yes, default constructor does nothing
+    extern (D) this(Expression e)
     {
-        super(loc, EXP.classReference);
-        assert(lit && lit.sd && lit.sd.isClassDeclaration());
-        this.value = lit;
-        this.type = type;
+        memcpy(&this, cast(void*)e, e.size);
     }
 
-    ClassDeclaration originalClass()
+    /* Extract pointer to Expression
+     */
+    extern (D) Expression exp() return
     {
-        return value.sd.isClassDeclaration();
+        return cast(Expression)&u;
     }
 
-    // Return index of the field, or -1 if not found
-    private int getFieldIndex(Type fieldtype, uint fieldoffset)
+    /* Convert to an allocated Expression
+     */
+    extern (D) Expression copy()
     {
-        ClassDeclaration cd = originalClass();
-        uint fieldsSoFar = 0;
-        for (size_t j = 0; j < value.elements.length; j++)
+        Expression e = exp();
+        //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
+        assert(e.size <= u.sizeof);
+        switch (e.op)
         {
-            while (j - fieldsSoFar >= cd.fields.length)
-            {
-                fieldsSoFar += cd.fields.length;
-                cd = cd.baseClass;
-            }
-            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
-            if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
-            {
-                return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
-            }
+            case EXP.cantExpression:    return CTFEExp.cantexp;
+            case EXP.voidExpression:    return CTFEExp.voidexp;
+            case EXP.break_:            return CTFEExp.breakexp;
+            case EXP.continue_:         return CTFEExp.continueexp;
+            case EXP.goto_:             return CTFEExp.gotoexp;
+            default:                    return e.copy();
         }
-        return -1;
     }
 
-    // Return index of the field, or -1 if not found
-    // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
-    int findFieldIndexByName(VarDeclaration v)
-    {
-        ClassDeclaration cd = originalClass();
-        size_t fieldsSoFar = 0;
-        for (size_t j = 0; j < value.elements.length; j++)
-        {
-            while (j - fieldsSoFar >= cd.fields.length)
-            {
-                fieldsSoFar += cd.fields.length;
-                cd = cd.baseClass;
-            }
-            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
-            if (v == v2)
-            {
-                return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
-            }
-        }
-        return -1;
-    }
+private:
+    // Ensure that the union is suitably aligned.
+    align(8) union _AnonStruct_u
+    {
+        char[__traits(classInstanceSize, Expression)] exp;
+        char[__traits(classInstanceSize, IntegerExp)] integerexp;
+        char[__traits(classInstanceSize, ErrorExp)] errorexp;
+        char[__traits(classInstanceSize, RealExp)] realexp;
+        char[__traits(classInstanceSize, ComplexExp)] complexexp;
+        char[__traits(classInstanceSize, SymOffExp)] symoffexp;
+        char[__traits(classInstanceSize, StringExp)] stringexp;
+        char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
+        char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
+        char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
+        char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp;
+        char[__traits(classInstanceSize, NullExp)] nullexp;
+        char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
+        char[__traits(classInstanceSize, AddrExp)] addrexp;
+        char[__traits(classInstanceSize, IndexExp)] indexexp;
+        char[__traits(classInstanceSize, SliceExp)] sliceexp;
+        char[__traits(classInstanceSize, VectorExp)] vectorexp;
+    }
+
+    _AnonStruct_u u;
+}
 
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
+void emplaceExp(T : Expression, Args...)(void* p, Args args)
+{
+    static if (__VERSION__ < 2099)
+        const init = typeid(T).initializer;
+    else
+        const init = __traits(initSymbol, T);
+    p[0 .. __traits(classInstanceSize, T)] = init[];
+    (cast(T)p).__ctor(args);
+}
+
+void emplaceExp(T : UnionExp)(T* p, Expression e)
+{
+    memcpy(p, cast(void*)e, e.size);
+}
+
+// Generate an error message when this exception is not caught
+void generateUncaughtError(ThrownExceptionExp tee)
+{
+    UnionExp ue = void;
+    Expression e = resolveSlice((*tee.thrown.value.elements)[0], &ue);
+    StringExp se = e.toStringExp();
+    error(tee.thrown.loc, "uncaught CTFE exception `%s(%s)`", tee.thrown.type.toChars(), se ? se.toChars() : e.toChars());
+    /* Also give the line where the throw statement was. We won't have it
+     * in the case where the ThrowStatement is generated internally
+     * (eg, in ScopeStatement)
+     */
+    if (tee.loc.isValid() && !tee.loc.equals(tee.thrown.loc))
+        .errorSupplemental(tee.loc, "thrown from here");
 }
 
 /*************************
@@ -121,100 +144,6 @@  int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pur
     return -1;
 }
 
-/***********************************************************
- * Fake class which holds the thrown exception.
- * Used for implementing exception handling.
- */
-extern (C++) final class ThrownExceptionExp : Expression
-{
-    ClassReferenceExp thrown;   // the thing being tossed
-
-    extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe
-    {
-        super(loc, EXP.thrownException);
-        this.thrown = victim;
-        this.type = victim.type;
-    }
-
-    override const(char)* toChars() const
-    {
-        return "CTFE ThrownException";
-    }
-
-    // Generate an error message when this exception is not caught
-    extern (D) void generateUncaughtError()
-    {
-        UnionExp ue = void;
-        Expression e = resolveSlice((*thrown.value.elements)[0], &ue);
-        StringExp se = e.toStringExp();
-        error(thrown.loc, "uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars());
-        /* Also give the line where the throw statement was. We won't have it
-         * in the case where the ThrowStatement is generated internally
-         * (eg, in ScopeStatement)
-         */
-        if (loc.isValid() && !loc.equals(thrown.loc))
-            .errorSupplemental(loc, "thrown from here");
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * This type is only used by the interpreter.
- */
-extern (C++) final class CTFEExp : Expression
-{
-    extern (D) this(EXP tok)
-    {
-        super(Loc.initial, tok);
-        type = Type.tvoid;
-    }
-
-    override const(char)* toChars() const
-    {
-        switch (op)
-        {
-        case EXP.cantExpression:
-            return "<cant>";
-        case EXP.voidExpression:
-            return "cast(void)0";
-        case EXP.showCtfeContext:
-            return "<error>";
-        case EXP.break_:
-            return "<break>";
-        case EXP.continue_:
-            return "<continue>";
-        case EXP.goto_:
-            return "<goto>";
-        default:
-            assert(0);
-        }
-    }
-
-    extern (D) __gshared CTFEExp cantexp;
-    extern (D) __gshared CTFEExp voidexp;
-    extern (D) __gshared CTFEExp breakexp;
-    extern (D) __gshared CTFEExp continueexp;
-    extern (D) __gshared CTFEExp gotoexp;
-    /* Used when additional information is needed regarding
-     * a ctfe error.
-     */
-    extern (D) __gshared CTFEExp showcontext;
-
-    extern (D) static bool isCantExp(const Expression e) @safe
-    {
-        return e && e.op == EXP.cantExpression;
-    }
-
-    extern (D) static bool isGotoExp(const Expression e) @safe
-    {
-        return e && e.op == EXP.goto_;
-    }
-}
-
 // True if 'e' is CTFEExp::cantexp, or an exception
 bool exceptionOrCantInterpret(const Expression e) @safe
 {
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
index f769473f591..eb3890bb58f 100644
--- a/gcc/d/dmd/dcast.d
+++ b/gcc/d/dmd/dcast.d
@@ -38,6 +38,7 @@  import dmd.init;
 import dmd.intrange;
 import dmd.mtype;
 import dmd.opover;
+import dmd.optimize;
 import dmd.root.ctfloat;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d
index c58b5857482..2bedccb71c1 100644
--- a/gcc/d/dmd/dmangle.d
+++ b/gcc/d/dmd/dmangle.d
@@ -152,6 +152,7 @@  import dmd.identifier;
 import dmd.mtype;
 import dmd.root.ctfloat;
 import dmd.common.outbuffer;
+import dmd.optimize;
 import dmd.root.aav;
 import dmd.root.string;
 import dmd.root.stringtable;
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index 548928a0124..e6dde181fab 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -793,7 +793,7 @@  extern (C++) final class Module : Package
         }
         else
         {
-            const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+            const bool doUnittests = global.params.parsingUnittestsRequired();
             scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests);
             p.transitionIn = global.params.v.vin;
             p.nextToken();
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 397c5e53d7f..637edd7bf08 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -57,6 +57,7 @@  import dmd.nogc;
 import dmd.nspace;
 import dmd.objc;
 import dmd.opover;
+import dmd.optimize;
 import dmd.parse;
 import dmd.root.array;
 import dmd.root.filename;
@@ -1951,7 +1952,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
         const len = buf.length;
         buf.writeByte(0);
         const str = buf.extractSlice()[0 .. len];
-        const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+        const bool doUnittests = global.params.parsingUnittestsRequired();
         auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut);
         scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
         p.transitionIn = global.params.v.vin;
@@ -3384,9 +3385,13 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
             if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
             {
-                OutBuffer buf;
-                MODtoBuffer(buf, tf.mod);
-                .error(funcdecl.loc, "%s `%s` without `this` cannot be `%s`", funcdecl.kind, funcdecl.toPrettyChars, buf.peekChars());
+                import core.bitop;
+                auto mods = MODtoChars(tf.mod);
+                .error(funcdecl.loc, "%s `%s` without `this` cannot be `%s`", funcdecl.kind, funcdecl.toPrettyChars, mods);
+                if (tf.next && tf.next.ty != Tvoid && popcnt(tf.mod) == 1)
+                    .errorSupplemental(funcdecl.loc,
+                        "did you mean to use `%s(%s)` as the return type?", mods, tf.next.toChars());
+
                 tf.mod = 0; // remove qualifiers
             }
 
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index 883f4ac94cf..4cf1bae7537 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -69,6 +69,7 @@  import dmd.initsem;
 import dmd.location;
 import dmd.mtype;
 import dmd.opover;
+import dmd.optimize;
 import dmd.root.array;
 import dmd.common.outbuffer;
 import dmd.rootobject;
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index 87611f4690f..47902213504 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -18,25 +18,19 @@  import core.stdc.stdio;
 import core.stdc.string;
 
 import dmd.aggregate;
-import dmd.aliasthis;
-import dmd.arrayop;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.ast_node;
 import dmd.gluelayer;
-import dmd.ctfeexpr;
-import dmd.ctorflow;
 import dmd.dclass;
 import dmd.declaration;
 import dmd.dimport;
 import dmd.dmodule;
-import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.errorsink;
-import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
 import dmd.hdrgen;
@@ -45,39 +39,20 @@  import dmd.identifier;
 import dmd.init;
 import dmd.location;
 import dmd.mtype;
-import dmd.opover;
-import dmd.optimize;
 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.rootobject;
 import dmd.root.string;
 import dmd.root.utf;
-import dmd.safe;
 import dmd.target;
 import dmd.tokens;
 import dmd.visitor;
 
 enum LOGSEMANTIC = false;
 
-void emplaceExp(T : Expression, Args...)(void* p, Args args)
-{
-    static if (__VERSION__ < 2099)
-        const init = typeid(T).initializer;
-    else
-        const init = __traits(initSymbol, T);
-    p[0 .. __traits(classInstanceSize, T)] = init[];
-    (cast(T)p).__ctor(args);
-}
-
-void emplaceExp(T : UnionExp)(T* p, Expression e)
-{
-    memcpy(p, cast(void*)e, e.size);
-}
-
 /// Return value for `checkModifiable`
 enum Modifiable
 {
@@ -118,45 +93,6 @@  inout(Expression) lastComma(inout Expression e)
 
 }
 
-/***********************************
- * Determine if a `this` is needed to access `d`.
- * Params:
- *      sc = context
- *      d = declaration to check
- * Returns:
- *      true means a `this` is needed
- */
-bool isNeedThisScope(Scope* sc, Declaration d)
-{
-    if (sc.intypeof == 1)
-        return false;
-
-    AggregateDeclaration ad = d.isThis();
-    if (!ad)
-        return false;
-    //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
-
-    for (Dsymbol s = sc.parent; s; s = s.toParentLocal())
-    {
-        //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
-        if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
-        {
-            if (ad2 == ad)
-                return false;
-            else if (ad2.isNested())
-                continue;
-            else
-                return true;
-        }
-        if (FuncDeclaration f = s.isFuncDeclaration())
-        {
-            if (f.isMemberLocal())
-                break;
-        }
-    }
-    return true;
-}
-
 /****************************************
  * Expand tuples in-place.
  *
@@ -315,70 +251,6 @@  TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe
     return null;
 }
 
-/****************************************************************/
-/* A type meant as a union of all the Expression types,
- * to serve essentially as a Variant that will sit on the stack
- * during CTFE to reduce memory consumption.
- */
-extern (D) struct UnionExp
-{
-    // yes, default constructor does nothing
-    extern (D) this(Expression e)
-    {
-        memcpy(&this, cast(void*)e, e.size);
-    }
-
-    /* Extract pointer to Expression
-     */
-    extern (D) Expression exp() return
-    {
-        return cast(Expression)&u;
-    }
-
-    /* Convert to an allocated Expression
-     */
-    extern (D) Expression copy()
-    {
-        Expression e = exp();
-        //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
-        assert(e.size <= u.sizeof);
-        switch (e.op)
-        {
-            case EXP.cantExpression:    return CTFEExp.cantexp;
-            case EXP.voidExpression:    return CTFEExp.voidexp;
-            case EXP.break_:            return CTFEExp.breakexp;
-            case EXP.continue_:         return CTFEExp.continueexp;
-            case EXP.goto_:             return CTFEExp.gotoexp;
-            default:                    return e.copy();
-        }
-    }
-
-private:
-    // Ensure that the union is suitably aligned.
-    align(8) union _AnonStruct_u
-    {
-        char[__traits(classInstanceSize, Expression)] exp;
-        char[__traits(classInstanceSize, IntegerExp)] integerexp;
-        char[__traits(classInstanceSize, ErrorExp)] errorexp;
-        char[__traits(classInstanceSize, RealExp)] realexp;
-        char[__traits(classInstanceSize, ComplexExp)] complexexp;
-        char[__traits(classInstanceSize, SymOffExp)] symoffexp;
-        char[__traits(classInstanceSize, StringExp)] stringexp;
-        char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
-        char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
-        char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
-        char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp;
-        char[__traits(classInstanceSize, NullExp)] nullexp;
-        char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
-        char[__traits(classInstanceSize, AddrExp)] addrexp;
-        char[__traits(classInstanceSize, IndexExp)] indexexp;
-        char[__traits(classInstanceSize, SliceExp)] sliceexp;
-        char[__traits(classInstanceSize, VectorExp)] vectorexp;
-    }
-
-    _AnonStruct_u u;
-}
-
 /************************ TypeDotIdExp ************************************/
 /* Things like:
  *      int.size
@@ -678,71 +550,6 @@  extern (C++) abstract class Expression : ASTNode
         return false;
     }
 
-    /*******************************
-     * Give error if we're not an lvalue.
-     * If we can, convert expression to be an lvalue.
-     */
-    Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (!e)
-            e = this;
-        else if (!loc.isValid())
-            loc = e.loc;
-
-        if (e.op == EXP.type)
-            error(loc, "`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
-        else
-            error(loc, "`%s` is not an lvalue and cannot be modified", e.toChars());
-
-        return ErrorExp.get();
-    }
-
-    Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars());
-        // See if this expression is a modifiable lvalue (i.e. not const)
-        if (checkModifiable(this, sc) == Modifiable.yes)
-        {
-            assert(type);
-            if (!type.isMutable())
-            {
-                if (auto dve = this.isDotVarExp())
-                {
-                    if (isNeedThisScope(sc, dve.var))
-                        for (Dsymbol s = sc.func; s; s = s.toParentLocal())
-                    {
-                        FuncDeclaration ff = s.isFuncDeclaration();
-                        if (!ff)
-                            break;
-                        if (!ff.type.isMutable)
-                        {
-                            error(loc, "cannot modify `%s` in `%s` function", toChars(), MODtoChars(type.mod));
-                            return ErrorExp.get();
-                        }
-                    }
-                }
-                error(loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), toChars());
-                return ErrorExp.get();
-            }
-            else if (!type.isAssignable())
-            {
-                error(loc, "cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members",
-                    toChars(), type.toChars());
-                return ErrorExp.get();
-            }
-        }
-        return toLvalue(sc, e);
-    }
-
-    /****************************************
-     * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
-     */
-    Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        this.loc = loc;
-        return this;
-    }
-
     /****************************************
      * Check that the expression has a valid type.
      * If not, generates an error "... has no type".
@@ -835,417 +642,6 @@  extern (C++) abstract class Expression : ASTNode
         return checkValue();
     }
 
-    extern (D) final bool checkDeprecated(Scope* sc, Dsymbol s)
-    {
-        return s.checkDeprecated(loc, sc);
-    }
-
-    extern (D) final bool checkDisabled(Scope* sc, Dsymbol s)
-    {
-        if (auto d = s.isDeclaration())
-        {
-            return d.checkDisabled(loc, sc);
-        }
-
-        return false;
-    }
-
-    /*********************************************
-     * Calling function f.
-     * Check the purity, i.e. if we're in a pure function
-     * we can only call other pure functions.
-     * Returns true if error occurs.
-     */
-    extern (D) final bool checkPurity(Scope* sc, FuncDeclaration f)
-    {
-        if (!sc.func)
-            return false;
-        if (sc.func == f)
-            return false;
-        if (sc.intypeof == 1)
-            return false;
-        if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
-            return false;
-
-        // If the call has a pure parent, then the called func must be pure.
-        if (!f.isPure() && checkImpure(sc, loc, null, f))
-        {
-            error(loc, "`pure` %s `%s` cannot call impure %s `%s`",
-                sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
-                f.toPrettyChars());
-
-            if (!f.isDtorDeclaration())
-                errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_);
-
-            checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one
-     * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but
-     * the generated dtor is not).
-     * In that case the method will identify and print all members causing the attribute
-     * missmatch.
-     *
-     * Params:
-     *   sc = scope
-     *   f  = potential `DtorDeclaration`
-     *   check = current check (e.g. whether it's pure)
-     *   checkName = the kind of check (e.g. `"pure"`)
-     */
-    extern (D) final void checkOverriddenDtor(Scope* sc, FuncDeclaration f,
-                scope bool function(DtorDeclaration) check, const string checkName
-    ) {
-        auto dd = f.isDtorDeclaration();
-        if (!dd || !dd.isGenerated())
-            return;
-
-        // DtorDeclaration without parents should fail at an earlier stage
-        auto ad = cast(AggregateDeclaration) f.toParent2();
-        assert(ad);
-
-        if (ad.userDtors.length)
-        {
-            if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
-                return;
-
-            // Sanity check
-            assert(!check(ad.fieldDtor));
-        }
-
-        dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
-                            dd.isGenerated() ? "generated " : "".ptr,
-                            ad.toChars,
-                            cast(int) checkName.length, checkName.ptr);
-
-        // Search for the offending fields
-        foreach (field; ad.fields)
-        {
-            // Only structs may define automatically called destructors
-            auto ts = field.type.isTypeStruct();
-            if (!ts)
-            {
-                // But they might be part of a static array
-                auto ta = field.type.isTypeSArray();
-                if (!ta)
-                    continue;
-
-                ts = ta.baseElemOf().isTypeStruct();
-                if (!ts)
-                    continue;
-            }
-
-            auto fieldSym = ts.toDsymbol(sc);
-            assert(fieldSym); // Resolving ts must succeed because missing defs. should error before
-
-            auto fieldSd = fieldSym.isStructDeclaration();
-            assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR
-
-            if (fieldSd.dtor && !check(fieldSd.dtor))
-            {
-                field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
-
-                if (fieldSd.dtor.isGenerated())
-                    checkOverriddenDtor(sc, fieldSd.dtor, check, checkName);
-                else
-                    fieldSd.dtor.loc.errorSupplemental("   %.*s `%s.~this` is declared here",
-                                            cast(int) checkName.length, checkName.ptr, fieldSd.toChars());
-            }
-        }
-    }
-
-    /*******************************************
-     * Accessing variable v.
-     * Check for purity and safety violations.
-     * Returns true if error occurs.
-     */
-    extern (D) final bool checkPurity(Scope* sc, VarDeclaration v)
-    {
-        //printf("v = %s %s\n", v.type.toChars(), v.toChars());
-        /* Look for purity and safety violations when accessing variable v
-         * from current function.
-         */
-        if (!sc.func)
-            return false;
-        if (sc.intypeof == 1)
-            return false; // allow violations inside typeof(expression)
-        if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
-            return false; // allow violations inside compile-time evaluated expressions and debug conditionals
-        if (v.ident == Id.ctfe)
-            return false; // magic variable never violates pure and safe
-        if (v.isImmutable())
-            return false; // always safe and pure to access immutables...
-        if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
-            return false; // or const global/parameter values which have no mutable indirections
-        if (v.storage_class & STC.manifest)
-            return false; // ...or manifest constants
-
-        // accessing empty structs is pure
-        // https://issues.dlang.org/show_bug.cgi?id=18694
-        // https://issues.dlang.org/show_bug.cgi?id=21464
-        // https://issues.dlang.org/show_bug.cgi?id=23589
-        if (v.type.ty == Tstruct)
-        {
-            StructDeclaration sd = (cast(TypeStruct)v.type).sym;
-            if (sd.members) // not opaque
-            {
-                if (sd.semanticRun >= PASS.semanticdone)
-                    sd.determineSize(v.loc);
-                if (sd.hasNoFields)
-                    return false;
-            }
-        }
-
-        bool err = false;
-        if (v.isDataseg())
-        {
-            // https://issues.dlang.org/show_bug.cgi?id=7533
-            // Accessing implicit generated __gate is pure.
-            if (v.ident == Id.gate)
-                return false;
-
-            if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v))
-            {
-                error(loc, "`pure` %s `%s` cannot access mutable static data `%s`",
-                    sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
-                err = true;
-            }
-        }
-        else
-        {
-            /* Given:
-             * void f() {
-             *   int fx;
-             *   pure void g() {
-             *     int gx;
-             *     /+pure+/ void h() {
-             *       int hx;
-             *       /+pure+/ void i() { }
-             *     }
-             *   }
-             * }
-             * i() can modify hx and gx but not fx
-             */
-
-            Dsymbol vparent = v.toParent2();
-            for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent))
-            {
-                if (s == vparent)
-                    break;
-
-                if (AggregateDeclaration ad = s.isAggregateDeclaration())
-                {
-                    if (ad.isNested())
-                        continue;
-                    break;
-                }
-                FuncDeclaration ff = s.isFuncDeclaration();
-                if (!ff)
-                    break;
-                if (ff.isNested() || ff.isThis())
-                {
-                    if (ff.type.isImmutable() ||
-                        ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
-                    {
-                        OutBuffer ffbuf;
-                        OutBuffer vbuf;
-                        MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
-                        MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
-                        error(loc, "%s%s `%s` cannot access %sdata `%s`",
-                            ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
-                        err = true;
-                        break;
-                    }
-                    continue;
-                }
-                break;
-            }
-        }
-
-        /* Do not allow safe functions to access __gshared data
-         */
-        if (v.storage_class & STC.gshared)
-        {
-            if (sc.setUnsafe(false, this.loc,
-                "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v))
-            {
-                err = true;
-            }
-        }
-
-        return err;
-    }
-
-    /*
-    Check if sc.func is impure or can be made impure.
-    Returns true on error, i.e. if sc.func is pure and cannot be made impure.
-    */
-    private static bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0)
-    {
-        return sc.func && (isRootTraitsCompilesScope(sc)
-                ? sc.func.isPureBypassingInference() >= PURE.weak
-                : sc.func.setImpure(loc, fmt, arg0));
-    }
-
-    /*********************************************
-     * Calling function f.
-     * Check the safety, i.e. if we're in a @safe function
-     * we can only call @safe or @trusted functions.
-     * Returns true if error occurs.
-     */
-    extern (D) final bool checkSafety(Scope* sc, FuncDeclaration f)
-    {
-        if (sc.func == f)
-            return false;
-        if (sc.intypeof == 1)
-            return false;
-        if (sc.flags & SCOPE.debug_)
-            return false;
-        if ((sc.flags & SCOPE.ctfe) && sc.func)
-            return false;
-
-        if (!sc.func)
-        {
-            if (sc.varDecl && !f.safetyInprocess && !f.isSafe() && !f.isTrusted())
-            {
-                if (sc.varDecl.storage_class & STC.safe)
-                {
-                    error(loc, "`@safe` variable `%s` cannot be initialized by calling `@system` function `%s`",
-                        sc.varDecl.toChars(), f.toChars());
-                    return true;
-                }
-                else
-                {
-                    sc.varDecl.storage_class |= STC.system;
-                    sc.varDecl.systemInferred = true;
-                }
-            }
-            return false;
-        }
-
-        if (!f.isSafe() && !f.isTrusted())
-        {
-            if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f))
-            {
-                if (!loc.isValid()) // e.g. implicitly generated dtor
-                    loc = sc.func.loc;
-
-                const prettyChars = f.toPrettyChars();
-                error(loc, "`@safe` %s `%s` cannot call `@system` %s `%s`",
-                    sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
-                    prettyChars);
-                if (!f.isDtorDeclaration)
-                    errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe);
-                .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
-
-                checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
-
-                return true;
-            }
-        }
-        else if (f.isSafe() && f.safetyViolation)
-        {
-            // for dip1000 by default transition, print deprecations for calling functions that will become `@system`
-            if (sc.func.isSafeBypassingInference())
-            {
-                .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars());
-                errorSupplementalInferredAttr(f, 10, true, STC.safe);
-            }
-            else if (!sc.func.safetyViolation)
-            {
-                import dmd.func : AttributeViolation;
-                sc.func.safetyViolation = new AttributeViolation(this.loc, null, f, null, null);
-            }
-        }
-        return false;
-    }
-
-    /*********************************************
-     * Calling function f.
-     * Check the @nogc-ness, i.e. if we're in a @nogc function
-     * we can only call other @nogc functions.
-     * Returns true if error occurs.
-     */
-    extern (D) final bool checkNogc(Scope* sc, FuncDeclaration f)
-    {
-        if (!sc.func)
-            return false;
-        if (sc.func == f)
-            return false;
-        if (sc.intypeof == 1)
-            return false;
-        if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
-            return false;
-        /* The original expressions (`new S(...)` or `new S[...]``) will be
-         * verified instead. This is to keep errors related to the original code
-         * and not the lowering.
-         */
-        if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT)
-            return false;
-
-        if (!f.isNogc())
-        {
-            if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
-            {
-                if (loc.linnum == 0) // e.g. implicitly generated dtor
-                    loc = sc.func.loc;
-
-                // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
-                // so don't print anything to avoid double error messages.
-                if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
-                    || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
-                    || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
-                {
-                    error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
-                        sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
-
-                    if (!f.isDtorDeclaration)
-                        f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
-                }
-
-                checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
-
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /********************************************
-     * Check that the postblit is callable if t is an array of structs.
-     * Returns true if error happens.
-     */
-    extern (D) final bool checkPostblit(Scope* sc, Type t)
-    {
-        if (auto ts = t.baseElemOf().isTypeStruct())
-        {
-            if (global.params.useTypeInfo && Type.dtypeinfo)
-            {
-                // https://issues.dlang.org/show_bug.cgi?id=11395
-                // Require TypeInfo generation for array concatenation
-                semanticTypeInfo(sc, t);
-            }
-
-            StructDeclaration sd = ts.sym;
-            if (sd.postblit)
-            {
-                if (sd.postblit.checkDisabled(loc, sc))
-                    return true;
-
-                //checkDeprecated(sc, sd.postblit);        // necessary?
-                checkPurity(sc, sd.postblit);
-                checkSafety(sc, sd.postblit);
-                checkNogc(sc, sd.postblit);
-                //checkAccess(sd, loc, sc, sd.postblit);   // necessary?
-                return false;
-            }
-        }
-        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)
@@ -1308,11 +704,6 @@  extern (C++) abstract class Expression : ASTNode
         return this;
     }
 
-    final Expression optimize(int result, bool keepLvalue = false)
-    {
-        return Expression_optimize(this, result, keepLvalue);
-    }
-
     final int isConst()
     {
         //printf("Expression::isConst(): %s\n", e.toChars());
@@ -1587,16 +978,6 @@  extern (C++) final class IntegerExp : Expression
         return typeof(return)(r);
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (!e)
-            e = this;
-        else if (!loc.isValid())
-            loc = e.loc;
-        error(e.loc, "cannot modify constant `%s`", e.toChars());
-        return ErrorExp.get();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1722,7 +1103,7 @@  extern (C++) final class IntegerExp : Expression
  */
 extern (C++) final class ErrorExp : Expression
 {
-    private extern (D) this()
+    extern (D) this()
     {
         super(Loc.initial, EXP.error);
         type = Type.terror;
@@ -1745,11 +1126,6 @@  extern (C++) final class ErrorExp : Expression
         return errorexp;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1987,11 +1363,6 @@  extern (C++) class IdentifierExp : Expression
         return true;
     }
 
-    override final Expression toLvalue(Scope* sc, Expression e)
-    {
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2036,11 +1407,6 @@  extern (C++) final class DsymbolExp : Expression
         return true;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2087,16 +1453,6 @@  extern (C++) class ThisExp : Expression
         return type.toBasetype().ty != Tclass;
     }
 
-    override final Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (type.toBasetype().ty == Tclass)
-        {
-            // Class `this` is an rvalue; struct `this` is an lvalue.
-            return Expression.toLvalue(sc, e);
-        }
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2464,18 +1820,6 @@  extern (C++) final class StringExp : Expression
         return (type && type.toBasetype().ty == Tsarray);
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
-        return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        error(loc, "cannot modify string literal `%s`", toChars());
-        return ErrorExp.get();
-    }
-
     /********************************
      * Convert string contents to a 0 terminated string,
      * allocated by mem.xmalloc().
@@ -3050,14 +2394,6 @@  extern (C++) final class StructLiteralExp : Expression
         return -1;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (sc.flags & SCOPE.Cfile)
-            return this;  // C struct literals are lvalues
-        else
-            return Expression.toLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3203,15 +2539,6 @@  extern (C++) final class TemplateExp : Expression
         return fd !is null;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (!fd)
-            return Expression.toLvalue(sc, e);
-
-        assert(sc);
-        return symbolToExp(fd, loc, sc, true);
-    }
-
     override bool checkType()
     {
         error(loc, "%s `%s` has no type", td.kind(), toChars());
@@ -3409,43 +2736,6 @@  extern (C++) final class VarExp : SymbolExp
         return true;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (var.storage_class & STC.manifest)
-        {
-            error(loc, "manifest constant `%s` cannot be modified", var.toChars());
-            return ErrorExp.get();
-        }
-        if (var.storage_class & STC.lazy_ && !delegateWasExtracted)
-        {
-            error(loc, "lazy variable `%s` cannot be modified", var.toChars());
-            return ErrorExp.get();
-        }
-        if (var.ident == Id.ctfe)
-        {
-            error(loc, "cannot modify compiler-generated variable `__ctfe`");
-            return ErrorExp.get();
-        }
-        if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574
-        {
-            error(loc, "cannot modify operator `$`");
-            return ErrorExp.get();
-        }
-        return this;
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        //printf("VarExp::modifiableLvalue('%s')\n", var.toChars());
-        if (var.storage_class & STC.manifest)
-        {
-            error(loc, "cannot modify manifest constant `%s`", toChars());
-            return ErrorExp.get();
-        }
-        // See if this expression is a modifiable lvalue (i.e. not const)
-        return Expression.modifiableLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3472,11 +2762,6 @@  extern (C++) final class OverExp : Expression
         return true;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3522,52 +2807,6 @@  extern (C++) final class FuncExp : Expression
         return false;
     }
 
-    extern (D) void genIdent(Scope* sc)
-    {
-        if (fd.ident == Id.empty)
-        {
-            const(char)[] s;
-            if (fd.fes)
-                s = "__foreachbody";
-            else if (fd.tok == TOK.reserved)
-                s = "__lambda";
-            else if (fd.tok == TOK.delegate_)
-                s = "__dgliteral";
-            else
-                s = "__funcliteral";
-
-            DsymbolTable symtab;
-            if (FuncDeclaration func = sc.parent.isFuncDeclaration())
-            {
-                if (func.localsymtab is null)
-                {
-                    // Inside template constraint, symtab is not set yet.
-                    // Initialize it lazily.
-                    func.localsymtab = new DsymbolTable();
-                }
-                symtab = func.localsymtab;
-            }
-            else
-            {
-                ScopeDsymbol sds = sc.parent.isScopeDsymbol();
-                if (!sds.symtab)
-                {
-                    // Inside template constraint, symtab may not be set yet.
-                    // Initialize it lazily.
-                    assert(sds.isTemplateInstance());
-                    sds.symtab = new DsymbolTable();
-                }
-                symtab = sds.symtab;
-            }
-            assert(symtab);
-            Identifier id = Identifier.generateId(s, symtab.length() + 1);
-            fd.ident = id;
-            if (td)
-                td.ident = id;
-            symtab.insert(td ? cast(Dsymbol)td : cast(Dsymbol)fd);
-        }
-    }
-
     override FuncExp syntaxCopy()
     {
         if (td)
@@ -3815,12 +3054,6 @@  extern (C++) abstract class UnaExp : Expression
 
     }
 
-    override final Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        e1 = e1.resolveLoc(loc, sc);
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3936,18 +3169,6 @@  extern (C++) class BinAssignExp : BinExp
         return true;
     }
 
-    override final Expression toLvalue(Scope* sc, Expression ex)
-    {
-        // Lvalue-ness will be handled in glue layer.
-        return this;
-    }
-
-    override final Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        // should check e1.checkModifiable() ?
-        return toLvalue(sc, this);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4134,78 +3355,26 @@  extern (C++) final class DotTemplateExp : UnaExp
  */
 extern (C++) final class DotVarExp : UnaExp
 {
-    Declaration var;
-    bool hasOverloads;
-
-    extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe
-    {
-        if (var.isVarDeclaration())
-            hasOverloads = false;
-
-        super(loc, EXP.dotVariable, e);
-        //printf("DotVarExp()\n");
-        this.var = var;
-        this.hasOverloads = hasOverloads;
-    }
-
-    override bool isLvalue()
-    {
-        if (e1.op != EXP.structLiteral)
-            return true;
-        auto vd = var.isVarDeclaration();
-        return !(vd && vd.isField());
-    }
-
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        //printf("DotVarExp::toLvalue(%s)\n", toChars());
-        if (sc && sc.flags & SCOPE.Cfile)
-        {
-            /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
-             * is an lvalue if the first expression is an lvalue.
-             */
-            if (!e1.isLvalue())
-                return Expression.toLvalue(sc, e);
-        }
-        if (!isLvalue())
-            return Expression.toLvalue(sc, e);
-        if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
-        {
-            if (VarDeclaration vd = var.isVarDeclaration())
-            {
-                auto ad = vd.isMember2();
-                if (ad && ad.fields.length == sc.ctorflow.fieldinit.length)
-                {
-                    foreach (i, f; ad.fields)
-                    {
-                        if (f == vd)
-                        {
-                            if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
-                            {
-                                /* If the address of vd is taken, assume it is thereby initialized
-                                 * https://issues.dlang.org/show_bug.cgi?id=15869
-                                 */
-                                modifyFieldVar(loc, sc, vd, e1);
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        return this;
-    }
+    Declaration var;
+    bool hasOverloads;
 
-    override Expression modifiableLvalue(Scope* sc, Expression e)
+    extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe
     {
-        version (none)
-        {
-            printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
-            printf("e1.type = %s\n", e1.type.toChars());
-            printf("var.type = %s\n", var.type.toChars());
-        }
+        if (var.isVarDeclaration())
+            hasOverloads = false;
+
+        super(loc, EXP.dotVariable, e);
+        //printf("DotVarExp()\n");
+        this.var = var;
+        this.hasOverloads = hasOverloads;
+    }
 
-        return Expression.modifiableLvalue(sc, e);
+    override bool isLvalue()
+    {
+        if (e1.op != EXP.structLiteral)
+            return true;
+        auto vd = var.isVarDeclaration();
+        return !(vd && vd.isField());
     }
 
     override void accept(Visitor v)
@@ -4239,49 +3408,6 @@  extern (C++) final class DotTemplateInstanceExp : UnaExp
         return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
     }
 
-    extern (D) bool findTempDecl(Scope* sc)
-    {
-        static if (LOGSEMANTIC)
-        {
-            printf("DotTemplateInstanceExp::findTempDecl('%s')\n", toChars());
-        }
-        if (ti.tempdecl)
-            return true;
-
-        Expression e = new DotIdExp(loc, e1, ti.name);
-        e = e.expressionSemantic(sc);
-        if (e.op == EXP.dot)
-            e = (cast(DotExp)e).e2;
-
-        Dsymbol s = null;
-        switch (e.op)
-        {
-        case EXP.overloadSet:
-            s = (cast(OverExp)e).vars;
-            break;
-
-        case EXP.dotTemplateDeclaration:
-            s = (cast(DotTemplateExp)e).td;
-            break;
-
-        case EXP.scope_:
-            s = (cast(ScopeExp)e).sds;
-            break;
-
-        case EXP.dotVariable:
-            s = (cast(DotVarExp)e).var;
-            break;
-
-        case EXP.variable:
-            s = (cast(VarExp)e).var;
-            break;
-
-        default:
-            return false;
-        }
-        return ti.updateTempDecl(sc, s);
-    }
-
     override bool checkType()
     {
         // Same logic as ScopeExp.checkType()
@@ -4486,13 +3612,6 @@  extern (C++) final class CallExp : UnaExp
         return false;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (isLvalue())
-            return this;
-        return Expression.toLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4600,30 +3719,6 @@  extern (C++) final class PtrExp : UnaExp
         return true;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        return this;
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars());
-        Declaration var;
-        if (auto se = e1.isSymOffExp())
-            var = se.var;
-        else if (auto ve = e1.isVarExp())
-            var = ve.var;
-        if (var && var.type.isFunction_Delegate_PtrToFunction())
-        {
-            if (var.type.isTypeFunction())
-                error(loc, "function `%s` is not an lvalue and cannot be modified", var.toChars());
-            else
-                error(loc, "function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
-            return ErrorExp.get();
-        }
-        return Expression.modifiableLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4755,19 +3850,6 @@  extern (C++) final class CastExp : UnaExp
             e1.type.mutableOf().unSharedOf().equals(to.mutableOf().unSharedOf());
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (sc && sc.flags & SCOPE.Cfile)
-        {
-            /* C11 6.5.4-5: A cast does not yield an lvalue.
-             */
-            return Expression.toLvalue(sc, e);
-        }
-        if (isLvalue())
-            return this;
-        return Expression.toLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4822,12 +3904,6 @@  extern (C++) final class VectorArrayExp : UnaExp
         return e1.isLvalue();
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        e1 = e1.toLvalue(sc, e);
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4885,18 +3961,6 @@  extern (C++) final class SliceExp : UnaExp
         return (type && type.toBasetype().ty == Tsarray);
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
-        return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        error(loc, "slice expression `%s` is not a modifiable lvalue", toChars());
-        return this;
-    }
-
     override Optional!bool toBool()
     {
         return e1.toBool();
@@ -4964,13 +4028,6 @@  extern (C++) final class ArrayExp : UnaExp
         return true;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (type && type.toBasetype().ty == Tvoid)
-            error(loc, "`void`s have no value");
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -5018,18 +4075,6 @@  extern (C++) final class CommaExp : BinExp
         return e2.isLvalue();
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        e2 = e2.toLvalue(sc, null);
-        return this;
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        e2 = e2.modifiableLvalue(sc, e);
-        return this;
-    }
-
     override Optional!bool toBool()
     {
         return e2.toBool();
@@ -5105,21 +4150,6 @@  extern (C++) final class DelegatePtrExp : UnaExp
         return e1.isLvalue();
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        e1 = e1.toLvalue(sc, e);
-        return this;
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        if (sc.setUnsafe(false, this.loc, "cannot modify delegate pointer in `@safe` code `%s`", this))
-        {
-            return ErrorExp.get();
-        }
-        return Expression.modifiableLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -5143,21 +4173,6 @@  extern (C++) final class DelegateFuncptrExp : UnaExp
         return e1.isLvalue();
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        e1 = e1.toLvalue(sc, e);
-        return this;
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        if (sc.setUnsafe(false, this.loc, "cannot modify delegate function pointer in `@safe` code `%s`", this))
-        {
-            return ErrorExp.get();
-        }
-        return Expression.modifiableLvalue(sc, e);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -5205,23 +4220,6 @@  extern (C++) final class IndexExp : BinExp
         return true;
     }
 
-    override Expression toLvalue(Scope* sc, Expression e)
-    {
-        if (isLvalue())
-            return this;
-        return Expression.toLvalue(sc, e);
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
-        Expression ex = markSettingAAElem();
-        if (ex.op == EXP.error)
-            return ex;
-
-        return Expression.modifiableLvalue(sc, e);
-    }
-
     extern (D) Expression markSettingAAElem()
     {
         if (e1.type.toBasetype().ty == Taarray)
@@ -5324,20 +4322,6 @@  extern (C++) class AssignExp : BinExp
         return true;
     }
 
-    override final Expression toLvalue(Scope* sc, Expression ex)
-    {
-        if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
-        {
-            return Expression.toLvalue(sc, ex);
-        }
-
-        /* In front-end level, AssignExp should make an lvalue of e1.
-         * Taking the address of e1 will be handled in low level layer,
-         * so this function does nothing.
-         */
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -5733,13 +4717,6 @@  extern (C++) final class CatExp : BinExp
         super(loc, EXP.concatenate, e1, e2);
     }
 
-    override Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        e1 = e1.resolveLoc(loc, sc);
-        e2 = e2.resolveLoc(loc, sc);
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -6074,28 +5051,6 @@  extern (C++) final class CondExp : BinExp
         return e1.isLvalue() && e2.isLvalue();
     }
 
-    override Expression toLvalue(Scope* sc, Expression ex)
-    {
-        // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
-        CondExp e = cast(CondExp)copy();
-        e.e1 = e1.toLvalue(sc, null).addressOf();
-        e.e2 = e2.toLvalue(sc, null).addressOf();
-        e.type = type.pointerTo();
-        return new PtrExp(loc, e, type);
-    }
-
-    override Expression modifiableLvalue(Scope* sc, Expression e)
-    {
-        if (!e1.isLvalue() && !e2.isLvalue())
-        {
-            error(loc, "conditional expression `%s` is not a modifiable lvalue", toChars());
-            return ErrorExp.get();
-        }
-        e1 = e1.modifiableLvalue(sc, e1);
-        e2 = e2.modifiableLvalue(sc, e2);
-        return toLvalue(sc, this);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -6145,19 +5100,6 @@  extern (C++) final class FileInitExp : DefaultInitExp
         super(loc, tok);
     }
 
-    override Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        //printf("FileInitExp::resolve() %s\n", toChars());
-        const(char)* s;
-        if (op == EXP.fileFullPath)
-            s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars());
-        else
-            s = loc.isValid() ? loc.filename : sc._module.ident.toChars();
-
-        Expression e = new StringExp(loc, s.toDString());
-        return e.expressionSemantic(sc);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -6174,12 +5116,6 @@  extern (C++) final class LineInitExp : DefaultInitExp
         super(loc, EXP.line);
     }
 
-    override Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
-        return e.expressionSemantic(sc);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -6196,13 +5132,6 @@  extern (C++) final class ModuleInitExp : DefaultInitExp
         super(loc, EXP.moduleString);
     }
 
-    override Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
-        Expression e = new StringExp(loc, s);
-        return e.expressionSemantic(sc);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -6219,19 +5148,6 @@  extern (C++) final class FuncInitExp : DefaultInitExp
         super(loc, EXP.functionString);
     }
 
-    override Expression resolveLoc(const ref Loc loc, Scope* sc)
-    {
-        const(char)* s;
-        if (sc.callsc && sc.callsc.func)
-            s = sc.callsc.func.Dsymbol.toPrettyChars();
-        else if (sc.func)
-            s = sc.func.Dsymbol.toPrettyChars();
-        else
-            s = "";
-        Expression e = new StringExp(loc, s.toDString());
-        return e.expressionSemantic(sc);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -6248,29 +5164,153 @@  extern (C++) final class PrettyFuncInitExp : DefaultInitExp
         super(loc, EXP.prettyFunction);
     }
 
-    override Expression resolveLoc(const ref Loc loc, Scope* sc)
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
+
+/***********************************************************
+ * A reference to a class, or an interface. We need this when we
+ * point to a base class (we must record what the type is).
+ */
+extern (C++) final class ClassReferenceExp : Expression
+{
+    StructLiteralExp value;
+
+    extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe
+    {
+        super(loc, EXP.classReference);
+        assert(lit && lit.sd && lit.sd.isClassDeclaration());
+        this.value = lit;
+        this.type = type;
+    }
+
+    ClassDeclaration originalClass()
     {
-        FuncDeclaration fd = (sc.callsc && sc.callsc.func)
-                        ? sc.callsc.func
-                        : sc.func;
+        return value.sd.isClassDeclaration();
+    }
 
-        const(char)* s;
-        if (fd)
+    // Return index of the field, or -1 if not found
+    int getFieldIndex(Type fieldtype, uint fieldoffset)
+    {
+        ClassDeclaration cd = originalClass();
+        uint fieldsSoFar = 0;
+        for (size_t j = 0; j < value.elements.length; j++)
         {
-            const funcStr = fd.Dsymbol.toPrettyChars();
-            OutBuffer buf;
-            functionToBufferWithIdent(fd.type.isTypeFunction(), buf, funcStr, fd.isStatic);
-            s = buf.extractChars();
+            while (j - fieldsSoFar >= cd.fields.length)
+            {
+                fieldsSoFar += cd.fields.length;
+                cd = cd.baseClass;
+            }
+            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+            if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size())
+            {
+                return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
+            }
         }
-        else
+        return -1;
+    }
+
+    // Return index of the field, or -1 if not found
+    // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
+    int findFieldIndexByName(VarDeclaration v)
+    {
+        ClassDeclaration cd = originalClass();
+        size_t fieldsSoFar = 0;
+        for (size_t j = 0; j < value.elements.length; j++)
         {
-            s = "";
+            while (j - fieldsSoFar >= cd.fields.length)
+            {
+                fieldsSoFar += cd.fields.length;
+                cd = cd.baseClass;
+            }
+            VarDeclaration v2 = cd.fields[j - fieldsSoFar];
+            if (v == v2)
+            {
+                return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar));
+            }
         }
+        return -1;
+    }
 
-        Expression e = new StringExp(loc, s.toDString());
-        e = e.expressionSemantic(sc);
-        e.type = Type.tstring;
-        return e;
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
+
+/***********************************************************
+ * This type is only used by the interpreter.
+ */
+extern (C++) final class CTFEExp : Expression
+{
+    extern (D) this(EXP tok)
+    {
+        super(Loc.initial, tok);
+        type = Type.tvoid;
+    }
+
+    override const(char)* toChars() const
+    {
+        switch (op)
+        {
+        case EXP.cantExpression:
+            return "<cant>";
+        case EXP.voidExpression:
+            return "cast(void)0";
+        case EXP.showCtfeContext:
+            return "<error>";
+        case EXP.break_:
+            return "<break>";
+        case EXP.continue_:
+            return "<continue>";
+        case EXP.goto_:
+            return "<goto>";
+        default:
+            assert(0);
+        }
+    }
+
+    extern (D) __gshared CTFEExp cantexp;
+    extern (D) __gshared CTFEExp voidexp;
+    extern (D) __gshared CTFEExp breakexp;
+    extern (D) __gshared CTFEExp continueexp;
+    extern (D) __gshared CTFEExp gotoexp;
+    /* Used when additional information is needed regarding
+     * a ctfe error.
+     */
+    extern (D) __gshared CTFEExp showcontext;
+
+    extern (D) static bool isCantExp(const Expression e) @safe
+    {
+        return e && e.op == EXP.cantExpression;
+    }
+
+    extern (D) static bool isGotoExp(const Expression e) @safe
+    {
+        return e && e.op == EXP.goto_;
+    }
+}
+
+/***********************************************************
+ * Fake class which holds the thrown exception.
+ * Used for implementing exception handling.
+ */
+extern (C++) final class ThrownExceptionExp : Expression
+{
+    ClassReferenceExp thrown;   // the thing being tossed
+
+    extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe
+    {
+        super(loc, EXP.thrownException);
+        this.thrown = victim;
+        this.type = victim.type;
+    }
+
+    override const(char)* toChars() const
+    {
+        return "CTFE ThrownException";
     }
 
     override void accept(Visitor v)
@@ -6330,154 +5370,6 @@  extern (C++) final class GenericExp : Expression
     }
 }
 
-/***************************************
- * Parameters:
- *      sc:     scope
- *      flag:   1: do not issue error message for invalid modification
-                2: the exp is a DotVarExp and a subfield of the leftmost
-                   variable is modified
- * Returns:
- *      Whether the type is modifiable
- */
-extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none)
-{
-    switch(exp.op)
-    {
-        case EXP.variable:
-            auto varExp = cast(VarExp)exp;
-
-            //printf("VarExp::checkModifiable %s", varExp.toChars());
-            assert(varExp.type);
-            return varExp.var.checkModify(varExp.loc, sc, null, flag);
-
-        case EXP.dotVariable:
-            auto dotVarExp = cast(DotVarExp)exp;
-
-            //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
-            if (dotVarExp.e1.op == EXP.this_)
-                return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag);
-
-            /* https://issues.dlang.org/show_bug.cgi?id=12764
-             * If inside a constructor and an expression of type `this.field.var`
-             * is encountered, where `field` is a struct declaration with
-             * default construction disabled, we must make sure that
-             * assigning to `var` does not imply that `field` was initialized
-             */
-            if (sc.func && sc.func.isCtorDeclaration())
-            {
-                // if inside a constructor scope and e1 of this DotVarExp
-                // is another DotVarExp, then check if the leftmost expression is a `this` identifier
-                if (auto dve = dotVarExp.e1.isDotVarExp())
-                {
-                    // Iterate the chain of DotVarExp to find `this`
-                    // Keep track whether access to fields was limited to union members
-                    // s.t. one can initialize an entire struct inside nested unions
-                    // (but not its members)
-                    bool onlyUnion = true;
-                    while (true)
-                    {
-                        auto v = dve.var.isVarDeclaration();
-                        assert(v);
-
-                        // Accessing union member?
-                        auto t = v.type.isTypeStruct();
-                        if (!t || !t.sym.isUnionDeclaration())
-                            onlyUnion = false;
-
-                        // Another DotVarExp left?
-                        if (!dve.e1 || dve.e1.op != EXP.dotVariable)
-                            break;
-
-                        dve = cast(DotVarExp) dve.e1;
-                    }
-
-                    if (dve.e1.op == EXP.this_)
-                    {
-                        scope v = dve.var.isVarDeclaration();
-                        /* if v is a struct member field with no initializer, no default construction
-                         * and v wasn't intialized before
-                         */
-                        if (v && v.isField() && !v._init && !v.ctorinit)
-                        {
-                            if (auto ts = v.type.isTypeStruct())
-                            {
-                                if (ts.sym.noDefaultCtor)
-                                {
-                                    /* checkModify will consider that this is an initialization
-                                     * of v while it is actually an assignment of a field of v
-                                     */
-                                    scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag);
-                                    if (modifyLevel == Modifiable.initialization)
-                                    {
-                                        // https://issues.dlang.org/show_bug.cgi?id=22118
-                                        // v is a union type field that was assigned
-                                        // a variable, therefore it counts as initialization
-                                        if (v.ctorinit)
-                                            return Modifiable.initialization;
-
-                                        return Modifiable.yes;
-                                    }
-                                    return modifyLevel;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            //printf("\te1 = %s\n", e1.toChars());
-            return dotVarExp.e1.checkModifiable(sc, flag);
-
-        case EXP.star:
-            auto ptrExp = cast(PtrExp)exp;
-            if (auto se = ptrExp.e1.isSymOffExp())
-            {
-                return se.var.checkModify(ptrExp.loc, sc, null, flag);
-            }
-            else if (auto ae = ptrExp.e1.isAddrExp())
-            {
-                return ae.e1.checkModifiable(sc, flag);
-            }
-            return Modifiable.yes;
-
-        case EXP.slice:
-            auto sliceExp = cast(SliceExp)exp;
-
-            //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars());
-            auto e1 = sliceExp.e1;
-            if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice)
-            {
-                return e1.checkModifiable(sc, flag);
-            }
-            return Modifiable.yes;
-
-        case EXP.comma:
-            return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
-
-        case EXP.index:
-            auto indexExp = cast(IndexExp)exp;
-            auto e1 = indexExp.e1;
-            if (e1.type.ty == Tsarray ||
-                e1.type.ty == Taarray ||
-                (e1.op == EXP.index && e1.type.ty != Tarray) ||
-                e1.op == EXP.slice)
-            {
-                return e1.checkModifiable(sc, flag);
-            }
-            return Modifiable.yes;
-
-        case EXP.question:
-            auto condExp = cast(CondExp)exp;
-            if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
-                && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
-                return Modifiable.yes;
-            return Modifiable.no;
-
-        default:
-            return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable
-    }
-}
-
 /**
  * Verify if the given identifier is _d_array{,set}ctor.
  *
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index f7f6b0b63ff..12ca6b4566c 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -50,7 +50,10 @@  struct Symbol;          // back end symbol
 Expression *ctfeInterpret(Expression *e);
 void expandTuples(Expressions *exps, Identifiers *names = nullptr);
 StringExp *toUTF8(StringExp *se, Scope *sc);
+Expression *resolveLoc(Expression *exp, const Loc &loc, Scope *sc);
 MATCH implicitConvTo(Expression *e, Type *t);
+Expression *toLvalue(Expression *_this, Scope *sc);
+Expression *modifiableLvalue(Expression* exp, Scope *sc);
 
 typedef unsigned char OwnedBy;
 enum
@@ -99,9 +102,6 @@  public:
     virtual complex_t toComplex();
     virtual StringExp *toStringExp();
     virtual bool isLvalue();
-    virtual Expression *toLvalue(Scope *sc, Expression *e);
-    virtual Expression *modifiableLvalue(Scope *sc, Expression *e);
-    virtual Expression *resolveLoc(const Loc &loc, Scope *sc);
     virtual bool checkType();
     virtual bool checkValue();
     Expression *addressOf();
@@ -244,7 +244,6 @@  public:
     real_t toImaginary() override;
     complex_t toComplex() override;
     Optional<bool> toBool() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
     dinteger_t getInteger() { return value; }
     template<int v>
@@ -254,7 +253,6 @@  public:
 class ErrorExp final : public Expression
 {
 public:
-    Expression *toLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 
     static ErrorExp *errorexp; // handy shared value
@@ -302,7 +300,6 @@  public:
 
     static IdentifierExp *create(const Loc &loc, Identifier *ident);
     bool isLvalue() override final;
-    Expression *toLvalue(Scope *sc, Expression *e) override final;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -320,7 +317,6 @@  public:
 
     DsymbolExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -331,8 +327,7 @@  public:
 
     ThisExp *syntaxCopy() override;
     Optional<bool> toBool() override;
-    bool isLvalue() override final;
-    Expression *toLvalue(Scope *sc, Expression *e) override final;
+    bool isLvalue() override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -370,8 +365,6 @@  public:
     StringExp *toStringExp() override;
     Optional<bool> toBool() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
     size_t numberOfCodeUnits(int tynto = 0) const;
     void writeTo(void* dest, bool zero, int tyto = 0) const;
@@ -469,7 +462,6 @@  public:
     static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
     bool equals(const RootObject * const o) const override;
     StructLiteralExp *syntaxCopy() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -501,7 +493,6 @@  public:
     FuncDeclaration *fd;
 
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
     bool checkType() override;
     bool checkValue() override;
     void accept(Visitor *v) override { v->visit(this); }
@@ -575,8 +566,6 @@  public:
     static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true);
     bool equals(const RootObject * const o) const override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -589,7 +578,6 @@  public:
     OverloadSet *vars;
 
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -678,7 +666,6 @@  public:
     Expression *e1;
 
     UnaExp *syntaxCopy() override;
-    Expression *resolveLoc(const Loc &loc, Scope *sc) override final;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -701,8 +688,6 @@  class BinAssignExp : public BinExp
 {
 public:
     bool isLvalue() override final;
-    Expression *toLvalue(Scope *sc, Expression *ex) override final;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override final;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -767,8 +752,6 @@  public:
     d_bool hasOverloads;
 
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -821,7 +804,6 @@  public:
 
     CallExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -836,8 +818,6 @@  class PtrExp final : public UnaExp
 {
 public:
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -882,7 +862,6 @@  public:
 
     CastExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -903,7 +882,6 @@  class VectorArrayExp final : public UnaExp
 {
 public:
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -926,8 +904,6 @@  private:
 public:
     SliceExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
     Optional<bool> toBool() override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -953,8 +929,6 @@  class DelegatePtrExp final : public UnaExp
 {
 public:
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -962,8 +936,6 @@  class DelegateFuncptrExp final : public UnaExp
 {
 public:
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -978,7 +950,6 @@  public:
 
     ArrayExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -997,8 +968,6 @@  public:
     d_bool isGenerated;
     d_bool allowCommaExp;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
     Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -1012,8 +981,6 @@  public:
 
     IndexExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -1047,7 +1014,6 @@  public:
     MemorySet memset;
 
     bool isLvalue() override final;
-    Expression *toLvalue(Scope *sc, Expression *ex) override final;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -1292,8 +1258,6 @@  public:
 
     CondExp *syntaxCopy() override;
     bool isLvalue() override;
-    Expression *toLvalue(Scope *sc, Expression *e) override;
-    Expression *modifiableLvalue(Scope *sc, Expression *e) override;
 
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -1320,35 +1284,30 @@  public:
 class FileInitExp final : public DefaultInitExp
 {
 public:
-    Expression *resolveLoc(const Loc &loc, Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class LineInitExp final : public DefaultInitExp
 {
 public:
-    Expression *resolveLoc(const Loc &loc, Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class ModuleInitExp final : public DefaultInitExp
 {
 public:
-    Expression *resolveLoc(const Loc &loc, Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class FuncInitExp final : public DefaultInitExp
 {
 public:
-    Expression *resolveLoc(const Loc &loc, Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
 class PrettyFuncInitExp final : public DefaultInitExp
 {
 public:
-    Expression *resolveLoc(const Loc &loc, Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 1ddb2b1ea08..d55ab3bcb26 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -87,6 +87,46 @@  import dmd.visitor;
 
 enum LOGSEMANTIC = false;
 
+/***********************************
+ * Determine if a `this` is needed to access `d`.
+ * Params:
+ *      sc = context
+ *      d = declaration to check
+ * Returns:
+ *      true means a `this` is needed
+ */
+private bool isNeedThisScope(Scope* sc, Declaration d)
+{
+    if (sc.intypeof == 1)
+        return false;
+
+    AggregateDeclaration ad = d.isThis();
+    if (!ad)
+        return false;
+    //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
+
+    for (Dsymbol s = sc.parent; s; s = s.toParentLocal())
+    {
+        //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
+        if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
+        {
+            if (ad2 == ad)
+                return false;
+            else if (ad2.isNested())
+                continue;
+            else
+                return true;
+        }
+        if (FuncDeclaration f = s.isFuncDeclaration())
+        {
+            if (f.isMemberLocal())
+                break;
+        }
+    }
+    return true;
+}
+
+
 /********************************************************
  * Perform semantic analysis and CTFE on expressions to produce
  * a string.
@@ -196,6 +236,51 @@  FuncDeclaration hasThis(Scope* sc)
 
 }
 
+extern (D) bool findTempDecl(DotTemplateInstanceExp exp, Scope* sc)
+{
+    auto ti = exp.ti;
+    auto e1 = exp.e1;
+    static if (LOGSEMANTIC)
+    {
+        printf("DotTemplateInstanceExp::findTempDecl('%s')\n", exp.toChars());
+    }
+    if (ti.tempdecl)
+        return true;
+
+    Expression e = new DotIdExp(exp.loc, e1, ti.name);
+    e = e.expressionSemantic(sc);
+    if (e.op == EXP.dot)
+        e = (cast(DotExp)e).e2;
+
+    Dsymbol s = null;
+    switch (e.op)
+    {
+    case EXP.overloadSet:
+        s = (cast(OverExp)e).vars;
+        break;
+
+    case EXP.dotTemplateDeclaration:
+        s = (cast(DotTemplateExp)e).td;
+        break;
+
+    case EXP.scope_:
+        s = (cast(ScopeExp)e).sds;
+        break;
+
+    case EXP.dotVariable:
+        s = (cast(DotVarExp)e).var;
+        break;
+
+    case EXP.variable:
+        s = (cast(VarExp)e).var;
+        break;
+
+    default:
+        return false;
+    }
+    return ti.updateTempDecl(sc, s);
+}
+
 /***********************************************************
  * Resolve `exp` as a compile-time known string.
  * Params:
@@ -1731,6 +1816,403 @@  private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc)
     return false;
 }
 
+/*********************************************
+ * Calling function f.
+ * Check the purity, i.e. if we're in a pure function
+ * we can only call other pure functions.
+ * Returns true if error occurs.
+ */
+private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc)
+{
+    if (!sc.func)
+        return false;
+    if (sc.func == f)
+        return false;
+    if (sc.intypeof == 1)
+        return false;
+    if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+        return false;
+
+    // If the call has a pure parent, then the called func must be pure.
+    if (!f.isPure() && checkImpure(sc, loc, null, f))
+    {
+        error(loc, "`pure` %s `%s` cannot call impure %s `%s`",
+            sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
+            f.toPrettyChars());
+
+        if (!f.isDtorDeclaration())
+            errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_);
+
+        f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one
+ * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but
+ * the generated dtor is not).
+ * In that case the method will identify and print all members causing the attribute
+ * missmatch.
+ *
+ * Params:
+ *   f  = potential `DtorDeclaration`
+ *   sc = scope
+ *   loc = location
+ *   check = current check (e.g. whether it's pure)
+ *   checkName = the kind of check (e.g. `"pure"`)
+ */
+void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc,
+            scope bool function(DtorDeclaration) check, const string checkName)
+{
+    auto dd = f.isDtorDeclaration();
+    if (!dd || !dd.isGenerated())
+        return;
+
+    // DtorDeclaration without parents should fail at an earlier stage
+    auto ad = cast(AggregateDeclaration) f.toParent2();
+    assert(ad);
+
+    if (ad.userDtors.length)
+    {
+        if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
+            return;
+
+        // Sanity check
+        assert(!check(ad.fieldDtor));
+    }
+
+    dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
+                        dd.isGenerated() ? "generated " : "".ptr,
+                        ad.toChars,
+                        cast(int) checkName.length, checkName.ptr);
+
+    // Search for the offending fields
+    foreach (field; ad.fields)
+    {
+        // Only structs may define automatically called destructors
+        auto ts = field.type.isTypeStruct();
+        if (!ts)
+        {
+            // But they might be part of a static array
+            auto ta = field.type.isTypeSArray();
+            if (!ta)
+                continue;
+
+            ts = ta.baseElemOf().isTypeStruct();
+            if (!ts)
+                continue;
+        }
+
+        auto fieldSym = ts.toDsymbol(sc);
+        assert(fieldSym); // Resolving ts must succeed because missing defs. should error before
+
+        auto fieldSd = fieldSym.isStructDeclaration();
+        assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR
+
+        if (fieldSd.dtor && !check(fieldSd.dtor))
+        {
+            field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
+
+            if (fieldSd.dtor.isGenerated())
+                fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName);
+            else
+                fieldSd.dtor.loc.errorSupplemental("   %.*s `%s.~this` is declared here",
+                                        cast(int) checkName.length, checkName.ptr, fieldSd.toChars());
+        }
+    }
+}
+
+/*******************************************
+ * Accessing variable v.
+ * Check for purity and safety violations.
+ * Returns true if error occurs.
+ */
+private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc)
+{
+    //printf("v = %s %s\n", v.type.toChars(), v.toChars());
+    /* Look for purity and safety violations when accessing variable v
+     * from current function.
+     */
+    if (!sc.func)
+        return false;
+    if (sc.intypeof == 1)
+        return false; // allow violations inside typeof(expression)
+    if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+        return false; // allow violations inside compile-time evaluated expressions and debug conditionals
+    if (v.ident == Id.ctfe)
+        return false; // magic variable never violates pure and safe
+    if (v.isImmutable())
+        return false; // always safe and pure to access immutables...
+    if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
+        return false; // or const global/parameter values which have no mutable indirections
+    if (v.storage_class & STC.manifest)
+        return false; // ...or manifest constants
+
+    // accessing empty structs is pure
+    // https://issues.dlang.org/show_bug.cgi?id=18694
+    // https://issues.dlang.org/show_bug.cgi?id=21464
+    // https://issues.dlang.org/show_bug.cgi?id=23589
+    if (v.type.ty == Tstruct)
+    {
+        StructDeclaration sd = (cast(TypeStruct)v.type).sym;
+        if (sd.members) // not opaque
+        {
+            if (sd.semanticRun >= PASS.semanticdone)
+                sd.determineSize(v.loc);
+            if (sd.hasNoFields)
+                return false;
+        }
+    }
+
+    bool err = false;
+    if (v.isDataseg())
+    {
+        // https://issues.dlang.org/show_bug.cgi?id=7533
+        // Accessing implicit generated __gate is pure.
+        if (v.ident == Id.gate)
+            return false;
+
+        if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v))
+        {
+            error(loc, "`pure` %s `%s` cannot access mutable static data `%s`",
+                sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
+            err = true;
+        }
+    }
+    else
+    {
+        /* Given:
+         * void f() {
+         *   int fx;
+         *   pure void g() {
+         *     int gx;
+         *     /+pure+/ void h() {
+         *       int hx;
+         *       /+pure+/ void i() { }
+         *     }
+         *   }
+         * }
+         * i() can modify hx and gx but not fx
+         */
+
+        Dsymbol vparent = v.toParent2();
+        for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent))
+        {
+            if (s == vparent)
+                break;
+
+            if (AggregateDeclaration ad = s.isAggregateDeclaration())
+            {
+                if (ad.isNested())
+                    continue;
+                break;
+            }
+            FuncDeclaration ff = s.isFuncDeclaration();
+            if (!ff)
+                break;
+            if (ff.isNested() || ff.isThis())
+            {
+                if (ff.type.isImmutable() ||
+                    ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
+                {
+                    OutBuffer ffbuf;
+                    OutBuffer vbuf;
+                    MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
+                    MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
+                    error(loc, "%s%s `%s` cannot access %sdata `%s`",
+                        ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
+                    err = true;
+                    break;
+                }
+                continue;
+            }
+            break;
+        }
+    }
+
+    /* Do not allow safe functions to access __gshared data
+     */
+    if (v.storage_class & STC.gshared)
+    {
+        if (sc.setUnsafe(false, loc,
+            "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v))
+        {
+            err = true;
+        }
+    }
+
+    return err;
+}
+
+/*
+Check if sc.func is impure or can be made impure.
+Returns true on error, i.e. if sc.func is pure and cannot be made impure.
+*/
+private bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0)
+{
+    return sc.func && (isRootTraitsCompilesScope(sc)
+            ? sc.func.isPureBypassingInference() >= PURE.weak
+            : sc.func.setImpure(loc, fmt, arg0));
+}
+
+/*********************************************
+ * Calling function f.
+ * Check the safety, i.e. if we're in a @safe function
+ * we can only call @safe or @trusted functions.
+ * Returns true if error occurs.
+ */
+private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc)
+{
+    if (sc.func == f)
+        return false;
+    if (sc.intypeof == 1)
+        return false;
+    if (sc.flags & SCOPE.debug_)
+        return false;
+    if ((sc.flags & SCOPE.ctfe) && sc.func)
+        return false;
+
+    if (!sc.func)
+    {
+        if (sc.varDecl && !f.safetyInprocess && !f.isSafe() && !f.isTrusted())
+        {
+            if (sc.varDecl.storage_class & STC.safe)
+            {
+                error(loc, "`@safe` variable `%s` cannot be initialized by calling `@system` function `%s`",
+                    sc.varDecl.toChars(), f.toChars());
+                return true;
+            }
+            else
+            {
+                sc.varDecl.storage_class |= STC.system;
+                sc.varDecl.systemInferred = true;
+            }
+        }
+        return false;
+    }
+
+    if (!f.isSafe() && !f.isTrusted())
+    {
+        if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f))
+        {
+            if (!loc.isValid()) // e.g. implicitly generated dtor
+                loc = sc.func.loc;
+
+            const prettyChars = f.toPrettyChars();
+            error(loc, "`@safe` %s `%s` cannot call `@system` %s `%s`",
+                sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
+                prettyChars);
+            if (!f.isDtorDeclaration)
+                errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe);
+            .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
+
+            f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
+
+            return true;
+        }
+    }
+    else if (f.isSafe() && f.safetyViolation)
+    {
+        // for dip1000 by default transition, print deprecations for calling functions that will become `@system`
+        if (sc.func.isSafeBypassingInference())
+        {
+            .deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars());
+            errorSupplementalInferredAttr(f, 10, true, STC.safe);
+        }
+        else if (!sc.func.safetyViolation)
+        {
+            import dmd.func : AttributeViolation;
+            sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null);
+        }
+    }
+    return false;
+}
+
+/*********************************************
+ * Calling function f.
+ * Check the @nogc-ness, i.e. if we're in a @nogc function
+ * we can only call other @nogc functions.
+ * Returns true if error occurs.
+ */
+private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
+{
+    if (!sc.func)
+        return false;
+    if (sc.func == f)
+        return false;
+    if (sc.intypeof == 1)
+        return false;
+    if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
+        return false;
+    /* The original expressions (`new S(...)` or `new S[...]``) will be
+     * verified instead. This is to keep errors related to the original code
+     * and not the lowering.
+     */
+    if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT)
+        return false;
+
+    if (!f.isNogc())
+    {
+        if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
+        {
+            if (loc.linnum == 0) // e.g. implicitly generated dtor
+                loc = sc.func.loc;
+
+            // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
+            // so don't print anything to avoid double error messages.
+            if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
+                || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
+                || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+            {
+                error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+                    sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
+
+                if (!f.isDtorDeclaration)
+                    f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
+            }
+
+            f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
+
+            return true;
+        }
+    }
+    return false;
+}
+
+/********************************************
+ * Check that the postblit is callable if t is an array of structs.
+ * Returns true if error happens.
+ */
+private bool checkPostblit(Type t, ref Loc loc, Scope* sc)
+{
+    if (auto ts = t.baseElemOf().isTypeStruct())
+    {
+        if (global.params.useTypeInfo && Type.dtypeinfo)
+        {
+            // https://issues.dlang.org/show_bug.cgi?id=11395
+            // Require TypeInfo generation for array concatenation
+            semanticTypeInfo(sc, t);
+        }
+
+        StructDeclaration sd = ts.sym;
+        if (sd.postblit)
+        {
+            if (sd.postblit.checkDisabled(loc, sc))
+                return true;
+
+            //checkDeprecated(sc, sd.postblit);        // necessary?
+            sd.postblit.checkPurity(loc, sc);
+            sd.postblit.checkSafety(loc, sc);
+            sd.postblit.checkNogc(loc, sc);
+            //checkAccess(sd, loc, sc, sd.postblit);   // necessary?
+            return false;
+        }
+    }
+    return false;
+}
+
 /***************************************
  * Pull out any properties.
  */
@@ -1942,7 +2424,7 @@  private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
     {
         if (auto v = ve.var.isVarDeclaration())
         {
-            if (ve.checkPurity(sc, v))
+            if (v.checkPurity(ve.loc, sc))
                 return ErrorExp.get();
         }
     }
@@ -2647,7 +3129,7 @@  private bool functionParameters(const ref Loc loc, Scope* sc,
                     ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
                     arg = ev.expressionSemantic(sc);
                 }
-                arg = arg.toLvalue(sc, arg);
+                arg = arg.toLvalue(sc);
 
                 // Look for mutable misaligned pointer, etc., in @safe mode
                 err |= checkUnsafeAccess(sc, arg, false, true);
@@ -2665,7 +3147,7 @@  private bool functionParameters(const ref Loc loc, Scope* sc,
                     ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v));
                     arg = ev.expressionSemantic(sc);
                 }
-                arg = arg.toLvalue(sc, arg);
+                arg = arg.toLvalue(sc);
 
                 // Look for mutable misaligned pointer, etc., in @safe mode
                 err |= checkUnsafeAccess(sc, arg, false, true);
@@ -2684,7 +3166,7 @@  private bool functionParameters(const ref Loc loc, Scope* sc,
                     err |= checkUnsafeAccess(sc, arg, false, true);
                     err |= checkDefCtor(arg.loc, t); // t must be default constructible
                 }
-                arg = arg.toLvalue(sc, arg);
+                arg = arg.toLvalue(sc);
             }
             else if (p.isLazy())
             {
@@ -4852,6 +5334,52 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         result = e;
     }
 
+    private void genIdent(FuncExp exp, Scope* sc)
+    {
+        if (exp.fd.ident == Id.empty)
+        {
+            const(char)[] s;
+            if (exp.fd.fes)
+                s = "__foreachbody";
+            else if (exp.fd.tok == TOK.reserved)
+                s = "__lambda";
+            else if (exp.fd.tok == TOK.delegate_)
+                s = "__dgliteral";
+            else
+                s = "__funcliteral";
+
+            DsymbolTable symtab;
+            if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+            {
+                if (func.localsymtab is null)
+                {
+                    // Inside template constraint, symtab is not set yet.
+                    // Initialize it lazily.
+                    func.localsymtab = new DsymbolTable();
+                }
+                symtab = func.localsymtab;
+            }
+            else
+            {
+                ScopeDsymbol sds = sc.parent.isScopeDsymbol();
+                if (!sds.symtab)
+                {
+                    // Inside template constraint, symtab may not be set yet.
+                    // Initialize it lazily.
+                    assert(sds.isTemplateInstance());
+                    sds.symtab = new DsymbolTable();
+                }
+                symtab = sds.symtab;
+            }
+            assert(symtab);
+            Identifier id = Identifier.generateId(s, symtab.length() + 1);
+            exp.fd.ident = id;
+            if (exp.td)
+                exp.td.ident = id;
+            symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd);
+        }
+    }
+
     override void visit(FuncExp exp)
     {
         static if (LOGSEMANTIC)
@@ -4882,7 +5410,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         //if (fd.treq)
         //    fd.treq = fd.treq.dsymbolSemantic(loc, sc);
 
-        exp.genIdent(sc);
+        genIdent(exp, sc);
 
         // Set target of return type inference
         if (exp.fd.treq && !exp.fd.type.nextOf())
@@ -4995,7 +5523,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return checkarg;
             }
 
-            exp.genIdent(sc);
+            genIdent(exp, sc);
 
             assert(exp.td.parameters && exp.td.parameters.length);
             exp.td.dsymbolSemantic(sc);
@@ -5257,7 +5785,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     ve.type = t.typeSemantic(exp.loc, sc);
                 }
                 VarDeclaration v = ve.var.isVarDeclaration();
-                if (v && ve.checkPurity(sc, v))
+                if (v && v.checkPurity(ve.loc, sc))
                     return setError();
             }
 
@@ -5885,9 +6413,9 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // Purity and safety check should run after testing arguments matching
             if (exp.f)
             {
-                exp.checkPurity(sc, exp.f);
-                exp.checkSafety(sc, exp.f);
-                exp.checkNogc(sc, exp.f);
+                exp.f.checkPurity(exp.loc, sc);
+                exp.f.checkSafety(exp.loc, sc);
+                exp.f.checkNogc(exp.loc, sc);
                 if (exp.f.checkNestedReference(sc, exp.loc))
                     return setError();
             }
@@ -6795,7 +7323,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         exp.e1 = exp.e1.expressionSemantic(sc);
-        exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+        exp.e1 = exp.e1.modifiableLvalue(sc);
         exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
         exp.type = exp.e1.type;
 
@@ -6873,7 +7401,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         uint errors = global.errors;
         const len = buf.length;
         const str = buf.extractChars()[0 .. len];
-        const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+        const bool doUnittests = global.params.parsingUnittestsRequired();
         auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut);
         scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
         p.transitionIn = global.params.v.vin;
@@ -7767,7 +8295,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 else
                 {
                     // `toLvalue` call further below is upon exp.e1, omitting & from the error message
-                    exp.toLvalue(sc, null);
+                    exp.toLvalue(sc);
                     return setError();
                 }
             }
@@ -7857,7 +8385,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
-        exp.e1 = exp.e1.toLvalue(sc, null);
+        exp.e1 = exp.e1.toLvalue(sc);
         if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
@@ -7934,7 +8462,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (!checkAddressVar(sc, exp.e1, v))
                     return setError();
 
-                ve.checkPurity(sc, v);
+                v.checkPurity(ve.loc, sc);
             }
             FuncDeclaration f = ve.var.isFuncDeclaration();
             if (f)
@@ -8006,7 +8534,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
              */
             if (VarDeclaration v = expToVariable(exp.e1))
             {
-                exp.e1.checkPurity(sc, v);
+                v.checkPurity(exp.e1.loc, sc);
             }
         }
         else if (wasCond)
@@ -8275,7 +8803,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
         exp.e1 = resolveProperties(sc, exp.e1);
-        exp.e1 = exp.e1.modifiableLvalue(sc, null);
+        exp.e1 = exp.e1.modifiableLvalue(sc);
         if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
@@ -8306,9 +8834,9 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (cd.dtor)
         {
             err |= !cd.dtor.functionSemantic();
-            err |= exp.checkPurity(sc, cd.dtor);
-            err |= exp.checkSafety(sc, cd.dtor);
-            err |= exp.checkNogc(sc, cd.dtor);
+            err |= cd.dtor.checkPurity(exp.loc, sc);
+            err |= cd.dtor.checkSafety(exp.loc, sc);
+            err |= cd.dtor.checkNogc(exp.loc, sc);
         }
         if (err)
             return setError();
@@ -9491,7 +10019,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+        exp.e1 = exp.e1.modifiableLvalue(sc);
         exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true);
 
         e = exp;
@@ -10253,7 +10781,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                             Expression ex;
                             ex = new IndexExp(exp.loc, ea, ek);
                             ex = ex.expressionSemantic(sc);
-                            ex = ex.modifiableLvalue(sc, ex); // allocate new slot
+                            ex = ex.modifiableLvalue(sc); // allocate new slot
                             ex = ex.optimize(WANTvalue);
 
                             ey = new ConstructExp(exp.loc, ex, ey);
@@ -10344,7 +10872,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             {
                 if (exp.op != EXP.blit && (e2x.op == EXP.slice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == EXP.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != EXP.slice && e2x.isLvalue()))
                 {
-                    if (e1x.checkPostblit(sc, t1))
+                    if (t1.checkPostblit(e1x.loc, sc))
                         return setError();
                 }
 
@@ -10446,7 +10974,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             // e1 is not an lvalue, but we let code generator handle it
 
-            auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1);
+            auto ale1x = ale.e1.modifiableLvalueImpl(sc, exp.e1);
             if (ale1x.op == EXP.error)
                 return setResult(ale1x);
             ale.e1 = ale1x;
@@ -10532,7 +11060,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 se = cast(SliceExp)se.e1;
             if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray)
             {
-                se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
+                se.e1 = se.e1.modifiableLvalueImpl(sc, exp.e1);
                 if (se.e1.op == EXP.error)
                     return setResult(se.e1);
             }
@@ -10556,7 +11084,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // Try to do a decent error message with the expression
             // before it gets constant folded
             if (exp.op == EXP.assign)
-                e1x = e1x.modifiableLvalue(sc, e1old);
+                e1x = e1x.modifiableLvalueImpl(sc, e1old);
 
             e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
 
@@ -10587,7 +11115,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // '= null' is the only allowable block assignment (Bug 7493)
             exp.memset = MemorySet.blockAssign;    // make it easy for back end to tell what this is
             e2x = e2x.implicitCastTo(sc, t1.nextOf());
-            if (exp.op != EXP.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf()))
+            if (exp.op != EXP.blit && e2x.isLvalue() && t1.nextOf.checkPostblit(exp.e1.loc, sc))
                 return setError();
         }
         else if (exp.e1.op == EXP.slice &&
@@ -10625,7 +11153,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  e2x.op == EXP.cast_ && (cast(UnaExp)e2x).e1.isLvalue() ||
                  e2x.op != EXP.slice && e2x.isLvalue()))
             {
-                if (exp.e1.checkPostblit(sc, t1.nextOf()))
+                if (t1.nextOf().checkPostblit(exp.e1.loc, sc))
                     return setError();
             }
 
@@ -11004,7 +11532,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         else
         {
-            exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+            exp.e1 = exp.e1.modifiableLvalue(sc);
         }
 
         if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating()))
@@ -11063,7 +11591,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
-        exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
+        exp.e1 = exp.e1.modifiableLvalue(sc);
         if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
@@ -11095,7 +11623,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             // EXP.concatenateAssign
             assert(exp.op == EXP.concatenateAssign);
-            if (exp.e1.checkPostblit(sc, tb1next))
+            if (tb1next.checkPostblit(exp.e1.loc, sc))
                 return setError();
 
             exp.e2 = exp.e2.castTo(sc, exp.e1.type);
@@ -11113,7 +11641,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (tb2.ty == Tclass && (cast(TypeClass)tb2).implicitConvToThroughAliasThis(tb1next))
                 goto Laliasthis;
             // Append element
-            if (exp.e2.checkPostblit(sc, tb2))
+            if (tb2.checkPostblit(exp.e2.loc, sc))
                 return setError();
 
             if (checkNewEscape(sc, exp.e2, false))
@@ -11746,7 +12274,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
             else
             {
-                if (exp.e2.checkPostblit(sc, tb2))
+                if (tb2.checkPostblit(exp.e2.loc, sc))
                     return setError();
                 // Postblit call will be done in runtime helper function
             }
@@ -11781,7 +12309,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
             else
             {
-                if (exp.e1.checkPostblit(sc, tb1))
+                if (tb1.checkPostblit(exp.e1.loc, sc))
                     return setError();
             }
 
@@ -11836,7 +12364,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         if (Type tbn = tb.nextOf())
         {
-            if (exp.checkPostblit(sc, tbn))
+            if (tbn.checkPostblit(exp.loc, sc))
                 return setError();
         }
         Type t1 = exp.e1.type.toBasetype();
@@ -13582,6 +14110,14 @@  private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc)
     return exp;
 }
 
+private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc)
+{
+    if (auto d = s.isDeclaration())
+        return d.checkDisabled(loc, sc);
+
+    return false;
+}
+
 /******************************
  * Resolve properties, i.e. `e1.ident`, without seeing UFCS.
  * Params:
@@ -13675,8 +14211,8 @@  Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
             // if 's' is a tuple variable, the tuple is returned.
             s = s.toAlias();
 
-            exp.checkDeprecated(sc, s);
-            exp.checkDisabled(sc, s);
+            s.checkDeprecated(exp.loc, sc);
+            s.checkDisabled(exp.loc, sc);
 
             if (auto em = s.isEnumMember())
             {
@@ -14466,12 +15002,112 @@  bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
     return check(e, returnRef);
 }
 
-/************************************************
- * Destructors are attached to VarDeclarations.
- * Hence, if expression returns a temp that needs a destructor,
- * make sure and create a VarDeclaration for that temp.
+/****************************************
+ * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
  */
-Expression addDtorHook(Expression e, Scope* sc)
+Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc)
+{
+    Expression visit(Expression exp)
+    {
+        if (auto unaExp = exp.isUnaExp())
+        {
+            unaExp.e1 = unaExp.e1.resolveLoc(loc, sc);
+            return unaExp;
+        }
+        exp.loc = loc;
+        return exp;
+    }
+
+    Expression visitCat(CatExp exp)
+    {
+        exp.e1 = exp.e1.resolveLoc(loc, sc);
+        exp.e2 = exp.e2.resolveLoc(loc, sc);
+        return exp;
+    }
+
+    Expression visitFileInit(FileInitExp exp)
+    {
+        //printf("FileInitExp::resolve() %s\n", exp.toChars());
+        const(char)* s;
+        if (exp.op == EXP.fileFullPath)
+            s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars());
+        else
+            s = loc.isValid() ? loc.filename : sc._module.ident.toChars();
+
+        Expression e = new StringExp(loc, s.toDString());
+        return e.expressionSemantic(sc);
+    }
+
+    Expression visitLineInit(LineInitExp _)
+    {
+        Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
+        return e.expressionSemantic(sc);
+    }
+
+    Expression visitModuleInit(ModuleInitExp _)
+    {
+        const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
+        Expression e = new StringExp(loc, s);
+        return e.expressionSemantic(sc);
+    }
+
+    Expression visitFuncInit(FuncInitExp _)
+    {
+        const(char)* s;
+        if (sc.callsc && sc.callsc.func)
+            s = sc.callsc.func.Dsymbol.toPrettyChars();
+        else if (sc.func)
+            s = sc.func.Dsymbol.toPrettyChars();
+        else
+            s = "";
+        Expression e = new StringExp(loc, s.toDString());
+        return e.expressionSemantic(sc);
+    }
+
+    Expression visitPrettyFunc(PrettyFuncInitExp _)
+    {
+        FuncDeclaration fd = (sc.callsc && sc.callsc.func)
+                        ? sc.callsc.func
+                        : sc.func;
+
+        const(char)* s;
+        if (fd)
+        {
+            const funcStr = fd.Dsymbol.toPrettyChars();
+            OutBuffer buf;
+            functionToBufferWithIdent(fd.type.isTypeFunction(), buf, funcStr, fd.isStatic);
+            s = buf.extractChars();
+        }
+        else
+        {
+            s = "";
+        }
+
+        Expression e = new StringExp(loc, s.toDString());
+        e = e.expressionSemantic(sc);
+        e.type = Type.tstring;
+        return e;
+    }
+
+    switch(exp.op)
+    {
+        default:                 return visit(exp);
+        case EXP.concatenate:    return visitCat(exp.isCatExp());
+        case EXP.file:
+        case EXP.fileFullPath:   return visitFileInit(exp.isFileInitExp());
+        case EXP.line:           return visitLineInit(exp.isLineInitExp);
+        case EXP.moduleString:   return visitModuleInit(exp.isModuleInitExp());
+        case EXP.functionString: return visitFuncInit(exp.isFuncInitExp());
+        case EXP.prettyFunction: return visitPrettyFunc(exp.isPrettyFuncInitExp());
+    }
+}
+
+/************************************************
+ * Destructors are attached to VarDeclarations.
+ * Hence, if expression returns a temp that needs a destructor,
+ * make sure and create a VarDeclaration for that temp.
+ */
+Expression addDtorHook(Expression e, Scope* sc)
 {
     Expression visit(Expression exp)
     {
@@ -14567,6 +15203,584 @@  Expression addDtorHook(Expression e, Scope* sc)
     }
 }
 
+/*******************************
+ * Try to convert an expression to be an lvalue.
+ *
+ * Give error if we're not an lvalue.
+ * Params:
+ *     _this = expression to convert
+ *     sc = scope
+ * Returns: converted expression, or `ErrorExp` on error
+*/
+extern(C++) Expression toLvalue(Expression _this, Scope* sc)
+{
+    return toLvalueImpl(_this, sc, _this);
+}
+
+// e = original un-lowered expression for error messages, in case of recursive calls
+private Expression toLvalueImpl(Expression _this, Scope* sc, Expression e) {
+    Expression visit(Expression _this)
+    {
+        // BinaryAssignExp does not have an EXP associated
+        // so it's treated on the default path.
+        // Lvalue-ness will be handled in glue :layer.
+        if (_this.isBinAssignExp())
+            return _this;
+        if (!_this.loc.isValid())
+            _this.loc = e.loc;
+
+        if (e.op == EXP.type)
+            error(_this.loc, "`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
+        else
+            error(_this.loc, "`%s` is not an lvalue and cannot be modified", e.toChars());
+
+        return ErrorExp.get();
+    }
+
+    Expression visitInteger(IntegerExp _this)
+    {
+        if (!_this.loc.isValid())
+            _this.loc = e.loc;
+        error(e.loc, "cannot modify constant `%s`", e.toChars());
+        return ErrorExp.get();
+    }
+
+    Expression visitThis(ThisExp _this)
+    {
+        if (_this.type.toBasetype().ty == Tclass)
+        {
+            // Class `this` is an rvalue; struct `this` is an lvalue.
+            return visit(_this);
+        }
+
+        return _this;
+    }
+
+    Expression visitString(StringExp _this)
+    {
+        //printf("StringExp::toLvalue(%s) type = %s\n", _this.toChars(), _this.type ? _this.type.toChars() : NULL);
+        return (_this.type && _this.type.toBasetype().ty == Tsarray) ? _this : visit(_this);
+    }
+
+    Expression visitStructLiteral(StructLiteralExp _this)
+    {
+        if (sc.flags & SCOPE.Cfile)
+            return _this;  // C struct literals are lvalues
+        else
+            return visit(_this);
+    }
+
+    Expression visitTemplate(TemplateExp _this)
+    {
+        if (!_this.fd)
+            return visit(_this);
+
+        assert(sc);
+        return symbolToExp(_this.fd, _this.loc, sc, true);
+
+    }
+
+    Expression visitVar(VarExp _this)
+    {
+        auto var = _this.var;
+        if (var.storage_class & STC.manifest)
+        {
+            error(_this.loc, "manifest constant `%s` cannot be modified", var.toChars());
+            return ErrorExp.get();
+        }
+        if (var.storage_class & STC.lazy_ && !_this.delegateWasExtracted)
+        {
+            error(_this.loc, "lazy variable `%s` cannot be modified", var.toChars());
+            return ErrorExp.get();
+        }
+        if (var.ident == Id.ctfe)
+        {
+            error(_this.loc, "cannot modify compiler-generated variable `__ctfe`");
+            return ErrorExp.get();
+        }
+        if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574
+        {
+            error(_this.loc, "cannot modify operator `$`");
+            return ErrorExp.get();
+        }
+        return _this;
+    }
+
+    Expression visitDotVar(DotVarExp _this)
+    {
+        auto e1 = _this.e1;
+        auto var = _this.var;
+        //printf("DotVarExp::toLvalue(%s)\n", toChars());
+        if (sc && sc.flags & SCOPE.Cfile)
+        {
+            /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
+             * is an lvalue if the first expression is an lvalue.
+             */
+            if (!e1.isLvalue())
+                return visit(_this);
+        }
+        if (!_this.isLvalue())
+            return visit(_this);
+        if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
+        {
+            if (VarDeclaration vd = var.isVarDeclaration())
+            {
+                auto ad = vd.isMember2();
+                if (ad && ad.fields.length == sc.ctorflow.fieldinit.length)
+                {
+                    foreach (i, f; ad.fields)
+                    {
+                        if (f == vd)
+                        {
+                            if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
+                            {
+                                /* If the address of vd is taken, assume it is thereby initialized
+                                 * https://issues.dlang.org/show_bug.cgi?id=15869
+                                 */
+                                modifyFieldVar(_this.loc, sc, vd, e1);
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return _this;
+    }
+
+    Expression visitCall(CallExp _this)
+    {
+        if (_this.isLvalue())
+            return _this;
+        return visit(_this);
+    }
+
+    Expression visitCast(CastExp _this)
+    {
+        if (sc && sc.flags & SCOPE.Cfile)
+        {
+            /* C11 6.5.4-5: A cast does not yield an lvalue.
+             */
+            return visit(_this);
+        }
+        if (_this.isLvalue())
+            return _this;
+        return visit(_this);
+    }
+
+    Expression visitVectorArray(VectorArrayExp _this)
+    {
+        _this.e1 = _this.e1.toLvalueImpl(sc, e);
+        return _this;
+    }
+
+    Expression visitSlice(SliceExp _this)
+    {
+        //printf("SliceExp::toLvalue(%s) _this.type = %s\n", _this.toChars(), _this.type ? _this.type.toChars() : NULL);
+        return (_this.type && _this.type.toBasetype().ty == Tsarray) ? _this : visit(_this);
+    }
+
+    Expression visitArray(ArrayExp _this)
+    {
+        if (_this.type && _this.type.toBasetype().ty == Tvoid)
+            error(_this.loc, "`void`s have no value");
+        return _this;
+    }
+
+    Expression visitComma(CommaExp _this)
+    {
+        _this.e2 = _this.e2.toLvalue(sc);
+        return _this;
+    }
+
+    Expression visitDelegatePointer(DelegatePtrExp _this)
+    {
+        _this.e1 = _this.e1.toLvalueImpl(sc, e);
+        return _this;
+    }
+
+    Expression visitDelegateFuncptr(DelegateFuncptrExp _this)
+    {
+        _this.e1 = _this.e1.toLvalueImpl(sc, e);
+        return _this;
+    }
+
+    Expression visitIndex(IndexExp _this)
+    {
+        if (_this.isLvalue())
+            return _this;
+        return visit(_this);
+    }
+
+    Expression visitAssign(AssignExp _this)
+    {
+        if (_this.e1.op == EXP.slice || _this.e1.op == EXP.arrayLength)
+        {
+            return visit(_this);
+        }
+
+        /* In front-end level, AssignExp should make an lvalue of e1.
+         * Taking the address of e1 will be handled in low level layer,
+         * so this function does nothing.
+         */
+        return _this;
+    }
+
+    Expression visitCond(CondExp _this)
+    {
+        // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
+        CondExp e = cast(CondExp)(_this.copy());
+        e.e1 = _this.e1.toLvalue(sc).addressOf();
+        e.e2 = _this.e2.toLvalue(sc).addressOf();
+        e.type = _this.type.pointerTo();
+        return new PtrExp(_this.loc, e, _this.type);
+
+    }
+
+    switch(_this.op)
+    {
+        default:                          return visit(_this);
+
+        case EXP.int64:                   return visitInteger(_this.isIntegerExp());
+        case EXP.error:                   return _this;
+        case EXP.identifier:              return _this;
+        case EXP.dSymbol:                 return _this;
+        case EXP.this_:                   return visitThis(_this.isThisExp());
+        case EXP.super_:                  return visitThis(_this.isSuperExp());
+        case EXP.string_:                 return visitString(_this.isStringExp());
+        case EXP.structLiteral:           return visitStructLiteral(_this.isStructLiteralExp());
+        case EXP.template_:               return visitTemplate(_this.isTemplateExp());
+        case EXP.variable:                return visitVar(_this.isVarExp());
+        case EXP.overloadSet:             return _this;
+        case EXP.dotVariable:             return visitDotVar(_this.isDotVarExp());
+        case EXP.call:                    return visitCall(_this.isCallExp());
+        case EXP.star:                    return _this;
+        case EXP.cast_:                   return visitCast(_this.isCastExp());
+        case EXP.vectorArray:             return visitVectorArray(_this.isVectorArrayExp());
+        case EXP.slice:                   return visitSlice(_this.isSliceExp());
+        case EXP.array:                   return visitArray(_this.isArrayExp());
+        case EXP.comma:                   return visitComma(_this.isCommaExp());
+        case EXP.delegatePointer:         return visitDelegatePointer(_this.isDelegatePtrExp());
+        case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp());
+        case EXP.index:                   return visitIndex(_this.isIndexExp());
+        case EXP.construct:               return visitAssign(_this.isConstructExp());
+        case EXP.loweredAssignExp:        return visitAssign(_this.isLoweredAssignExp());
+        case EXP.blit:                    return visitAssign(_this.isBlitExp());
+        case EXP.assign:                  return visitAssign(_this.isAssignExp());
+        case EXP.question:                return visitCond(_this.isCondExp());
+    }
+}
+
+/***************************************
+ * Parameters:
+ *      sc:     scope
+ *      flag:   1: do not issue error message for invalid modification
+                2: the exp is a DotVarExp and a subfield of the leftmost
+                   variable is modified
+ * Returns:
+ *      Whether the type is modifiable
+ */
+Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none)
+{
+    switch(exp.op)
+    {
+        case EXP.variable:
+            auto varExp = cast(VarExp)exp;
+
+            //printf("VarExp::checkModifiable %s", varExp.toChars());
+            assert(varExp.type);
+            return varExp.var.checkModify(varExp.loc, sc, null, flag);
+
+        case EXP.dotVariable:
+            auto dotVarExp = cast(DotVarExp)exp;
+
+            //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
+            if (dotVarExp.e1.op == EXP.this_)
+                return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag);
+
+            /* https://issues.dlang.org/show_bug.cgi?id=12764
+             * If inside a constructor and an expression of type `this.field.var`
+             * is encountered, where `field` is a struct declaration with
+             * default construction disabled, we must make sure that
+             * assigning to `var` does not imply that `field` was initialized
+             */
+            if (sc.func && sc.func.isCtorDeclaration())
+            {
+                // if inside a constructor scope and e1 of this DotVarExp
+                // is another DotVarExp, then check if the leftmost expression is a `this` identifier
+                if (auto dve = dotVarExp.e1.isDotVarExp())
+                {
+                    // Iterate the chain of DotVarExp to find `this`
+                    // Keep track whether access to fields was limited to union members
+                    // s.t. one can initialize an entire struct inside nested unions
+                    // (but not its members)
+                    bool onlyUnion = true;
+                    while (true)
+                    {
+                        auto v = dve.var.isVarDeclaration();
+                        assert(v);
+
+                        // Accessing union member?
+                        auto t = v.type.isTypeStruct();
+                        if (!t || !t.sym.isUnionDeclaration())
+                            onlyUnion = false;
+
+                        // Another DotVarExp left?
+                        if (!dve.e1 || dve.e1.op != EXP.dotVariable)
+                            break;
+
+                        dve = cast(DotVarExp) dve.e1;
+                    }
+
+                    if (dve.e1.op == EXP.this_)
+                    {
+                        scope v = dve.var.isVarDeclaration();
+                        /* if v is a struct member field with no initializer, no default construction
+                         * and v wasn't intialized before
+                         */
+                        if (v && v.isField() && !v._init && !v.ctorinit)
+                        {
+                            if (auto ts = v.type.isTypeStruct())
+                            {
+                                if (ts.sym.noDefaultCtor)
+                                {
+                                    /* checkModify will consider that this is an initialization
+                                     * of v while it is actually an assignment of a field of v
+                                     */
+                                    scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag);
+                                    if (modifyLevel == Modifiable.initialization)
+                                    {
+                                        // https://issues.dlang.org/show_bug.cgi?id=22118
+                                        // v is a union type field that was assigned
+                                        // a variable, therefore it counts as initialization
+                                        if (v.ctorinit)
+                                            return Modifiable.initialization;
+
+                                        return Modifiable.yes;
+                                    }
+                                    return modifyLevel;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            //printf("\te1 = %s\n", e1.toChars());
+            return dotVarExp.e1.checkModifiable(sc, flag);
+
+        case EXP.star:
+            auto ptrExp = cast(PtrExp)exp;
+            if (auto se = ptrExp.e1.isSymOffExp())
+            {
+                return se.var.checkModify(ptrExp.loc, sc, null, flag);
+            }
+            else if (auto ae = ptrExp.e1.isAddrExp())
+            {
+                return ae.e1.checkModifiable(sc, flag);
+            }
+            return Modifiable.yes;
+
+        case EXP.slice:
+            auto sliceExp = cast(SliceExp)exp;
+
+            //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars());
+            auto e1 = sliceExp.e1;
+            if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice)
+            {
+                return e1.checkModifiable(sc, flag);
+            }
+            return Modifiable.yes;
+
+        case EXP.comma:
+            return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
+
+        case EXP.index:
+            auto indexExp = cast(IndexExp)exp;
+            auto e1 = indexExp.e1;
+            if (e1.type.ty == Tsarray ||
+                e1.type.ty == Taarray ||
+                (e1.op == EXP.index && e1.type.ty != Tarray) ||
+                e1.op == EXP.slice)
+            {
+                return e1.checkModifiable(sc, flag);
+            }
+            return Modifiable.yes;
+
+        case EXP.question:
+            auto condExp = cast(CondExp)exp;
+            if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
+                && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
+                return Modifiable.yes;
+            return Modifiable.no;
+
+        default:
+            return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable
+    }
+}
+
+/**
+ * Similar to `toLvalue`, but also enforce it is mutable or raise an error.
+ * Params:
+ *     _this = Expression to convert
+ *     sc = scope
+ * Returns: `_this` converted to an lvalue, or an `ErrorExp`
+ */
+extern(C++) Expression modifiableLvalue(Expression _this, Scope* sc)
+{
+    return modifiableLvalueImpl(_this, sc, _this);
+}
+
+// e = original / un-lowered expression to print in error messages
+private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression e)
+{
+    Expression visit(Expression exp)
+    {
+        //printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars());
+        // See if this expression is a modifiable lvalue (i.e. not const)
+        if (exp.isBinAssignExp())
+            return exp.toLvalue(sc);
+
+        auto type = exp.type;
+        if (checkModifiable(exp, sc) == Modifiable.yes)
+        {
+            assert(type);
+            if (!type.isMutable())
+            {
+                if (auto dve = exp.isDotVarExp())
+                {
+                    if (isNeedThisScope(sc, dve.var))
+                        for (Dsymbol s = sc.func; s; s = s.toParentLocal())
+                    {
+                        FuncDeclaration ff = s.isFuncDeclaration();
+                        if (!ff)
+                            break;
+                        if (!ff.type.isMutable)
+                        {
+                            error(exp.loc, "cannot modify `%s` in `%s` function", exp.toChars(), MODtoChars(type.mod));
+                            return ErrorExp.get();
+                        }
+                    }
+                }
+                error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toChars());
+                return ErrorExp.get();
+            }
+            else if (!type.isAssignable())
+            {
+                error(exp.loc, "cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members",
+                    exp.toChars(), type.toChars());
+                return ErrorExp.get();
+            }
+        }
+        return exp.toLvalueImpl(sc, e);
+    }
+
+    Expression visitString(StringExp exp)
+    {
+        error(exp.loc, "cannot modify string literal `%s`", exp.toChars());
+        return ErrorExp.get();
+    }
+
+    Expression visitVar(VarExp exp)
+    {
+        //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars());
+        if (exp.var.storage_class & STC.manifest)
+        {
+            error(exp.loc, "cannot modify manifest constant `%s`", exp.toChars());
+            return ErrorExp.get();
+        }
+        // See if this expression is a modifiable lvalue (i.e. not const)
+        return visit(exp);
+    }
+
+    Expression visitPtr(PtrExp exp)
+    {
+        //printf("PtrExp::modifiableLvalue() %s, type %s\n", exp.toChars(), exp.type.toChars());
+        Declaration var;
+        auto e1 = exp.e1;
+        if (auto se = e1.isSymOffExp())
+            var = se.var;
+        else if (auto ve = e1.isVarExp())
+            var = ve.var;
+        if (var && var.type.isFunction_Delegate_PtrToFunction())
+        {
+            if (var.type.isTypeFunction())
+                error(exp.loc, "function `%s` is not an lvalue and cannot be modified", var.toChars());
+            else
+                error(exp.loc, "function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
+            return ErrorExp.get();
+        }
+        return visit(exp);
+    }
+
+    Expression visitSlice(SliceExp exp)
+    {
+        error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toChars());
+        return exp;
+    }
+
+    Expression visitComma(CommaExp exp)
+    {
+        exp.e2 = exp.e2.modifiableLvalueImpl(sc, e);
+        return exp;
+    }
+
+    Expression visitDelegatePtr(DelegatePtrExp exp)
+    {
+        if (sc.setUnsafe(false, exp.loc, "cannot modify delegate pointer in `@safe` code `%s`", exp))
+        {
+            return ErrorExp.get();
+        }
+        return visit(exp);
+    }
+
+    Expression visitDelegateFuncptr(DelegateFuncptrExp exp)
+    {
+        if (sc.setUnsafe(false, exp.loc, "cannot modify delegate function pointer in `@safe` code `%s`", exp))
+        {
+            return ErrorExp.get();
+        }
+        return visit(exp);
+    }
+
+    Expression visitIndex(IndexExp exp)
+    {
+        //printf("IndexExp::modifiableLvalue(%s)\n", exp.toChars());
+        Expression ex = exp.markSettingAAElem();
+        if (ex.op == EXP.error)
+            return ex;
+
+        return visit(exp);
+    }
+
+    Expression visitCond(CondExp exp)
+    {
+        if (!exp.e1.isLvalue() && !exp.e2.isLvalue())
+        {
+            error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toChars());
+            return ErrorExp.get();
+        }
+        exp.e1 = exp.e1.modifiableLvalue(sc);
+        exp.e2 = exp.e2.modifiableLvalue(sc);
+        return exp.toLvalue(sc);
+    }
+
+    switch(_this.op)
+    {
+        default:                          return visit(_this);
+        case EXP.string_:                 return visitString(_this.isStringExp());
+        case EXP.variable:                return visitVar(_this.isVarExp());
+        case EXP.star:                    return visitPtr(_this.isPtrExp());
+        case EXP.slice:                   return visitSlice(_this.isSliceExp());
+        case EXP.comma:                   return visitComma(_this.isCommaExp());
+        case EXP.delegatePointer:         return visitDelegatePtr(_this.isDelegatePtrExp());
+        case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp());
+        case EXP.index:                   return visitIndex(_this.isIndexExp());
+        case EXP.question:                return visitCond(_this.isCondExp());
+    }
+}
+
+
 /****************************************************
  * Determine if `exp`, which gets its address taken, can do so safely.
  * Params:
@@ -14676,15 +15890,12 @@  bool checkAddressable(Expression e, Scope* sc)
  */
 private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration f)
 {
-    with(exp)
-    {
-        bool error = checkDisabled(sc, f);
-        error |= checkDeprecated(sc, f);
-        error |= checkPurity(sc, f);
-        error |= checkSafety(sc, f);
-        error |= checkNogc(sc, f);
-        return error;
-    }
+    bool error = f.checkDisabled(exp.loc, sc);
+    error |= f.checkDeprecated(exp.loc, sc);
+    error |= f.checkPurity(exp.loc, sc);
+    error |= f.checkSafety(exp.loc, sc);
+    error |= f.checkNogc(exp.loc, sc);
+    return error;
 }
 
 /*******************************
diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d
index 2f6fae3a1ab..8d88207a566 100644
--- a/gcc/d/dmd/globals.d
+++ b/gcc/d/dmd/globals.d
@@ -250,6 +250,12 @@  extern (C++) struct Param
     const(char)[] resfile;
     const(char)[] exefile;
     const(char)[] mapfile;
+
+    ///
+    bool parsingUnittestsRequired()
+    {
+        return useUnitTests || ddoc.doOutput || dihdr.doOutput;
+    }
 }
 
 enum mars_ext = "d";        // for D source files
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index d935bd3480b..8325081dbd2 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -41,6 +41,7 @@  import dmd.identifier;
 import dmd.init;
 import dmd.mtype;
 import dmd.nspace;
+import dmd.optimize;
 import dmd.parse;
 import dmd.root.complex;
 import dmd.root.ctfloat;
diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d
index 5494fecd70c..92837b45842 100644
--- a/gcc/d/dmd/iasmgcc.d
+++ b/gcc/d/dmd/iasmgcc.d
@@ -302,7 +302,7 @@  Ldone:
 extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
 {
     //printf("GccAsmStatement.semantic()\n");
-    const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+    const bool doUnittests = global.params.parsingUnittestsRequired();
     scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
 
     // Make a safe copy of the token list before parsing.
@@ -341,7 +341,7 @@  extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
             e = e.expressionSemantic(sc);
             // Check argument is a valid lvalue/rvalue.
             if (i < s.outputargs)
-                e = e.modifiableLvalue(sc, null);
+                e = e.modifiableLvalue(sc);
             else if (e.checkValue())
                 e = ErrorExp.get();
             (*s.args)[i] = e;
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index 632c0d0a682..139db0f59e9 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -38,6 +38,7 @@  import dmd.init;
 import dmd.location;
 import dmd.mtype;
 import dmd.opover;
+import dmd.optimize;
 import dmd.statement;
 import dmd.target;
 import dmd.tokens;
diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d
index a1214b2623e..b8faec76d60 100644
--- a/gcc/d/dmd/lexer.d
+++ b/gcc/d/dmd/lexer.d
@@ -3258,6 +3258,24 @@  class Lexer
         scanloc.linnum = scanloc.linnum + 1;
         line = p;
     }
+
+    /****************************
+     * Print the tokens from the current `token` to the end,
+     * while not advancing the parser forward.
+     * Useful for debugging.
+     */
+    void printRestOfTokens()
+    {
+        auto tk = &token;
+        while (1)
+        {
+            printf("%s ", (*tk).toChars());
+            if (tk.value == TOK.endOfFile)
+                break;
+            tk = peek(tk);
+        }
+        printf("\n");
+    }
 }
 
 
diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d
index addcd0103d0..b445b7b707b 100644
--- a/gcc/d/dmd/opover.d
+++ b/gcc/d/dmd/opover.d
@@ -34,6 +34,7 @@  import dmd.id;
 import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
+import dmd.optimize;
 import dmd.statement;
 import dmd.tokens;
 import dmd.typesem;
diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d
index 0065b016f83..69028fac21d 100644
--- a/gcc/d/dmd/optimize.d
+++ b/gcc/d/dmd/optimize.d
@@ -272,9 +272,9 @@  package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
  * Returns:
  *      Constant folded version of `e`
  */
-Expression Expression_optimize(Expression e, int result, bool keepLvalue)
+Expression optimize(Expression e, int result, bool keepLvalue = false)
 {
-    //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
+    //printf("optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
     Expression ret = e;
 
     void errorReturn()
@@ -288,7 +288,7 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
     {
         if (!e)
             return false;
-        Expression ex = Expression_optimize(e, flags, keepLvalue);
+        Expression ex = optimize(e, flags, keepLvalue);
         if (ex.op == EXP.error)
         {
             ret = ex; // store error result
@@ -591,7 +591,7 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
 
                     Expression add = new AddExp(ae.loc, ex, new IntegerExp(ae.e2.loc, offset, ae.e2.type));
                     add.type = e.type;
-                    ret = Expression_optimize(add, result, keepLvalue);
+                    ret = optimize(add, result, keepLvalue);
                     return;
                 }
             }
@@ -1239,7 +1239,7 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
                 ret = new CastExp(e.loc, ret, Type.tvoid);
                 ret.type = e.type;
             }
-            ret = Expression_optimize(ret, result, false);
+            ret = optimize(ret, result, false);
             return;
         }
         expOptimize(e.e2, WANTvalue);
@@ -1294,7 +1294,7 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
                 // `["c"] ~ "a" ~ "b"` becoming `["c"] ~ "ab"`
                 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
                 cex.type = e.type;
-                Expression ex = Expression_optimize(cex, result, false);
+                Expression ex = optimize(cex, result, false);
                 if (ex != cex)
                 {
                     e.e1 = ce1.e1;
@@ -1323,9 +1323,9 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
             return;
         const opt = e.econd.toBool();
         if (opt.hasValue(true))
-            ret = Expression_optimize(e.e1, result, keepLvalue);
+            ret = optimize(e.e1, result, keepLvalue);
         else if (opt.hasValue(false))
-            ret = Expression_optimize(e.e2, result, keepLvalue);
+            ret = optimize(e.e2, result, keepLvalue);
         else
         {
             expOptimize(e.e1, result, keepLvalue);
diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d
index 0b0ca916db2..bf220f31d9b 100644
--- a/gcc/d/dmd/semantic3.d
+++ b/gcc/d/dmd/semantic3.d
@@ -54,6 +54,7 @@  import dmd.nspace;
 import dmd.ob;
 import dmd.objc;
 import dmd.opover;
+import dmd.optimize;
 import dmd.parse;
 import dmd.root.filename;
 import dmd.common.outbuffer;
@@ -917,7 +918,7 @@  private extern(C++) final class Semantic3Visitor : Visitor
                         if (f.isref)
                         {
                             // Function returns a reference
-                            exp = exp.toLvalue(sc2, exp);
+                            exp = exp.toLvalue(sc2);
                             checkReturnEscapeRef(sc2, exp, false);
                             exp = exp.optimize(WANTvalue, /*keepLvalue*/ true);
                         }
diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d
index d43d915e2e3..f8b2c26df7f 100644
--- a/gcc/d/dmd/statementsem.d
+++ b/gcc/d/dmd/statementsem.d
@@ -55,6 +55,7 @@  import dmd.location;
 import dmd.mtype;
 import dmd.mustuse;
 import dmd.nogc;
+import dmd.optimize;
 import dmd.opover;
 import dmd.parse;
 import dmd.common.outbuffer;
@@ -4887,7 +4888,7 @@  private Statements* flatten(Statement statement, Scope* sc)
             const len = buf.length;
             buf.writeByte(0);
             const str = buf.extractSlice()[0 .. len];
-            const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+            const bool doUnittests = global.params.parsingUnittestsRequired();
             auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
             scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
             p.transitionIn = global.params.v.vin;
diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d
index 923f1a99e80..1d18de31829 100644
--- a/gcc/d/dmd/staticcond.d
+++ b/gcc/d/dmd/staticcond.d
@@ -22,6 +22,7 @@  import dmd.expressionsem;
 import dmd.globals;
 import dmd.identifier;
 import dmd.mtype;
+import dmd.optimize;
 import dmd.root.array;
 import dmd.common.outbuffer;
 import dmd.tokens;
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
index ca2af79dde4..79df7fde02b 100644
--- a/gcc/d/dmd/traits.d
+++ b/gcc/d/dmd/traits.d
@@ -42,6 +42,7 @@  import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.nogc;
+import dmd.optimize;
 import dmd.parse;
 import dmd.root.array;
 import dmd.root.speller;
@@ -1875,16 +1876,24 @@  Expression semanticTraits(TraitsExp e, Scope* sc)
             return dimError(1);
 
         auto o = (*e.args)[0];
-        Type t = isType(o);
-        AggregateDeclaration ad = t ? isAggregate(t) : null;
 
-        // Interfaces don't have an init symbol and hence cause linker errors
-        if (!ad || ad.isInterfaceDeclaration())
+        ErrorExp badArgument()
         {
             error(e.loc, "struct / class type expected as argument to __traits(initSymbol) instead of `%s`", o.toChars());
             return ErrorExp.get();
         }
 
+        Type t = isType(o);
+
+        if (!t || t.isTypeEnum())
+            return badArgument();
+
+        AggregateDeclaration ad = isAggregate(t);
+
+        // Interfaces don't have an init symbol and hence cause linker errors
+        if (!ad || ad.isInterfaceDeclaration())
+            return badArgument();
+
         Declaration d = new SymbolDeclaration(ad.loc, ad);
         d.type = Type.tvoid.arrayOf().constOf();
         d.storage_class |= STC.rvalue;
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index bbe11f63d4b..4a4c5d4f7f0 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -53,6 +53,7 @@  import dmd.visitor;
 import dmd.mtype;
 import dmd.objc;
 import dmd.opover;
+import dmd.optimize;
 import dmd.parse;
 import dmd.root.complex;
 import dmd.root.ctfloat;
@@ -1098,7 +1099,7 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             if (isRefOrOut && !isAuto &&
                 !(global.params.previewIn && (fparam.storageClass & STC.in_)) &&
                 global.params.rvalueRefParam != FeatureState.enabled)
-                e = e.toLvalue(sc, e);
+                e = e.toLvalue(sc);
 
             fparam.defaultArg = e;
             return (e.op != EXP.error);
@@ -3748,7 +3749,7 @@  Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
         }
         // check before alias resolution; the alias itself might be deprecated!
         if (s.isAliasDeclaration)
-            e.checkDeprecated(sc, s);
+            s.checkDeprecated(e.loc, sc);
         s = s.toAlias();
 
         if (auto em = s.isEnumMember())
@@ -5009,7 +5010,7 @@  RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc)
     const len = buf.length;
     buf.writeByte(0);
     const str = buf.extractSlice()[0 .. len];
-    const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput;
+    const bool doUnittests = global.params.parsingUnittestsRequired();
     auto locm = adjustLocForMixin(str, loc, global.params.mixinOut);
     scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
     p.transitionIn = global.params.v.vin;
diff --git a/gcc/testsuite/gdc.test/compilable/previewin.d b/gcc/testsuite/gdc.test/compilable/previewin.d
index 8926fbd6aa7..558005c5280 100644
--- a/gcc/testsuite/gdc.test/compilable/previewin.d
+++ b/gcc/testsuite/gdc.test/compilable/previewin.d
@@ -79,14 +79,11 @@  version (Win64)
 {
     void checkReal(in real p)
     {
-        // ref for x87 real, value for double-precision real
-        static assert(__traits(isRef, p) == (real.sizeof > 8));
     }
 
     struct RGB { ubyte r, g, b; }
     void checkNonPowerOf2(in RGB p)
     {
-        static assert(__traits(isRef, p));
     }
 }
 else version (X86_64) // Posix x86_64
@@ -94,7 +91,6 @@  else version (X86_64) // Posix x86_64
     struct Empty {} // 1 dummy byte passed on the stack
     void checkEmptyStruct(in Empty p)
     {
-        static assert(!__traits(isRef, p));
     }
 
     static if (is(__vector(double[4])))
@@ -102,7 +98,6 @@  else version (X86_64) // Posix x86_64
         struct AvxVectorWrapper { __vector(double[4]) a; } // 256 bits
         void checkAvxVector(in AvxVectorWrapper p)
         {
-            static assert(!__traits(isRef, p));
         }
     }
 }
@@ -111,6 +106,5 @@  else version (AArch64)
     alias HVA = __vector(float[4])[4]; // can be passed in 4 vector registers
     void checkHVA(in HVA p)
     {
-        static assert(!__traits(isRef, p));
     }
 }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail212.d b/gcc/testsuite/gdc.test/fail_compilation/fail212.d
index 5f308638b9c..780cd8279e5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail212.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail212.d
@@ -1,10 +1,14 @@ 
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail212.d(14): Error: function `fail212.S.bar` without `this` cannot be `const`
+fail_compilation/fail212.d(10): Error: function `fail212.baz` without `this` cannot be `const`
+fail_compilation/fail212.d(10):        did you mean to use `const(int)` as the return type?
+fail_compilation/fail212.d(18): Error: function `fail212.S.bar` without `this` cannot be `const`
 ---
 */
 
+const int baz();
+
 struct S
 {
     void foo() const
diff --git a/gcc/testsuite/gdc.test/runnable/previewin.d b/gcc/testsuite/gdc.test/runnable/previewin.d
index 117070dfe5e..50f22ee22bf 100644
--- a/gcc/testsuite/gdc.test/runnable/previewin.d
+++ b/gcc/testsuite/gdc.test/runnable/previewin.d
@@ -154,27 +154,25 @@  struct WithDtor
 @safe pure nothrow @nogc:
 
 // By value
-void testin1(in uint p) { static assert(!__traits(isRef, p)); }
+void testin1(in uint p) { }
 // By ref because of size
-void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); }
+void testin2(in ulong[64] p) { }
 // By value or ref depending on size (or structs always passed by reference)
-void testin3(in ValueT p) { static assert(!__traits(isRef, p) || true); }
-void testin3(in RefT p) { static assert(__traits(isRef, p)); }
+void testin3(in ValueT p) { }
+void testin3(in RefT p) { }
 // By ref because of size (or arrays always passed by reference)
-void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); }
-void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); }
+void testin4(in ValueT[64] p) { }
+void testin4(in RefT[4] p) { }
 
 // By ref because of non-copyability
-void testin5(in NonCopyable noncopy) { static assert(__traits(isRef, noncopy)); }
-static assert(testin5.mangleof == "_D9previewin7testin5FNaNbNiNfIKSQBe11NonCopyableZv"); // incl. `ref`
+void testin5(in NonCopyable noncopy) { }
 //  By ref because of postblit
-void testin6(in WithPostblit withpostblit) { static assert(__traits(isRef, withpostblit)); }
+void testin6(in WithPostblit withpostblit) { }
 //  By ref because of copy ctor
-void testin7(in WithCopyCtor withcopy) { static assert(__traits(isRef, withcopy)); }
+void testin7(in WithCopyCtor withcopy) { }
 //  By ref because of dtor
 void testin8(in WithDtor withdtor, scope bool* isTestOver)
 {
-    static assert(__traits(isRef, withdtor));
     if (isTestOver)
         *isTestOver = true;
 }
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 8c536ce71a5..95b2778d141 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@ 
-1c98326e787e504d9045004e593273ec99b13121
+fc06c514a8c4492f60fc89b8c4f857e6932fbcbd
 
 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/container/array.d b/libphobos/src/std/container/array.d
index 0d6be93a1c1..ad120c1c74f 100644
--- a/libphobos/src/std/container/array.d
+++ b/libphobos/src/std/container/array.d
@@ -594,6 +594,10 @@  if (!is(immutable T == immutable bool))
         assert(capacity == values.length); // We check that reserve has been called before the loop.
     }
 
+    /// ditto
+    // needed when T is an array and only one argument is passed
+    this(T single) { __ctor!T(single); }
+
     /**
      * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
      */
@@ -1282,6 +1286,13 @@  if (!is(immutable T == immutable bool))
     }
 }
 
+@system unittest
+{
+    import std.algorithm.comparison : equal;
+    auto a = Array!string("test");
+    assert(a[].equal(["test"]));
+}
+
 @safe unittest
 {
     // https://issues.dlang.org/show_bug.cgi?id=13621
diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d
index 4f4183c4cec..330ef88aa06 100644
--- a/libphobos/src/std/logger/package.d
+++ b/libphobos/src/std/logger/package.d
@@ -54,6 +54,7 @@  $(UL
     $(LI `trace`)
     $(LI `info`)
     $(LI `warning`)
+    #(LI `error`)
     $(LI `critical`)
     $(LI `fatal`)
 )
diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d
index 89cfa07cc1f..fec5c85cbef 100644
--- a/libphobos/src/std/range/primitives.d
+++ b/libphobos/src/std/range/primitives.d
@@ -1015,12 +1015,27 @@  See_Also:
 enum bool isForwardRange(R) = isInputRange!R
     && is(typeof((R r) { return r.save; } (R.init)) == R);
 
+/// ditto
+enum bool isForwardRange(R, E) =
+    .isForwardRange!R && isQualifierConvertible!(ElementType!R, E);
+
 ///
 @safe unittest
 {
     static assert(!isForwardRange!(int));
     static assert( isForwardRange!(int[]));
     static assert( isForwardRange!(inout(int)[]));
+
+    static assert( isForwardRange!(int[], const int));
+    static assert(!isForwardRange!(int[], immutable int));
+
+    static assert(!isForwardRange!(const(int)[], int));
+    static assert( isForwardRange!(const(int)[], const int));
+    static assert(!isForwardRange!(const(int)[], immutable int));
+
+    static assert(!isForwardRange!(immutable(int)[], int));
+    static assert( isForwardRange!(immutable(int)[], const int));
+    static assert( isForwardRange!(immutable(int)[], immutable int));
 }
 
 @safe unittest