public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-5887] d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
@ 2021-12-10  4:27 Iain Buclaw
  0 siblings, 0 replies; only message in thread
From: Iain Buclaw @ 2021-12-10  4:27 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:9c7d5e8846edb28e5421211ee8eaad93e234de2c

commit r12-5887-g9c7d5e8846edb28e5421211ee8eaad93e234de2c
Author: Iain Buclaw <ibuclaw@gdcproject.org>
Date:   Fri Dec 10 03:14:20 2021 +0100

    d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
    
    D front-end changes:
    
        - Import dmd mainline development.
        - Split off enum EXP from enum TOK.
        - Integer promotions now follow C integral promotions by default.
        - Implements __traits(initSymbol).
        - Lowering of array construction has been moved to the dmd
          front-end.
        - Fix segfault in dmd.lexer from unaligned read (PR103529).
    
    Druntime changes:
    
        - Import druntime mainline development.
        - Define SIG_BLOCK for Solaris (PR103528).
    
    Phobos changes:
    
        - Import phobos mainline development.
    
    gcc/d/ChangeLog:
    
            PR d/103529
            * dmd/MERGE: Merge upstream dmd 3982604c5.
            * Make-lang.in (D_FRONTEND_OBJS): Add d/root-optional.o.
            * d-attribs.cc (build_attributes): Update for new front-end interface.
            * d-codegen.cc (d_build_call): Likewise.
            * d-compiler.cc (Compiler::paintAsType): Likewise.
            * d-lang.cc (d_handle_option): Remove OPT_fpreview_intpromote, add
            handling of OPT_frevert_intpromote.
            * d-port.cc (Port::valcpy): Assert buffer is aligned.
            * d-target.cc (Target::isVectorOpSupported): Update for new front-end
            interface.
            * decl.cc (layout_class_initializer): Likewise.
            * expr.cc (lvalue_p): Likewise.
            (binop_assignment): Likewise.
            (ExprVisitor::visit): Likewise.
            (ExprVisitor::visit (AssignExp *)): Remove generation of _d_arrayctor
            and _d_arraysetctor library helpers.
            (ExprVisitor::visit (VarExp *)): Support __traits(initSymbol).
            * intrinsics.cc (expand_intrinsic_rotate): Update for new front-end
            interface.
            * lang.opt (fpreview=intpromote): Remove.
            (frevert=intpromote): New.
            * runtime.def (ARRAYCTOR): Remove.
            (ARRAYSETCTOR): Remove.
            * toir.cc (IRVisitor::visit): Update for new front-end interface.
            * types.cc (layout_aggregate_members): Likewise.
            * dmd/root/optional.d: New file.
            * dmd/root/optional.h: New file.
    
    libphobos/ChangeLog:
    
            PR d/103528
            * libdruntime/MERGE: Merge upstream druntime bc58b1e9.
            * libdruntime/Makefile.am (DRUNTIME_DSOURCES_LINUX): Remove
            core/sys/linux/syscalls.d.
            * libdruntime/Makefile.in: Regenerate.
            * src/MERGE: Merge upstream phobos 12329adb6.
            * testsuite/libphobos.config/config.exp: Add test22523.
            * libdruntime/core/sys/linux/syscalls.d: Removed.
            * testsuite/libphobos.config/test22523.d: New test.

Diff:
---
 gcc/d/Make-lang.in                                 |    1 +
 gcc/d/d-attribs.cc                                 |    6 +-
 gcc/d/d-codegen.cc                                 |    6 +-
 gcc/d/d-compiler.cc                                |    4 +-
 gcc/d/d-lang.cc                                    |   11 +-
 gcc/d/d-port.cc                                    |    2 +
 gcc/d/d-target.cc                                  |   30 +-
 gcc/d/decl.cc                                      |    2 +-
 gcc/d/dmd/MERGE                                    |    2 +-
 gcc/d/dmd/access.d                                 |    2 +-
 gcc/d/dmd/aggregate.d                              |   14 +-
 gcc/d/dmd/aliasthis.d                              |    4 +-
 gcc/d/dmd/arrayop.d                                |   79 +-
 gcc/d/dmd/astenums.d                               |    4 +-
 gcc/d/dmd/attrib.d                                 |   11 +-
 gcc/d/dmd/blockexit.d                              |   20 +-
 gcc/d/dmd/builtin.d                                |    2 -
 gcc/d/dmd/canthrow.d                               |    2 +-
 gcc/d/dmd/clone.d                                  |    8 +-
 gcc/d/dmd/common/outbuffer.h                       |    2 +-
 gcc/d/dmd/cond.d                                   |    2 +-
 gcc/d/dmd/constfold.d                              |  166 +--
 gcc/d/dmd/cparse.d                                 |   45 +-
 gcc/d/dmd/cppmangle.d                              |    2 +-
 gcc/d/dmd/ctfeexpr.d                               |  394 ++---
 gcc/d/dmd/dcast.d                                  |  176 ++-
 gcc/d/dmd/declaration.d                            |   40 +-
 gcc/d/dmd/declaration.h                            |    9 +-
 gcc/d/dmd/dinterpret.d                             |  634 ++++----
 gcc/d/dmd/dmangle.d                                |    4 +-
 gcc/d/dmd/dstruct.d                                |   18 +-
 gcc/d/dmd/dsymbol.d                                |   16 +-
 gcc/d/dmd/dsymbolsem.d                             |   56 +-
 gcc/d/dmd/dtemplate.d                              |  137 +-
 gcc/d/dmd/dtoh.d                                   |   11 +-
 gcc/d/dmd/escape.d                                 |   65 +-
 gcc/d/dmd/expression.d                             |  784 +++++-----
 gcc/d/dmd/expression.h                             |   28 +-
 gcc/d/dmd/expressionsem.d                          |  802 +++++-----
 gcc/d/dmd/foreachvar.d                             |    2 +-
 gcc/d/dmd/func.d                                   |    9 +-
 gcc/d/dmd/globals.d                                |    2 +-
 gcc/d/dmd/hdrgen.d                                 |  195 ++-
 gcc/d/dmd/iasmgcc.d                                |    4 +-
 gcc/d/dmd/id.d                                     |    3 +
 gcc/d/dmd/initsem.d                                |   56 +-
 gcc/d/dmd/lambdacomp.d                             |    5 +-
 gcc/d/dmd/lexer.d                                  |   22 +-
 gcc/d/dmd/mtype.d                                  |   16 +-
 gcc/d/dmd/nogc.d                                   |    6 +-
 gcc/d/dmd/ob.d                                     |    6 +-
 gcc/d/dmd/objc.d                                   |   12 +-
 gcc/d/dmd/opover.d                                 |  323 ++--
 gcc/d/dmd/optimize.d                               | 1535 ++++++++++----------
 gcc/d/dmd/parse.d                                  |  340 ++---
 gcc/d/dmd/printast.d                               |    7 +-
 gcc/d/dmd/root/dcompat.h                           |    3 +
 gcc/d/dmd/root/file.d                              |   18 +-
 gcc/d/dmd/root/optional.d                          |   86 ++
 gcc/d/dmd/root/optional.h                          |   42 +
 gcc/d/dmd/safe.d                                   |    4 +-
 gcc/d/dmd/semantic2.d                              |   18 +-
 gcc/d/dmd/semantic3.d                              |    4 +-
 gcc/d/dmd/sideeffect.d                             |   84 +-
 gcc/d/dmd/statementsem.d                           |  882 ++++++-----
 gcc/d/dmd/staticcond.d                             |   51 +-
 gcc/d/dmd/target.d                                 |    5 +-
 gcc/d/dmd/target.h                                 |    4 +-
 gcc/d/dmd/templateparamsem.d                       |    6 +-
 gcc/d/dmd/tokens.d                                 |  160 +-
 gcc/d/dmd/tokens.h                                 |  628 +++++---
 gcc/d/dmd/traits.d                                 |   41 +-
 gcc/d/dmd/typesem.d                                |  124 +-
 gcc/d/expr.cc                                      |  259 ++--
 gcc/d/intrinsics.cc                                |    2 +-
 gcc/d/lang.opt                                     |    8 +-
 gcc/d/runtime.def                                  |    7 -
 gcc/d/toir.cc                                      |    3 +-
 gcc/d/types.cc                                     |    2 +-
 .../gdc.test/compilable/covariant_override.d       |   34 +
 gcc/testsuite/gdc.test/compilable/emptygenmain.d   |    3 +
 gcc/testsuite/gdc.test/compilable/noreturn1.d      |   12 +
 gcc/testsuite/gdc.test/compilable/test17870.d      |   26 +
 gcc/testsuite/gdc.test/compilable/test19873.d      |   37 +
 gcc/testsuite/gdc.test/compilable/test21719.d      |   21 +
 gcc/testsuite/gdc.test/compilable/test22254.d      |   19 +
 gcc/testsuite/gdc.test/compilable/test22510.d      |   18 +
 .../gdc.test/fail_compilation/covariant_override.d |   35 +
 .../gdc.test/fail_compilation/fail10964.d          |    4 +-
 .../gdc.test/fail_compilation/fail10968.d          |   38 +-
 .../gdc.test/fail_compilation/fail16997.d          |   38 +-
 gcc/testsuite/gdc.test/fail_compilation/fail809.d  |   12 -
 gcc/testsuite/gdc.test/fail_compilation/fob2.d     |    2 +-
 .../gdc.test/fail_compilation/imports/test20023b.d |   10 +
 gcc/testsuite/gdc.test/fail_compilation/retscope.d |    8 +-
 .../gdc.test/fail_compilation/test15191.d          |   42 +-
 .../gdc.test/fail_compilation/test17977.d          |   20 +
 .../gdc.test/fail_compilation/test20023.d          |   16 +
 .../gdc.test/fail_compilation/traits_initSymbol.d  |   63 +
 gcc/testsuite/gdc.test/runnable/b19294.d           |  163 +++
 gcc/testsuite/gdc.test/runnable/mars1.d            |    2 +-
 gcc/testsuite/gdc.test/runnable/test15862.d        |   39 +
 gcc/testsuite/gdc.test/runnable/test21367.d        |   47 +
 gcc/testsuite/gdc.test/runnable/test22227.d        |   16 +
 gcc/testsuite/gdc.test/runnable/testOpApply.d      |   31 +-
 gcc/testsuite/gdc.test/runnable/testcgelem.d       |    2 +-
 gcc/testsuite/gdc.test/runnable/testconst.d        |    8 +-
 .../gdc.test/runnable/traits_initSymbol.d          |  119 ++
 gcc/testsuite/gdc.test/runnable/xtest46.d          |    7 +-
 .../gdc.test/runnable_cxx/extra-files/cpp7925.cpp  |  103 ++
 gcc/testsuite/gdc.test/runnable_cxx/test7925.d     |  151 ++
 libphobos/libdruntime/MERGE                        |    2 +-
 libphobos/libdruntime/Makefile.am                  |    5 +-
 libphobos/libdruntime/Makefile.in                  |   12 +-
 libphobos/libdruntime/core/demangle.d              |    8 +-
 .../libdruntime/core/internal/array/construction.d |   43 +-
 libphobos/libdruntime/core/internal/convert.d      |    8 +-
 libphobos/libdruntime/core/internal/lifetime.d     |   49 +-
 libphobos/libdruntime/core/internal/string.d       |    2 +-
 libphobos/libdruntime/core/internal/utf.d          |   10 +-
 libphobos/libdruntime/core/lifetime.d              |  111 +-
 libphobos/libdruntime/core/memory.d                |   21 +-
 libphobos/libdruntime/core/stdc/stdlib.d           |   10 +
 libphobos/libdruntime/core/stdc/string.d           |   28 +-
 libphobos/libdruntime/core/stdc/wchar_.d           |   26 +-
 libphobos/libdruntime/core/stdcpp/exception.d      |    2 +
 libphobos/libdruntime/core/sync/mutex.d            |    4 +-
 libphobos/libdruntime/core/sys/bionic/string.d     |    2 +-
 libphobos/libdruntime/core/sys/darwin/mach/nlist.d |    2 +-
 libphobos/libdruntime/core/sys/darwin/string.d     |    2 +-
 .../libdruntime/core/sys/dragonflybsd/string.d     |    2 +-
 libphobos/libdruntime/core/sys/freebsd/string.d    |    2 +-
 libphobos/libdruntime/core/sys/linux/string.d      |    2 +-
 libphobos/libdruntime/core/sys/linux/syscalls.d    |  745 ----------
 libphobos/libdruntime/core/sys/linux/unistd.d      |   26 +-
 libphobos/libdruntime/core/sys/netbsd/string.d     |    2 +-
 libphobos/libdruntime/core/sys/openbsd/string.d    |    2 +-
 libphobos/libdruntime/core/sys/posix/signal.d      |   26 +-
 libphobos/libdruntime/core/sys/posix/string.d      |    8 +-
 libphobos/libdruntime/core/sys/posix/sys/socket.d  |    2 +-
 libphobos/libdruntime/core/sys/solaris/sys/elf.d   |    5 +-
 .../libdruntime/core/sys/solaris/sys/elf_386.d     |    3 -
 .../libdruntime/core/sys/solaris/sys/elf_SPARC.d   |    3 -
 libphobos/libdruntime/core/sys/windows/dbghelp.d   |    8 +-
 libphobos/libdruntime/core/thread/osthread.d       |    5 +-
 libphobos/libdruntime/core/thread/threadbase.d     |    5 +-
 libphobos/libdruntime/object.d                     |    8 +-
 libphobos/libdruntime/rt/aaA.d                     |    4 +-
 libphobos/libdruntime/rt/cast_.d                   |    2 +-
 libphobos/libdruntime/rt/config.d                  |    3 +
 libphobos/libdruntime/rt/lifetime.d                |    4 +-
 libphobos/libdruntime/rt/monitor_.d                |    2 +-
 libphobos/src/MERGE                                |    2 +-
 libphobos/src/std/algorithm/iteration.d            |   52 +-
 libphobos/src/std/algorithm/mutation.d             |   29 +-
 libphobos/src/std/algorithm/sorting.d              |   26 +-
 libphobos/src/std/concurrency.d                    |   32 +-
 libphobos/src/std/container/dlist.d                |    6 +
 libphobos/src/std/container/rbtree.d               |    2 +-
 libphobos/src/std/datetime/interval.d              |    6 +-
 libphobos/src/std/datetime/systime.d               |   27 +-
 libphobos/src/std/datetime/timezone.d              |   74 +-
 libphobos/src/std/file.d                           |   28 +-
 libphobos/src/std/internal/cstring.d               |    2 +-
 libphobos/src/std/internal/math/biguintcore.d      |   12 +-
 libphobos/src/std/json.d                           |   10 +-
 libphobos/src/std/net/isemail.d                    |    2 +-
 libphobos/src/std/process.d                        |    4 +-
 libphobos/src/std/random.d                         |   12 +-
 libphobos/src/std/stdio.d                          |    2 +-
 libphobos/src/std/typecons.d                       |   35 +-
 libphobos/src/std/uni/package.d                    |    4 +-
 libphobos/src/std/utf.d                            |   12 +-
 libphobos/testsuite/libphobos.config/config.exp    |    1 +
 libphobos/testsuite/libphobos.config/test22523.d   |   11 +
 175 files changed, 6436 insertions(+), 4902 deletions(-)

diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index d7f714760f7..00169a743a1 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -162,6 +162,7 @@ D_FRONTEND_OBJS = \
 	d/root-filename.o \
 	d/root-hash.o \
 	d/root-longdouble.o \
+	d/root-optional.o \
 	d/root-port.o \
 	d/root-region.o \
 	d/root-rmem.o \
diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc
index 04b9791ab1b..5c9f569d1c4 100644
--- a/gcc/d/d-attribs.cc
+++ b/gcc/d/d-attribs.cc
@@ -337,10 +337,10 @@ build_attributes (Expressions *eattrs)
 	continue;
 
       /* Get the result of the attribute if it hasn't already been folded.  */
-      if (attr->op == TOKcall)
+      if (attr->op == EXP::call)
 	attr = attr->ctfeInterpret ();
 
-      if (attr->op != TOKstructliteral)
+      if (attr->op != EXP::structLiteral)
 	{
 	  warning_at (make_location_t (attr->loc), OPT_Wattributes,
 		      "%qE attribute has no effect",
@@ -353,7 +353,7 @@ build_attributes (Expressions *eattrs)
       Expressions *elems = attr->isStructLiteralExp ()->elements;
       Expression *e0 = (*elems)[0];
 
-      if (e0->op != TOKstring)
+      if (e0->op != EXP::string_)
 	{
 	  warning_at (make_location_t (attr->loc), OPT_Wattributes,
 		      "unknown attribute %qs", e0->toChars());
diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index c082ac5ab80..39c3c6ce987 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -2154,9 +2154,9 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
 	{
 	Lagain:
 	  Expression *arg = (*arguments)[i];
-	  gcc_assert (arg->op != TOKtuple);
+	  gcc_assert (arg->op != EXP::tuple);
 
-	  if (arg->op == TOKcomma)
+	  if (arg->op == EXP::comma)
 	    {
 	      CommaExp *ce = arg->isCommaExp ();
 	      tree tce = build_expr (ce->e1);
@@ -2200,7 +2200,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
 	      /* Nested structs also have ADDRESSABLE set, but if the type has
 		 neither a copy constructor nor a destructor available, then we
 		 need to take care of copying its value before passing it.  */
-	      if (arg->op == TOKstructliteral || (!sd->postblit && !sd->dtor))
+	      if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor))
 		targ = force_target_expr (targ);
 
 	      targ = convert (build_reference_type (TREE_TYPE (targ)),
diff --git a/gcc/d/d-compiler.cc b/gcc/d/d-compiler.cc
index 3df40073ac5..c1e78c0a5de 100644
--- a/gcc/d/d-compiler.cc
+++ b/gcc/d/d-compiler.cc
@@ -50,7 +50,7 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
     cst = build_integer_cst (expr->toInteger (), build_ctype (expr->type));
   else if (expr->type->isfloating ())
     cst = build_float_cst (expr->toReal (), expr->type);
-  else if (expr->op == TOKarrayliteral)
+  else if (expr->op == EXP::arrayLiteral)
     {
       /* Build array as VECTOR_CST, assumes EXPR is constant.  */
       Expressions *elements = expr->isArrayLiteralExp ()->elements;
@@ -99,7 +99,7 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
       cst = native_interpret_expr (vectype, buffer, len);
 
       Expression *e = d_eval_constant_expression (expr->loc, cst);
-      gcc_assert (e != NULL && e->op == TOKvector);
+      gcc_assert (e != NULL && e->op == EXP::vector);
 
       return e->isVectorExp ()->e1;
     }
diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
index 576eefcc01f..2c5d206a95f 100644
--- a/gcc/d/d-lang.cc
+++ b/gcc/d/d-lang.cc
@@ -620,10 +620,6 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       global.params.inclusiveInContracts = value;
       break;
 
-    case OPT_fpreview_intpromote:
-      global.params.fix16997 = value;
-      break;
-
     case OPT_fpreview_nosharedaccess:
       global.params.noSharedAccess = value;
       break;
@@ -642,8 +638,9 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
 
     case OPT_frevert_all:
       global.params.useDIP25 = FeatureState::disabled;
-      global.params.markdown = !value;
       global.params.dtorFields = FeatureState::disabled;
+      global.params.fix16997 = !value;
+      global.params.markdown = !value;
       break;
 
     case OPT_frevert_dip25:
@@ -654,6 +651,10 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       global.params.dtorFields = FeatureState::disabled;
       break;
 
+    case OPT_frevert_intpromote:
+      global.params.fix16997 = !value;
+      break;
+
     case OPT_frevert_markdown:
       global.params.markdown = !value;
       break;
diff --git a/gcc/d/d-port.cc b/gcc/d/d-port.cc
index 58b3fe138b2..4e867a7db4a 100644
--- a/gcc/d/d-port.cc
+++ b/gcc/d/d-port.cc
@@ -145,6 +145,8 @@ Port::readlongBE (const void *buffer)
 void
 Port::valcpy (void *buffer, uint64_t value, size_t sz)
 {
+  gcc_assert (((size_t) buffer) % sz == 0);
+
   switch (sz)
     {
     case 1:
diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc
index 21417dddf78..dd244f12119 100644
--- a/gcc/d/d-target.cc
+++ b/gcc/d/d-target.cc
@@ -287,7 +287,7 @@ Target::isVectorTypeSupported (int sz, Type *type)
    Returns true if the operation is supported or type is not a vector.  */
 
 bool
-Target::isVectorOpSupported (Type *type, unsigned op, Type *)
+Target::isVectorOpSupported (Type *type, EXP op, Type *)
 {
   if (type->ty != TY::Tvector)
     return true;
@@ -299,31 +299,31 @@ Target::isVectorOpSupported (Type *type, unsigned op, Type *)
   /* Don't support if expression cannot be represented.  */
   switch (op)
     {
-    case TOKpow:
-    case TOKpowass:
+    case EXP::pow:
+    case EXP::powAssign:
       /* pow() is lowered as a function call.  */
       return false;
 
-    case TOKmod:
-    case TOKmodass:
+    case EXP::mod:
+    case EXP::modAssign:
       /* fmod() is lowered as a function call.  */
       if (type->isfloating ())
 	return false;
       break;
 
-    case TOKandand:
-    case TOKoror:
+    case EXP::andAnd:
+    case EXP::orOr:
       /* Logical operators must have a result type of bool.  */
       return false;
 
-    case TOKle:
-    case TOKlt:
-    case TOKge:
-    case TOKgt:
-    case TOKequal:
-    case TOKnotequal:
-    case TOKidentity:
-    case TOKnotidentity:
+    case EXP::lessOrEqual:
+    case EXP::lessThan:
+    case EXP::greaterOrEqual:
+    case EXP::greaterThan:
+    case EXP::equal:
+    case EXP::notEqual:
+    case EXP::identity:
+    case EXP::notIdentity:
       /* Comparison operators must have a result type of bool.  */
       return false;
 
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index c69f5664e8c..bbde4a669e4 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -2239,7 +2239,7 @@ layout_class_initializer (ClassDeclaration *cd)
   ne->type = cd->type;
 
   Expression *e = ne->ctfeInterpret ();
-  gcc_assert (e->op == TOKclassreference);
+  gcc_assert (e->op == EXP::classReference);
 
   return build_class_instance (e->isClassReferenceExp ());
 }
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index d23e1fedba4..4bae16c86eb 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-568496d5b6ed02d577dfa86f73c7bb4edee05813
+3982604c54e8770585985a33577fbf19b9b5c9ce
 
 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/access.d b/gcc/d/dmd/access.d
index 944c9d3e124..d8a65179a9e 100644
--- a/gcc/d/dmd/access.d
+++ b/gcc/d/dmd/access.d
@@ -201,7 +201,7 @@ bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d)
     {
         // Do access check
         ClassDeclaration cd = tc.sym;
-        if (e.op == TOK.super_)
+        if (e.op == EXP.super_)
         {
             if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
                 cd = cd2;
diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d
index 1fe8e809aa5..dc772e8eeac 100644
--- a/gcc/d/dmd/aggregate.d
+++ b/gcc/d/dmd/aggregate.d
@@ -472,7 +472,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         }
         foreach (e; *elements)
         {
-            if (e && e.op == TOK.error)
+            if (e && e.op == EXP.error)
                 return false;
         }
 
@@ -565,6 +565,18 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
 
     override final Type getType()
     {
+        /* Apply storage classes to forward references. (Issue 22254)
+         * Note: Avoid interfaces for now. Implementing qualifiers on interface
+         * definitions exposed some issues in their TypeInfo generation in DMD.
+         * Related PR: https://github.com/dlang/dmd/pull/13312
+         */
+        if (semanticRun == PASS.init && !isInterfaceDeclaration())
+        {
+            auto stc = storage_class;
+            if (_scope)
+                stc |= _scope.stc;
+            type = type.addSTC(stc);
+        }
         return type;
     }
 
diff --git a/gcc/d/dmd/aliasthis.d b/gcc/d/dmd/aliasthis.d
index e048cdc2e1b..80db47ded1d 100644
--- a/gcc/d/dmd/aliasthis.d
+++ b/gcc/d/dmd/aliasthis.d
@@ -91,7 +91,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find
         if (ad.aliasthis)
         {
             Loc loc = e.loc;
-            Type tthis = (e.op == TOK.type ? e.type : null);
+            Type tthis = (e.op == EXP.type ? e.type : null);
             const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0);
             uint olderrors = gag ? global.startGagging() : 0;
             e = dotExp(e.type, sc, e, ad.aliasthis.ident, flags);
@@ -100,7 +100,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find
 
             if (tthis && ad.aliasthis.sym.needThis())
             {
-                if (e.op == TOK.variable)
+                if (e.op == EXP.variable)
                 {
                     if (auto fd = (cast(VarExp)e).var.isFuncDeclaration())
                     {
diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d
index e2b33194f06..a234501075c 100644
--- a/gcc/d/dmd/arrayop.d
+++ b/gcc/d/dmd/arrayop.d
@@ -23,6 +23,7 @@ import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
+import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.mtype;
@@ -37,9 +38,9 @@ import dmd.visitor;
 bool isArrayOpValid(Expression e)
 {
     //printf("isArrayOpValid() %s\n", e.toChars());
-    if (e.op == TOK.slice)
+    if (e.op == EXP.slice)
         return true;
-    if (e.op == TOK.arrayLiteral)
+    if (e.op == EXP.arrayLiteral)
     {
         Type t = e.type.toBasetype();
         while (t.ty == Tarray || t.ty == Tsarray)
@@ -53,17 +54,17 @@ bool isArrayOpValid(Expression e)
         {
             return isArrayOpValid((cast(UnaExp)e).e1);
         }
-        if (isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == TOK.assign)
+        if (isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == EXP.assign)
         {
             BinExp be = cast(BinExp)e;
             return isArrayOpValid(be.e1) && isArrayOpValid(be.e2);
         }
-        if (e.op == TOK.construct)
+        if (e.op == EXP.construct)
         {
             BinExp be = cast(BinExp)e;
-            return be.e1.op == TOK.slice && isArrayOpValid(be.e2);
+            return be.e1.op == EXP.slice && isArrayOpValid(be.e2);
         }
-        // if (e.op == TOK.call)
+        // if (e.op == EXP.call)
         // {
         // TODO: Decide if [] is required after arrayop calls.
         // }
@@ -74,7 +75,7 @@ bool isArrayOpValid(Expression e)
 
 bool isNonAssignmentArrayOp(Expression e)
 {
-    if (e.op == TOK.slice)
+    if (e.op == EXP.slice)
         return isNonAssignmentArrayOp((cast(SliceExp)e).e1);
 
     Type tb = e.type.toBasetype();
@@ -166,11 +167,11 @@ Expression arrayOp(BinAssignExp e, Scope* sc)
     if (tn && (!tn.isMutable() || !tn.isAssignable()))
     {
         e.error("slice `%s` is not mutable", e.e1.toChars());
-        if (e.op == TOK.addAssign)
+        if (e.op == EXP.addAssign)
             checkPossibleAddCatError!(AddAssignExp, CatAssignExp)(e.isAddAssignExp);
         return ErrorExp.get();
     }
-    if (e.e1.op == TOK.arrayLiteral)
+    if (e.e1.op == EXP.arrayLiteral)
     {
         return e.e1.modifiableLvalue(sc, e.e1);
     }
@@ -228,7 +229,7 @@ private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions*
                 // RPN, prefix unary ops with u
                 OutBuffer buf;
                 buf.writestring("u");
-                buf.writestring(Token.toString(e.op));
+                buf.writestring(EXPtoString(e.op));
                 e.e1.accept(this);
                 tiargs.push(new StringExp(Loc.initial, buf.extractSlice()).expressionSemantic(sc));
             }
@@ -246,7 +247,7 @@ private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions*
                 // RPN
                 e.e1.accept(this);
                 e.e2.accept(this);
-                tiargs.push(new StringExp(Loc.initial, Token.toString(e.op)).expressionSemantic(sc));
+                tiargs.push(new StringExp(Loc.initial, EXPtoString(e.op)).expressionSemantic(sc));
             }
         }
     }
@@ -274,12 +275,12 @@ bool isArrayOpImplicitCast(TypeDArray tfrom, TypeDArray tto)
 /***********************************************
  * Test if expression is a unary array op.
  */
-bool isUnaArrayOp(TOK op)
+bool isUnaArrayOp(EXP op)
 {
     switch (op)
     {
-    case TOK.negate:
-    case TOK.tilde:
+    case EXP.negate:
+    case EXP.tilde:
         return true;
     default:
         break;
@@ -290,19 +291,19 @@ bool isUnaArrayOp(TOK op)
 /***********************************************
  * Test if expression is a binary array op.
  */
-bool isBinArrayOp(TOK op)
+bool isBinArrayOp(EXP op)
 {
     switch (op)
     {
-    case TOK.add:
-    case TOK.min:
-    case TOK.mul:
-    case TOK.div:
-    case TOK.mod:
-    case TOK.xor:
-    case TOK.and:
-    case TOK.or:
-    case TOK.pow:
+    case EXP.add:
+    case EXP.min:
+    case EXP.mul:
+    case EXP.div:
+    case EXP.mod:
+    case EXP.xor:
+    case EXP.and:
+    case EXP.or:
+    case EXP.pow:
         return true;
     default:
         break;
@@ -313,19 +314,19 @@ bool isBinArrayOp(TOK op)
 /***********************************************
  * Test if expression is a binary assignment array op.
  */
-bool isBinAssignArrayOp(TOK op)
+bool isBinAssignArrayOp(EXP op)
 {
     switch (op)
     {
-    case TOK.addAssign:
-    case TOK.minAssign:
-    case TOK.mulAssign:
-    case TOK.divAssign:
-    case TOK.modAssign:
-    case TOK.xorAssign:
-    case TOK.andAssign:
-    case TOK.orAssign:
-    case TOK.powAssign:
+    case EXP.addAssign:
+    case EXP.minAssign:
+    case EXP.mulAssign:
+    case EXP.divAssign:
+    case EXP.modAssign:
+    case EXP.xorAssign:
+    case EXP.andAssign:
+    case EXP.orAssign:
+    case EXP.powAssign:
         return true;
     default:
         break;
@@ -339,9 +340,9 @@ bool isBinAssignArrayOp(TOK op)
 bool isArrayOpOperand(Expression e)
 {
     //printf("Expression.isArrayOpOperand() %s\n", e.toChars());
-    if (e.op == TOK.slice)
+    if (e.op == EXP.slice)
         return true;
-    if (e.op == TOK.arrayLiteral)
+    if (e.op == EXP.arrayLiteral)
     {
         Type t = e.type.toBasetype();
         while (t.ty == Tarray || t.ty == Tsarray)
@@ -354,7 +355,7 @@ bool isArrayOpOperand(Expression e)
         return (isUnaArrayOp(e.op) ||
                 isBinArrayOp(e.op) ||
                 isBinAssignArrayOp(e.op) ||
-                e.op == TOK.assign);
+                e.op == EXP.assign);
     }
     return false;
 }
@@ -371,9 +372,9 @@ bool isArrayOpOperand(Expression e)
 ErrorExp arrayOpInvalidError(Expression e)
 {
     e.error("invalid array operation `%s` (possible missing [])", e.toChars());
-    if (e.op == TOK.add)
+    if (e.op == EXP.add)
         checkPossibleAddCatError!(AddExp, CatExp)(e.isAddExp());
-    else if (e.op == TOK.addAssign)
+    else if (e.op == EXP.addAssign)
         checkPossibleAddCatError!(AddAssignExp, CatAssignExp)(e.isAddAssignExp());
     return ErrorExp.get();
 }
diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d
index df88bb97bdf..f6387651e5f 100644
--- a/gcc/d/dmd/astenums.d
+++ b/gcc/d/dmd/astenums.d
@@ -62,7 +62,7 @@ enum STC : ulong  // transfer changes to declaration.h
     foreach_            = 0x4000,   /// variable for foreach loop
     variadic            = 0x8000,   /// the `variadic` parameter in: T foo(T a, U b, V variadic...)
 
-    ctorinit            = 0x1_0000,   /// can only be set inside constructor
+    //                  = 0x1_0000,
     templateparameter   = 0x2_0000,   /// template parameter
     ref_                = 0x4_0000,   /// `ref`
     scope_              = 0x8_0000,   /// `scope`
@@ -74,7 +74,7 @@ enum STC : ulong  // transfer changes to declaration.h
 
     returninferred      = 0x100_0000,   /// `return` has been inferred and should not be part of mangling, `return_` must also be set
     immutable_          = 0x200_0000,   /// `immutable`
-    init                = 0x400_0000,   /// has explicit initializer
+    //                  = 0x400_0000,
     manifest            = 0x800_0000,   /// manifest constant
 
     nodtor              = 0x1000_0000,   /// do not run destructor
diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d
index 0bf40ef9a72..e8704aabf73 100644
--- a/gcc/d/dmd/attrib.d
+++ b/gcc/d/dmd/attrib.d
@@ -929,12 +929,13 @@ extern (C++) final class PragmaDeclaration : AttribDeclaration
             (*args)[0] = e;
         }
 
-        if (e.isBool(true))
+        const opt = e.toBool();
+        if (opt.isEmpty())
+            return PINLINE.default_;
+        else if (opt.get())
             return PINLINE.always;
-        else if (e.isBool(false))
-            return PINLINE.never;
         else
-            return PINLINE.default_;
+            return PINLINE.never;
     }
 
     override const(char)* kind() const
@@ -1198,7 +1199,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration
 
         // expand static foreach
         import dmd.statementsem: makeTupleForeach;
-        Dsymbols* d = makeTupleForeach!(true,true)(_scope, sfe.aggrfe, decl, sfe.needExpansion).decl;
+        Dsymbols* d = makeTupleForeach(_scope, true, true, sfe.aggrfe, decl, sfe.needExpansion).decl;
         if (d) // process generated declarations
         {
             // Add members lazily.
diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d
index 0ecd6351f7d..5945644482a 100644
--- a/gcc/d/dmd/blockexit.d
+++ b/gcc/d/dmd/blockexit.d
@@ -94,15 +94,15 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             result = BE.fallthru;
             if (s.exp)
             {
-                if (s.exp.op == TOK.halt)
+                if (s.exp.op == EXP.halt)
                 {
                     result = BE.halt;
                     return;
                 }
-                if (s.exp.op == TOK.assert_)
+                if (s.exp.op == EXP.assert_)
                 {
                     AssertExp a = cast(AssertExp)s.exp;
-                    if (a.e1.isBool(false)) // if it's an assert(0)
+                    if (a.e1.toBool().hasValue(false)) // if it's an assert(0)
                     {
                         result = BE.halt;
                         return;
@@ -216,7 +216,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             {
                 if (canThrow(s.condition, func, mustNotThrow))
                     result |= BE.throw_;
-                if (!(result & BE.break_) && s.condition.isBool(true))
+                if (!(result & BE.break_) && s.condition.toBool().hasValue(true))
                     result &= ~BE.fallthru;
             }
             result &= ~(BE.break_ | BE.continue_);
@@ -235,9 +235,10 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             {
                 if (canThrow(s.condition, func, mustNotThrow))
                     result |= BE.throw_;
-                if (s.condition.isBool(true))
+                const opt = s.condition.toBool();
+                if (opt.hasValue(true))
                     result &= ~BE.fallthru;
-                else if (s.condition.isBool(false))
+                else if (opt.hasValue(false))
                     return;
             }
             else
@@ -274,11 +275,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
             result = BE.none;
             if (canThrow(s.condition, func, mustNotThrow))
                 result |= BE.throw_;
-            if (s.condition.isBool(true))
+
+            const opt = s.condition.toBool();
+            if (opt.hasValue(true))
             {
                 result |= blockExit(s.ifbody, func, mustNotThrow);
             }
-            else if (s.condition.isBool(false))
+            else if (opt.hasValue(false))
             {
                 result |= blockExit(s.elsebody, func, mustNotThrow);
             }
@@ -534,4 +537,3 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
     s.accept(be);
     return be.result;
 }
-
diff --git a/gcc/d/dmd/builtin.d b/gcc/d/dmd/builtin.d
index c4f84b12968..2f5b6c734a6 100644
--- a/gcc/d/dmd/builtin.d
+++ b/gcc/d/dmd/builtin.d
@@ -13,8 +13,6 @@
 
 module dmd.builtin;
 
-import core.stdc.math;
-import core.stdc.string;
 import dmd.arraytypes;
 import dmd.expression;
 import dmd.func;
diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
index ed05af6ac7c..b67a9d14dd4 100644
--- a/gcc/d/dmd/canthrow.d
+++ b/gcc/d/dmd/canthrow.d
@@ -150,7 +150,7 @@ extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow
         override void visit(AssignExp ae)
         {
             // blit-init cannot throw
-            if (ae.op == TOK.blit)
+            if (ae.op == EXP.blit)
                 return;
             /* Element-wise assignment could invoke postblits.
              */
diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d
index da66812b954..c536d25f0a3 100644
--- a/gcc/d/dmd/clone.d
+++ b/gcc/d/dmd/clone.d
@@ -578,7 +578,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
     fop.generated = true;
     Expression e1 = new IdentifierExp(loc, Id.p);
     Expression e2 = new IdentifierExp(loc, Id.q);
-    Expression e = new EqualExp(TOK.equal, loc, e1, e2);
+    Expression e = new EqualExp(EXP.equal, loc, e1, e2);
     fop.fbody = new ReturnStatement(loc, e);
     uint errors = global.startGagging(); // Do not report errors
     Scope* sc2 = sc.push();
@@ -642,13 +642,13 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
                 Dsymbol s = null;
                 switch (e.op)
                 {
-                case TOK.overloadSet:
+                case EXP.overloadSet:
                     s = (cast(OverExp)e).vars;
                     break;
-                case TOK.scope_:
+                case EXP.scope_:
                     s = (cast(ScopeExp)e).sds;
                     break;
-                case TOK.variable:
+                case EXP.variable:
                     s = (cast(VarExp)e).var;
                     break;
                 default:
diff --git a/gcc/d/dmd/common/outbuffer.h b/gcc/d/dmd/common/outbuffer.h
index a5e3f9c541d..ce23436d782 100644
--- a/gcc/d/dmd/common/outbuffer.h
+++ b/gcc/d/dmd/common/outbuffer.h
@@ -49,7 +49,7 @@ public:
     void reserve(d_size_t nbytes);
     void setsize(d_size_t size);
     void reset();
-    void write(const void *data, size_t nbytes);
+    void write(const void *data, d_size_t nbytes);
     void writestring(const char *string);
     void prependstring(const char *string);
     void writenl();                     // write newline
diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d
index 05bd4bd550d..abf28144c41 100644
--- a/gcc/d/dmd/cond.d
+++ b/gcc/d/dmd/cond.d
@@ -152,7 +152,7 @@ extern (C++) final class StaticForeach : RootObject
         sc = sc.endCTFE();
         el = el.optimize(WANTvalue);
         el = el.ctfeInterpret();
-        if (el.op == TOK.int64)
+        if (el.op == EXP.int64)
         {
             Expressions *es = void;
             if (auto ale = aggr.isArrayLiteralExp())
diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d
index 1dada60de3b..3ca23f24d7e 100644
--- a/gcc/d/dmd/constfold.d
+++ b/gcc/d/dmd/constfold.d
@@ -56,13 +56,13 @@ int isConst(Expression e)
     //printf("Expression::isConst(): %s\n", e.toChars());
     switch (e.op)
     {
-    case TOK.int64:
-    case TOK.float64:
-    case TOK.complex80:
+    case EXP.int64:
+    case EXP.float64:
+    case EXP.complex80:
         return 1;
-    case TOK.null_:
+    case EXP.null_:
         return 0;
-    case TOK.symbolOffset:
+    case EXP.symbolOffset:
         return 2;
     default:
         return 0;
@@ -71,13 +71,13 @@ int isConst(Expression e)
 }
 
 /**********************************
- * Initialize a TOK.cantExpression Expression.
+ * Initialize a EXP.cantExpression Expression.
  * Params:
  *      ue = where to write it
  */
 void cantExp(out UnionExp ue)
 {
-    emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+    emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
 }
 
 /* =============================== constFold() ============================== */
@@ -120,7 +120,7 @@ UnionExp Not(Type type, Expression e1)
 {
     UnionExp ue = void;
     Loc loc = e1.loc;
-    emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(false) ? 1 : 0, type);
+    emplaceExp!(IntegerExp)(&ue, loc, e1.toBool().hasValue(false) ? 1 : 0, type);
     return ue;
 }
 
@@ -128,7 +128,7 @@ private UnionExp Bool(Type type, Expression e1)
 {
     UnionExp ue = void;
     Loc loc = e1.loc;
-    emplaceExp!(IntegerExp)(&ue, loc, e1.isBool(true) ? 1 : 0, type);
+    emplaceExp!(IntegerExp)(&ue, loc, e1.toBool().hasValue(true) ? 1 : 0, type);
     return ue;
 }
 
@@ -222,13 +222,13 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2)
         }
         emplaceExp!(ComplexExp)(&ue, loc, v, type);
     }
-    else if (e1.op == TOK.symbolOffset)
+    else if (e1.op == EXP.symbolOffset)
     {
         SymOffExp soe = cast(SymOffExp)e1;
         emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e2.toInteger());
         ue.exp().type = type;
     }
-    else if (e2.op == TOK.symbolOffset)
+    else if (e2.op == EXP.symbolOffset)
     {
         SymOffExp soe = cast(SymOffExp)e2;
         emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset + e1.toInteger());
@@ -325,7 +325,7 @@ UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2)
         }
         emplaceExp!(ComplexExp)(&ue, loc, v, type);
     }
-    else if (e1.op == TOK.symbolOffset)
+    else if (e1.op == EXP.symbolOffset)
     {
         SymOffExp soe = cast(SymOffExp)e1;
         emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset - e2.toInteger());
@@ -722,26 +722,26 @@ UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2)
     return ue;
 }
 
-/* Also returns TOK.cantExpression if cannot be computed.
+/* Also returns EXP.cantExpression if cannot be computed.
  */
-UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
+UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2)
 {
     UnionExp ue = void;
     int cmp = 0;
     real_t r1 = CTFloat.zero;
     real_t r2 = CTFloat.zero;
     //printf("Equal(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
-    assert(op == TOK.equal || op == TOK.notEqual);
-    if (e1.op == TOK.null_)
+    assert(op == EXP.equal || op == EXP.notEqual);
+    if (e1.op == EXP.null_)
     {
-        if (e2.op == TOK.null_)
+        if (e2.op == EXP.null_)
             cmp = 1;
-        else if (e2.op == TOK.string_)
+        else if (e2.op == EXP.string_)
         {
             StringExp es2 = cast(StringExp)e2;
             cmp = (0 == es2.len);
         }
-        else if (e2.op == TOK.arrayLiteral)
+        else if (e2.op == EXP.arrayLiteral)
         {
             ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
             cmp = !es2.elements || (0 == es2.elements.dim);
@@ -752,14 +752,14 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
             return ue;
         }
     }
-    else if (e2.op == TOK.null_)
+    else if (e2.op == EXP.null_)
     {
-        if (e1.op == TOK.string_)
+        if (e1.op == EXP.string_)
         {
             StringExp es1 = cast(StringExp)e1;
             cmp = (0 == es1.len);
         }
-        else if (e1.op == TOK.arrayLiteral)
+        else if (e1.op == EXP.arrayLiteral)
         {
             ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
             cmp = !es1.elements || (0 == es1.elements.dim);
@@ -770,7 +770,7 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
             return ue;
         }
     }
-    else if (e1.op == TOK.string_ && e2.op == TOK.string_)
+    else if (e1.op == EXP.string_ && e2.op == EXP.string_)
     {
         StringExp es1 = cast(StringExp)e1;
         StringExp es2 = cast(StringExp)e2;
@@ -787,7 +787,7 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
         else
             cmp = 0;
     }
-    else if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral)
+    else if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral)
     {
         ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
         ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2;
@@ -803,7 +803,7 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
             {
                 auto ee1 = es1[i];
                 auto ee2 = es2[i];
-                ue = Equal(TOK.equal, loc, Type.tint32, ee1, ee2);
+                ue = Equal(EXP.equal, loc, Type.tint32, ee1, ee2);
                 if (CTFEExp.isCantExp(ue.exp()))
                     return ue;
                 cmp = cast(int)ue.exp().toInteger();
@@ -812,7 +812,7 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
             }
         }
     }
-    else if (e1.op == TOK.arrayLiteral && e2.op == TOK.string_)
+    else if (e1.op == EXP.arrayLiteral && e2.op == EXP.string_)
     {
         // Swap operands and use common code
         Expression etmp = e1;
@@ -820,7 +820,7 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
         e2 = etmp;
         goto Lsa;
     }
-    else if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral)
+    else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral)
     {
     Lsa:
         StringExp es1 = cast(StringExp)e1;
@@ -847,7 +847,7 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
             }
         }
     }
-    else if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral)
+    else if (e1.op == EXP.structLiteral && e2.op == EXP.structLiteral)
     {
         StructLiteralExp es1 = cast(StructLiteralExp)e1;
         StructLiteralExp es2 = cast(StructLiteralExp)e2;
@@ -873,8 +873,8 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
                     cmp = 0;
                     break;
                 }
-                ue = Equal(TOK.equal, loc, Type.tint32, ee1, ee2);
-                if (ue.exp().op == TOK.cantExpression)
+                ue = Equal(EXP.equal, loc, Type.tint32, ee1, ee2);
+                if (ue.exp().op == EXP.cantExpression)
                     return ue;
                 cmp = cast(int)ue.exp().toInteger();
                 if (cmp == 0)
@@ -920,25 +920,25 @@ UnionExp Equal(TOK op, const ref Loc loc, Type type, Expression e1, Expression e
         cantExp(ue);
         return ue;
     }
-    if (op == TOK.notEqual)
+    if (op == EXP.notEqual)
         cmp ^= 1;
     emplaceExp!(IntegerExp)(&ue, loc, cmp, type);
     return ue;
 }
 
-UnionExp Identity(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
+UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2)
 {
     UnionExp ue = void;
     int cmp;
-    if (e1.op == TOK.null_)
+    if (e1.op == EXP.null_)
     {
-        cmp = (e2.op == TOK.null_);
+        cmp = (e2.op == EXP.null_);
     }
-    else if (e2.op == TOK.null_)
+    else if (e2.op == EXP.null_)
     {
         cmp = 0;
     }
-    else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset)
+    else if (e1.op == EXP.symbolOffset && e2.op == EXP.symbolOffset)
     {
         SymOffExp es1 = cast(SymOffExp)e1;
         SymOffExp es2 = cast(SymOffExp)e2;
@@ -962,24 +962,24 @@ UnionExp Identity(TOK op, const ref Loc loc, Type type, Expression e1, Expressio
         }
         else
         {
-            ue = Equal((op == TOK.identity) ? TOK.equal : TOK.notEqual, loc, type, e1, e2);
+            ue = Equal((op == EXP.identity) ? EXP.equal : EXP.notEqual, loc, type, e1, e2);
             return ue;
         }
     }
-    if (op == TOK.notIdentity)
+    if (op == EXP.notIdentity)
         cmp ^= 1;
     emplaceExp!(IntegerExp)(&ue, loc, cmp, type);
     return ue;
 }
 
-UnionExp Cmp(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
+UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2)
 {
     UnionExp ue = void;
     dinteger_t n;
     real_t r1 = CTFloat.zero;
     real_t r2 = CTFloat.zero;
     //printf("Cmp(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
-    if (e1.op == TOK.string_ && e2.op == TOK.string_)
+    if (e1.op == EXP.string_ && e2.op == EXP.string_)
     {
         StringExp es1 = cast(StringExp)e1;
         StringExp es2 = cast(StringExp)e2;
@@ -1032,7 +1032,7 @@ UnionExp Cmp(TOK op, const ref Loc loc, Type type, Expression e1, Expression e2)
     return ue;
 }
 
-/* Also returns TOK.cantExpression if cannot be computed.
+/* Also returns EXP.cantExpression if cannot be computed.
  *  to: type to cast to
  *  type: type to paint the result
  */
@@ -1048,7 +1048,7 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1)
         emplaceExp!(UnionExp)(&ue, e1);
         return ue;
     }
-    if (e1.op == TOK.vector && (cast(TypeVector)e1.type).basetype.equals(type) && type.equals(to))
+    if (e1.op == EXP.vector && (cast(TypeVector)e1.type).basetype.equals(type) && type.equals(to))
     {
         Expression ex = (cast(VectorExp)e1).e1;
         emplaceExp!(UnionExp)(&ue, ex);
@@ -1067,14 +1067,14 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1)
     }
     /* Allow casting from one string type to another
      */
-    if (e1.op == TOK.string_)
+    if (e1.op == EXP.string_)
     {
         if (tb.ty == Tarray && typeb.ty == Tarray && tb.nextOf().size() == typeb.nextOf().size())
         {
             goto L1;
         }
     }
-    if (e1.op == TOK.arrayLiteral && typeb == tb)
+    if (e1.op == EXP.arrayLiteral && typeb == tb)
     {
     L1:
         Expression ex = expType(to, e1);
@@ -1157,7 +1157,7 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1)
     {
         cantExp(ue);
     }
-    else if (tb.ty == Tstruct && e1.op == TOK.int64)
+    else if (tb.ty == Tstruct && e1.op == EXP.int64)
     {
         // Struct = 0;
         StructDeclaration sd = tb.toDsymbol(null).isStructDeclaration();
@@ -1169,7 +1169,7 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1)
             UnionExp zero;
             emplaceExp!(IntegerExp)(&zero, 0);
             ue = Cast(loc, v.type, v.type, zero.exp());
-            if (ue.exp().op == TOK.cantExpression)
+            if (ue.exp().op == EXP.cantExpression)
                 return ue;
             elements.push(ue.exp().copy());
         }
@@ -1193,18 +1193,18 @@ UnionExp ArrayLength(Type type, Expression e1)
 {
     UnionExp ue = void;
     Loc loc = e1.loc;
-    if (e1.op == TOK.string_)
+    if (e1.op == EXP.string_)
     {
         StringExp es1 = cast(StringExp)e1;
         emplaceExp!(IntegerExp)(&ue, loc, es1.len, type);
     }
-    else if (e1.op == TOK.arrayLiteral)
+    else if (e1.op == EXP.arrayLiteral)
     {
         ArrayLiteralExp ale = cast(ArrayLiteralExp)e1;
         size_t dim = ale.elements ? ale.elements.dim : 0;
         emplaceExp!(IntegerExp)(&ue, loc, dim, type);
     }
-    else if (e1.op == TOK.assocArrayLiteral)
+    else if (e1.op == EXP.assocArrayLiteral)
     {
         AssocArrayLiteralExp ale = cast(AssocArrayLiteralExp)e1;
         size_t dim = ale.keys.dim;
@@ -1220,7 +1220,7 @@ UnionExp ArrayLength(Type type, Expression e1)
     return ue;
 }
 
-/* Also return TOK.cantExpression if this fails
+/* Also return EXP.cantExpression if this fails
  */
 UnionExp Index(Type type, Expression e1, Expression e2)
 {
@@ -1228,7 +1228,7 @@ UnionExp Index(Type type, Expression e1, Expression e2)
     Loc loc = e1.loc;
     //printf("Index(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
     assert(e1.type);
-    if (e1.op == TOK.string_ && e2.op == TOK.int64)
+    if (e1.op == EXP.string_ && e2.op == EXP.int64)
     {
         StringExp es1 = cast(StringExp)e1;
         uinteger_t i = e2.toInteger();
@@ -1242,7 +1242,7 @@ UnionExp Index(Type type, Expression e1, Expression e2)
             emplaceExp!(IntegerExp)(&ue, loc, es1.charAt(i), type);
         }
     }
-    else if (e1.type.toBasetype().ty == Tsarray && e2.op == TOK.int64)
+    else if (e1.type.toBasetype().ty == Tsarray && e2.op == EXP.int64)
     {
         TypeSArray tsa = cast(TypeSArray)e1.type.toBasetype();
         uinteger_t length = tsa.dim.toInteger();
@@ -1252,7 +1252,7 @@ UnionExp Index(Type type, Expression e1, Expression e2)
             e1.error("array index %llu is out of bounds `%s[0 .. %llu]`", i, e1.toChars(), length);
             emplaceExp!(ErrorExp)(&ue);
         }
-        else if (e1.op == TOK.arrayLiteral)
+        else if (e1.op == EXP.arrayLiteral)
         {
             ArrayLiteralExp ale = cast(ArrayLiteralExp)e1;
             auto e = ale[cast(size_t)i];
@@ -1266,10 +1266,10 @@ UnionExp Index(Type type, Expression e1, Expression e2)
         else
             cantExp(ue);
     }
-    else if (e1.type.toBasetype().ty == Tarray && e2.op == TOK.int64)
+    else if (e1.type.toBasetype().ty == Tarray && e2.op == EXP.int64)
     {
         uinteger_t i = e2.toInteger();
-        if (e1.op == TOK.arrayLiteral)
+        if (e1.op == EXP.arrayLiteral)
         {
             ArrayLiteralExp ale = cast(ArrayLiteralExp)e1;
             if (i >= ale.elements.dim)
@@ -1291,7 +1291,7 @@ UnionExp Index(Type type, Expression e1, Expression e2)
         else
             cantExp(ue);
     }
-    else if (e1.op == TOK.assocArrayLiteral)
+    else if (e1.op == EXP.assocArrayLiteral)
     {
         AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e1;
         /* Search the keys backwards, in case there are duplicate keys
@@ -1300,10 +1300,10 @@ UnionExp Index(Type type, Expression e1, Expression e2)
         {
             i--;
             Expression ekey = (*ae.keys)[i];
-            ue = Equal(TOK.equal, loc, Type.tbool, ekey, e2);
+            ue = Equal(EXP.equal, loc, Type.tbool, ekey, e2);
             if (CTFEExp.isCantExp(ue.exp()))
                 return ue;
-            if (ue.exp().isBool(true))
+            if (ue.exp().toBool().hasValue(true))
             {
                 Expression e = (*ae.values)[i];
                 e.type = type;
@@ -1322,7 +1322,7 @@ UnionExp Index(Type type, Expression e1, Expression e2)
     return ue;
 }
 
-/* Also return TOK.cantExpression if this fails
+/* Also return EXP.cantExpression if this fails
  */
 UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr)
 {
@@ -1347,7 +1347,7 @@ UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr)
                  newupr <= upr);
     }
 
-    if (e1.op == TOK.string_ && lwr.op == TOK.int64 && upr.op == TOK.int64)
+    if (e1.op == EXP.string_ && lwr.op == EXP.int64 && upr.op == EXP.int64)
     {
         StringExp es1 = cast(StringExp)e1;
         const uinteger_t ilwr = lwr.toInteger();
@@ -1367,7 +1367,7 @@ UnionExp Slice(Type type, Expression e1, Expression lwr, Expression upr)
             es.type = type;
         }
     }
-    else if (e1.op == TOK.arrayLiteral && lwr.op == TOK.int64 && upr.op == TOK.int64 && !hasSideEffect(e1))
+    else if (e1.op == EXP.arrayLiteral && lwr.op == EXP.int64 && upr.op == EXP.int64 && !hasSideEffect(e1))
     {
         ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
         const uinteger_t ilwr = lwr.toInteger();
@@ -1480,14 +1480,14 @@ private Expressions* copyElements(Expression e1, Expression e2 = null)
         }
     }
 
-    if (e1.op == TOK.arrayLiteral)
+    if (e1.op == EXP.arrayLiteral)
         append(cast(ArrayLiteralExp)e1);
     else
         elems.push(e1);
 
     if (e2)
     {
-        if (e2.op == TOK.arrayLiteral)
+        if (e2.op == EXP.arrayLiteral)
             append(cast(ArrayLiteralExp)e2);
         else
             elems.push(e2);
@@ -1496,7 +1496,7 @@ private Expressions* copyElements(Expression e1, Expression e2 = null)
     return elems;
 }
 
-/* Also return TOK.cantExpression if this fails
+/* Also return EXP.cantExpression if this fails
  */
 UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
 {
@@ -1507,13 +1507,13 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
     Type t2 = e2.type.toBasetype();
     //printf("Cat(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars());
     //printf("\tt1 = %s, t2 = %s, type = %s\n", t1.toChars(), t2.toChars(), type.toChars());
-    if (e1.op == TOK.null_ && (e2.op == TOK.int64 || e2.op == TOK.structLiteral))
+    if (e1.op == EXP.null_ && (e2.op == EXP.int64 || e2.op == EXP.structLiteral))
     {
         e = e2;
         t = t1;
         goto L2;
     }
-    else if ((e1.op == TOK.int64 || e1.op == TOK.structLiteral) && e2.op == TOK.null_)
+    else if ((e1.op == EXP.int64 || e1.op == EXP.structLiteral) && e2.op == EXP.null_)
     {
         e = e1;
         t = t2;
@@ -1547,7 +1547,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.null_ && e2.op == TOK.null_)
+    else if (e1.op == EXP.null_ && e2.op == EXP.null_)
     {
         if (type == e1.type)
         {
@@ -1575,7 +1575,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.string_ && e2.op == TOK.string_)
+    else if (e1.op == EXP.string_ && e2.op == EXP.string_)
     {
         // Concatenate the strings
         StringExp es1 = cast(StringExp)e1;
@@ -1604,7 +1604,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral())
+    else if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral())
     {
         // [chars] ~ string --> [chars]
         StringExp es = cast(StringExp)e2;
@@ -1621,7 +1621,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral())
+    else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral())
     {
         // string ~ [chars] --> [chars]
         StringExp es = cast(StringExp)e1;
@@ -1638,7 +1638,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.string_ && e2.op == TOK.int64)
+    else if (e1.op == EXP.string_ && e2.op == EXP.int64)
     {
         // string ~ char --> string
         StringExp es1 = cast(StringExp)e1;
@@ -1663,7 +1663,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.int64 && e2.op == TOK.string_)
+    else if (e1.op == EXP.int64 && e2.op == EXP.string_)
     {
         // [w|d]?char ~ string --> string
         // We assume that we only ever prepend one char of the same type
@@ -1684,7 +1684,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+    else if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
     {
         // Concatenate the arrays
         auto elems = copyElements(e1, e2);
@@ -1701,12 +1701,12 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf()))
+    else if (e1.op == EXP.arrayLiteral && e2.op == EXP.null_ && t1.nextOf().equals(t2.nextOf()))
     {
         e = e1;
         goto L3;
     }
-    else if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+    else if (e1.op == EXP.null_ && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
     {
         e = e2;
     L3:
@@ -1725,9 +1725,9 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if ((e1.op == TOK.arrayLiteral || e1.op == TOK.null_) && e1.type.toBasetype().nextOf() && e1.type.toBasetype().nextOf().equals(e2.type))
+    else if ((e1.op == EXP.arrayLiteral || e1.op == EXP.null_) && e1.type.toBasetype().nextOf() && e1.type.toBasetype().nextOf().equals(e2.type))
     {
-        auto elems = (e1.op == TOK.arrayLiteral)
+        auto elems = (e1.op == EXP.arrayLiteral)
                 ? copyElements(e1) : new Expressions();
         elems.push(e2);
 
@@ -1743,7 +1743,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e2.op == TOK.arrayLiteral && e2.type.toBasetype().nextOf().equals(e1.type))
+    else if (e2.op == EXP.arrayLiteral && e2.type.toBasetype().nextOf().equals(e1.type))
     {
         auto elems = copyElements(e1, e2);
 
@@ -1759,13 +1759,13 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2)
         assert(ue.exp().type);
         return ue;
     }
-    else if (e1.op == TOK.null_ && e2.op == TOK.string_)
+    else if (e1.op == EXP.null_ && e2.op == EXP.string_)
     {
         t = e1.type;
         e = e2;
         goto L1;
     }
-    else if (e1.op == TOK.string_ && e2.op == TOK.null_)
+    else if (e1.op == EXP.string_ && e2.op == EXP.null_)
     {
         e = e1;
         t = e2.type;
@@ -1801,13 +1801,13 @@ UnionExp Ptr(Type type, Expression e1)
 {
     //printf("Ptr(e1 = %s)\n", e1.toChars());
     UnionExp ue = void;
-    if (e1.op == TOK.add)
+    if (e1.op == EXP.add)
     {
         AddExp ae = cast(AddExp)e1;
-        if (ae.e1.op == TOK.address && ae.e2.op == TOK.int64)
+        if (ae.e1.op == EXP.address && ae.e2.op == EXP.int64)
         {
             AddrExp ade = cast(AddrExp)ae.e1;
-            if (ade.e1.op == TOK.structLiteral)
+            if (ade.e1.op == EXP.structLiteral)
             {
                 StructLiteralExp se = cast(StructLiteralExp)ade.e1;
                 uint offset = cast(uint)ae.e2.toInteger();
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index 7d8ab67ee6a..dfc45e0bc31 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -212,7 +212,7 @@ final class CParser(AST) : Parser!AST
         case TOK.sizeof_:
         Lexp:
             auto exp = cparseExpression();
-            if (token.value == TOK.identifier && exp.op == TOK.identifier)
+            if (token.value == TOK.identifier && exp.op == EXP.identifier)
             {
                 error("found `%s` when expecting `;` or `=`, did you mean `%s %s = %s`?", peek(&token).toChars(), exp.toChars(), token.toChars(), peek(peek(&token)).toChars());
                 nextToken();
@@ -781,11 +781,11 @@ final class CParser(AST) : Parser!AST
                 break;
 
             case TOK.plusPlus:
-                e = new AST.PostExp(TOK.plusPlus, loc, e);
+                e = new AST.PostExp(EXP.plusPlus, loc, e);
                 break;
 
             case TOK.minusMinus:
-                e = new AST.PostExp(TOK.minusMinus, loc, e);
+                e = new AST.PostExp(EXP.minusMinus, loc, e);
                 break;
 
             case TOK.leftParenthesis:
@@ -841,14 +841,14 @@ final class CParser(AST) : Parser!AST
             // Parse `++` as an unary operator so that cast expressions only give
             // an error for being non-lvalues.
             e = cparseCastExp();
-            e = new AST.PreExp(TOK.prePlusPlus, loc, e);
+            e = new AST.PreExp(EXP.prePlusPlus, loc, e);
             break;
 
         case TOK.minusMinus:
             nextToken();
             // Parse `--` as an unary operator, same as prefix increment.
             e = cparseCastExp();
-            e = new AST.PreExp(TOK.preMinusMinus, loc, e);
+            e = new AST.PreExp(EXP.preMinusMinus, loc, e);
             break;
 
         case TOK.and:
@@ -1122,14 +1122,15 @@ final class CParser(AST) : Parser!AST
         const loc = token.loc;
 
         auto e = cparseShiftExp();
-        TOK op = token.value;
 
-        switch (op)
+        EXP op = EXP.reserved;
+        switch (token.value)
         {
-        case TOK.lessThan:
-        case TOK.lessOrEqual:
-        case TOK.greaterThan:
-        case TOK.greaterOrEqual:
+        case TOK.lessThan:       op = EXP.lessThan; goto Lcmp;
+        case TOK.lessOrEqual:    op = EXP.lessOrEqual; goto Lcmp;
+        case TOK.greaterThan:    op = EXP.greaterThan; goto Lcmp;
+        case TOK.greaterOrEqual: op = EXP.greaterOrEqual; goto Lcmp;
+        Lcmp:
             nextToken();
             auto e2 = cparseShiftExp();
             e = new AST.CmpExp(op, loc, e, e2);
@@ -1153,12 +1154,13 @@ final class CParser(AST) : Parser!AST
         const loc = token.loc;
 
         auto e = cparseRelationalExp();
-        const TOK op = token.value;
 
-        switch (op)
+        EXP op = EXP.reserved;
+        switch (token.value)
         {
-        case TOK.equal:
-        case TOK.notEqual:
+        case TOK.equal:         op = EXP.equal;    goto Lequal;
+        case TOK.notEqual:      op = EXP.notEqual; goto Lequal;
+        Lequal:
             nextToken();
             auto e2 = cparseRelationalExp();
             e = new AST.EqualExp(op, loc, e, e2);
@@ -1245,7 +1247,7 @@ final class CParser(AST) : Parser!AST
         {
             nextToken();
             auto e2 = cparseOrExp();
-            e = new AST.LogicalExp(loc, TOK.andAnd, e, e2);
+            e = new AST.LogicalExp(loc, EXP.andAnd, e, e2);
         }
         return e;
     }
@@ -1265,7 +1267,7 @@ final class CParser(AST) : Parser!AST
         {
             nextToken();
             auto e2 = cparseAndAndExp();
-            e = new AST.LogicalExp(loc, TOK.orOr, e, e2);
+            e = new AST.LogicalExp(loc, EXP.orOr, e, e2);
         }
         return e;
     }
@@ -1693,7 +1695,11 @@ final class CParser(AST) : Parser!AST
             switch (token.value)
             {
                 case TOK.identifier:
-                    error("missing comma");
+                    if (s)
+                    {
+                        error("missing comma or semicolon after declaration of `%s`, found `%s` instead", s.toChars(), token.toChars());
+                        goto Lend;
+                    }
                     goto default;
 
                 case TOK.semicolon:
@@ -1707,7 +1713,8 @@ final class CParser(AST) : Parser!AST
                     break;
 
                 default:
-                    error("`=`, `;` or `,` expected");
+                    error("`=`, `;` or `,` expected to end declaration instead of `%s`", token.toChars());
+                Lend:
                     while (token.value != TOK.semicolon && token.value != TOK.endOfFile)
                         nextToken();
                     nextToken();
diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d
index df742c0bd8f..4ad79da02f2 100644
--- a/gcc/d/dmd/cppmangle.d
+++ b/gcc/d/dmd/cppmangle.d
@@ -490,7 +490,7 @@ private final class CppMangleVisitor : Visitor
                 mangle_function(d.isFuncDeclaration());
                 buf.writestring("EE");
             }
-            else if (e && e.op == TOK.variable && (cast(VarExp)e).var.isVarDeclaration())
+            else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration())
             {
                 VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration();
                 buf.writeByte('L');
diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d
index 7f76d7565e0..9cd09bab643 100644
--- a/gcc/d/dmd/ctfeexpr.d
+++ b/gcc/d/dmd/ctfeexpr.d
@@ -46,7 +46,7 @@ extern (C++) final class ClassReferenceExp : Expression
 
     extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type)
     {
-        super(loc, TOK.classReference, __traits(classInstanceSize, ClassReferenceExp));
+        super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp));
         assert(lit && lit.sd && lit.sd.isClassDeclaration());
         this.value = lit;
         this.type = type;
@@ -131,7 +131,7 @@ extern (C++) final class ThrownExceptionExp : Expression
 
     extern (D) this(const ref Loc loc, ClassReferenceExp victim)
     {
-        super(loc, TOK.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
+        super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp));
         this.thrown = victim;
         this.type = victim.type;
     }
@@ -167,7 +167,7 @@ extern (C++) final class ThrownExceptionExp : Expression
  */
 extern (C++) final class CTFEExp : Expression
 {
-    extern (D) this(TOK tok)
+    extern (D) this(EXP tok)
     {
         super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp));
         type = Type.tvoid;
@@ -177,17 +177,17 @@ extern (C++) final class CTFEExp : Expression
     {
         switch (op)
         {
-        case TOK.cantExpression:
+        case EXP.cantExpression:
             return "<cant>";
-        case TOK.voidExpression:
+        case EXP.voidExpression:
             return "cast(void)0";
-        case TOK.showCtfeContext:
+        case EXP.showCtfeContext:
             return "<error>";
-        case TOK.break_:
+        case EXP.break_:
             return "<break>";
-        case TOK.continue_:
+        case EXP.continue_:
             return "<continue>";
-        case TOK.goto_:
+        case EXP.goto_:
             return "<goto>";
         default:
             assert(0);
@@ -206,19 +206,19 @@ extern (C++) final class CTFEExp : Expression
 
     extern (D) static bool isCantExp(const Expression e)
     {
-        return e && e.op == TOK.cantExpression;
+        return e && e.op == EXP.cantExpression;
     }
 
     extern (D) static bool isGotoExp(const Expression e)
     {
-        return e && e.op == TOK.goto_;
+        return e && e.op == EXP.goto_;
     }
 }
 
 // True if 'e' is CTFEExp::cantexp, or an exception
 bool exceptionOrCantInterpret(const Expression e)
 {
-    return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException || e.op == TOK.showCtfeContext);
+    return e && (e.op == EXP.cantExpression || e.op == EXP.thrownException || e.op == EXP.showCtfeContext);
 }
 
 /************** Aggregate literals (AA/string/array/struct) ******************/
@@ -231,29 +231,29 @@ bool needToCopyLiteral(const Expression expr)
     {
         switch (e.op)
         {
-        case TOK.arrayLiteral:
+        case EXP.arrayLiteral:
             return (cast(ArrayLiteralExp)e).ownedByCtfe == OwnedBy.code;
-        case TOK.assocArrayLiteral:
+        case EXP.assocArrayLiteral:
             return (cast(AssocArrayLiteralExp)e).ownedByCtfe == OwnedBy.code;
-        case TOK.structLiteral:
+        case EXP.structLiteral:
             return (cast(StructLiteralExp)e).ownedByCtfe == OwnedBy.code;
-        case TOK.string_:
-        case TOK.this_:
-        case TOK.variable:
+        case EXP.string_:
+        case EXP.this_:
+        case EXP.variable:
             return false;
-        case TOK.assign:
+        case EXP.assign:
             return false;
-        case TOK.index:
-        case TOK.dotVariable:
-        case TOK.slice:
-        case TOK.cast_:
+        case EXP.index:
+        case EXP.dotVariable:
+        case EXP.slice:
+        case EXP.cast_:
             e = (cast(UnaExp)e).e1;
             continue;
-        case TOK.concatenate:
+        case EXP.concatenate:
             return needToCopyLiteral((cast(BinExp)e).e1) || needToCopyLiteral((cast(BinExp)e).e2);
-        case TOK.concatenateAssign:
-        case TOK.concatenateElemAssign:
-        case TOK.concatenateDcharAssign:
+        case EXP.concatenateAssign:
+        case EXP.concatenateElemAssign:
+        case EXP.concatenateDcharAssign:
             e = (cast(BinExp)e).e2;
             continue;
         default:
@@ -358,7 +358,7 @@ UnionExp copyLiteral(Expression e)
         r.origin = sle.origin;
         return ue;
     }
-    if (e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.symbolOffset || e.op == TOK.null_ || e.op == TOK.variable || e.op == TOK.dotVariable || e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.char_ || e.op == TOK.complex80 || e.op == TOK.void_ || e.op == TOK.vector || e.op == TOK.typeid_)
+    if (e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.symbolOffset || e.op == EXP.null_ || e.op == EXP.variable || e.op == EXP.dotVariable || e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.char_ || e.op == EXP.complex80 || e.op == EXP.void_ || e.op == EXP.vector || e.op == EXP.typeid_)
     {
         // Simple value types
         // Keep e1 for DelegateExp and DotVarExp
@@ -372,7 +372,7 @@ UnionExp copyLiteral(Expression e)
         if (se.type.toBasetype().ty == Tsarray)
         {
             // same with resolveSlice()
-            if (se.e1.op == TOK.null_)
+            if (se.e1.op == EXP.null_)
             {
                 emplaceExp!(NullExp)(&ue, se.loc, se.type);
                 return ue;
@@ -415,7 +415,7 @@ UnionExp copyLiteral(Expression e)
         emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type);
         return ue;
     }
-    if (e.op == TOK.error)
+    if (e.op == EXP.error)
     {
         emplaceExp!(UnionExp)(&ue, e);
         return ue;
@@ -468,11 +468,11 @@ private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit)
     {
         emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2);
     }
-    else if (lit.op == TOK.arrayLiteral)
+    else if (lit.op == EXP.arrayLiteral)
     {
         emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
     }
-    else if (lit.op == TOK.string_)
+    else if (lit.op == EXP.string_)
     {
         // For strings, we need to introduce another level of indirection
         emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy());
@@ -490,7 +490,7 @@ private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit)
     {
         // Can't type paint from struct to struct*; this needs another
         // level of indirection
-        if (lit.op == TOK.structLiteral && isPointer(type))
+        if (lit.op == EXP.structLiteral && isPointer(type))
             lit.error("CTFE internal error: painting `%s`", type.toChars());
         ue = copyLiteral(lit);
     }
@@ -511,7 +511,7 @@ Expression resolveSlice(Expression e, UnionExp* pue = null)
     SliceExp se = e.isSliceExp();
     if (!se)
         return e;
-    if (se.e1.op == TOK.null_)
+    if (se.e1.op == EXP.null_)
         return se.e1;
     if (pue)
     {
@@ -531,13 +531,13 @@ uinteger_t resolveArrayLength(const Expression e)
 {
     switch (e.op)
     {
-        case TOK.vector:
+        case EXP.vector:
             return e.isVectorExp().dim;
 
-        case TOK.null_:
+        case EXP.null_:
             return 0;
 
-        case TOK.slice:
+        case EXP.slice:
         {
             auto se = cast(SliceExp)e;
             const ilo = se.lwr.toInteger();
@@ -545,16 +545,16 @@ uinteger_t resolveArrayLength(const Expression e)
             return iup - ilo;
         }
 
-        case TOK.string_:
+        case EXP.string_:
             return e.isStringExp().len;
 
-        case TOK.arrayLiteral:
+        case EXP.arrayLiteral:
         {
             const ale = e.isArrayLiteralExp();
             return ale.elements ? ale.elements.dim : 0;
         }
 
-        case TOK.assocArrayLiteral:
+        case EXP.assocArrayLiteral:
         {
             return e.isAssocArrayLiteralExp().keys.dim;
         }
@@ -667,7 +667,7 @@ bool isPointer(Type t)
 // For CTFE only. Returns true if 'e' is true or a non-null pointer.
 bool isTrueBool(Expression e)
 {
-    return e.isBool(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != TOK.null_);
+    return e.toBool().hasValue(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != EXP.null_);
 }
 
 /* Is it safe to convert from srcPointee* to destPointee* ?
@@ -725,12 +725,12 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
         const ex = dve.e1;
         const v = dve.var.isVarDeclaration();
         assert(v);
-        StructLiteralExp se = (ex.op == TOK.classReference)
+        StructLiteralExp se = (ex.op == EXP.classReference)
             ? (cast(ClassReferenceExp)ex).value
             : cast(StructLiteralExp)ex;
 
         // We can't use getField, because it makes a copy
-        const i = (ex.op == TOK.classReference)
+        const i = (ex.op == EXP.classReference)
             ? (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset)
             : se.getFieldIndex(e.type, v.offset);
         e = (*se.elements)[i];
@@ -738,7 +738,7 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
     if (auto ie = e.isIndexExp())
     {
         // Note that each AA element is part of its own memory block
-        if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == TOK.string_ || ie.e1.op == TOK.arrayLiteral) && ie.e2.op == TOK.int64)
+        if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64)
         {
             *ofs = ie.e2.toInteger();
             return ie.e1;
@@ -747,7 +747,7 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs)
     if (auto se = e.isSliceExp())
     {
         if (se && e.type.toBasetype().ty == Tsarray &&
-           (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral) && se.lwr.op == TOK.int64)
+           (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64)
         {
             *ofs = se.lwr.toInteger();
             return se.e1;
@@ -773,17 +773,17 @@ bool pointToSameMemoryBlock(Expression agg1, Expression agg2)
         return true;
     // For integers cast to pointers, we regard them as non-comparable
     // unless they are identical. (This may be overly strict).
-    if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger())
+    if (agg1.op == EXP.int64 && agg2.op == EXP.int64 && agg1.toInteger() == agg2.toInteger())
     {
         return true;
     }
     // Note that type painting can occur with VarExp, so we
     // must compare the variables being pointed to.
-    if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)
+    if (agg1.op == EXP.variable && agg2.op == EXP.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)
     {
         return true;
     }
-    if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
+    if (agg1.op == EXP.symbolOffset && agg2.op == EXP.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
     {
         return true;
     }
@@ -803,14 +803,14 @@ UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expressi
         const sz = pointee.size();
         emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
     }
-    else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ &&
+    else if (agg1.op == EXP.string_ && agg2.op == EXP.string_ &&
              (cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr)
     {
         Type pointee = (cast(TypePointer)agg1.type).next;
         const sz = pointee.size();
         emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type);
     }
-    else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset &&
+    else if (agg1.op == EXP.symbolOffset && agg2.op == EXP.symbolOffset &&
              (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var)
     {
         emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type);
@@ -818,28 +818,28 @@ UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expressi
     else
     {
         error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars());
-        emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+        emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
     }
     return ue;
 }
 
 // Return eptr op e2, where eptr is a pointer, e2 is an integer,
-// and op is TOK.add or TOK.min
-UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2)
+// and op is EXP.add or EXP.min
+UnionExp pointerArithmetic(const ref Loc loc, EXP op, Type type, Expression eptr, Expression e2)
 {
     UnionExp ue;
     if (eptr.type.nextOf().ty == Tvoid)
     {
         error(loc, "cannot perform arithmetic on `void*` pointers at compile time");
     Lcant:
-        emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+        emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
         return ue;
     }
-    if (eptr.op == TOK.address)
+    if (eptr.op == EXP.address)
         eptr = (cast(AddrExp)eptr).e1;
     dinteger_t ofs1;
     Expression agg1 = getAggregateFromPointer(eptr, &ofs1);
-    if (agg1.op == TOK.symbolOffset)
+    if (agg1.op == EXP.symbolOffset)
     {
         if ((cast(SymOffExp)agg1).var.type.ty != Tsarray)
         {
@@ -847,7 +847,7 @@ UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr
             goto Lcant;
         }
     }
-    else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral)
+    else if (agg1.op != EXP.string_ && agg1.op != EXP.arrayLiteral)
     {
         error(loc, "cannot perform pointer arithmetic on non-arrays at compile time");
         goto Lcant;
@@ -857,7 +857,7 @@ UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr
     dinteger_t sz = pointee.size();
     sinteger_t indx;
     dinteger_t len;
-    if (agg1.op == TOK.symbolOffset)
+    if (agg1.op == EXP.symbolOffset)
     {
         indx = ofs1 / sz;
         len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger();
@@ -869,9 +869,9 @@ UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr
         indx = ofs1;
         len = dollar.toInteger();
     }
-    if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus)
+    if (op == EXP.add || op == EXP.addAssign || op == EXP.plusPlus)
         indx += ofs2 / sz;
-    else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus)
+    else if (op == EXP.min || op == EXP.minAssign || op == EXP.minusMinus)
         indx -= ofs2 / sz;
     else
     {
@@ -883,14 +883,14 @@ UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr
         error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len);
         goto Lcant;
     }
-    if (agg1.op == TOK.symbolOffset)
+    if (agg1.op == EXP.symbolOffset)
     {
         emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz);
         SymOffExp se = cast(SymOffExp)ue.exp();
         se.type = type;
         return ue;
     }
-    if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_)
+    if (agg1.op != EXP.arrayLiteral && agg1.op != EXP.string_)
     {
         error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars());
         goto Lcant;
@@ -918,31 +918,31 @@ UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr
 
 // Return 1 if true, 0 if false
 // -1 if comparison is illegal because they point to non-comparable memory blocks
-int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
+int comparePointers(EXP op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2)
 {
     if (pointToSameMemoryBlock(agg1, agg2))
     {
         int n;
         switch (op)
         {
-        case TOK.lessThan:
+        case EXP.lessThan:
             n = (ofs1 < ofs2);
             break;
-        case TOK.lessOrEqual:
+        case EXP.lessOrEqual:
             n = (ofs1 <= ofs2);
             break;
-        case TOK.greaterThan:
+        case EXP.greaterThan:
             n = (ofs1 > ofs2);
             break;
-        case TOK.greaterOrEqual:
+        case EXP.greaterOrEqual:
             n = (ofs1 >= ofs2);
             break;
-        case TOK.identity:
-        case TOK.equal:
+        case EXP.identity:
+        case EXP.equal:
             n = (ofs1 == ofs2);
             break;
-        case TOK.notIdentity:
-        case TOK.notEqual:
+        case EXP.notIdentity:
+        case EXP.notEqual:
             n = (ofs1 != ofs2);
             break;
         default:
@@ -950,29 +950,29 @@ int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, d
         }
         return n;
     }
-    const null1 = (agg1.op == TOK.null_);
-    const null2 = (agg2.op == TOK.null_);
+    const null1 = (agg1.op == EXP.null_);
+    const null2 = (agg2.op == EXP.null_);
     int cmp;
     if (null1 || null2)
     {
         switch (op)
         {
-        case TOK.lessThan:
+        case EXP.lessThan:
             cmp = null1 && !null2;
             break;
-        case TOK.greaterThan:
+        case EXP.greaterThan:
             cmp = !null1 && null2;
             break;
-        case TOK.lessOrEqual:
+        case EXP.lessOrEqual:
             cmp = null1;
             break;
-        case TOK.greaterOrEqual:
+        case EXP.greaterOrEqual:
             cmp = null2;
             break;
-        case TOK.identity:
-        case TOK.equal:
-        case TOK.notIdentity: // 'cmp' gets inverted below
-        case TOK.notEqual:
+        case EXP.identity:
+        case EXP.equal:
+        case EXP.notIdentity: // 'cmp' gets inverted below
+        case EXP.notEqual:
             cmp = (null1 == null2);
             break;
         default:
@@ -983,17 +983,17 @@ int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, d
     {
         switch (op)
         {
-        case TOK.identity:
-        case TOK.equal:
-        case TOK.notIdentity: // 'cmp' gets inverted below
-        case TOK.notEqual:
+        case EXP.identity:
+        case EXP.equal:
+        case EXP.notIdentity: // 'cmp' gets inverted below
+        case EXP.notEqual:
             cmp = 0;
             break;
         default:
             return -1; // memory blocks are different
         }
     }
-    if (op == TOK.notIdentity || op == TOK.notEqual)
+    if (op == EXP.notIdentity || op == EXP.notEqual)
         cmp ^= 1;
     return cmp;
 }
@@ -1019,35 +1019,35 @@ Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to)
 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
 bool isCtfeComparable(Expression e)
 {
-    if (e.op == TOK.slice)
+    if (e.op == EXP.slice)
         e = (cast(SliceExp)e).e1;
     if (e.isConst() != 1)
     {
-        if (e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.arrayLiteral || e.op == TOK.structLiteral || e.op == TOK.assocArrayLiteral || e.op == TOK.classReference)
+        if (e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.function_ || e.op == EXP.delegate_ || e.op == EXP.arrayLiteral || e.op == EXP.structLiteral || e.op == EXP.assocArrayLiteral || e.op == EXP.classReference)
         {
             return true;
         }
         // https://issues.dlang.org/show_bug.cgi?id=14123
         // TypeInfo object is comparable in CTFE
-        if (e.op == TOK.typeid_)
+        if (e.op == EXP.typeid_)
             return true;
         return false;
     }
     return true;
 }
 
-/// Map TOK comparison ops
-private bool numCmp(N)(TOK op, N n1, N n2)
+/// Map EXP comparison ops
+private bool numCmp(N)(EXP op, N n1, N n2)
 {
     switch (op)
     {
-    case TOK.lessThan:
+    case EXP.lessThan:
         return n1 < n2;
-    case TOK.lessOrEqual:
+    case EXP.lessOrEqual:
         return n1 <= n2;
-    case TOK.greaterThan:
+    case EXP.greaterThan:
         return n1 > n2;
-    case TOK.greaterOrEqual:
+    case EXP.greaterOrEqual:
         return n1 >= n2;
 
     default:
@@ -1056,35 +1056,35 @@ private bool numCmp(N)(TOK op, N n1, N n2)
 }
 
 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-bool specificCmp(TOK op, int rawCmp)
+bool specificCmp(EXP op, int rawCmp)
 {
     return numCmp!int(op, rawCmp, 0);
 }
 
 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2)
+bool intUnsignedCmp(EXP op, dinteger_t n1, dinteger_t n2)
 {
     return numCmp!dinteger_t(op, n1, n2);
 }
 
 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2)
+bool intSignedCmp(EXP op, sinteger_t n1, sinteger_t n2)
 {
     return numCmp!sinteger_t(op, n1, n2);
 }
 
 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
-bool realCmp(TOK op, real_t r1, real_t r2)
+bool realCmp(EXP op, real_t r1, real_t r2)
 {
     // Don't rely on compiler, handle NAN arguments separately
     if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
     {
         switch (op)
         {
-        case TOK.lessThan:
-        case TOK.lessOrEqual:
-        case TOK.greaterThan:
-        case TOK.greaterOrEqual:
+        case EXP.lessThan:
+        case EXP.lessOrEqual:
+        case EXP.greaterThan:
+        case EXP.greaterOrEqual:
             return false;
 
         default:
@@ -1128,7 +1128,7 @@ private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinte
     auto se2 = x2.isStringExp();
     auto ae2 = x2.isArrayLiteralExp();
 
-    // Now both must be either TOK.arrayLiteral or TOK.string_
+    // Now both must be either EXP.arrayLiteral or EXP.string_
     if (se1 && se2)
         return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len);
     if (se1 && ae2)
@@ -1171,13 +1171,13 @@ private FuncDeclaration funcptrOf(Expression e)
         return de.func;
     if (auto fe = e.isFuncExp())
         return fe.fd;
-    assert(e.op == TOK.null_);
+    assert(e.op == EXP.null_);
     return null;
 }
 
 private bool isArray(const Expression e)
 {
-    return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_;
+    return e.op == EXP.arrayLiteral || e.op == EXP.string_ || e.op == EXP.slice || e.op == EXP.null_;
 }
 
 /*****
@@ -1192,14 +1192,14 @@ private bool isArray(const Expression e)
  */
 private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false)
 {
-    if (e1.op == TOK.classReference || e2.op == TOK.classReference)
+    if (e1.op == EXP.classReference || e2.op == EXP.classReference)
     {
-        if (e1.op == TOK.classReference && e2.op == TOK.classReference &&
+        if (e1.op == EXP.classReference && e2.op == EXP.classReference &&
             (cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value)
             return 0;
         return 1;
     }
-    if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_)
+    if (e1.op == EXP.typeid_ && e2.op == EXP.typeid_)
     {
         // printf("e1: %s\n", e1.toChars());
         // printf("e2: %s\n", e2.toChars());
@@ -1210,7 +1210,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
         return t1 != t2;
     }
     // null == null, regardless of type
-    if (e1.op == TOK.null_ && e2.op == TOK.null_)
+    if (e1.op == EXP.null_ && e2.op == EXP.null_)
         return 0;
     if (e1.type.ty == Tpointer && e2.type.ty == Tpointer)
     {
@@ -1218,7 +1218,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
         dinteger_t ofs1, ofs2;
         Expression agg1 = getAggregateFromPointer(e1, &ofs1);
         Expression agg2 = getAggregateFromPointer(e2, &ofs2);
-        if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
+        if ((agg1 == agg2) || (agg1.op == EXP.variable && agg2.op == EXP.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
         {
             if (ofs1 == ofs2)
                 return 0;
@@ -1232,17 +1232,17 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
             return 1;
         // If both are delegate literals, assume they have the
         // same closure pointer. TODO: We don't support closures yet!
-        if (e1.op == TOK.function_ && e2.op == TOK.function_)
+        if (e1.op == EXP.function_ && e2.op == EXP.function_)
             return 0;
-        assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_);
+        assert(e1.op == EXP.delegate_ && e2.op == EXP.delegate_);
         // Same .funcptr. Do they have the same .ptr?
         Expression ptr1 = (cast(DelegateExp)e1).e1;
         Expression ptr2 = (cast(DelegateExp)e2).e1;
         dinteger_t ofs1, ofs2;
         Expression agg1 = getAggregateFromPointer(ptr1, &ofs1);
         Expression agg2 = getAggregateFromPointer(ptr2, &ofs2);
-        // If they are TOK.variable, it means they are FuncDeclarations
-        if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
+        // If they are EXP.variable, it means they are FuncDeclarations
+        if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == EXP.variable && agg2.op == EXP.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var))
         {
             return 0;
         }
@@ -1293,7 +1293,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
         }
         return c1 != c2;
     }
-    if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral)
+    if (e1.op == EXP.structLiteral && e2.op == EXP.structLiteral)
     {
         StructLiteralExp es1 = cast(StructLiteralExp)e1;
         StructLiteralExp es2 = cast(StructLiteralExp)e2;
@@ -1314,7 +1314,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
                 Expression ee2 = (*es2.elements)[i];
 
                 // https://issues.dlang.org/show_bug.cgi?id=16284
-                if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp
+                if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp
                     continue;
 
                 if (ee1 == ee2)
@@ -1328,7 +1328,7 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
             return 0; // All elements are equal
         }
     }
-    if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral)
+    if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.assocArrayLiteral)
     {
         AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1;
         AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2;
@@ -1367,27 +1367,27 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
 }
 
 /// Evaluate ==, !=.  Resolves slices before comparing. Returns 0 or 1
-bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2)
+bool ctfeEqual(const ref Loc loc, EXP op, Expression e1, Expression e2)
 {
-    return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual);
+    return !ctfeRawCmp(loc, e1, e2) ^ (op == EXP.notEqual);
 }
 
 /// Evaluate is, !is.  Resolves slices before comparing. Returns 0 or 1
-bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2)
+bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2)
 {
     //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
-    //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op),
-    //    Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars());
+    //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr,
+    //    EXPtoString(e1.op).ptr, e1.toChars(), EXPtoString(e2.op).ptr, e1.toChars());
     bool cmp;
-    if (e1.op == TOK.null_)
+    if (e1.op == EXP.null_)
     {
-        cmp = (e2.op == TOK.null_);
+        cmp = (e2.op == EXP.null_);
     }
-    else if (e2.op == TOK.null_)
+    else if (e2.op == EXP.null_)
     {
         cmp = false;
     }
-    else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset)
+    else if (e1.op == EXP.symbolOffset && e2.op == EXP.symbolOffset)
     {
         SymOffExp es1 = cast(SymOffExp)e1;
         SymOffExp es2 = cast(SymOffExp)e2;
@@ -1407,13 +1407,13 @@ bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2)
     {
         cmp = !ctfeRawCmp(loc, e1, e2, true);
     }
-    if (op == TOK.notIdentity || op == TOK.notEqual)
+    if (op == EXP.notIdentity || op == EXP.notEqual)
         cmp ^= true;
     return cmp;
 }
 
 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
-bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2)
+bool ctfeCmp(const ref Loc loc, EXP op, Expression e1, Expression e2)
 {
     Type t1 = e1.type.toBasetype();
     Type t2 = e2.type.toBasetype();
@@ -1435,7 +1435,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
     Type t1 = e1.type.toBasetype();
     Type t2 = e2.type.toBasetype();
     UnionExp ue;
-    if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral())
+    if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral())
     {
         // [chars] ~ string => string (only valid for CTFE)
         StringExp es1 = cast(StringExp)e2;
@@ -1448,9 +1448,9 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         foreach (size_t i; 0 .. es2.elements.dim)
         {
             Expression es2e = (*es2.elements)[i];
-            if (es2e.op != TOK.int64)
+            if (es2e.op != EXP.int64)
             {
-                emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+                emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
                 return ue;
             }
             dinteger_t v = es2e.toInteger();
@@ -1464,7 +1464,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         es.type = type;
         return ue;
     }
-    if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral())
+    if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral())
     {
         // string ~ [chars] => string (only valid for CTFE)
         // Concatenate the strings
@@ -1478,9 +1478,9 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         foreach (size_t i; 0 .. es2.elements.dim)
         {
             Expression es2e = (*es2.elements)[i];
-            if (es2e.op != TOK.int64)
+            if (es2e.op != EXP.int64)
             {
-                emplaceExp!(CTFEExp)(&ue, TOK.cantExpression);
+                emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
                 return ue;
             }
             const v = es2e.toInteger();
@@ -1495,7 +1495,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         es.type = type;
         return ue;
     }
-    if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+    if (e1.op == EXP.arrayLiteral && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
     {
         //  [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
         ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1;
@@ -1505,13 +1505,13 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2)
         es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements));
         return ue;
     }
-    if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf()))
+    if (e1.op == EXP.arrayLiteral && e2.op == EXP.null_ && t1.nextOf().equals(t2.nextOf()))
     {
         //  [ e1 ] ~ null ----> [ e1 ].dup
         ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy());
         return ue;
     }
-    if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
+    if (e1.op == EXP.null_ && e2.op == EXP.arrayLiteral && t1.nextOf().equals(t2.nextOf()))
     {
         //  null ~ [ e2 ] ----> [ e2 ].dup
         ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy());
@@ -1532,7 +1532,7 @@ Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2
     {
         --i;
         Expression ekey = (*ae.keys)[i];
-        const int eq = ctfeEqual(loc, TOK.equal, ekey, e2);
+        const int eq = ctfeEqual(loc, EXP.equal, ekey, e2);
         if (eq)
         {
             return (*ae.values)[i];
@@ -1581,10 +1581,10 @@ Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expres
         return paintTypeOntoLiteral(pue, to, e);
     }
 
-    if (e.op == TOK.null_)
+    if (e.op == EXP.null_)
         return paint();
 
-    if (e.op == TOK.classReference)
+    if (e.op == EXP.classReference)
     {
         // Disallow reinterpreting class casts. Do this by ensuring that
         // the original class can implicitly convert to the target class
@@ -1603,7 +1603,7 @@ Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expres
         return paint();
 
     // Allow casting away const for struct literals
-    if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
+    if (e.op == EXP.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0))
         return paint();
 
     Expression r;
@@ -1647,14 +1647,14 @@ Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expres
  */
 void assignInPlace(Expression dest, Expression src)
 {
-    if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_))
+    if (!(dest.op == EXP.structLiteral || dest.op == EXP.arrayLiteral || dest.op == EXP.string_))
     {
         printf("invalid op %d %d\n", src.op, dest.op);
         assert(0);
     }
     Expressions* oldelems;
     Expressions* newelems;
-    if (dest.op == TOK.structLiteral)
+    if (dest.op == EXP.structLiteral)
     {
         assert(dest.op == src.op);
         oldelems = (cast(StructLiteralExp)dest).elements;
@@ -1666,22 +1666,22 @@ void assignInPlace(Expression dest, Expression src)
             foreach (_; 0 .. newelems.dim - oldelems.dim)
                 oldelems.push(null);
     }
-    else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral)
+    else if (dest.op == EXP.arrayLiteral && src.op == EXP.arrayLiteral)
     {
         oldelems = (cast(ArrayLiteralExp)dest).elements;
         newelems = (cast(ArrayLiteralExp)src).elements;
     }
-    else if (dest.op == TOK.string_ && src.op == TOK.string_)
+    else if (dest.op == EXP.string_ && src.op == EXP.string_)
     {
         sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0);
         return;
     }
-    else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_)
+    else if (dest.op == EXP.arrayLiteral && src.op == EXP.string_)
     {
         sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0);
         return;
     }
-    else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_)
+    else if (src.op == EXP.arrayLiteral && dest.op == EXP.string_)
     {
         sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0);
         return;
@@ -1696,12 +1696,12 @@ void assignInPlace(Expression dest, Expression src)
     {
         Expression e = (*newelems)[i];
         Expression o = (*oldelems)[i];
-        if (e.op == TOK.structLiteral)
+        if (e.op == EXP.structLiteral)
         {
             assert(o.op == e.op);
             assignInPlace(o, e);
         }
-        else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray)
+        else if (e.type.ty == Tsarray && e.op != EXP.void_ && o.type.ty == Tsarray)
         {
             assignInPlace(o, e);
         }
@@ -1724,7 +1724,7 @@ Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae,
     {
         j--;
         Expression ekey = (*aae.keys)[j];
-        int eq = ctfeEqual(loc, TOK.equal, ekey, index);
+        int eq = ctfeEqual(loc, EXP.equal, ekey, index);
         if (eq)
         {
             (*valuesx)[j] = newval;
@@ -1752,13 +1752,13 @@ UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expres
     auto elements = new Expressions(newlen);
     // Resolve slices
     size_t indxlo = 0;
-    if (oldval.op == TOK.slice)
+    if (oldval.op == EXP.slice)
     {
         indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger();
         oldval = (cast(SliceExp)oldval).e1;
     }
     size_t copylen = oldlen < newlen ? oldlen : newlen;
-    if (oldval.op == TOK.string_)
+    if (oldval.op == EXP.string_)
     {
         StringExp oldse = cast(StringExp)oldval;
         void* s = mem.xcalloc(newlen + 1, oldse.sz);
@@ -1793,7 +1793,7 @@ UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expres
     {
         if (oldlen != 0)
         {
-            assert(oldval.op == TOK.arrayLiteral);
+            assert(oldval.op == EXP.arrayLiteral);
             ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval;
             foreach (size_t i; 0 .. copylen)
                 (*elements)[i] = (*ae.elements)[indxlo + i];
@@ -1825,13 +1825,13 @@ bool isCtfeValueValid(Expression newval)
     Type tb = newval.type.toBasetype();
     switch (newval.op)
     {
-        case TOK.int64:
-        case TOK.float64:
-        case TOK.char_:
-        case TOK.complex80:
+        case EXP.int64:
+        case EXP.float64:
+        case EXP.char_:
+        case EXP.complex80:
             return tb.isscalar();
 
-        case TOK.null_:
+        case EXP.null_:
             return tb.ty == Tnull    ||
                    tb.ty == Tpointer ||
                    tb.ty == Tarray   ||
@@ -1839,75 +1839,75 @@ bool isCtfeValueValid(Expression newval)
                    tb.ty == Tclass   ||
                    tb.ty == Tdelegate;
 
-        case TOK.string_:
+        case EXP.string_:
             return true; // CTFE would directly use the StringExp in AST.
 
-        case TOK.arrayLiteral:
+        case EXP.arrayLiteral:
             return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
 
-        case TOK.assocArrayLiteral:
+        case EXP.assocArrayLiteral:
             return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
 
-        case TOK.structLiteral:
+        case EXP.structLiteral:
             return true; //((StructLiteralExp *)newval)->ownedByCtfe;
 
-        case TOK.classReference:
+        case EXP.classReference:
             return true;
 
-        case TOK.type:
+        case EXP.type:
             return true;
 
-        case TOK.vector:
+        case EXP.vector:
             return true; // vector literal
 
-        case TOK.function_:
+        case EXP.function_:
             return true; // function literal or delegate literal
 
-        case TOK.delegate_:
+        case EXP.delegate_:
         {
             // &struct.func or &clasinst.func
             // &nestedfunc
             Expression ethis = (cast(DelegateExp)newval).e1;
-            return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func);
+            return (ethis.op == EXP.structLiteral || ethis.op == EXP.classReference || ethis.op == EXP.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func);
         }
 
-        case TOK.symbolOffset:
+        case EXP.symbolOffset:
         {
             // function pointer, or pointer to static variable
             Declaration d = (cast(SymOffExp)newval).var;
             return d.isFuncDeclaration() || d.isDataseg();
         }
 
-        case TOK.typeid_:
+        case EXP.typeid_:
         {
             // always valid
             return true;
         }
 
-        case TOK.address:
+        case EXP.address:
         {
             // e1 should be a CTFE reference
             Expression e1 = (cast(AddrExp)newval).e1;
             return tb.ty == Tpointer &&
             (
-                (e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) ||
-                 e1.op == TOK.variable ||
-                 e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) ||
-                 e1.op == TOK.index && isCtfeReferenceValid(e1) ||
-                 e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray
+                (e1.op == EXP.structLiteral || e1.op == EXP.arrayLiteral) && isCtfeValueValid(e1) ||
+                 e1.op == EXP.variable ||
+                 e1.op == EXP.dotVariable && isCtfeReferenceValid(e1) ||
+                 e1.op == EXP.index && isCtfeReferenceValid(e1) ||
+                 e1.op == EXP.slice && e1.type.toBasetype().ty == Tsarray
             );
         }
 
-        case TOK.slice:
+        case EXP.slice:
         {
             // e1 should be an array aggregate
             const SliceExp se = cast(SliceExp)newval;
-            assert(se.lwr && se.lwr.op == TOK.int64);
-            assert(se.upr && se.upr.op == TOK.int64);
-            return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral);
+            assert(se.lwr && se.lwr.op == EXP.int64);
+            assert(se.upr && se.upr.op == EXP.int64);
+            return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral);
         }
 
-        case TOK.void_:
+        case EXP.void_:
             return true; // uninitialized value
 
         default:
@@ -1920,10 +1920,10 @@ bool isCtfeReferenceValid(Expression newval)
 {
     switch (newval.op)
     {
-        case TOK.this_:
+        case EXP.this_:
             return true;
 
-        case TOK.variable:
+        case EXP.variable:
         {
             const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration();
             assert(v);
@@ -1931,16 +1931,16 @@ bool isCtfeReferenceValid(Expression newval)
             return true;
         }
 
-        case TOK.index:
+        case EXP.index:
         {
             const Expression eagg = (cast(IndexExp)newval).e1;
-            return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral;
+            return eagg.op == EXP.string_ || eagg.op == EXP.arrayLiteral || eagg.op == EXP.assocArrayLiteral;
         }
 
-        case TOK.dotVariable:
+        case EXP.dotVariable:
         {
             Expression eagg = (cast(DotVarExp)newval).e1;
-            return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg);
+            return (eagg.op == EXP.structLiteral || eagg.op == EXP.classReference) && isCtfeValueValid(eagg);
         }
 
         default:
@@ -1959,44 +1959,44 @@ void showCtfeExpr(Expression e, int level = 0)
     // We need the struct definition to detect block assignment
     StructDeclaration sd = null;
     ClassDeclaration cd = null;
-    if (e.op == TOK.structLiteral)
+    if (e.op == EXP.structLiteral)
     {
         elements = (cast(StructLiteralExp)e).elements;
         sd = (cast(StructLiteralExp)e).sd;
         printf("STRUCT type = %s %p:\n", e.type.toChars(), e);
     }
-    else if (e.op == TOK.classReference)
+    else if (e.op == EXP.classReference)
     {
         elements = (cast(ClassReferenceExp)e).value.elements;
         cd = (cast(ClassReferenceExp)e).originalClass();
         printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value);
     }
-    else if (e.op == TOK.arrayLiteral)
+    else if (e.op == EXP.arrayLiteral)
     {
         elements = (cast(ArrayLiteralExp)e).elements;
         printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e);
     }
-    else if (e.op == TOK.assocArrayLiteral)
+    else if (e.op == EXP.assocArrayLiteral)
     {
         printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e);
     }
-    else if (e.op == TOK.string_)
+    else if (e.op == EXP.string_)
     {
         printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr);
     }
-    else if (e.op == TOK.slice)
+    else if (e.op == EXP.slice)
     {
         printf("SLICE %p: %s\n", e, e.toChars());
         showCtfeExpr((cast(SliceExp)e).e1, level + 1);
     }
-    else if (e.op == TOK.variable)
+    else if (e.op == EXP.variable)
     {
         printf("VAR %p %s\n", e, e.toChars());
         VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
         if (v && getValue(v))
             showCtfeExpr(getValue(v), level + 1);
     }
-    else if (e.op == TOK.address)
+    else if (e.op == EXP.address)
     {
         // This is potentially recursive. We mustn't try to print the thing we're pointing to.
         printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars());
@@ -2069,7 +2069,7 @@ UnionExp voidInitLiteral(Type t, VarDeclaration var)
         Expression elem = voidInitLiteral(tsa.next, var).copy();
         // For aggregate value types (structs, static arrays) we must
         // create an a separate copy for each element.
-        const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral);
+        const mustCopy = (elem.op == EXP.arrayLiteral || elem.op == EXP.structLiteral);
         const d = cast(size_t)tsa.dim.toInteger();
         auto elements = new Expressions(d);
         foreach (i; 0 .. d)
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
index 87c3adae9e6..2e5a79d0feb 100644
--- a/gcc/d/dmd/dcast.d
+++ b/gcc/d/dmd/dcast.d
@@ -29,6 +29,7 @@ import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
+import dmd.hdrgen;
 import dmd.impcnvtab;
 import dmd.id;
 import dmd.importc;
@@ -994,7 +995,7 @@ MATCH implicitConvTo(Expression e, Type t)
             Type typeb = e.type.toBasetype();
 
             // Look for pointers to functions where the functions are overloaded.
-            if (e.e1.op == TOK.overloadSet &&
+            if (e.e1.op == EXP.overloadSet &&
                 (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
             {
                 OverExp eo = e.e1.isOverExp();
@@ -1019,7 +1020,7 @@ MATCH implicitConvTo(Expression e, Type t)
                 }
             }
 
-            if (e.e1.op == TOK.variable &&
+            if (e.e1.op == EXP.variable &&
                 typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
                 tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
             {
@@ -1443,7 +1444,7 @@ MATCH implicitConvTo(Expression e, Type t)
             }
 
             // Enhancement 10724
-            if (tb.ty == Tpointer && e.e1.op == TOK.string_)
+            if (tb.ty == Tpointer && e.e1.op == EXP.string_)
                 e.e1.accept(this);
         }
 
@@ -1564,7 +1565,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 result = e;
                 return;
             }
-            if (e.op == TOK.variable)
+            if (e.op == EXP.variable)
             {
                 VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
                 if (v && v.storage_class & STC.manifest)
@@ -1728,6 +1729,11 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                     }
                     const fsize = t1b.nextOf().size();
                     const tsize = tob.nextOf().size();
+                    if (fsize == SIZE_INVALID || tsize == SIZE_INVALID)
+                    {
+                        result = ErrorExp.get();
+                        return;
+                    }
                     if (fsize != tsize)
                     {
                         const dim = t1b.isTypeSArray().dim.toInteger();
@@ -1846,7 +1852,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         override void visit(StructLiteralExp e)
         {
             visit(cast(Expression)e);
-            if (result.op == TOK.structLiteral)
+            if (result.op == EXP.structLiteral)
                 (cast(StructLiteralExp)result).stype = t; // commit type
         }
 
@@ -1868,6 +1874,13 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             }
 
             StringExp se = e;
+
+            void lcast()
+            {
+                result = new CastExp(e.loc, se, t);
+                result.type = t; // so semantic() won't be run on e
+            }
+
             if (!e.committed)
             {
                 se = cast(StringExp)e.copy();
@@ -1942,7 +1955,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                     se = cast(StringExp)e.copy();
                     copied = 1;
                 }
-                goto Lcast;
+                return lcast();
             }
             if (typeb.ty != Tsarray && typeb.ty != Tarray && typeb.ty != Tpointer)
             {
@@ -1951,10 +1964,16 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                     se = cast(StringExp)e.copy();
                     copied = 1;
                 }
-                goto Lcast;
+                return lcast();
             }
 
-            if (typeb.nextOf().size() == tb.nextOf().size())
+            const nextSz = typeb.nextOf().size();
+            if (nextSz == SIZE_INVALID)
+            {
+                result = ErrorExp.get();
+                return;
+            }
+            if (nextSz == tb.nextOf().size())
             {
                 if (!copied)
                 {
@@ -2135,7 +2154,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
             }
 
             // Look for pointers to functions where the functions are overloaded.
-            if (e.e1.op == TOK.overloadSet &&
+            if (e.e1.op == EXP.overloadSet &&
                 (tb.ty == Tpointer || tb.ty == Tdelegate) && tb.nextOf().ty == Tfunction)
             {
                 OverExp eo = cast(OverExp)e.e1;
@@ -2169,7 +2188,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 }
             }
 
-            if (e.e1.op == TOK.variable &&
+            if (e.e1.op == EXP.variable &&
                 typeb.ty == Tpointer && typeb.nextOf().ty == Tfunction &&
                 tb.ty == Tpointer && tb.nextOf().ty == Tfunction)
             {
@@ -2621,7 +2640,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 if (e.e1.implicitConvTo(t1b) > MATCH.nomatch)
                 {
                     Expression e1x = e.e1.implicitCastTo(sc, t1b);
-                    assert(e1x.op != TOK.error);
+                    assert(e1x.op != EXP.error);
                     e = cast(SliceExp)e.copy();
                     e.e1 = e1x;
                     e.type = t;
@@ -2732,10 +2751,10 @@ Expression inferType(Expression e, Type t, int flag = 0)
 
     if (t) switch (e.op)
     {
-        case TOK.arrayLiteral:      return visitAle(cast(ArrayLiteralExp) e);
-        case TOK.assocArrayLiteral: return visitAar(cast(AssocArrayLiteralExp) e);
-        case TOK.function_:         return visitFun(cast(FuncExp) e);
-        case TOK.question:          return visitTer(cast(CondExp) e);
+        case EXP.arrayLiteral:      return visitAle(cast(ArrayLiteralExp) e);
+        case EXP.assocArrayLiteral: return visitAar(cast(AssocArrayLiteralExp) e);
+        case EXP.function_:         return visitFun(cast(FuncExp) e);
+        case EXP.question:          return visitTer(cast(CondExp) e);
         default:
     }
     return e;
@@ -2789,7 +2808,7 @@ Expression scaleFactor(BinExp be, Scope* sc)
     if (sc.func && !sc.intypeof)
     {
         eoff = eoff.optimize(WANTvalue);
-        if (eoff.op == TOK.int64 && eoff.toInteger() == 0)
+        if (eoff.op == EXP.int64 && eoff.toInteger() == 0)
         {
         }
         else if (sc.func.setUnsafe())
@@ -2811,7 +2830,7 @@ Expression scaleFactor(BinExp be, Scope* sc)
  */
 private bool isVoidArrayLiteral(Expression e, Type other)
 {
-    while (e.op == TOK.arrayLiteral && e.type.ty == Tarray && ((cast(ArrayLiteralExp)e).elements.dim == 1))
+    while (e.op == EXP.arrayLiteral && e.type.ty == Tarray && ((cast(ArrayLiteralExp)e).elements.dim == 1))
     {
         auto ale = cast(ArrayLiteralExp)e;
         e = ale[0];
@@ -2823,7 +2842,7 @@ private bool isVoidArrayLiteral(Expression e, Type other)
     if (other.ty != Tsarray && other.ty != Tarray)
         return false;
     Type t = e.type;
-    return (e.op == TOK.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && (cast(ArrayLiteralExp)e).elements.dim == 0);
+    return (e.op == EXP.arrayLiteral && t.ty == Tarray && t.nextOf().ty == Tvoid && (cast(ArrayLiteralExp)e).elements.dim == 0);
 }
 
 /**
@@ -2833,7 +2852,7 @@ private bool isVoidArrayLiteral(Expression e, Type other)
  *
  * Params:
  *     sc  = Current scope
- *     op  = Operator such as `e1 op e2`. In practice, either TOK.question
+ *     op  = Operator such as `e1 op e2`. In practice, either EXP.question
  *           or one of the binary operator.
  *     pe1 = The LHS of the operation, will be rewritten
  *     pe2 = The RHS of the operation, will be rewritten
@@ -2841,7 +2860,7 @@ private bool isVoidArrayLiteral(Expression e, Type other)
  * Returns:
  *      The resulting type in case of success, `null` in case of error
  */
-Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
+Type typeMerge(Scope* sc, EXP op, ref Expression pe1, ref Expression pe2)
 {
     //printf("typeMerge() %s op %s\n", e1.toChars(), e2.toChars());
 
@@ -2906,9 +2925,9 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
         }
     }
 
-    if (op != TOK.question || t1b.ty != t2b.ty && (t1b.isTypeBasic() && t2b.isTypeBasic()))
+    if (op != EXP.question || t1b.ty != t2b.ty && (t1b.isTypeBasic() && t2b.isTypeBasic()))
     {
-        if (op == TOK.question && t1b.ty.isSomeChar() && t2b.ty.isSomeChar())
+        if (op == EXP.question && t1b.ty.isSomeChar() && t2b.ty.isSomeChar())
         {
             e1 = e1.castTo(sc, Type.tdchar);
             e2 = e2.castTo(sc, Type.tdchar);
@@ -3077,7 +3096,7 @@ Lagain:
         return null;
     }
 
-    if ((t1.ty == Tsarray || t1.ty == Tarray) && (e2.op == TOK.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == TOK.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1)))
+    if ((t1.ty == Tsarray || t1.ty == Tarray) && (e2.op == EXP.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == EXP.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1)))
     {
         /*  (T[n] op void*)   => T[]
          *  (T[]  op void*)   => T[]
@@ -3089,7 +3108,7 @@ Lagain:
         return coerce(t1.nextOf().arrayOf());
     }
 
-    if ((t2.ty == Tsarray || t2.ty == Tarray) && (e1.op == TOK.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == TOK.arrayLiteral && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2)))
+    if ((t2.ty == Tsarray || t2.ty == Tarray) && (e1.op == EXP.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == EXP.arrayLiteral && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2)))
     {
         /*  (void*   op T[n]) => T[]
          *  (void*   op T[])  => T[]
@@ -3107,9 +3126,9 @@ Lagain:
         // Tsarray op [x, y, ...] should to be Tsarray
         // https://issues.dlang.org/show_bug.cgi?id=14737
         // Tsarray ~ [x, y, ...] should to be Tarray
-        if (t1.ty == Tsarray && e2.op == TOK.arrayLiteral && op != TOK.concatenate)
+        if (t1.ty == Tsarray && e2.op == EXP.arrayLiteral && op != EXP.concatenate)
             return convert(e2, t1);
-        if (m == MATCH.constant && (op == TOK.addAssign || op == TOK.minAssign || op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign || op == TOK.powAssign || op == TOK.andAssign || op == TOK.orAssign || op == TOK.xorAssign))
+        if (m == MATCH.constant && (op == EXP.addAssign || op == EXP.minAssign || op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign || op == EXP.powAssign || op == EXP.andAssign || op == EXP.orAssign || op == EXP.xorAssign))
         {
             // Don't make the lvalue const
             return Lret(t2);
@@ -3121,7 +3140,7 @@ Lagain:
     {
         // https://issues.dlang.org/show_bug.cgi?id=7285
         // https://issues.dlang.org/show_bug.cgi?id=14737
-        if (t2.ty == Tsarray && e1.op == TOK.arrayLiteral && op != TOK.concatenate)
+        if (t2.ty == Tsarray && e1.op == EXP.arrayLiteral && op != EXP.concatenate)
             return convert(e1, t2);
         return convert(e2, t1);
     }
@@ -3134,9 +3153,9 @@ Lagain:
         Type t1n = t1.nextOf();
         Type t2n = t2.nextOf();
         ubyte mod;
-        if (e1.op == TOK.null_ && e2.op != TOK.null_)
+        if (e1.op == EXP.null_ && e2.op != EXP.null_)
             mod = t2n.mod;
-        else if (e1.op != TOK.null_ && e2.op == TOK.null_)
+        else if (e1.op != EXP.null_ && e2.op == EXP.null_)
             mod = t1n.mod;
         else if (!t1n.isImmutable() && !t2n.isImmutable() && t1n.isShared() != t2n.isShared())
             return null;
@@ -3161,9 +3180,9 @@ Lagain:
         if (t1.mod != t2.mod)
         {
             ubyte mod;
-            if (e1.op == TOK.null_ && e2.op != TOK.null_)
+            if (e1.op == EXP.null_ && e2.op != EXP.null_)
                 mod = t2.mod;
-            else if (e1.op != TOK.null_ && e2.op == TOK.null_)
+            else if (e1.op != EXP.null_ && e2.op == EXP.null_)
                 mod = t1.mod;
             else if (!t1.isImmutable() && !t2.isImmutable() && t1.isShared() != t2.isShared())
                 return null;
@@ -3352,9 +3371,9 @@ Lagain:
         goto Lagain;
     }
 
-    if ((e1.op == TOK.string_ || e1.op == TOK.null_) && e1.implicitConvTo(t2))
+    if ((e1.op == EXP.string_ || e1.op == EXP.null_) && e1.implicitConvTo(t2))
         return convert(e1, t2);
-    if ((e2.op == TOK.string_ || e2.op == TOK.null_) && e2.implicitConvTo(t1))
+    if ((e2.op == EXP.string_ || e2.op == EXP.null_) && e2.implicitConvTo(t1))
         return convert(e2, t1);
     if (t1.ty == Tsarray && t2.ty == Tsarray && e2.implicitConvTo(t1.nextOf().arrayOf()))
         return coerce(t1.nextOf().arrayOf());
@@ -3427,6 +3446,69 @@ LmodCompare:
     if (t1.ty == Tnull && (t2.ty == Tpointer || t2.ty == Taarray || t2.ty == Tarray))
         return convert(e1, t2);
 
+    /// Covers array operations for user-defined types
+    Type checkArrayOpType(Expression e1, Expression e2, EXP op, Scope *sc)
+    {
+        // scalar op scalar - we shouldn't be here
+        if (e1.type.ty != Tarray && e1.type.ty != Tsarray && e2.type.ty != Tarray && e2.type.ty != Tsarray)
+            return null;
+
+        // only supporting slices and array literals
+        if (!e1.isSliceExp() && !e1.isArrayLiteralExp() && !e2.isSliceExp() && !e2.isArrayLiteralExp())
+            return null;
+
+        // start with e1 op e2 and if either one of e1 or e2 is a slice or array literal,
+        // replace it with the first element of the array
+        Expression lhs = e1;
+        Expression rhs = e2;
+
+        // T[x .. y] op ?
+        if (e1.isSliceExp())
+            lhs = new IndexExp(Loc.initial, (cast(UnaExp)e1).e1, IntegerExp.literal!0);
+
+        // [t1, t2, .. t3] op ?
+        if (e1.isArrayLiteralExp())
+            lhs = (cast(ArrayLiteralExp)e1).opIndex(0);
+
+        // ? op U[z .. t]
+        if (e2.isSliceExp())
+            rhs = new IndexExp(Loc.initial, (cast(UnaExp)e2).e1, IntegerExp.literal!0);
+
+        // ? op [u1, u2, .. u3]
+        if (e2.isArrayLiteralExp())
+            rhs = (cast(ArrayLiteralExp)e2).opIndex(0);
+
+        // create a new binary expression with the new lhs and rhs (at this stage, at least
+        // one of lhs/rhs has been replaced with the 0'th element of the array it was before)
+        Expression exp;
+        switch (op)
+        {
+            case EXP.add:
+                exp = new AddExp(Loc.initial, lhs, rhs); break;
+            case EXP.min:
+                exp = new MinExp(Loc.initial, lhs, rhs); break;
+            case EXP.mul:
+                exp = new MulExp(Loc.initial, lhs, rhs); break;
+            case EXP.div:
+                exp = new DivExp(Loc.initial, lhs, rhs); break;
+            case EXP.pow:
+                exp = new PowExp(Loc.initial, lhs, rhs); break;
+            default:
+                exp = null;
+        }
+
+        if (exp)
+        {
+            // if T op U is valid and has type V
+            // then T[] op U and T op U[] should be valid and have type V[]
+            Expression e = exp.trySemantic(sc);
+            if (e && e.type)
+                return e.type.arrayOf;
+        }
+
+        return null;
+    }
+
     if (t1.ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e1))
     {
         if (e2.implicitConvTo(t1.nextOf()))
@@ -3463,8 +3545,12 @@ LmodCompare:
                     e2 = e2.castTo(sc, t);
                 return Lret(t);
             }
-            return null;
         }
+
+        t = checkArrayOpType(e1, e2, op, sc);
+        if (t !is null)
+            return Lret(t);
+
         return null;
     }
     else if (t2.ty == Tarray && isBinArrayOp(op) && isArrayOpOperand(e2))
@@ -3483,15 +3569,19 @@ LmodCompare:
             t = e1.type.arrayOf();
         }
         else
-            return null;
+        {
+            t = checkArrayOpType(e1, e2, op, sc);
+            if (t is null)
+                return null;
+        }
 
-        //printf("test %s\n", Token::toChars(op));
+        //printf("test %s\n", EXPtoString(op).ptr);
         e1 = e1.optimize(WANTvalue);
         if (isCommutative(op) && e1.isConst())
         {
             /* Swap operands to minimize number of functions generated
              */
-            //printf("swap %s\n", Token::toChars(op));
+            //printf("swap %s\n", EXPtoString(op).ptr);
             Expression tmp = e1;
             e1 = e2;
             e2 = tmp;
@@ -3512,7 +3602,7 @@ Expression typeCombine(BinExp be, Scope* sc)
     Expression errorReturn()
     {
         Expression ex = be.incompatibleTypes();
-        if (ex.op == TOK.error)
+        if (ex.op == EXP.error)
             return ex;
         return ErrorExp.get();
     }
@@ -3520,7 +3610,7 @@ Expression typeCombine(BinExp be, Scope* sc)
     Type t1 = be.e1.type.toBasetype();
     Type t2 = be.e2.type.toBasetype();
 
-    if (be.op == TOK.min || be.op == TOK.add)
+    if (be.op == EXP.min || be.op == EXP.add)
     {
         // struct+struct, and class+class are errors
         if (t1.ty == Tstruct && t2.ty == Tstruct)
@@ -3540,9 +3630,9 @@ Expression typeCombine(BinExp be, Scope* sc)
         return errorReturn();
 
     // If the types have no value, return an error
-    if (be.e1.op == TOK.error)
+    if (be.e1.op == EXP.error)
         return be.e1;
-    if (be.e2.op == TOK.error)
+    if (be.e2.op == EXP.error)
         return be.e2;
     return null;
 }
@@ -3606,8 +3696,8 @@ void fix16997(Scope* sc, UnaExp ue)
             case Tchar:
             case Twchar:
             case Tdchar:
-                ue.deprecation("integral promotion not done for `%s`, use '-preview=intpromote' switch or `%scast(int)(%s)`",
-                    ue.toChars(), Token.toChars(ue.op), ue.e1.toChars());
+                ue.deprecation("integral promotion not done for `%s`, remove '-revert=intpromote' switch or `%scast(int)(%s)`",
+                    ue.toChars(), EXPtoString(ue.op).ptr, ue.e1.toChars());
                 break;
 
             default:
diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d
index e3f135a03d8..18c8ca2da74 100644
--- a/gcc/d/dmd/declaration.d
+++ b/gcc/d/dmd/declaration.d
@@ -94,7 +94,7 @@ bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1)
             ((fd.isCtorDeclaration() && var.isField()) ||
              (fd.isStaticCtorDeclaration() && !var.isField())) &&
             fd.toParentDecl() == var.toParent2() &&
-            (!e1 || e1.op == TOK.this_))
+            (!e1 || e1.op == EXP.this_))
         {
             bool result = true;
 
@@ -250,7 +250,10 @@ extern (C++) abstract class Declaration : Dsymbol
     override final d_uns64 size(const ref Loc loc)
     {
         assert(type);
-        return type.size();
+        const sz = type.size();
+        if (sz == SIZE_INVALID)
+            errors = true;
+        return sz;
     }
 
     /**
@@ -371,7 +374,7 @@ extern (C++) abstract class Declaration : Dsymbol
             }
         }
 
-        if (e1 && e1.op == TOK.this_ && isField())
+        if (e1 && e1.op == EXP.this_ && isField())
         {
             VarDeclaration vthis = (cast(ThisExp)e1).var;
             for (Scope* scx = sc; scx; scx = scx.enclosing)
@@ -385,7 +388,7 @@ extern (C++) abstract class Declaration : Dsymbol
             }
         }
 
-        if (v && (isCtorinit() || isField()))
+        if (v && (v.isCtorinit() || isField()))
         {
             // It's only modifiable if inside the right constructor
             if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_))
@@ -434,11 +437,6 @@ extern (C++) abstract class Declaration : Dsymbol
         return false;
     }
 
-    final bool isCtorinit() const pure nothrow @nogc @safe
-    {
-        return (storage_class & STC.ctorinit) != 0;
-    }
-
     final bool isFinal() const pure nothrow @nogc @safe
     {
         return (storage_class & STC.final_) != 0;
@@ -655,7 +653,7 @@ extern (C++) final class TupleDeclaration : Declaration
             if (o.dyncast() == DYNCAST.expression)
             {
                 Expression e = cast(Expression)o;
-                if (e.op == TOK.dSymbol)
+                if (e.op == EXP.dSymbol)
                 {
                     DsymbolExp ve = cast(DsymbolExp)e;
                     Declaration d = ve.s.isDeclaration();
@@ -1062,6 +1060,7 @@ extern (C++) class VarDeclaration : Declaration
     bool ctorinit;                  // it has been initialized in a ctor
     bool iscatchvar;                // this is the exception object variable in catch() clause
     bool isowner;                   // this is an Owner, despite it being `scope`
+    bool setInCtorOnly;             // field can only be set in a constructor, as it is const or immutable
 
     // Both these mean the var is not rebindable once assigned,
     // and the destructor gets run when it goes out of scope
@@ -1131,7 +1130,7 @@ extern (C++) class VarDeclaration : Declaration
                 RootObject o = (*v2.objects)[i];
                 assert(o.dyncast() == DYNCAST.expression);
                 Expression e = cast(Expression)o;
-                assert(e.op == TOK.dSymbol);
+                assert(e.op == EXP.dSymbol);
                 DsymbolExp se = cast(DsymbolExp)e;
                 se.s.setFieldOffset(ad, fieldState, isunion);
             }
@@ -1250,6 +1249,11 @@ extern (C++) class VarDeclaration : Declaration
         return false;
     }
 
+    final bool isCtorinit() const pure nothrow @nogc @safe
+    {
+        return setInCtorOnly;
+    }
+
     /*******************************
      * Does symbol go into data segment?
      * Includes extern variables.
@@ -1607,7 +1611,7 @@ extern (C++) class VarDeclaration : Declaration
             ExpInitializer ez = _init.isExpInitializer();
             assert(ez);
             Expression e = ez.exp;
-            if (e.op == TOK.construct || e.op == TOK.blit)
+            if (e.op == EXP.construct || e.op == EXP.blit)
                 e = (cast(AssignExp)e).e2;
             return lambdaCheckForNestedRef(e, sc);
         }
@@ -1670,11 +1674,11 @@ extern (C++) class VarDeclaration : Declaration
         assert(this.loc != Loc.initial);
         assert(v.loc != Loc.initial);
 
-        if (auto ld = this.loc.linnum - v.loc.linnum)
-            return ld < 0;
+        if (this.loc.linnum != v.loc.linnum)
+            return this.loc.linnum < v.loc.linnum;
 
-        if (auto cd = this.loc.charnum - v.loc.charnum)
-            return cd < 0;
+        if (this.loc.charnum != v.loc.charnum)
+            return this.loc.charnum < v.loc.charnum;
 
         // Default fallback
         return this.sequenceNumber < v.sequenceNumber;
@@ -1930,9 +1934,9 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
  */
 extern (C++) final class SymbolDeclaration : Declaration
 {
-    StructDeclaration dsym;
+    AggregateDeclaration dsym;
 
-    extern (D) this(const ref Loc loc, StructDeclaration dsym)
+    extern (D) this(const ref Loc loc, AggregateDeclaration dsym)
     {
         super(loc, dsym.ident);
         this.dsym = dsym;
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index 4a4c3530417..884146e1416 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -53,7 +53,7 @@ struct IntRange;
     #define STCforeach            0x4000ULL    /// variable for foreach loop
     #define STCvariadic           0x8000ULL    /// the `variadic` parameter in: T foo(T a, U b, V variadic...)
 
-    #define STCctorinit           0x10000ULL    /// can only be set inside constructor
+    //                            0x10000ULL
     #define STCtemplateparameter  0x20000ULL    /// template parameter
     #define STCref                0x40000ULL    /// `ref`
     #define STCscope              0x80000ULL    /// `scope`
@@ -65,7 +65,7 @@ struct IntRange;
 
     #define STCreturninferred     0x1000000ULL    /// `return` has been inferred and should not be part of mangling, `return` must also be set
     #define STCimmutable          0x2000000ULL    /// `immutable`
-    #define STCinit               0x4000000ULL    /// has explicit initializer
+    //                            0x4000000ULL
     #define STCmanifest           0x8000000ULL    /// manifest constant
 
     #define STCnodtor             0x10000000ULL    /// do not run destructor
@@ -131,7 +131,6 @@ public:
     virtual bool isDataseg();
     virtual bool isThreadlocal();
     virtual bool isCodeseg() const;
-    bool isCtorinit() const     { return (storage_class & STCctorinit) != 0; }
     bool isFinal() const        { return (storage_class & STCfinal) != 0; }
     virtual bool isAbstract()   { return (storage_class & STCabstract) != 0; }
     bool isConst() const        { return (storage_class & STCconst) != 0; }
@@ -247,6 +246,7 @@ public:
     bool ctorinit;              // it has been initialized in a ctor
     bool iscatchvar;            // this is the exception object variable in catch() clause
     bool isowner;               // this is an Owner, despite it being `scope`
+    bool setInCtorOnly;         // field can only be set in a constructor, as it is const or immutable
     bool onstack;               // it is a class that was allocated on the stack
     bool mynew;                 // it is a class new'd with custom operator new
     char canassign;             // it can be assigned to
@@ -266,6 +266,7 @@ public:
     bool needThis();
     bool isExport() const;
     bool isImportedSymbol() const;
+    bool isCtorinit() const;
     bool isDataseg();
     bool isThreadlocal();
     bool isCTFE();
@@ -303,7 +304,7 @@ public:
 class SymbolDeclaration : public Declaration
 {
 public:
-    StructDeclaration *dsym;
+    AggregateDeclaration *dsym;
 
     // Eliminate need for dynamic_cast
     SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; }
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
index a1f36c0c48f..891adb3c6bd 100644
--- a/gcc/d/dmd/dinterpret.d
+++ b/gcc/d/dmd/dinterpret.d
@@ -34,6 +34,7 @@ import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
+import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
@@ -63,26 +64,26 @@ public Expression ctfeInterpret(Expression e)
 {
     switch (e.op)
     {
-        case TOK.int64:
-        case TOK.float64:
-        case TOK.complex80:
-        case TOK.null_:
-        case TOK.void_:
-        case TOK.string_:
-        case TOK.this_:
-        case TOK.super_:
-        case TOK.type:
-        case TOK.typeid_:
-        case TOK.template_:              // non-eponymous template/instance
-        case TOK.scope_:                 // ditto
-        case TOK.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
-        case TOK.dotTemplateInstance:    // ditto
-        case TOK.dot:                    // ditto
+        case EXP.int64:
+        case EXP.float64:
+        case EXP.complex80:
+        case EXP.null_:
+        case EXP.void_:
+        case EXP.string_:
+        case EXP.this_:
+        case EXP.super_:
+        case EXP.type:
+        case EXP.typeid_:
+        case EXP.template_:              // non-eponymous template/instance
+        case EXP.scope_:                 // ditto
+        case EXP.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
+        case EXP.dotTemplateInstance:    // ditto
+        case EXP.dot:                    // ditto
              if (e.type.ty == Terror)
                 return ErrorExp.get();
-            goto case TOK.error;
+            goto case EXP.error;
 
-        case TOK.error:
+        case EXP.error:
             return e;
 
         default:
@@ -115,7 +116,7 @@ public Expression ctfeInterpret(Expression e)
  */
 public Expression ctfeInterpretForPragmaMsg(Expression e)
 {
-    if (e.op == TOK.error || e.op == TOK.type)
+    if (e.op == EXP.error || e.op == EXP.type)
         return e;
 
     // It's also OK for it to be a function declaration (happens only with
@@ -410,7 +411,7 @@ private struct InterState
  *      thisarg   = 'this', if a needThis() function, NULL if not.
  *
  * Returns:
- * result expression if successful, TOK.cantExpression if not,
+ * result expression if successful, EXP.cantExpression if not,
  * or CTFEExp if function returned void.
  */
 private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg)
@@ -507,7 +508,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
             /* Struct literals are passed by value, but we don't need to
              * copy them if they are passed as const
              */
-            if (earg.op == TOK.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_)))
+            if (earg.op == EXP.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_)))
                 earg = copyLiteral(earg).copy();
         }
         if (auto tee = earg.isThrownExceptionExp())
@@ -563,7 +564,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
         }
         ctfeGlobals.stack.push(v);
 
-        if (fparam.isReference() && earg.op == TOK.variable &&
+        if (fparam.isReference() && earg.op == EXP.variable &&
             earg.isVarExp().var.toParent2() == fd)
         {
             VarDeclaration vx = earg.isVarExp().var.isVarDeclaration();
@@ -660,16 +661,16 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
         }
         else
         {
-            assert(!e || (e.op != TOK.continue_ && e.op != TOK.break_));
+            assert(!e || (e.op != EXP.continue_ && e.op != EXP.break_));
             break;
         }
     }
     // If fell off the end of a void function, return void
     if (!e && tf.next.ty == Tvoid)
         e = CTFEExp.voidexp;
-    if (tf.isref && e.op == TOK.variable && e.isVarExp().var == fd.vthis)
+    if (tf.isref && e.op == EXP.variable && e.isVarExp().var == fd.vthis)
         e = thisarg;
-    if (tf.isref && fd.isThis2 && e.op == TOK.index)
+    if (tf.isref && fd.isThis2 && e.op == EXP.index)
     {
         auto ie = e.isIndexExp();
         auto pe = ie.e1.isPtrExp();
@@ -733,14 +734,14 @@ public:
         this.goal = goal;
     }
 
-    // If e is TOK.throw_exception or TOK.cantExpression,
+    // If e is EXP.throw_exception or EXP.cantExpression,
     // set it to 'result' and returns true.
     bool exceptionOrCant(Expression e)
     {
         if (exceptionOrCantInterpret(e))
         {
             // Make sure e is not pointing to a stack temporary
-            result = (e.op == TOK.cantExpression) ? CTFEExp.cantexp : e;
+            result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e;
             return true;
         }
         return false;
@@ -839,7 +840,7 @@ public:
                 continue;
             if (exceptionOrCant(e))
                 return;
-            if (e.op == TOK.break_)
+            if (e.op == EXP.break_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -850,7 +851,7 @@ public:
                 result = null;
                 return;
             }
-            if (e.op == TOK.continue_)
+            if (e.op == EXP.continue_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -894,7 +895,7 @@ public:
 
         if (isTrueBool(e))
             result = interpret(pue, s.ifbody, istate);
-        else if (e.isBool(false))
+        else if (e.toBool().hasValue(false))
             result = interpret(pue, s.elsebody, istate);
         else
         {
@@ -935,7 +936,7 @@ public:
             if (auto eaddr = e.isAddrExp())
                 x = eaddr.e1;
             VarDeclaration v;
-            while (x.op == TOK.variable && (v = (cast(VarExp)x).var.isVarDeclaration()) !is null)
+            while (x.op == EXP.variable && (v = (cast(VarExp)x).var.isVarDeclaration()) !is null)
             {
                 if (v.storage_class & STC.ref_)
                 {
@@ -952,7 +953,7 @@ public:
                 else
                     break;
             }
-            // TODO: If it is a TOK.dotVariable or TOK.index, we should check that it is not
+            // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not
             // pointing to a local struct or static array.
         }
         if (auto se = e.isStructLiteralExp())
@@ -1023,7 +1024,7 @@ public:
             return;
         }
 
-        // We need to treat pointers specially, because TOK.symbolOffset can be used to
+        // We need to treat pointers specially, because EXP.symbolOffset can be used to
         // return a value OR a pointer
         Expression e = interpret(pue, s.exp, istate);
         if (exceptionOrCant(e))
@@ -1122,7 +1123,7 @@ public:
 
             if (exceptionOrCant(e))
                 return;
-            if (e && e.op == TOK.break_)
+            if (e && e.op == EXP.break_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -1132,7 +1133,7 @@ public:
                 istate.gotoTarget = null;
                 break;
             }
-            if (e && e.op == TOK.continue_)
+            if (e && e.op == EXP.continue_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -1158,7 +1159,7 @@ public:
                 result = CTFEExp.cantexp;
                 return;
             }
-            if (e.isBool(false))
+            if (e.toBool().hasValue(false))
                 break;
             assert(isTrueBool(e));
         }
@@ -1189,7 +1190,7 @@ public:
                 Expression e = interpret(&ue, s.condition, istate);
                 if (exceptionOrCant(e))
                     return;
-                if (e.isBool(false))
+                if (e.toBool().hasValue(false))
                     break;
                 assert(isTrueBool(e));
             }
@@ -1201,7 +1202,7 @@ public:
 
             if (exceptionOrCant(e))
                 return;
-            if (e && e.op == TOK.break_)
+            if (e && e.op == EXP.break_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -1211,7 +1212,7 @@ public:
                 istate.gotoTarget = null;
                 break;
             }
-            if (e && e.op == TOK.continue_)
+            if (e && e.op == EXP.continue_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -1263,7 +1264,7 @@ public:
                 return;
             if (exceptionOrCant(e))
                 return;
-            if (e && e.op == TOK.break_)
+            if (e && e.op == EXP.break_)
             {
                 if (istate.gotoTarget && istate.gotoTarget != s)
                 {
@@ -1290,7 +1291,7 @@ public:
                 Expression ecase = interpret(&uecase, cs.exp, istate);
                 if (exceptionOrCant(ecase))
                     return;
-                if (ctfeEqual(cs.exp.loc, TOK.equal, econdition, ecase))
+                if (ctfeEqual(cs.exp.loc, EXP.equal, econdition, ecase))
                 {
                     scase = cs;
                     break;
@@ -1310,7 +1311,7 @@ public:
         istate.start = scase;
         Expression e = interpret(pue, s._body, istate);
         assert(!istate.start); // jump must not fail
-        if (e && e.op == TOK.break_)
+        if (e && e.op == EXP.break_)
         {
             if (istate.gotoTarget && istate.gotoTarget != s)
             {
@@ -1515,7 +1516,7 @@ public:
             (*collateral.value.elements)[bypass] = boss;
             return newest;
         }
-        while ((*boss.value.elements)[next].op == TOK.classReference)
+        while ((*boss.value.elements)[next].op == EXP.classReference)
         {
             boss = cast(ClassReferenceExp)(*boss.value.elements)[next];
         }
@@ -1606,7 +1607,7 @@ public:
         if (exceptionOrCant(e))
             return;
 
-        assert(e.op == TOK.classReference);
+        assert(e.op == EXP.classReference);
         result = ctfeEmplaceExp!ThrownExceptionExp(s.loc, e.isClassReferenceExp());
     }
 
@@ -1630,7 +1631,7 @@ public:
         }
 
         // If it is with(Enum) {...}, just execute the body.
-        if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type)
+        if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type)
         {
             result = interpret(pue, s._body, istate);
             return;
@@ -1707,7 +1708,7 @@ public:
     {
         debug (LOG)
         {
-            printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), Token.toChars(e.op), e.toChars());
+            printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), EXPtoString(e.op).ptr, e.toChars());
             printf("type = %s\n", e.type.toChars());
             showCtfeExpr(e);
         }
@@ -1755,9 +1756,9 @@ public:
         {
             if (istate && istate.fd.isThis2)
             {
-                assert(result.op == TOK.address);
+                assert(result.op == EXP.address);
                 result = (cast(AddrExp)result).e1;
-                assert(result.op == TOK.arrayLiteral);
+                assert(result.op == EXP.arrayLiteral);
                 result = (*(cast(ArrayLiteralExp)result).elements)[0];
                 if (e.type.ty == Tstruct)
                 {
@@ -1765,7 +1766,7 @@ public:
                 }
                 return;
             }
-            assert(result.op == TOK.structLiteral || result.op == TOK.classReference || result.op == TOK.type);
+            assert(result.op == EXP.structLiteral || result.op == EXP.classReference || result.op == EXP.type);
             return;
         }
         e.error("value of `this` is not known at compile time");
@@ -1909,7 +1910,7 @@ public:
             dinteger_t indx = e.offset / sz;
             assert(sz * indx == e.offset);
             Expression aggregate = null;
-            if (val.op == TOK.arrayLiteral || val.op == TOK.string_)
+            if (val.op == EXP.arrayLiteral || val.op == EXP.string_)
             {
                 aggregate = val;
             }
@@ -2054,13 +2055,13 @@ public:
                     return CTFEExp.cantexp;
                 assert(e.type);
 
-                if (e.op == TOK.construct || e.op == TOK.blit)
+                if (e.op == EXP.construct || e.op == EXP.blit)
                 {
                     AssignExp ae = cast(AssignExp)e;
                     e = ae.e2;
                 }
 
-                if (e.op == TOK.error)
+                if (e.op == EXP.error)
                 {
                     // FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
                 }
@@ -2142,12 +2143,20 @@ public:
         }
         else if (SymbolDeclaration s = d.isSymbolDeclaration())
         {
+            // exclude void[]-typed `__traits(initSymbol)`
+            if (auto ta = s.type.toBasetype().isTypeDArray())
+            {
+                assert(ta.next.ty == Tvoid);
+                error(loc, "cannot determine the address of the initializer symbol during CTFE");
+                return CTFEExp.cantexp;
+            }
+
             // Struct static initializers, for example
             e = s.dsym.type.defaultInitLiteral(loc);
-            if (e.op == TOK.error)
+            if (e.op == EXP.error)
                 error(loc, "CTFE failed because of previous errors in `%s.init`", s.toChars());
             e = e.expressionSemantic(null);
-            if (e.op == TOK.error)
+            if (e.op == EXP.error)
                 e = CTFEExp.cantexp;
             else // Convert NULL to CTFEExp
                 e = interpret(e, istate, goal);
@@ -2195,10 +2204,10 @@ public:
                 {
                     // Strip off the nest of ref variables
                     Expression ev = getValue(v);
-                    if (ev.op == TOK.variable ||
-                        ev.op == TOK.index ||
-                        (ev.op == TOK.slice && ev.type.toBasetype().ty == Tsarray) ||
-                        ev.op == TOK.dotVariable)
+                    if (ev.op == EXP.variable ||
+                        ev.op == EXP.index ||
+                        (ev.op == EXP.slice && ev.type.toBasetype().ty == Tsarray) ||
+                        ev.op == EXP.dotVariable)
                     {
                         result = interpret(pue, ev, istate, goal);
                         return;
@@ -2363,13 +2372,13 @@ public:
             if (exceptionOrCant(ex))
                 return;
 
-            if (result.op == TOK.null_)
+            if (result.op == EXP.null_)
             {
                 e.error("null pointer dereference evaluating typeid. `%s` is `null`", ex.toChars());
                 result = CTFEExp.cantexp;
                 return;
             }
-            if (result.op != TOK.classReference)
+            if (result.op != EXP.classReference)
             {
                 e.error("CTFE internal error: determining classinfo");
                 result = CTFEExp.cantexp;
@@ -2406,7 +2415,7 @@ public:
             // A tuple of assignments can contain void (Bug 5676).
             if (goal == CTFEGoal.Nothing)
                 continue;
-            if (ex.op == TOK.voidExpression)
+            if (ex.op == EXP.voidExpression)
             {
                 e.error("CTFE internal error: void element `%s` in tuple", exp.toChars());
                 assert(0);
@@ -2464,7 +2473,7 @@ public:
             else
             {
                 // segfault bug 6250
-                assert(exp.op != TOK.index || (cast(IndexExp)exp).e1 != e);
+                assert(exp.op != EXP.index || (cast(IndexExp)exp).e1 != e);
 
                 ex = interpretRegion(exp, istate);
                 if (exceptionOrCant(ex))
@@ -2569,7 +2578,7 @@ public:
             for (size_t j = i; j < keysx.dim; j++)
             {
                 auto ekey2 = (*keysx)[j];
-                if (!ctfeEqual(e.loc, TOK.equal, ekey, ekey2))
+                if (!ctfeEqual(e.loc, EXP.equal, ekey, ekey2))
                     continue;
 
                 // Remove ekey
@@ -2904,15 +2913,15 @@ public:
             return;
         switch (e.op)
         {
-        case TOK.negate:
+        case EXP.negate:
             *pue = Neg(e.type, e1);
             break;
 
-        case TOK.tilde:
+        case EXP.tilde:
             *pue = Com(e.type, e1);
             break;
 
-        case TOK.not:
+        case EXP.not:
             *pue = Not(e.type, e1);
             break;
 
@@ -2948,7 +2957,7 @@ public:
         {
             printf("%s BinExp::interpretCommon() %s\n", e.loc.toChars(), e.toChars());
         }
-        if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == TOK.min)
+        if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == EXP.min)
         {
             UnionExp ue1 = void;
             Expression e1 = interpret(&ue1, e.e1, istate);
@@ -2976,7 +2985,7 @@ public:
             result = (*pue).exp();
             return;
         }
-        if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == TOK.add)
+        if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == EXP.add)
         {
             UnionExp ue1 = void;
             Expression e1 = interpret(&ue1, e.e1, istate);
@@ -3015,7 +3024,7 @@ public:
         if (!evalOperand(&ue2, e.e2, e2))
             return;
 
-        if (e.op == TOK.rightShift || e.op == TOK.leftShift || e.op == TOK.unsignedRightShift)
+        if (e.op == EXP.rightShift || e.op == EXP.leftShift || e.op == EXP.unsignedRightShift)
         {
             const sinteger_t i2 = e2.toInteger();
             const d_uns64 sz = e1.type.size() * 8;
@@ -3073,13 +3082,13 @@ public:
             {
                 // The following should really be an assert()
                 e1.error("CTFE internal error: non-constant value `%s`", e1.toChars());
-                emplaceExp!CTFEExp(&ue, TOK.cantExpression);
+                emplaceExp!CTFEExp(&ue, EXP.cantExpression);
                 return ue;
             }
             if (e2.isConst() != 1)
             {
                 e2.error("CTFE internal error: non-constant value `%s`", e2.toChars());
-                emplaceExp!CTFEExp(&ue, TOK.cantExpression);
+                emplaceExp!CTFEExp(&ue, EXP.cantExpression);
                 return ue;
             }
 
@@ -3116,7 +3125,7 @@ public:
             const cmp = comparePointers(e.op, agg1, ofs1, agg2, ofs2);
             if (cmp == -1)
             {
-                char dir = (e.op == TOK.greaterThan || e.op == TOK.greaterOrEqual) ? '<' : '>';
+                char dir = (e.op == EXP.greaterThan || e.op == EXP.greaterOrEqual) ? '<' : '>';
                 e.error("the ordering of pointers to unrelated memory blocks is indeterminate in CTFE. To check if they point to the same memory block, use both `>` and `<` inside `&&` or `||`, eg `%s && %s %c= %s + 1`", e.toChars(), e.e1.toChars(), dir, e.e2.toChars());
                 result = CTFEExp.cantexp;
                 return;
@@ -3162,73 +3171,73 @@ public:
     {
         switch (e.op)
         {
-        case TOK.add:
+        case EXP.add:
             interpretCommon(e, &Add);
             return;
 
-        case TOK.min:
+        case EXP.min:
             interpretCommon(e, &Min);
             return;
 
-        case TOK.mul:
+        case EXP.mul:
             interpretCommon(e, &Mul);
             return;
 
-        case TOK.div:
+        case EXP.div:
             interpretCommon(e, &Div);
             return;
 
-        case TOK.mod:
+        case EXP.mod:
             interpretCommon(e, &Mod);
             return;
 
-        case TOK.leftShift:
+        case EXP.leftShift:
             interpretCommon(e, &Shl);
             return;
 
-        case TOK.rightShift:
+        case EXP.rightShift:
             interpretCommon(e, &Shr);
             return;
 
-        case TOK.unsignedRightShift:
+        case EXP.unsignedRightShift:
             interpretCommon(e, &Ushr);
             return;
 
-        case TOK.and:
+        case EXP.and:
             interpretCommon(e, &And);
             return;
 
-        case TOK.or:
+        case EXP.or:
             interpretCommon(e, &Or);
             return;
 
-        case TOK.xor:
+        case EXP.xor:
             interpretCommon(e, &Xor);
             return;
 
-        case TOK.pow:
+        case EXP.pow:
             interpretCommon(e, &Pow);
             return;
 
-        case TOK.equal:
-        case TOK.notEqual:
+        case EXP.equal:
+        case EXP.notEqual:
             interpretCompareCommon(e, &ctfeEqual);
             return;
 
-        case TOK.identity:
-        case TOK.notIdentity:
+        case EXP.identity:
+        case EXP.notIdentity:
             interpretCompareCommon(e, &ctfeIdentity);
             return;
 
-        case TOK.lessThan:
-        case TOK.lessOrEqual:
-        case TOK.greaterThan:
-        case TOK.greaterOrEqual:
+        case EXP.lessThan:
+        case EXP.lessOrEqual:
+        case EXP.greaterThan:
+        case EXP.greaterOrEqual:
             interpretCompareCommon(e, &ctfeCmp);
             return;
 
         default:
-            printf("be = '%s' %s at [%s]\n", Token.toChars(e.op), e.toChars(), e.loc.toChars());
+            printf("be = '%s' %s at [%s]\n", EXPtoString(e.op).ptr, e.toChars(), e.loc.toChars());
             assert(0);
         }
     }
@@ -3286,7 +3295,7 @@ public:
          * So we need to recurse to determine if it is a block assignment.
          */
         bool isBlockAssignment = false;
-        if (e1.op == TOK.slice)
+        if (e1.op == EXP.slice)
         {
             // a[] = e can have const e. So we compare the naked types.
             Type tdst = e1.type.toBasetype();
@@ -3306,7 +3315,7 @@ public:
         //      Deal with reference assignment
         // ---------------------------------------
         // If it is a construction of a ref variable, it is a ref assignment
-        if ((e.op == TOK.construct || e.op == TOK.blit) &&
+        if ((e.op == EXP.construct || e.op == EXP.blit) &&
             ((cast(AssignExp)e).memset == MemorySet.referenceInit))
         {
             assert(!fp);
@@ -3329,7 +3338,7 @@ public:
 
         if (fp)
         {
-            while (e1.op == TOK.cast_)
+            while (e1.op == EXP.cast_)
             {
                 CastExp ce = cast(CastExp)e1;
                 e1 = ce.e1;
@@ -3342,7 +3351,7 @@ public:
         AssocArrayLiteralExp existingAA = null;
         Expression lastIndex = null;
         Expression oldval = null;
-        if (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+        if (e1.op == EXP.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
         {
             // ---------------------------------------
             //      Deal with AA index assignment
@@ -3356,7 +3365,7 @@ public:
              */
             IndexExp ie = cast(IndexExp)e1;
             int depth = 0; // how many nested AA indices are there?
-            while (ie.e1.op == TOK.index && (cast(IndexExp)ie.e1).e1.type.toBasetype().ty == Taarray)
+            while (ie.e1.op == EXP.index && (cast(IndexExp)ie.e1).e1.type.toBasetype().ty == Taarray)
             {
                 assert(ie.modifiable);
                 ie = cast(IndexExp)ie.e1;
@@ -3430,7 +3439,7 @@ public:
                 oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
 
                 Expression newaae = oldval;
-                while (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+                while (e1.op == EXP.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
                 {
                     Expression ekey = interpretRegion((cast(IndexExp)e1).e2, istate);
                     if (exceptionOrCant(ekey))
@@ -3465,13 +3474,13 @@ public:
             assert(existingAA && lastIndex);
             e1 = null; // stomp
         }
-        else if (e1.op == TOK.arrayLength)
+        else if (e1.op == EXP.arrayLength)
         {
             oldval = interpretRegion(e1, istate);
             if (exceptionOrCant(oldval))
                 return;
         }
-        else if (e.op == TOK.construct || e.op == TOK.blit)
+        else if (e.op == EXP.construct || e.op == EXP.blit)
         {
             // Unless we have a simple var assignment, we're
             // only modifying part of the variable. So we need to make sure
@@ -3501,10 +3510,10 @@ public:
             if (exceptionOrCant(e1))
                 return;
 
-            if (e1.op == TOK.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
+            if (e1.op == EXP.index && (cast(IndexExp)e1).e1.type.toBasetype().ty == Taarray)
             {
                 IndexExp ie = cast(IndexExp)e1;
-                assert(ie.e1.op == TOK.assocArrayLiteral);
+                assert(ie.e1.op == EXP.assocArrayLiteral);
                 existingAA = cast(AssocArrayLiteralExp)ie.e1;
                 lastIndex = ie.e2;
             }
@@ -3516,7 +3525,7 @@ public:
         Expression newval = interpretRegion(e.e2, istate);
         if (exceptionOrCant(newval))
             return;
-        if (e.op == TOK.blit && newval.op == TOK.int64)
+        if (e.op == EXP.blit && newval.op == EXP.int64)
         {
             Type tbn = e.type.baseElemOf();
             if (tbn.ty == Tstruct)
@@ -3524,7 +3533,7 @@ public:
                 /* Look for special case of struct being initialized with 0.
                  */
                 newval = e.type.defaultInitLiteral(e.loc);
-                if (newval.op == TOK.error)
+                if (newval.op == EXP.error)
                 {
                     result = CTFEExp.cantexp;
                     return;
@@ -3554,7 +3563,7 @@ public:
             if (e.e1.type.ty != Tpointer)
             {
                 // ~= can create new values (see bug 6052)
-                if (e.op == TOK.concatenateAssign || e.op == TOK.concatenateElemAssign || e.op == TOK.concatenateDcharAssign)
+                if (e.op == EXP.concatenateAssign || e.op == EXP.concatenateElemAssign || e.op == EXP.concatenateDcharAssign)
                 {
                     // We need to dup it and repaint the type. For a dynamic array
                     // we can skip duplication, because it gets copied later anyway.
@@ -3574,10 +3583,10 @@ public:
                 newval = (*fp)(e.loc, e.type, oldval, newval).copy();
             }
             else if (e.e2.type.isintegral() &&
-                     (e.op == TOK.addAssign ||
-                      e.op == TOK.minAssign ||
-                      e.op == TOK.plusPlus ||
-                      e.op == TOK.minusMinus))
+                     (e.op == EXP.addAssign ||
+                      e.op == EXP.minAssign ||
+                      e.op == EXP.plusPlus ||
+                      e.op == EXP.minusMinus))
             {
                 newval = pointerArithmetic(e.loc, e.op, e.type, oldval, newval).copy();
             }
@@ -3612,7 +3621,7 @@ public:
             result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
             return;
         }
-        if (e1.op == TOK.arrayLength)
+        if (e1.op == EXP.arrayLength)
         {
             /* Change the assignment from:
              *  arr.length = n;
@@ -3692,11 +3701,11 @@ public:
 
         /* Block assignment or element-wise assignment.
          */
-        if (e1.op == TOK.slice ||
-            e1.op == TOK.vector ||
-            e1.op == TOK.arrayLiteral ||
-            e1.op == TOK.string_ ||
-            e1.op == TOK.null_ && e1.type.toBasetype().ty == Tarray)
+        if (e1.op == EXP.slice ||
+            e1.op == EXP.vector ||
+            e1.op == EXP.arrayLiteral ||
+            e1.op == EXP.string_ ||
+            e1.op == EXP.null_ && e1.type.toBasetype().ty == Tarray)
         {
             // Note that slice assignments don't support things like ++, so
             // we don't need to remember 'returnValue'.
@@ -3709,8 +3718,8 @@ public:
                 if (auto dve = e1x.isDotVarExp())
                 {
                     auto ex = dve.e1;
-                    auto sle = ex.op == TOK.structLiteral ? (cast(StructLiteralExp)ex)
-                             : ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value
+                    auto sle = ex.op == EXP.structLiteral ? (cast(StructLiteralExp)ex)
+                             : ex.op == EXP.classReference ? (cast(ClassReferenceExp)ex).value
                              : null;
                     auto v = dve.var.isVarDeclaration();
                     if (!sle || !v)
@@ -3745,7 +3754,7 @@ public:
             if (v is v2 || !v.isOverlappedWith(v2))
                 continue;
             auto e = (*sle.elements)[i];
-            if (e.op != TOK.void_)
+            if (e.op != EXP.void_)
                 (*sle.elements)[i] = voidInitLiteral(e.type, v).copy();
         }
     }
@@ -3768,8 +3777,8 @@ public:
              *  e.v = newval
              */
             auto ex = dve.e1;
-            auto sle = ex.op == TOK.structLiteral ? (cast(StructLiteralExp)ex)
-                     : ex.op == TOK.classReference ? (cast(ClassReferenceExp)ex).value
+            auto sle = ex.op == EXP.structLiteral ? (cast(StructLiteralExp)ex)
+                     : ex.op == EXP.classReference ? (cast(ClassReferenceExp)ex).value
                      : null;
             auto v = (cast(DotVarExp)e1).var.isVarDeclaration();
             if (!sle || !v)
@@ -3783,7 +3792,7 @@ public:
                 return CTFEExp.cantexp;
             }
 
-            int fieldi = ex.op == TOK.structLiteral ? findFieldIndexByName(sle.sd, v)
+            int fieldi = ex.op == EXP.structLiteral ? findFieldIndexByName(sle.sd, v)
                        : (cast(ClassReferenceExp)ex).findFieldIndexByName(v);
             if (fieldi == -1)
             {
@@ -3820,7 +3829,7 @@ public:
                 existingSE.setCodeUnit(index, cast(dchar)newval.toInteger());
                 return null;
             }
-            if (aggregate.op != TOK.arrayLiteral)
+            if (aggregate.op != EXP.arrayLiteral)
             {
                 e.error("index assignment `%s` is not yet supported in CTFE ", e.toChars());
                 return CTFEExp.cantexp;
@@ -3848,7 +3857,7 @@ public:
         if (auto ve = newval.isVectorExp())
         {
             // Ensure ve is an array literal, and not a broadcast
-            if (ve.e1.op == TOK.int64 || ve.e1.op == TOK.float64) // if broadcast
+            if (ve.e1.op == EXP.int64 || ve.e1.op == EXP.float64) // if broadcast
             {
                 UnionExp ue = void;
                 Expression ex = interpretVectorToArray(&ue, ve);
@@ -3856,13 +3865,13 @@ public:
             }
         }
 
-        if (newval.op == TOK.structLiteral && oldval)
+        if (newval.op == EXP.structLiteral && oldval)
         {
-            assert(oldval.op == TOK.structLiteral || oldval.op == TOK.arrayLiteral || oldval.op == TOK.string_);
+            assert(oldval.op == EXP.structLiteral || oldval.op == EXP.arrayLiteral || oldval.op == EXP.string_);
             newval = copyLiteral(newval).copy();
             assignInPlace(oldval, newval);
         }
-        else if (wantCopy && e.op == TOK.assign)
+        else if (wantCopy && e.op == EXP.assign)
         {
             // Currently postblit/destructor calls on static array are done
             // in the druntime internal functions so they don't appear in AST.
@@ -3878,8 +3887,8 @@ public:
                     return CTFEExp.cantexp;
                 }
             }
-            assert(oldval.op == TOK.arrayLiteral);
-            assert(newval.op == TOK.arrayLiteral);
+            assert(oldval.op == EXP.arrayLiteral);
+            assert(newval.op == EXP.arrayLiteral);
 
             Expressions* oldelems = (cast(ArrayLiteralExp)oldval).elements;
             Expressions* newelems = (cast(ArrayLiteralExp)newval).elements;
@@ -3907,7 +3916,7 @@ public:
             if (wantCopy)
                 newval = copyLiteral(newval).copy();
 
-            if (t1b.ty == Tsarray && e.op == TOK.construct && e.e2.isLvalue())
+            if (t1b.ty == Tsarray && e.op == EXP.construct && e.e2.isLvalue())
             {
                 // https://issues.dlang.org/show_bug.cgi?id=9245
                 if (Expression ex = evaluatePostblit(istate, newval))
@@ -3923,7 +3932,7 @@ public:
             *payload = oldval;
 
         // Blit assignment should return the newly created value.
-        if (e.op == TOK.blit)
+        if (e.op == EXP.blit)
             return oldval;
 
         return null;
@@ -3938,7 +3947,7 @@ public:
      * This could be a slice assignment or a block assignment, and
      * dest could be either an array literal, or a string.
      *
-     * Returns TOK.cantExpression on failure. If there are no errors,
+     * Returns EXP.cantExpression on failure. If there are no errors,
      * it returns aggregate[low..upp], except that as an optimisation,
      * if goal == CTFEGoal.Nothing, it will return NULL
      */
@@ -4024,7 +4033,7 @@ public:
                 lowerbound = 0;
                 upperbound = se.len;
             }
-            else if (e1.op == TOK.null_)
+            else if (e1.op == EXP.null_)
             {
                 lowerbound = 0;
                 upperbound = 0;
@@ -4088,7 +4097,7 @@ public:
                         return CTFEExp.cantexp;
                     }
                 }
-                assert(newval.op != TOK.slice);
+                assert(newval.op != EXP.slice);
             }
             if (auto se = newval.isStringExp())
             {
@@ -4126,7 +4135,7 @@ public:
                 return CTFEExp.cantexp;
             }
 
-            if (newval.op == TOK.slice && !isBlockAssignment)
+            if (newval.op == EXP.slice && !isBlockAssignment)
             {
                 auto se = cast(SliceExp)newval;
                 auto aggr2 = se.e1;
@@ -4142,7 +4151,7 @@ public:
                     // Currently overlapping for struct array is allowed.
                     // The order of elements processing depends on the overlapping.
                     // https://issues.dlang.org/show_bug.cgi?id=14024
-                    assert(aggr2.op == TOK.arrayLiteral);
+                    assert(aggr2.op == EXP.arrayLiteral);
                     Expressions* oldelems = existingAE.elements;
                     Expressions* newelems = (cast(ArrayLiteralExp)aggr2).elements;
 
@@ -4210,9 +4219,9 @@ public:
                 }
                 // no overlapping
                 //length?
-                assert(newval.op != TOK.slice);
+                assert(newval.op != EXP.slice);
             }
-            if (newval.op == TOK.string_ && !isBlockAssignment)
+            if (newval.op == EXP.string_ && !isBlockAssignment)
             {
                 /* Mixed slice: it was initialized as an array literal of chars/integers.
                  * Now a slice of it is being set with a string.
@@ -4220,12 +4229,12 @@ public:
                 sliceAssignArrayLiteralFromString(existingAE, cast(StringExp)newval, cast(size_t)firstIndex);
                 return newval;
             }
-            if (newval.op == TOK.arrayLiteral && !isBlockAssignment)
+            if (newval.op == EXP.arrayLiteral && !isBlockAssignment)
             {
                 Expressions* oldelems = existingAE.elements;
                 Expressions* newelems = (cast(ArrayLiteralExp)newval).elements;
                 Type elemtype = existingAE.type.nextOf();
-                bool needsPostblit = e.op != TOK.blit && e.e2.isLvalue();
+                bool needsPostblit = e.op != EXP.blit && e.e2.isLvalue();
                 foreach (j, newelem; *newelems)
                 {
                     newelem = paintTypeOntoLiteral(elemtype, newelem);
@@ -4265,7 +4274,7 @@ public:
                     bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type);
                     for (size_t k = lwr; k < upr; k++)
                     {
-                        if (!directblk && (*w)[k].op == TOK.arrayLiteral)
+                        if (!directblk && (*w)[k].op == EXP.arrayLiteral)
                         {
                             // Multidimensional array block assign
                             if (Expression ex = assignTo(cast(ArrayLiteralExp)(*w)[k]))
@@ -4303,7 +4312,7 @@ public:
 
             Type tn = newval.type.toBasetype();
             bool wantRef = (tn.ty == Tarray || isAssocArray(tn) || tn.ty == Tclass);
-            bool cow = newval.op != TOK.structLiteral && newval.op != TOK.arrayLiteral && newval.op != TOK.string_;
+            bool cow = newval.op != EXP.structLiteral && newval.op != EXP.arrayLiteral && newval.op != EXP.string_;
             Type tb = tn.baseElemOf();
             StructDeclaration sd = (tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null);
 
@@ -4311,8 +4320,8 @@ public:
             rb.istate = istate;
             rb.newval = newval;
             rb.refCopy = wantRef || cow;
-            rb.needsPostblit = sd && sd.postblit && e.op != TOK.blit && e.e2.isLvalue();
-            rb.needsDtor = sd && sd.dtor && e.op == TOK.assign;
+            rb.needsPostblit = sd && sd.postblit && e.op != EXP.blit && e.e2.isLvalue();
+            rb.needsDtor = sd && sd.dtor && e.op == EXP.assign;
             if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound))
                 return ex;
 
@@ -4338,57 +4347,57 @@ public:
     {
         switch (e.op)
         {
-        case TOK.addAssign:
+        case EXP.addAssign:
             interpretAssignCommon(e, &Add);
             return;
 
-        case TOK.minAssign:
+        case EXP.minAssign:
             interpretAssignCommon(e, &Min);
             return;
 
-        case TOK.concatenateAssign:
-        case TOK.concatenateElemAssign:
-        case TOK.concatenateDcharAssign:
+        case EXP.concatenateAssign:
+        case EXP.concatenateElemAssign:
+        case EXP.concatenateDcharAssign:
             interpretAssignCommon(e, &ctfeCat);
             return;
 
-        case TOK.mulAssign:
+        case EXP.mulAssign:
             interpretAssignCommon(e, &Mul);
             return;
 
-        case TOK.divAssign:
+        case EXP.divAssign:
             interpretAssignCommon(e, &Div);
             return;
 
-        case TOK.modAssign:
+        case EXP.modAssign:
             interpretAssignCommon(e, &Mod);
             return;
 
-        case TOK.leftShiftAssign:
+        case EXP.leftShiftAssign:
             interpretAssignCommon(e, &Shl);
             return;
 
-        case TOK.rightShiftAssign:
+        case EXP.rightShiftAssign:
             interpretAssignCommon(e, &Shr);
             return;
 
-        case TOK.unsignedRightShiftAssign:
+        case EXP.unsignedRightShiftAssign:
             interpretAssignCommon(e, &Ushr);
             return;
 
-        case TOK.andAssign:
+        case EXP.andAssign:
             interpretAssignCommon(e, &And);
             return;
 
-        case TOK.orAssign:
+        case EXP.orAssign:
             interpretAssignCommon(e, &Or);
             return;
 
-        case TOK.xorAssign:
+        case EXP.xorAssign:
             interpretAssignCommon(e, &Xor);
             return;
 
-        case TOK.powAssign:
+        case EXP.powAssign:
             interpretAssignCommon(e, &Pow);
             return;
 
@@ -4403,7 +4412,7 @@ public:
         {
             printf("%s PostExp::interpret() %s\n", e.loc.toChars(), e.toChars());
         }
-        if (e.op == TOK.plusPlus)
+        if (e.op == EXP.plusPlus)
             interpretAssignCommon(e, &Add, 1);
         else
             interpretAssignCommon(e, &Min, 1);
@@ -4421,19 +4430,19 @@ public:
     static int isPointerCmpExp(Expression e, Expression* p1, Expression* p2)
     {
         int ret = 1;
-        while (e.op == TOK.not)
+        while (e.op == EXP.not)
         {
             ret *= -1;
             e = (cast(NotExp)e).e1;
         }
         switch (e.op)
         {
-        case TOK.lessThan:
-        case TOK.lessOrEqual:
+        case EXP.lessThan:
+        case EXP.lessOrEqual:
             ret *= -1;
             goto case; /+ fall through +/
-        case TOK.greaterThan:
-        case TOK.greaterOrEqual:
+        case EXP.greaterThan:
+        case EXP.greaterOrEqual:
             *p1 = (cast(BinExp)e).e1;
             *p2 = (cast(BinExp)e).e2;
             if (!(isPointer((*p1).type) && isPointer((*p2).type)))
@@ -4465,7 +4474,7 @@ public:
      */
     private void interpretFourPointerRelation(UnionExp* pue, BinExp e)
     {
-        assert(e.op == TOK.andAnd || e.op == TOK.orOr);
+        assert(e.op == EXP.andAnd || e.op == EXP.orOr);
 
         /*  It can only be an isInside expression, if both e1 and e2 are
          *  directional pointer comparisons.
@@ -4506,7 +4515,7 @@ public:
         Expression agg1 = getAggregateFromPointer(p1, &ofs1);
         Expression agg2 = getAggregateFromPointer(p2, &ofs2);
 
-        if (!pointToSameMemoryBlock(agg1, agg2) && agg1.op != TOK.null_ && agg2.op != TOK.null_)
+        if (!pointToSameMemoryBlock(agg1, agg2) && agg1.op != EXP.null_ && agg2.op != EXP.null_)
         {
             // Here it is either CANT_INTERPRET,
             // or an IsInside comparison returning false.
@@ -4545,7 +4554,7 @@ public:
                 (dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) && pointToSameMemoryBlock(agg2, agg4)))
             {
                 // it's a legal two-sided comparison
-                emplaceExp!(IntegerExp)(pue, e.loc, (e.op == TOK.andAnd) ? 0 : 1, e.type);
+                emplaceExp!(IntegerExp)(pue, e.loc, (e.op == EXP.andAnd) ? 0 : 1, e.type);
                 result = pue.exp();
                 return;
             }
@@ -4582,29 +4591,29 @@ public:
          * Returns:
          *      negate operator
          */
-        static TOK negateRelation(TOK op) pure
+        static EXP negateRelation(EXP op) pure
         {
             switch (op)
             {
-                case TOK.greaterOrEqual:  op = TOK.lessThan;       break;
-                case TOK.greaterThan:     op = TOK.lessOrEqual;    break;
-                case TOK.lessOrEqual:     op = TOK.greaterThan;    break;
-                case TOK.lessThan:        op = TOK.greaterOrEqual; break;
+                case EXP.greaterOrEqual:  op = EXP.lessThan;       break;
+                case EXP.greaterThan:     op = EXP.lessOrEqual;    break;
+                case EXP.lessOrEqual:     op = EXP.greaterThan;    break;
+                case EXP.lessThan:        op = EXP.greaterOrEqual; break;
                 default:                  assert(0);
             }
             return op;
         }
 
-        const TOK cmpop = nott ? negateRelation(ex.op) : ex.op;
+        const EXP cmpop = nott ? negateRelation(ex.op) : ex.op;
         const cmp = comparePointers(cmpop, agg1, ofs1, agg2, ofs2);
         // We already know this is a valid comparison.
         assert(cmp >= 0);
-        if (e.op == TOK.andAnd && cmp == 1 || e.op == TOK.orOr && cmp == 0)
+        if (e.op == EXP.andAnd && cmp == 1 || e.op == EXP.orOr && cmp == 0)
         {
             result = interpret(pue, e.e2, istate);
             return;
         }
-        emplaceExp!(IntegerExp)(pue, e.loc, (e.op == TOK.andAnd) ? 0 : 1, e.type);
+        emplaceExp!(IntegerExp)(pue, e.loc, (e.op == EXP.andAnd) ? 0 : 1, e.type);
         result = pue.exp();
     }
 
@@ -4625,22 +4634,22 @@ public:
             return;
 
         bool res;
-        const andand = e.op == TOK.andAnd;
-        if (andand ? result.isBool(false) : isTrueBool(result))
+        const andand = e.op == EXP.andAnd;
+        if (andand ? result.toBool().hasValue(false) : isTrueBool(result))
             res = !andand;
-        else if (andand ? isTrueBool(result) : result.isBool(false))
+        else if (andand ? isTrueBool(result) : result.toBool().hasValue(false))
         {
             UnionExp ue2 = void;
             result = interpret(&ue2, e.e2, istate);
             if (exceptionOrCant(result))
                 return;
-            if (result.op == TOK.voidExpression)
+            if (result.op == EXP.voidExpression)
             {
                 assert(e.type.ty == Tvoid);
                 result = null;
                 return;
             }
-            if (result.isBool(false))
+            if (result.toBool().hasValue(false))
                 res = false;
             else if (isTrueBool(result))
                 res = true;
@@ -4762,8 +4771,8 @@ public:
                 if (auto ce = ea.isCastExp())
                     ea = ce.e1;
 
-                // printf("2 ea = %s, %s %s\n", ea.type.toChars(), Token.toChars(ea.op), ea.toChars());
-                if (ea.op == TOK.variable || ea.op == TOK.symbolOffset)
+                // printf("2 ea = %s, %s %s\n", ea.type.toChars(), EXPtoString(ea.op).ptr, ea.toChars());
+                if (ea.op == EXP.variable || ea.op == EXP.symbolOffset)
                     result = getVarExp(e.loc, istate, (cast(SymbolExp)ea).var, CTFEGoal.RValue);
                 else if (auto ae = ea.isAddrExp())
                     result = interpretRegion(ae.e1, istate);
@@ -4805,6 +4814,29 @@ public:
                 result = interpretRegion(ae, istate);
                 return;
             }
+            else if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor)
+            {
+                // In expressionsem.d `T[x] ea = eb;` was lowered to `_d_array{,set}ctor(ea[], eb[]);`.
+                // The following code will rewrite it back to `ea = eb` and then interpret that expression.
+                if (fd.ident == Id._d_arraysetctor)
+                    assert(e.arguments.dim == 2);
+                else
+                    assert(e.arguments.dim == 3);
+
+                Expression ea = (*e.arguments)[0];
+                if (ea.isCastExp)
+                    ea = ea.isCastExp.e1;
+
+                Expression eb = (*e.arguments)[1];
+                if (eb.isCastExp && fd.ident == Id._d_arrayctor)
+                    eb = eb.isCastExp.e1;
+
+                ConstructExp ce = new ConstructExp(e.loc, ea, eb);
+                ce.type = ea.type;
+
+                result = interpret(ce, istate);
+                return;
+            }
         }
         else if (auto soe = ecall.isSymOffExp())
         {
@@ -4848,7 +4880,7 @@ public:
             // Currently this is satisfied because closure is not yet supported.
             assert(!fd.isNested() || fd.needThis());
 
-            if (pthis.op == TOK.typeid_)
+            if (pthis.op == EXP.typeid_)
             {
                 pthis.error("static variable `%s` cannot be read at compile time", pthis.toChars());
                 result = CTFEExp.cantexp;
@@ -4856,7 +4888,7 @@ public:
             }
             assert(pthis);
 
-            if (pthis.op == TOK.null_)
+            if (pthis.op == EXP.null_)
             {
                 assert(pthis.type.toBasetype().ty == Tclass);
                 e.error("function call through null class reference `%s`", pthis.toChars());
@@ -4864,7 +4896,7 @@ public:
                 return;
             }
 
-            assert(pthis.op == TOK.structLiteral || pthis.op == TOK.classReference || pthis.op == TOK.type);
+            assert(pthis.op == EXP.structLiteral || pthis.op == EXP.classReference || pthis.op == EXP.type);
 
             if (fd.isVirtual() && !e.directcall)
             {
@@ -4899,7 +4931,7 @@ public:
         }
 
         result = interpretFunction(pue, fd, istate, e.arguments, pthis);
-        if (result.op == TOK.voidExpression)
+        if (result.op == EXP.voidExpression)
             return;
         if (!exceptionOrCantInterpret(result))
         {
@@ -4929,7 +4961,7 @@ public:
         // If it creates a variable, and there's no context for
         // the variable to be created in, we need to create one now.
         InterState istateComma;
-        if (!istate && firstComma(e.e1).op == TOK.declaration)
+        if (!istate && firstComma(e.e1).op == EXP.declaration)
         {
             ctfeGlobals.stack.startFrame(null);
             istate = &istateComma;
@@ -4946,8 +4978,8 @@ public:
 
         // If the comma returns a temporary variable, it needs to be an lvalue
         // (this is particularly important for struct constructors)
-        if (e.e1.op == TOK.declaration &&
-            e.e2.op == TOK.variable &&
+        if (e.e1.op == EXP.declaration &&
+            e.e2.op == EXP.variable &&
             e.e1.isDeclarationExp().declaration == e.e2.isVarExp().var &&
             e.e2.isVarExp().var.storage_class & STC.ctfe)
         {
@@ -4967,7 +4999,7 @@ public:
                 newval = interpretRegion(newval, istate);
                 if (exceptionOrCant(newval))
                     return endTempStackFrame();
-                if (newval.op != TOK.voidExpression)
+                if (newval.op != EXP.voidExpression)
                 {
                     // v isn't necessarily null.
                     setValueWithoutChecking(v, copyLiteral(newval).copy());
@@ -4999,7 +5031,7 @@ public:
 
         if (isPointer(e.econd.type))
         {
-            if (econd.op != TOK.null_)
+            if (econd.op != EXP.null_)
             {
                 econd = IntegerExp.createBool(true);
             }
@@ -5010,7 +5042,7 @@ public:
             result = interpret(pue, e.e1, istate, goal);
             incUsageCtfe(istate, e.e1.loc);
         }
-        else if (econd.isBool(false))
+        else if (econd.toBool().hasValue(false))
         {
             result = interpret(pue, e.e2, istate, goal);
             incUsageCtfe(istate, e.e2.loc);
@@ -5033,7 +5065,7 @@ public:
         assert(e1);
         if (exceptionOrCant(e1))
             return;
-        if (e1.op != TOK.string_ && e1.op != TOK.arrayLiteral && e1.op != TOK.slice && e1.op != TOK.null_)
+        if (e1.op != EXP.string_ && e1.op != EXP.arrayLiteral && e1.op != EXP.slice && e1.op != EXP.null_)
         {
             e.error("`%s` cannot be evaluated at compile time", e.toChars());
             result = CTFEExp.cantexp;
@@ -5055,7 +5087,7 @@ public:
     {
         if (auto ale = e.e1.isArrayLiteralExp())
             return ale;         // it's already an array literal
-        if (e.e1.op == TOK.int64 || e.e1.op == TOK.float64)
+        if (e.e1.op == EXP.int64 || e.e1.op == EXP.float64)
         {
             // Convert literal __vector(int) -> __vector([array])
             auto elements = new Expressions(e.dim);
@@ -5086,7 +5118,7 @@ public:
         assert(e1);
         if (exceptionOrCant(e1))
             return;
-        if (e1.op != TOK.arrayLiteral && e1.op != TOK.int64 && e1.op != TOK.float64)
+        if (e1.op != EXP.arrayLiteral && e1.op != EXP.int64 && e1.op != EXP.float64)
         {
             e.error("`%s` cannot be evaluated at compile time", e.toChars());
             result = CTFEExp.cantexp;
@@ -5115,7 +5147,7 @@ public:
         if (auto ve = e1.isVectorExp())
         {
             result = interpretVectorToArray(pue, ve);
-            if (result.op != TOK.vector)
+            if (result.op != EXP.vector)
                 return;
         }
         e.error("`%s` cannot be evaluated at compile time", e.toChars());
@@ -5169,24 +5201,24 @@ public:
             dinteger_t ofs;
             Expression agg = getAggregateFromPointer(e1, &ofs);
 
-            if (agg.op == TOK.null_)
+            if (agg.op == EXP.null_)
             {
                 e.error("cannot index through null pointer `%s`", e.e1.toChars());
                 return false;
             }
-            if (agg.op == TOK.int64)
+            if (agg.op == EXP.int64)
             {
                 e.error("cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
                 return false;
             }
             // Pointer to a non-array variable
-            if (agg.op == TOK.symbolOffset)
+            if (agg.op == EXP.symbolOffset)
             {
                 e.error("mutable variable `%s` cannot be %s at compile time, even through a pointer", cast(char*)(modify ? "modified" : "read"), (cast(SymOffExp)agg).var.toChars());
                 return false;
             }
 
-            if (agg.op == TOK.arrayLiteral || agg.op == TOK.string_)
+            if (agg.op == EXP.arrayLiteral || agg.op == EXP.string_)
             {
                 dinteger_t len = resolveArrayLength(agg);
                 if (ofs + indx >= len)
@@ -5211,7 +5243,7 @@ public:
         Expression e1 = interpretRegion(e.e1, istate);
         if (exceptionOrCantInterpret(e1))
             return false;
-        if (e1.op == TOK.null_)
+        if (e1.op == EXP.null_)
         {
             e.error("cannot index null array `%s`", e.e1.toChars());
             return false;
@@ -5225,11 +5257,11 @@ public:
 
         // Set the $ variable, and find the array literal to modify
         dinteger_t len;
-        if (e1.op == TOK.variable && e1.type.toBasetype().ty == Tsarray)
+        if (e1.op == EXP.variable && e1.type.toBasetype().ty == Tsarray)
             len = e1.type.toBasetype().isTypeSArray().dim.toInteger();
         else
         {
-            if (e1.op != TOK.arrayLiteral && e1.op != TOK.string_ && e1.op != TOK.slice && e1.op != TOK.vector)
+            if (e1.op != EXP.arrayLiteral && e1.op != EXP.string_ && e1.op != EXP.slice && e1.op != EXP.vector)
             {
                 e.error("cannot determine length of `%s` at compile time", e.e1.toChars());
                 return false;
@@ -5248,7 +5280,7 @@ public:
             ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside []
         if (exceptionOrCantInterpret(e2))
             return false;
-        if (e2.op != TOK.int64)
+        if (e2.op != EXP.int64)
         {
             e.error("CTFE internal error: non-integral index `[%s]`", e.e2.toChars());
             return false;
@@ -5297,7 +5329,7 @@ public:
                 result = CTFEExp.cantexp;
                 return;
             }
-            if (agg.op == TOK.arrayLiteral || agg.op == TOK.string_)
+            if (agg.op == EXP.arrayLiteral || agg.op == EXP.string_)
             {
                 if (goal == CTFEGoal.LValue)
                 {
@@ -5327,7 +5359,7 @@ public:
             Expression e1 = interpretRegion(e.e1, istate);
             if (exceptionOrCant(e1))
                 return;
-            if (e1.op == TOK.null_)
+            if (e1.op == EXP.null_)
             {
                 if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable)
                 {
@@ -5355,7 +5387,7 @@ public:
                 return;
             }
 
-            assert(e1.op == TOK.assocArrayLiteral);
+            assert(e1.op == EXP.assocArrayLiteral);
             UnionExp e2tmp = void;
             e2 = resolveSlice(e2, &e2tmp);
             result = findKeyInAA(e.loc, cast(AssocArrayLiteralExp)e1, e2);
@@ -5387,7 +5419,7 @@ public:
         result = ctfeIndex(pue, e.loc, e.type, agg, indexToAccess);
         if (exceptionOrCant(result))
             return;
-        if (result.op == TOK.void_)
+        if (result.op == EXP.void_)
         {
             e.error("`%s` is used before initialized", e.toChars());
             errorSupplemental(result.loc, "originally uninitialized here");
@@ -5410,7 +5442,7 @@ public:
             Expression e1 = interpretRegion(e.e1, istate);
             if (exceptionOrCant(e1))
                 return;
-            if (e1.op == TOK.int64)
+            if (e1.op == EXP.int64)
             {
                 e.error("cannot slice invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
                 result = CTFEExp.cantexp;
@@ -5432,7 +5464,7 @@ public:
             Expression agg = getAggregateFromPointer(e1, &ofs);
             ilwr += ofs;
             iupr += ofs;
-            if (agg.op == TOK.null_)
+            if (agg.op == EXP.null_)
             {
                 if (iupr == ilwr)
                 {
@@ -5444,19 +5476,19 @@ public:
                 result = CTFEExp.cantexp;
                 return;
             }
-            if (agg.op == TOK.symbolOffset)
+            if (agg.op == EXP.symbolOffset)
             {
                 e.error("slicing pointers to static variables is not supported in CTFE");
                 result = CTFEExp.cantexp;
                 return;
             }
-            if (agg.op != TOK.arrayLiteral && agg.op != TOK.string_)
+            if (agg.op != EXP.arrayLiteral && agg.op != EXP.string_)
             {
                 e.error("pointer `%s` cannot be sliced at compile time (it does not point to an array)", e.e1.toChars());
                 result = CTFEExp.cantexp;
                 return;
             }
-            assert(agg.op == TOK.arrayLiteral || agg.op == TOK.string_);
+            assert(agg.op == EXP.arrayLiteral || agg.op == EXP.string_);
             dinteger_t len = ArrayLength(Type.tsize_t, agg).exp().toInteger();
             //Type *pointee = ((TypePointer *)agg.type)->next;
             if (iupr > (len + 1) || iupr < ilwr)
@@ -5503,11 +5535,11 @@ public:
         /* Set dollar to the length of the array
          */
         uinteger_t dollar;
-        if ((e1.op == TOK.variable || e1.op == TOK.dotVariable) && e1.type.toBasetype().ty == Tsarray)
+        if ((e1.op == EXP.variable || e1.op == EXP.dotVariable) && e1.type.toBasetype().ty == Tsarray)
             dollar = e1.type.toBasetype().isTypeSArray().dim.toInteger();
         else
         {
-            if (e1.op != TOK.arrayLiteral && e1.op != TOK.string_ && e1.op != TOK.null_ && e1.op != TOK.slice && e1.op != TOK.vector)
+            if (e1.op != EXP.arrayLiteral && e1.op != EXP.string_ && e1.op != EXP.null_ && e1.op != EXP.slice && e1.op != EXP.vector)
             {
                 e.error("cannot determine length of `%s` at compile time", e1.toChars());
                 result = CTFEExp.cantexp;
@@ -5546,7 +5578,7 @@ public:
 
         uinteger_t ilwr = lwr.toInteger();
         uinteger_t iupr = upr.toInteger();
-        if (e1.op == TOK.null_)
+        if (e1.op == EXP.null_)
         {
             if (ilwr == 0 && iupr == 0)
             {
@@ -5578,7 +5610,7 @@ public:
             result.type = e.type;
             return;
         }
-        if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_)
+        if (e1.op == EXP.arrayLiteral || e1.op == EXP.string_)
         {
             if (iupr < ilwr || dollar < iupr)
             {
@@ -5604,13 +5636,13 @@ public:
         Expression e2 = interpretRegion(e.e2, istate);
         if (exceptionOrCant(e2))
             return;
-        if (e2.op == TOK.null_)
+        if (e2.op == EXP.null_)
         {
             emplaceExp!(NullExp)(pue, e.loc, e.type);
             result = pue.exp();
             return;
         }
-        if (e2.op != TOK.assocArrayLiteral)
+        if (e2.op != EXP.assocArrayLiteral)
         {
             e.error("`%s` cannot be interpreted at compile time", e.toChars());
             result = CTFEExp.cantexp;
@@ -5663,7 +5695,7 @@ public:
          * result in [x,y] and then x or y is on the stack.
          * But if they are both strings, we can, because it isn't the x~[y] case.
          */
-        if (!(e1.op == TOK.string_ && e2.op == TOK.string_))
+        if (!(e1.op == EXP.string_ && e2.op == EXP.string_))
         {
             if (e1 == ue1.exp())
                 e1 = ue1.copy();
@@ -5706,7 +5738,7 @@ public:
         if (exceptionOrCant(result))
             return;
 
-        if (result.op == TOK.null_)
+        if (result.op == EXP.null_)
         {
             result = CTFEExp.voidexp;
             return;
@@ -5716,7 +5748,7 @@ public:
         switch (tb.ty)
         {
         case Tclass:
-            if (result.op != TOK.classReference)
+            if (result.op != EXP.classReference)
             {
                 e.error("`delete` on invalid class reference `%s`", result.toChars());
                 result = CTFEExp.cantexp;
@@ -5752,8 +5784,8 @@ public:
             tb = (cast(TypePointer)tb).next.toBasetype();
             if (tb.ty == Tstruct)
             {
-                if (result.op != TOK.address ||
-                    (cast(AddrExp)result).e1.op != TOK.structLiteral)
+                if (result.op != EXP.address ||
+                    (cast(AddrExp)result).e1.op != EXP.structLiteral)
                 {
                     e.error("`delete` on invalid struct pointer `%s`", result.toChars());
                     result = CTFEExp.cantexp;
@@ -5776,7 +5808,7 @@ public:
             auto tv = tb.nextOf().baseElemOf();
             if (tv.ty == Tstruct)
             {
-                if (result.op != TOK.arrayLiteral)
+                if (result.op != EXP.arrayLiteral)
                 {
                     e.error("`delete` on invalid struct array `%s`", result.toChars());
                     result = CTFEExp.cantexp;
@@ -5819,11 +5851,11 @@ public:
             result = CTFEExp.voidexp;
             return;
         }
-        if (e.to.ty == Tpointer && e1.op != TOK.null_)
+        if (e.to.ty == Tpointer && e1.op != EXP.null_)
         {
             Type pointee = (cast(TypePointer)e.type).next;
             // Implement special cases of normally-unsafe casts
-            if (e1.op == TOK.int64)
+            if (e1.op == EXP.int64)
             {
                 // Happens with Windows HANDLEs, for example.
                 result = paintTypeOntoLiteral(pue, e.to, e1);
@@ -5869,7 +5901,7 @@ public:
 
             if (auto se = e1.isSliceExp())
             {
-                if (se.e1.op == TOK.null_)
+                if (se.e1.op == EXP.null_)
                 {
                     result = paintTypeOntoLiteral(pue, e.type, se.e1);
                     return;
@@ -5881,7 +5913,7 @@ public:
                 result = pue.exp();
                 return;
             }
-            if (e1.op == TOK.arrayLiteral || e1.op == TOK.string_)
+            if (e1.op == EXP.arrayLiteral || e1.op == EXP.string_)
             {
                 // Create a CTFE pointer &[1,2,3][0] or &"abc"[0]
                 auto ei = ctfeEmplaceExp!IndexExp(e.loc, e1, ctfeEmplaceExp!IntegerExp(e.loc, 0, Type.tsize_t));
@@ -5890,7 +5922,7 @@ public:
                 result = pue.exp();
                 return;
             }
-            if (e1.op == TOK.index && !(cast(IndexExp)e1).e1.type.equals(e1.type))
+            if (e1.op == EXP.index && !(cast(IndexExp)e1).e1.type.equals(e1.type))
             {
                 // type painting operation
                 IndexExp ie = cast(IndexExp)e1;
@@ -5899,7 +5931,7 @@ public:
                     // get the original type. For strings, it's just the type...
                     Type origType = ie.e1.type.nextOf();
                     // ..but for arrays of type void*, it's the type of the element
-                    if (ie.e1.op == TOK.arrayLiteral && ie.e2.op == TOK.int64)
+                    if (ie.e1.op == EXP.arrayLiteral && ie.e2.op == EXP.int64)
                     {
                         ArrayLiteralExp ale = cast(ArrayLiteralExp)ie.e1;
                         const indx = cast(size_t)ie.e2.toInteger();
@@ -5939,7 +5971,7 @@ public:
                     return;
                 }
 
-                if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && ae.e1.op == TOK.index)
+                if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && ae.e1.op == EXP.index)
                 {
                     // &val[idx]
                     dinteger_t dim = (cast(TypeSArray)pointee.toBasetype()).dim.toInteger();
@@ -5956,7 +5988,7 @@ public:
                 }
             }
 
-            if (e1.op == TOK.variable || e1.op == TOK.symbolOffset)
+            if (e1.op == EXP.variable || e1.op == EXP.symbolOffset)
             {
                 // type painting operation
                 Type origType = (cast(SymbolExp)e1).var.type;
@@ -5977,7 +6009,7 @@ public:
 
             // Check if we have a null pointer (eg, inside a struct)
             e1 = interpretRegion(e1, istate);
-            if (e1.op != TOK.null_)
+            if (e1.op != EXP.null_)
             {
                 e.error("pointer cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars());
                 result = CTFEExp.cantexp;
@@ -5990,10 +6022,10 @@ public:
             e1 = interpretRegion(e.e1, istate);
             if (exceptionOrCant(e1))
                 return;
-            assert(e1.op == TOK.vector);
+            assert(e1.op == EXP.vector);
             e1 = interpretVectorToArray(pue, e1.isVectorExp());
         }
-        if (e.to.ty == Tarray && e1.op == TOK.slice)
+        if (e.to.ty == Tarray && e1.op == EXP.slice)
         {
             // Note that the slice may be void[], so when checking for dangerous
             // casts, we need to use the original type, which is se.e1.
@@ -6023,11 +6055,11 @@ public:
         auto tobt = e.to.toBasetype();
         if (tobt.ty == Tbool && e1.type.ty == Tpointer)
         {
-            emplaceExp!(IntegerExp)(pue, e.loc, e1.op != TOK.null_, e.to);
+            emplaceExp!(IntegerExp)(pue, e.loc, e1.op != EXP.null_, e.to);
             result = pue.exp();
             return;
         }
-        else if (tobt.isTypeBasic() && e1.op == TOK.null_)
+        else if (tobt.isTypeBasic() && e1.op == EXP.null_)
         {
             if (tobt.isintegral())
                 emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to);
@@ -6051,7 +6083,7 @@ public:
         if (isTrueBool(e1))
         {
         }
-        else if (e1.isBool(false))
+        else if (e1.toBool().hasValue(false))
         {
             if (e.msg)
             {
@@ -6106,7 +6138,7 @@ public:
         // Constant fold *(&structliteral + offset)
         if (auto ae = e.e1.isAddExp())
         {
-            if (ae.e1.op == TOK.address && ae.e2.op == TOK.int64)
+            if (ae.e1.op == EXP.address && ae.e2.op == EXP.int64)
             {
                 AddrExp ade = cast(AddrExp)ae.e1;
                 Expression ex = interpretRegion(ade.e1, istate);
@@ -6128,7 +6160,7 @@ public:
         if (exceptionOrCant(result))
             return;
 
-        if (result.op == TOK.function_)
+        if (result.op == EXP.function_)
             return;
         if (auto soe = result.isSymOffExp())
         {
@@ -6142,9 +6174,9 @@ public:
         if (result.isStringExp())
             return;
 
-        if (result.op != TOK.address)
+        if (result.op != EXP.address)
         {
-            if (result.op == TOK.null_)
+            if (result.op == EXP.null_)
                 e.error("dereference of null pointer `%s`", e.e1.toChars());
             else
                 e.error("dereference of invalid pointer `%s`", result.toChars());
@@ -6155,7 +6187,7 @@ public:
         // *(&x) ==> x
         result = (cast(AddrExp)result).e1;
 
-        if (result.op == TOK.slice && e.type.toBasetype().ty == Tsarray)
+        if (result.op == EXP.slice && e.type.toBasetype().ty == Tsarray)
         {
             /* aggr[lwr..upr]
              * upr may exceed the upper boundary of aggr, but the check is deferred
@@ -6212,7 +6244,7 @@ public:
             return;
         }
 
-        if (ex.op == TOK.null_)
+        if (ex.op == EXP.null_)
         {
             if (ex.type.toBasetype().ty == Tclass)
                 e.error("class `%s` is `null` and cannot be dereferenced", e.e1.toChars());
@@ -6225,18 +6257,18 @@ public:
         StructLiteralExp se;
         int i;
 
-        if (ex.op != TOK.structLiteral && ex.op != TOK.classReference && ex.op != TOK.typeid_)
+        if (ex.op != EXP.structLiteral && ex.op != EXP.classReference && ex.op != EXP.typeid_)
         {
             return notImplementedYet();
         }
 
         // We can't use getField, because it makes a copy
-        if (ex.op == TOK.classReference)
+        if (ex.op == EXP.classReference)
         {
             se = (cast(ClassReferenceExp)ex).value;
             i = (cast(ClassReferenceExp)ex).findFieldIndexByName(v);
         }
-        else if (ex.op == TOK.typeid_)
+        else if (ex.op == EXP.typeid_)
         {
             if (v.ident == Identifier.idPool("name"))
             {
@@ -6336,7 +6368,7 @@ public:
         Expression index = interpret(e.e2, istate);
         if (exceptionOrCant(index))
             return;
-        if (agg.op == TOK.null_)
+        if (agg.op == EXP.null_)
         {
             result = CTFEExp.voidexp;
             return;
@@ -6349,7 +6381,7 @@ public:
         foreach (j, evalue; *valuesx)
         {
             Expression ekey = (*keysx)[j];
-            int eq = ctfeEqual(e.loc, TOK.equal, ekey, index);
+            int eq = ctfeEqual(e.loc, EXP.equal, ekey, index);
             if (eq)
                 ++removed;
             else if (removed != 0)
@@ -6435,11 +6467,11 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF
     // mimicking UnionExp.copy, but with region allocation
     switch (uexp.op)
     {
-        case TOK.cantExpression: return CTFEExp.cantexp;
-        case TOK.voidExpression: return CTFEExp.voidexp;
-        case TOK.break_:         return CTFEExp.breakexp;
-        case TOK.continue_:      return CTFEExp.continueexp;
-        case TOK.goto_:          return CTFEExp.gotoexp;
+        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:                 break;
     }
     auto p = ctfeGlobals.region.malloc(uexp.size);
@@ -6454,7 +6486,7 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF
  *    istate = context
  * Returns:
  *      NULL    continue to next statement
- *      TOK.cantExpression      cannot interpret statement at compile time
+ *      EXP.cantExpression      cannot interpret statement at compile time
  *      !NULL   expression from return statement, or thrown exception
  */
 Expression interpret(UnionExp* pue, Statement s, InterState* istate)
@@ -6490,7 +6522,7 @@ private Expression scrubReturnValue(const ref Loc loc, Expression e)
      */
     static bool isVoid(const Expression e, bool checkArrayType = false) pure
     {
-        if (e.op == TOK.void_)
+        if (e.op == EXP.void_)
             return true;
 
         static bool isEntirelyVoid(const Expressions* elems)
@@ -6539,7 +6571,7 @@ private Expression scrubReturnValue(const ref Loc loc, Expression e)
             else
             {
                 e = scrubReturnValue(loc, e);
-                if (CTFEExp.isCantExp(e) || e.op == TOK.error)
+                if (CTFEExp.isCantExp(e) || e.op == EXP.error)
                     return e;
             }
         }
@@ -6560,7 +6592,7 @@ private Expression scrubReturnValue(const ref Loc loc, Expression e)
         return null;
     }
 
-    if (e.op == TOK.classReference)
+    if (e.op == EXP.classReference)
     {
         StructLiteralExp sle = (cast(ClassReferenceExp)e).value;
         if (auto ex = scrubSE(sle))
@@ -6640,7 +6672,7 @@ private Expression scrubCacheValue(Expression e)
         return null;
     }
 
-    if (e.op == TOK.classReference)
+    if (e.op == EXP.classReference)
     {
         if (auto ex = scrubSE((cast(ClassReferenceExp)e).value))
             return ex;
@@ -6717,14 +6749,14 @@ private Expression copyRegionExp(Expression e)
 
     switch (e.op)
     {
-        case TOK.classReference:
+        case EXP.classReference:
         {
             auto cre = e.isClassReferenceExp();
             cre.value = copyRegionExp(cre.value).isStructLiteralExp();
             break;
         }
 
-        case TOK.structLiteral:
+        case EXP.structLiteral:
         {
             auto sle = e.isStructLiteralExp();
 
@@ -6754,7 +6786,7 @@ private Expression copyRegionExp(Expression e)
             return slec;
         }
 
-        case TOK.arrayLiteral:
+        case EXP.arrayLiteral:
         {
             auto ale = e.isArrayLiteralExp();
             ale.basis = copyRegionExp(ale.basis);
@@ -6762,12 +6794,12 @@ private Expression copyRegionExp(Expression e)
             break;
         }
 
-        case TOK.assocArrayLiteral:
+        case EXP.assocArrayLiteral:
             copyArray(e.isAssocArrayLiteralExp().keys);
             copyArray(e.isAssocArrayLiteralExp().values);
             break;
 
-        case TOK.slice:
+        case EXP.slice:
         {
             auto se = e.isSliceExp();
             se.e1  = copyRegionExp(se.e1);
@@ -6776,7 +6808,7 @@ private Expression copyRegionExp(Expression e)
             break;
         }
 
-        case TOK.tuple:
+        case EXP.tuple:
         {
             auto te = e.isTupleExp();
             te.e0 = copyRegionExp(te.e0);
@@ -6784,17 +6816,17 @@ private Expression copyRegionExp(Expression e)
             break;
         }
 
-        case TOK.address:
-        case TOK.delegate_:
-        case TOK.vector:
-        case TOK.dotVariable:
+        case EXP.address:
+        case EXP.delegate_:
+        case EXP.vector:
+        case EXP.dotVariable:
         {
             UnaExp ue = cast(UnaExp)e;
             ue.e1 = copyRegionExp(ue.e1);
             break;
         }
 
-        case TOK.index:
+        case EXP.index:
         {
             BinExp be = cast(BinExp)e;
             be.e1 = copyRegionExp(be.e1);
@@ -6802,30 +6834,30 @@ private Expression copyRegionExp(Expression e)
             break;
         }
 
-        case TOK.this_:
-        case TOK.super_:
-        case TOK.variable:
-        case TOK.type:
-        case TOK.function_:
-        case TOK.typeid_:
-        case TOK.string_:
-        case TOK.int64:
-        case TOK.error:
-        case TOK.float64:
-        case TOK.complex80:
-        case TOK.null_:
-        case TOK.void_:
-        case TOK.symbolOffset:
-        case TOK.char_:
+        case EXP.this_:
+        case EXP.super_:
+        case EXP.variable:
+        case EXP.type:
+        case EXP.function_:
+        case EXP.typeid_:
+        case EXP.string_:
+        case EXP.int64:
+        case EXP.error:
+        case EXP.float64:
+        case EXP.complex80:
+        case EXP.null_:
+        case EXP.void_:
+        case EXP.symbolOffset:
+        case EXP.char_:
             break;
 
-        case TOK.cantExpression:
-        case TOK.voidExpression:
-        case TOK.showCtfeContext:
+        case EXP.cantExpression:
+        case EXP.voidExpression:
+        case EXP.showCtfeContext:
             return e;
 
         default:
-            printf("e: %s, %s\n", Token.toChars(e.op), e.toChars());
+            printf("e: %s, %s\n", EXPtoString(e.op).ptr, e.toChars());
             assert(0);
     }
 
@@ -6848,7 +6880,7 @@ private Expression interpret_length(UnionExp* pue, InterState* istate, Expressio
     if (auto aae = earg.isAssocArrayLiteralExp())
         len = aae.keys.dim;
     else
-        assert(earg.op == TOK.null_);
+        assert(earg.op == EXP.null_);
     emplaceExp!(IntegerExp)(pue, earg.loc, len, Type.tsize_t);
     return pue.exp();
 }
@@ -6862,12 +6894,12 @@ private Expression interpret_keys(UnionExp* pue, InterState* istate, Expression
     earg = interpret(pue, earg, istate);
     if (exceptionOrCantInterpret(earg))
         return earg;
-    if (earg.op == TOK.null_)
+    if (earg.op == EXP.null_)
     {
         emplaceExp!(NullExp)(pue, earg.loc, earg.type);
         return pue.exp();
     }
-    if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+    if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
         return null;
     AssocArrayLiteralExp aae = earg.isAssocArrayLiteralExp();
     auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.keys);
@@ -6885,12 +6917,12 @@ private Expression interpret_values(UnionExp* pue, InterState* istate, Expressio
     earg = interpret(pue, earg, istate);
     if (exceptionOrCantInterpret(earg))
         return earg;
-    if (earg.op == TOK.null_)
+    if (earg.op == EXP.null_)
     {
         emplaceExp!(NullExp)(pue, earg.loc, earg.type);
         return pue.exp();
     }
-    if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+    if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
         return null;
     auto aae = earg.isAssocArrayLiteralExp();
     auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.values);
@@ -6909,12 +6941,12 @@ private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression e
     earg = interpret(pue, earg, istate);
     if (exceptionOrCantInterpret(earg))
         return earg;
-    if (earg.op == TOK.null_)
+    if (earg.op == EXP.null_)
     {
         emplaceExp!(NullExp)(pue, earg.loc, earg.type);
         return pue.exp();
     }
-    if (earg.op != TOK.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
+    if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
         return null;
     auto aae = copyLiteral(earg).copy().isAssocArrayLiteralExp();
     for (size_t i = 0; i < aae.keys.dim; i++)
@@ -6935,7 +6967,7 @@ private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expressi
     aa = interpret(aa, istate);
     if (exceptionOrCantInterpret(aa))
         return aa;
-    if (aa.op != TOK.assocArrayLiteral)
+    if (aa.op != EXP.assocArrayLiteral)
     {
         emplaceExp!(IntegerExp)(pue, deleg.loc, 0, Type.tsize_t);
         return pue.exp();
@@ -7316,7 +7348,7 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const re
     }
     if (pthis && !fd.fbody && fd.isCtorDeclaration() && fd.parent && fd.parent.parent && fd.parent.parent.ident == Id.object)
     {
-        if (pthis.op == TOK.classReference && fd.parent.ident == Id.Throwable)
+        if (pthis.op == EXP.classReference && fd.parent.ident == Id.Throwable)
         {
             // At present, the constructors just copy their arguments into the struct.
             // But we might need some magic if stack tracing gets added to druntime.
@@ -7383,7 +7415,7 @@ private Expression evaluatePostblit(InterState* istate, Expression e)
         }
         return null;
     }
-    if (e.op == TOK.structLiteral)
+    if (e.op == EXP.structLiteral)
     {
         // e.__postblit()
         UnionExp ue = void;
@@ -7412,7 +7444,7 @@ private Expression evaluateDtor(InterState* istate, Expression e)
         foreach_reverse (elem; *ale.elements)
             e = evaluateDtor(istate, elem);
     }
-    else if (e.op == TOK.structLiteral)
+    else if (e.op == EXP.structLiteral)
     {
         // e.__dtor()
         e = interpretFunction(&ue, sd.dtor, istate, null, e);
diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d
index c417f93c353..065968025a6 100644
--- a/gcc/d/dmd/dmangle.d
+++ b/gcc/d/dmd/dmangle.d
@@ -979,7 +979,7 @@ public:
                     goto Lsa;
                 }
                 buf.writeByte('V');
-                if (ea.op == TOK.tuple)
+                if (ea.op == EXP.tuple)
                 {
                     ea.error("tuple is not a valid template value argument");
                     continue;
@@ -987,7 +987,7 @@ public:
                 // Now that we know it is not an alias, we MUST obtain a value
                 uint olderr = global.errors;
                 ea = ea.ctfeInterpret();
-                if (ea.op == TOK.error || olderr != global.errors)
+                if (ea.op == EXP.error || olderr != global.errors)
                     continue;
 
                 /* Use type mangling that matches what it would be for a function parameter
diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d
index 0925e7cd38a..1f05642d301 100644
--- a/gcc/d/dmd/dstruct.d
+++ b/gcc/d/dmd/dstruct.d
@@ -522,14 +522,14 @@ private bool _isZeroInit(Expression exp)
 {
     switch (exp.op)
     {
-        case TOK.int64:
+        case EXP.int64:
             return exp.toInteger() == 0;
 
-        case TOK.null_:
-        case TOK.false_:
+        case EXP.null_:
+        case EXP.false_:
             return true;
 
-        case TOK.structLiteral:
+        case EXP.structLiteral:
         {
             auto sle = cast(StructLiteralExp) exp;
             foreach (i; 0 .. sle.sd.fields.dim)
@@ -546,7 +546,7 @@ private bool _isZeroInit(Expression exp)
             return true;
         }
 
-        case TOK.arrayLiteral:
+        case EXP.arrayLiteral:
         {
             auto ale = cast(ArrayLiteralExp)exp;
 
@@ -566,7 +566,7 @@ private bool _isZeroInit(Expression exp)
             return true;
         }
 
-        case TOK.string_:
+        case EXP.string_:
         {
             StringExp se = cast(StringExp)exp;
 
@@ -581,14 +581,14 @@ private bool _isZeroInit(Expression exp)
             return true;
         }
 
-        case TOK.vector:
+        case EXP.vector:
         {
             auto ve = cast(VectorExp) exp;
             return _isZeroInit(ve.e1);
         }
 
-        case TOK.float64:
-        case TOK.complex80:
+        case EXP.float64:
+        case EXP.complex80:
         {
             import dmd.root.ctfloat : CTFloat;
             return (exp.toReal()      is CTFloat.zero) &&
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index 9aa435d6463..b1d1b1d8d7c 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -1785,11 +1785,11 @@ extern (C++) final class WithScopeSymbol : ScopeDsymbol
         Expression eold = null;
         for (Expression e = withstate.exp; e != eold; e = resolveAliasThis(_scope, e))
         {
-            if (e.op == TOK.scope_)
+            if (e.op == EXP.scope_)
             {
                 s = (cast(ScopeExp)e).sds;
             }
-            else if (e.op == TOK.type)
+            else if (e.op == EXP.type)
             {
                 s = e.type.toDsymbol(null);
             }
@@ -1826,14 +1826,14 @@ extern (C++) final class WithScopeSymbol : ScopeDsymbol
 extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
 {
     // either a SliceExp, an IndexExp, an ArrayExp, a TypeTuple or a TupleDeclaration.
-    // Discriminated using DYNCAST and, for expressions, also TOK
+    // Discriminated using DYNCAST and, for expressions, also EXP
     private RootObject arrayContent;
     Scope* sc;
 
     extern (D) this(Scope* sc, Expression exp)
     {
         super(exp.loc, null);
-        assert(exp.op == TOK.index || exp.op == TOK.slice || exp.op == TOK.array);
+        assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array);
         this.sc = sc;
         this.arrayContent = exp;
     }
@@ -1950,7 +1950,7 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
             else if (ce.type && (t = ce.type.toBasetype()) !is null && (t.ty == Tstruct || t.ty == Tclass))
             {
                 // Look for opDollar
-                assert(exp.op == TOK.array || exp.op == TOK.slice);
+                assert(exp.op == EXP.array || exp.op == EXP.slice);
                 AggregateDeclaration ad = isAggregate(t);
                 assert(ad);
                 Dsymbol s = ad.search(loc, Id.opDollar);
@@ -1962,11 +1962,11 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
                 if (TemplateDeclaration td = s.isTemplateDeclaration())
                 {
                     dinteger_t dim = 0;
-                    if (exp.op == TOK.array)
+                    if (exp.op == EXP.array)
                     {
                         dim = (cast(ArrayExp)exp).currentDimension;
                     }
-                    else if (exp.op == TOK.slice)
+                    else if (exp.op == EXP.slice)
                     {
                         dim = 0; // slices are currently always one-dimensional
                     }
@@ -1987,7 +1987,7 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
                      * Note that it's impossible to have both template & function opDollar,
                      * because both take no arguments.
                      */
-                    if (exp.op == TOK.array && (cast(ArrayExp)exp).arguments.dim != 1)
+                    if (exp.op == EXP.array && (cast(ArrayExp)exp).arguments.dim != 1)
                     {
                         exp.error("`%s` only defines opDollar for one dimension", ad.toChars());
                         return null;
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 047c1eb6f69..0bf9a80f37d 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -133,7 +133,7 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
         e = e.ctfeInterpret();
         exp = e;                // could be re-evaluated if exps are assigned to more than one AlignDeclaration by CParser.applySpecifier(),
                                 // e.g. `_Alignas(8) int a, b;`
-        if (e.op == TOK.error)
+        if (e.op == EXP.error)
             errors = true;
         else
         {
@@ -611,7 +611,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             }
         Lnomatch:
 
-            if (ie && ie.op == TOK.tuple)
+            if (ie && ie.op == EXP.tuple)
             {
                 auto te = ie.isTupleExp();
                 size_t tedim = te.exps.dim;
@@ -652,6 +652,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     storage_class |= arg.storageClass;
                 auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class);
                 //printf("declaring field %s of type %s\n", v.toChars(), v.type.toChars());
+                v.overlapped = dsym.overlapped;
+
                 v.dsymbolSemantic(sc);
 
                 if (sc.scopesym)
@@ -879,11 +881,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         if ((!dsym._init || dsym._init.isVoidInitializer) && !fd)
         {
             // If not mutable, initializable by constructor only
-            dsym.storage_class |= STC.ctorinit;
+            dsym.setInCtorOnly = true;
         }
 
         if (dsym._init)
-            dsym.storage_class |= STC.init; // remember we had an explicit initializer
+        { } // remember we had an explicit initializer
         else if (dsym.storage_class & STC.manifest)
             dsym.error("manifest constants must have initializers");
 
@@ -1013,7 +1015,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     exp = exp.expressionSemantic(sc);
                     dsym.canassign--;
                     exp = exp.optimize(WANTvalue);
-                    if (exp.op == TOK.error)
+                    if (exp.op == EXP.error)
                     {
                         dsym._init = new ErrorInitializer();
                         ei = null;
@@ -1024,7 +1026,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     if (ei && dsym.isScope())
                     {
                         Expression ex = ei.exp.lastComma();
-                        if (ex.op == TOK.blit || ex.op == TOK.construct)
+                        if (ex.op == EXP.blit || ex.op == EXP.construct)
                             ex = (cast(AssignExp)ex).e2;
                         if (auto ne = ex.isNewExp())
                         {
@@ -1057,7 +1059,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     // Don't run CTFE for the temporary variables inside typeof
                     dsym._init = dsym._init.initializerSemantic(sc, dsym.type, sc.intypeof == 1 ? INITnointerpret : INITinterpret);
                     const init_err = dsym._init.isExpInitializer();
-                    if (init_err && init_err.exp.op == TOK.showCtfeContext)
+                    if (init_err && init_err.exp.op == EXP.showCtfeContext)
                     {
                          errorSupplemental(dsym.loc, "compile time context created here");
                     }
@@ -1085,7 +1087,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     dsym.inuse++;
                     // Bug 20549. Don't try this on modules or packages, syntaxCopy
                     // could crash (inf. recursion) on a mod/pkg referencing itself
-                    if (ei && (ei.exp.op != TOK.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
+                    if (ei && (ei.exp.op != EXP.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
                     {
                         if (ei.exp.type)
                         {
@@ -1241,7 +1243,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             width.error("bit-field `%s` has zero width", dsym.toChars());
             dsym.errors = true;
         }
-        const max_width = dsym.type.size() * 8;
+        const sz = dsym.type.size();
+        if (sz == SIZE_INVALID)
+            dsym.errors = true;
+        const max_width = sz * 8;
         if (uwidth > max_width)
         {
             width.error("width `%lld` of bit-field `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
@@ -1459,7 +1464,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
     override void visit(AnonDeclaration scd)
     {
-        //printf("\tAnonDeclaration::semantic %s %p\n", isunion ? "union" : "struct", this);
+        //printf("\tAnonDeclaration::semantic isunion:%d ptr:%p\n", scd.isunion, scd);
         assert(sc.parent);
         auto p = sc.parent.pastMixin();
         auto ad = p.isAggregateDeclaration();
@@ -1479,6 +1484,11 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             for (size_t i = 0; i < scd.decl.dim; i++)
             {
                 Dsymbol s = (*scd.decl)[i];
+                if (auto var = s.isVarDeclaration)
+                {
+                    if (scd.isunion)
+                        var.overlapped = true;
+                }
                 s.dsymbolSemantic(sc);
             }
             sc = sc.pop();
@@ -1651,7 +1661,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     e = resolveProperties(sc, e);
                     sc = sc.endCTFE();
                     e = ctfeInterpretForPragmaMsg(e);
-                    if (e.op == TOK.error)
+                    if (e.op == EXP.error)
                     {
                         errorSupplemental(pd.loc, "while evaluating `pragma(msg, %s)`", (*pd.args)[i].toChars());
                         return;
@@ -2185,7 +2195,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     e = resolveProperties(sc, e);
                     e = e.integralPromotions(sc);
                     e = e.ctfeInterpret();
-                    if (e.op == TOK.error)
+                    if (e.op == EXP.error)
                         return errorReturn(em);
                     auto ie = e.isIntegerExp();
                     if (!ie)
@@ -2303,7 +2313,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             e = e.expressionSemantic(sc);
             e = resolveProperties(sc, e);
             e = e.ctfeInterpret();
-            if (e.op == TOK.error)
+            if (e.op == EXP.error)
                 return errorReturn();
             if (first && !em.ed.memtype && !em.ed.isAnonymous())
             {
@@ -2331,7 +2341,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                         ev = ev.implicitCastTo(sc, em.ed.memtype);
                         ev = ev.ctfeInterpret();
                         ev = ev.castTo(sc, em.ed.type);
-                        if (ev.op == TOK.error)
+                        if (ev.op == EXP.error)
                             em.ed.errors = true;
                         enm.value = ev;
                     });
@@ -2429,7 +2439,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             // Set value to (eprev + 1).
             // But first check that (eprev != emax)
             assert(eprev);
-            Expression e = new EqualExp(TOK.equal, em.loc, eprev, emax);
+            Expression e = new EqualExp(EXP.equal, em.loc, eprev, emax);
             e = e.expressionSemantic(sc);
             e = e.ctfeInterpret();
             if (e.toInteger())
@@ -2446,7 +2456,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             e = e.ctfeInterpret();
 
             // save origValue (without cast) for better json output
-            if (e.op != TOK.error) // avoid duplicate diagnostics
+            if (e.op != EXP.error) // avoid duplicate diagnostics
             {
                 assert(emprev.origValue);
                 em.origValue = new AddExp(em.loc, emprev.origValue, IntegerExp.literal!1);
@@ -2454,12 +2464,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 em.origValue = em.origValue.ctfeInterpret();
             }
 
-            if (e.op == TOK.error)
+            if (e.op == EXP.error)
                 return errorReturn();
             if (e.type.isfloating())
             {
                 // Check that e != eprev (not always true for floats)
-                Expression etest = new EqualExp(TOK.equal, em.loc, e, eprev);
+                Expression etest = new EqualExp(EXP.equal, em.loc, e, eprev);
                 etest = etest.expressionSemantic(sc);
                 etest = etest.ctfeInterpret();
                 if (etest.toInteger())
@@ -4162,7 +4172,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
             Expression e = new IdentifierExp(Loc.initial, v.ident);
             e = new AddAssignExp(Loc.initial, e, IntegerExp.literal!1);
-            e = new EqualExp(TOK.notEqual, Loc.initial, e, IntegerExp.literal!1);
+            e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!1);
             s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
 
             sa.push(s);
@@ -4239,7 +4249,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
             Expression e = new IdentifierExp(Loc.initial, v.ident);
             e = new AddAssignExp(Loc.initial, e, IntegerExp.literal!(-1));
-            e = new EqualExp(TOK.notEqual, Loc.initial, e, IntegerExp.literal!0);
+            e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!0);
             s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
 
             sa.push(s);
@@ -4616,6 +4626,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
         if (cldec.errors)
             cldec.type = Type.terror;
+        if (cldec.semanticRun == PASS.init)
+            cldec.type = cldec.type.addSTC(sc.stc | cldec.storage_class);
         cldec.type = cldec.type.typeSemantic(cldec.loc, sc);
         if (auto tc = cldec.type.isTypeClass())
             if (tc.sym != cldec)
@@ -6422,7 +6434,7 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc)
                 s = getDsymbol(e);
                 if (!s)
                 {
-                    if (e.op != TOK.error)
+                    if (e.op != EXP.error)
                         ds.error("cannot alias an expression `%s`", e.toChars());
                     return errorRet();
                 }
@@ -6606,7 +6618,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc)
                 s = getDsymbol(e);
                 if (!s)
                 {
-                    if (e.op != TOK.error)
+                    if (e.op != EXP.error)
                         ds.error("cannot alias an expression `%s`", e.toChars());
                     return errorRet();
                 }
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index 9d7957a975d..5dedcba5627 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -144,7 +144,7 @@ extern (C++) bool isError(const RootObject o)
     if (const t = isType(o))
         return (t.ty == Terror);
     if (const e = isExpression(o))
-        return (e.op == TOK.error || !e.type || e.type.ty == Terror);
+        return (e.op == EXP.error || !e.type || e.type.ty == Terror);
     if (const v = isTuple(o))
         return arrayObjectIsError(&v.objects);
     const s = isDsymbol(o);
@@ -297,8 +297,8 @@ private bool match(RootObject o1, RootObject o2)
 
         static if (log)
         {
-            printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", Token.toChars(e1.op), e1.toChars());
-            printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", Token.toChars(e2.op), e2.toChars());
+            printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", EXPtoString(e1.op).ptr, e1.toChars());
+            printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", EXPtoString(e2.op).ptr, e2.toChars());
         }
 
         // two expressions can be equal although they do not have the same
@@ -421,26 +421,26 @@ private size_t expressionHash(Expression e)
 
     switch (e.op)
     {
-    case TOK.int64:
+    case EXP.int64:
         return cast(size_t) e.isIntegerExp().getInteger();
 
-    case TOK.float64:
+    case EXP.float64:
         return CTFloat.hash(e.isRealExp().value);
 
-    case TOK.complex80:
+    case EXP.complex80:
         auto ce = e.isComplexExp();
         return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
 
-    case TOK.identifier:
+    case EXP.identifier:
         return cast(size_t)cast(void*) e.isIdentifierExp().ident;
 
-    case TOK.null_:
+    case EXP.null_:
         return cast(size_t)cast(void*) e.isNullExp().type;
 
-    case TOK.string_:
+    case EXP.string_:
         return calcHash(e.isStringExp.peekData());
 
-    case TOK.tuple:
+    case EXP.tuple:
     {
         auto te = e.isTupleExp();
         size_t hash = 0;
@@ -450,7 +450,7 @@ private size_t expressionHash(Expression e)
         return hash;
     }
 
-    case TOK.arrayLiteral:
+    case EXP.arrayLiteral:
     {
         auto ae = e.isArrayLiteralExp();
         size_t hash;
@@ -459,7 +459,7 @@ private size_t expressionHash(Expression e)
         return hash;
     }
 
-    case TOK.assocArrayLiteral:
+    case EXP.assocArrayLiteral:
     {
         auto ae = e.isAssocArrayLiteralExp();
         size_t hash;
@@ -469,7 +469,7 @@ private size_t expressionHash(Expression e)
         return hash;
     }
 
-    case TOK.structLiteral:
+    case EXP.structLiteral:
     {
         auto se = e.isStructLiteralExp();
         size_t hash;
@@ -478,10 +478,10 @@ private size_t expressionHash(Expression e)
         return hash;
     }
 
-    case TOK.variable:
+    case EXP.variable:
         return cast(size_t)cast(void*) e.isVarExp().var;
 
-    case TOK.function_:
+    case EXP.function_:
         return cast(size_t)cast(void*) e.isFuncExp().fd;
 
     default:
@@ -1629,7 +1629,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
                             farg = (*fargs)[argi + i];
 
                             // Check invalid arguments to detect errors early.
-                            if (farg.op == TOK.error || farg.type.ty == Terror)
+                            if (farg.op == EXP.error || farg.type.ty == Terror)
                                 return nomatch();
 
                             if (!(fparam.storageClass & STC.lazy_) && farg.type.ty == Tvoid)
@@ -1825,7 +1825,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
                 }
                 {
                     // Check invalid arguments to detect errors early.
-                    if (farg.op == TOK.error || farg.type.ty == Terror)
+                    if (farg.op == EXP.error || farg.type.ty == Terror)
                         return nomatch();
 
                     Type att = null;
@@ -1837,7 +1837,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
                     }
                     Type argtype = farg.type;
 
-                    if (!(fparam.storageClass & STC.lazy_) && argtype.ty == Tvoid && farg.op != TOK.function_)
+                    if (!(fparam.storageClass & STC.lazy_) && argtype.ty == Tvoid && farg.op != EXP.function_)
                         return nomatch();
 
                     // https://issues.dlang.org/show_bug.cgi?id=12876
@@ -1853,17 +1853,17 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
                         Type taai;
                         if (argtype.ty == Tarray && (prmtype.ty == Tsarray || prmtype.ty == Taarray && (taai = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
                         {
-                            if (farg.op == TOK.string_)
+                            if (farg.op == EXP.string_)
                             {
                                 StringExp se = cast(StringExp)farg;
                                 argtype = se.type.nextOf().sarrayOf(se.len);
                             }
-                            else if (farg.op == TOK.arrayLiteral)
+                            else if (farg.op == EXP.arrayLiteral)
                             {
                                 ArrayLiteralExp ae = cast(ArrayLiteralExp)farg;
                                 argtype = ae.type.nextOf().sarrayOf(ae.elements.dim);
                             }
-                            else if (farg.op == TOK.slice)
+                            else if (farg.op == EXP.slice)
                             {
                                 SliceExp se = cast(SliceExp)farg;
                                 if (Type tsa = toStaticArrayType(se))
@@ -1939,7 +1939,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
                     {
                         if (!farg.isLvalue())
                         {
-                            if ((farg.op == TOK.string_ || farg.op == TOK.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray))
+                            if ((farg.op == EXP.string_ || farg.op == EXP.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray))
                             {
                                 // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
                             }
@@ -2280,13 +2280,13 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
         Declaration d;
         VarDeclaration v = null;
 
-        if (ea && ea.op == TOK.type)
+        if (ea && ea.op == EXP.type)
             ta = ea.type;
-        else if (ea && ea.op == TOK.scope_)
+        else if (ea && ea.op == EXP.scope_)
             sa = (cast(ScopeExp)ea).sds;
-        else if (ea && (ea.op == TOK.this_ || ea.op == TOK.super_))
+        else if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_))
             sa = (cast(ThisExp)ea).var;
-        else if (ea && ea.op == TOK.function_)
+        else if (ea && ea.op == EXP.function_)
         {
             if ((cast(FuncExp)ea).td)
                 sa = (cast(FuncExp)ea).td;
@@ -3826,7 +3826,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
                 if (tparam.ty == Tsarray)
                 {
                     TypeSArray tsa = cast(TypeSArray)tparam;
-                    if (tsa.dim.op == TOK.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter)
+                    if (tsa.dim.op == EXP.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter)
                     {
                         Identifier id = (cast(VarExp)tsa.dim).var.ident;
                         i = templateIdentifierLookup(id, parameters);
@@ -4209,7 +4209,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
                         /* If it is one of the template parameters for this template,
                          * we should not attempt to interpret it. It already has a value.
                          */
-                        if (e2.op == TOK.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter))
+                        if (e2.op == EXP.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter))
                         {
                             /*
                              * (T:Number!(e2), int e2)
@@ -4847,9 +4847,9 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
                 // Reset inference target for the later re-semantic
                 e.fd.treq = null;
 
-                if (ex.op == TOK.error)
+                if (ex.op == EXP.error)
                     return;
-                if (ex.op != TOK.function_)
+                if (ex.op != EXP.function_)
                     return;
                 visit(ex.type);
                 return;
@@ -6642,13 +6642,13 @@ extern (C++) class TemplateInstance : ScopeDsymbol
             else if (ea)
             {
             Lexpr:
-                //printf("+[%d] ea = %s %s\n", j, Token.toChars(ea.op), ea.toChars());
+                //printf("+[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
                 if (flags & 1) // only used by __traits
                 {
                     ea = ea.expressionSemantic(sc);
 
                     // must not interpret the args, excepting template parameters
-                    if (ea.op != TOK.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter))
+                    if (ea.op != EXP.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter))
                     {
                         ea = ea.optimize(WANTvalue);
                     }
@@ -6659,7 +6659,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                     ea = ea.expressionSemantic(sc);
                     sc = sc.endCTFE();
 
-                    if (ea.op == TOK.variable)
+                    if (ea.op == EXP.variable)
                     {
                         /* If the parameter is a function that is not called
                          * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
@@ -6687,8 +6687,8 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                             ea = ErrorExp.get();
                     }
                 }
-                //printf("-[%d] ea = %s %s\n", j, Token.toChars(ea.op), ea.toChars());
-                if (ea.op == TOK.tuple)
+                //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
+                if (ea.op == EXP.tuple)
                 {
                     // Expand tuple
                     TupleExp te = cast(TupleExp)ea;
@@ -6703,24 +6703,24 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                     j--;
                     continue;
                 }
-                if (ea.op == TOK.error)
+                if (ea.op == EXP.error)
                 {
                     err = true;
                     continue;
                 }
                 (*tiargs)[j] = ea;
 
-                if (ea.op == TOK.type)
+                if (ea.op == EXP.type)
                 {
                     ta = ea.type;
                     goto Ltype;
                 }
-                if (ea.op == TOK.scope_)
+                if (ea.op == EXP.scope_)
                 {
                     sa = (cast(ScopeExp)ea).sds;
                     goto Ldsym;
                 }
-                if (ea.op == TOK.function_)
+                if (ea.op == EXP.function_)
                 {
                     FuncExp fe = cast(FuncExp)ea;
                     /* A function literal, that is passed to template and
@@ -6741,24 +6741,24 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                         //goto Ldsym;
                     }
                 }
-                if (ea.op == TOK.dotVariable && !(flags & 1))
+                if (ea.op == EXP.dotVariable && !(flags & 1))
                 {
                     // translate expression to dsymbol.
                     sa = (cast(DotVarExp)ea).var;
                     goto Ldsym;
                 }
-                if (ea.op == TOK.template_)
+                if (ea.op == EXP.template_)
                 {
                     sa = (cast(TemplateExp)ea).td;
                     goto Ldsym;
                 }
-                if (ea.op == TOK.dotTemplateDeclaration && !(flags & 1))
+                if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1))
                 {
                     // translate expression to dsymbol.
                     sa = (cast(DotTemplateExp)ea).td;
                     goto Ldsym;
                 }
-                if (ea.op == TOK.dot)
+                if (ea.op == EXP.dot)
                 {
                     if (auto se = (cast(DotExp)ea).e2.isScopeExp())
                     {
@@ -7256,17 +7256,17 @@ extern (C++) class TemplateInstance : ScopeDsymbol
             Tuple va = isTuple(o);
             if (ea)
             {
-                if (ea.op == TOK.variable)
+                if (ea.op == EXP.variable)
                 {
                     sa = (cast(VarExp)ea).var;
                     goto Lsa;
                 }
-                if (ea.op == TOK.this_)
+                if (ea.op == EXP.this_)
                 {
                     sa = (cast(ThisExp)ea).var;
                     goto Lsa;
                 }
-                if (ea.op == TOK.function_)
+                if (ea.op == EXP.function_)
                 {
                     if ((cast(FuncExp)ea).td)
                         sa = (cast(FuncExp)ea).td;
@@ -7275,7 +7275,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                     goto Lsa;
                 }
                 // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
-                if (ea.op != TOK.int64 && ea.op != TOK.float64 && ea.op != TOK.complex80 && ea.op != TOK.null_ && ea.op != TOK.string_ && ea.op != TOK.arrayLiteral && ea.op != TOK.assocArrayLiteral && ea.op != TOK.structLiteral)
+                if (ea.op != EXP.int64 && ea.op != EXP.float64 && ea.op != EXP.complex80 && ea.op != EXP.null_ && ea.op != EXP.string_ && ea.op != EXP.arrayLiteral && ea.op != EXP.assocArrayLiteral && ea.op != EXP.structLiteral)
                 {
                     ea.error("expression `%s` is not a valid template value argument", ea.toChars());
                     errors = true;
@@ -7319,6 +7319,19 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                                 goto L1; // dparent is most nested
                             }
                         }
+                        //https://issues.dlang.org/show_bug.cgi?id=17870
+                        if (dparent.isClassDeclaration() && enclosing.isClassDeclaration())
+                        {
+                            auto pc = dparent.isClassDeclaration();
+                            auto ec = enclosing.isClassDeclaration();
+                            if (pc.isBaseOf(ec, null))
+                                goto L1;
+                            else if (ec.isBaseOf(pc, null))
+                            {
+                                enclosing = dparent;
+                                goto L1;
+                            }
+                        }
                         error("`%s` is nested in both `%s` and `%s`", toChars(), enclosing.toChars(), dparent.toChars());
                         errors = true;
                     }
@@ -7584,15 +7597,15 @@ void unSpeculative(Scope* sc, RootObject o)
 bool definitelyValueParameter(Expression e)
 {
     // None of these can be value parameters
-    if (e.op == TOK.tuple || e.op == TOK.scope_ ||
-        e.op == TOK.type || e.op == TOK.dotType ||
-        e.op == TOK.template_ || e.op == TOK.dotTemplateDeclaration ||
-        e.op == TOK.function_ || e.op == TOK.error ||
-        e.op == TOK.this_ || e.op == TOK.super_ ||
-        e.op == TOK.dot)
+    if (e.op == EXP.tuple || e.op == EXP.scope_ ||
+        e.op == EXP.type || e.op == EXP.dotType ||
+        e.op == EXP.template_ || e.op == EXP.dotTemplateDeclaration ||
+        e.op == EXP.function_ || e.op == EXP.error ||
+        e.op == EXP.this_ || e.op == EXP.super_ ||
+        e.op == EXP.dot)
         return false;
 
-    if (e.op != TOK.dotVariable)
+    if (e.op != EXP.dotVariable)
         return true;
 
     /* Template instantiations involving a DotVar expression are difficult.
@@ -7606,20 +7619,20 @@ bool definitelyValueParameter(Expression e)
     if (f)
         return false;
 
-    while (e.op == TOK.dotVariable)
+    while (e.op == EXP.dotVariable)
     {
         e = (cast(DotVarExp)e).e1;
     }
     // this.x.y and super.x.y couldn't possibly be valid values.
-    if (e.op == TOK.this_ || e.op == TOK.super_)
+    if (e.op == EXP.this_ || e.op == EXP.super_)
         return false;
 
     // e.type.x could be an alias
-    if (e.op == TOK.dotType)
+    if (e.op == EXP.dotType)
         return false;
 
     // var.x.y is the only other possible form of alias
-    if (e.op != TOK.variable)
+    if (e.op != EXP.variable)
         return true;
 
     VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
@@ -8010,7 +8023,7 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ
             uint olderrors = global.startGagging();
             ei = resolveProperties(sc, ei);
             ei = ei.ctfeInterpret();
-            if (global.endGagging(olderrors) || ei.op == TOK.error)
+            if (global.endGagging(olderrors) || ei.op == EXP.error)
                 return matchArgNoMatch();
 
             /* https://issues.dlang.org/show_bug.cgi?id=14520
@@ -8032,7 +8045,7 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ
             m = MATCH.convert;
         }
 
-        if (ei && ei.op == TOK.variable)
+        if (ei && ei.op == EXP.variable)
         {
             // Resolve const variables that we had skipped earlier
             ei = ei.ctfeInterpret();
@@ -8110,9 +8123,9 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ
         Type ta = isType(oarg);
         RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
         Expression ea = isExpression(oarg);
-        if (ea && (ea.op == TOK.this_ || ea.op == TOK.super_))
+        if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_))
             sa = (cast(ThisExp)ea).var;
-        else if (ea && ea.op == TOK.scope_)
+        else if (ea && ea.op == EXP.scope_)
             sa = (cast(ScopeExp)ea).sds;
         if (sa)
         {
diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d
index e19adfc4b14..51f53c8402f 100644
--- a/gcc/d/dmd/dtoh.d
+++ b/gcc/d/dmd/dtoh.d
@@ -21,6 +21,7 @@ import dmd.attrib;
 import dmd.dsymbol;
 import dmd.errors;
 import dmd.globals;
+import dmd.hdrgen;
 import dmd.identifier;
 import dmd.root.filename;
 import dmd.visitor;
@@ -2395,7 +2396,7 @@ public:
     {
         debug (Debug_DtoH) mixin(traceVisit!e);
 
-        buf.writestring(tokToString(e.op));
+        buf.writestring(expToString(e.op));
         e.e1.accept(this);
     }
 
@@ -2405,20 +2406,20 @@ public:
 
         e.e1.accept(this);
         buf.writeByte(' ');
-        buf.writestring(tokToString(e.op));
+        buf.writestring(expToString(e.op));
         buf.writeByte(' ');
         e.e2.accept(this);
     }
 
     /// Translates operator `op` into the C++ representation
-    private extern(D) static string tokToString(const TOK op)
+    private extern(D) static string expToString(const EXP op)
     {
-        switch (op) with (TOK)
+        switch (op) with (EXP)
         {
             case identity:      return "==";
             case notIdentity:   return "!=";
             default:
-                return Token.toString(op);
+                return EXPtoString(op);
         }
     }
 
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index d502f80975b..62f87e506ab 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -536,8 +536,8 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
 {
     enum log = false;
     if (log) printf("checkAssignEscape(e: %s)\n", e.toChars());
-    if (e.op != TOK.assign && e.op != TOK.blit && e.op != TOK.construct &&
-        e.op != TOK.concatenateAssign && e.op != TOK.concatenateElemAssign && e.op != TOK.concatenateDcharAssign)
+    if (e.op != EXP.assign && e.op != EXP.blit && e.op != EXP.construct &&
+        e.op != EXP.concatenateAssign && e.op != EXP.concatenateElemAssign && e.op != EXP.concatenateDcharAssign)
         return false;
     auto ae = cast(BinExp)e;
     Expression e1 = ae.e1;
@@ -568,7 +568,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
 
     VarDeclaration va = expToVariable(e1);
 
-    if (va && e.op == TOK.concatenateElemAssign)
+    if (va && e.op == EXP.concatenateElemAssign)
     {
         /* https://issues.dlang.org/show_bug.cgi?id=17842
          * Draw an equivalence between:
@@ -596,11 +596,6 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
 
     FuncDeclaration fd = sc.func;
 
-    // Try to infer 'scope' for va if in a function not marked @system
-    bool inferScope = false;
-    if (va && fd && fd.type && fd.type.isTypeFunction())
-        inferScope = fd.type.isTypeFunction().trust != TRUST.system;
-    //printf("inferScope = %d, %d\n", inferScope, (va.storage_class & STCmaybescope) != 0);
 
     // Determine if va is a parameter that is an indirect reference
     const bool vaIsRef = va && va.storage_class & STC.parameter &&
@@ -677,7 +672,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
                 if (va.isScope())
                     continue;
 
-                if (inferScope && !va.doNotInferScope)
+                if (!va.doNotInferScope)
                 {
                     if (log) printf("inferring scope for lvalue %s\n", va.toChars());
                     va.storage_class |= STC.scope_ | STC.scopeinferred;
@@ -713,7 +708,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
 
             if (va && !va.isDataseg() && !va.doNotInferScope)
             {
-                if (!va.isScope() && inferScope)
+                if (!va.isScope())
                 {   /* v is scope, and va is not scope, so va needs to
                      * infer scope
                      */
@@ -749,7 +744,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
             {
                 if (va && !va.isDataseg() && !va.doNotInferScope)
                 {
-                    if (!va.isScope() && inferScope)
+                    if (!va.isScope())
                     {   //printf("inferring scope for %s\n", va.toChars());
                         va.storage_class |= STC.scope_ | STC.scopeinferred;
                     }
@@ -840,7 +835,7 @@ ByRef:
 
         if (va && !va.isDataseg() && !va.doNotInferScope)
         {
-            if (!va.isScope() && inferScope)
+            if (!va.isScope())
             {   //printf("inferring scope for %s\n", va.toChars());
                 va.storage_class |= STC.scope_ | STC.scopeinferred;
             }
@@ -848,7 +843,7 @@ ByRef:
                 va.storage_class |= STC.return_ | STC.returninferred;
             continue;
         }
-        if (e1.op == TOK.structLiteral)
+        if (e1.op == EXP.structLiteral)
             continue;
         if (fd.setUnsafe())
         {
@@ -890,7 +885,7 @@ ByRef:
                 /* Don't infer STC.scope_ for va, because then a closure
                  * won't be generated for fd.
                  */
-                //if (!va.isScope() && inferScope)
+                //if (!va.isScope())
                     //va.storage_class |= STC.scope_ | STC.scopeinferred;
                 continue;
             }
@@ -909,7 +904,7 @@ ByRef:
 
         /* Do not allow slicing of a static array returned by a function
          */
-        if (ee.op == TOK.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() &&
+        if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() &&
             !(va && va.storage_class & STC.temp))
         {
             if (!gag)
@@ -919,7 +914,7 @@ ByRef:
             continue;
         }
 
-        if (ee.op == TOK.call && ee.type.toBasetype().isTypeStruct() &&
+        if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() &&
             (!va || !(va.storage_class & STC.temp)) &&
             fd.setUnsafe())
         {
@@ -930,7 +925,7 @@ ByRef:
             continue;
         }
 
-        if (ee.op == TOK.structLiteral &&
+        if (ee.op == EXP.structLiteral &&
             (!va || !(va.storage_class & STC.temp)) &&
             fd.setUnsafe())
         {
@@ -943,7 +938,7 @@ ByRef:
 
         if (va && !va.isDataseg() && !va.doNotInferScope)
         {
-            if (!va.isScope() && inferScope)
+            if (!va.isScope())
             {   //printf("inferring scope for %s\n", va.toChars());
                 va.storage_class |= STC.scope_ | STC.scopeinferred;
             }
@@ -993,17 +988,13 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag)
         if (v.isScope() && !v.iscatchvar)       // special case: allow catch var to be rethrown
                                                 // despite being `scope`
         {
-            if (sc._module && sc._module.isRoot())
+            if (global.params.useDIP1000 == FeatureState.enabled) // https://issues.dlang.org/show_bug.cgi?id=17029
             {
-                // Only look for errors if in module listed on command line
-                if (global.params.useDIP1000 == FeatureState.enabled) // https://issues.dlang.org/show_bug.cgi?id=17029
-                {
-                    if (!gag)
-                        error(e.loc, "scope variable `%s` may not be thrown", v.toChars());
-                    result = true;
-                }
-                continue;
+                if (!gag)
+                    error(e.loc, "scope variable `%s` may not be thrown", v.toChars());
+                result = true;
             }
+            continue;
         }
         else
         {
@@ -1051,7 +1042,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag)
 
         if (v.isScope())
         {
-            if (sc._module && sc._module.isRoot() &&
+            if (
                 /* This case comes up when the ReturnStatement of a __foreachbody is
                  * checked for escapes by the caller of __foreachbody. Skip it.
                  *
@@ -1128,9 +1119,6 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag)
         if (!v.isReference())
             continue;
 
-        if (!sc._module || !sc._module.isRoot())
-            continue;
-
         // https://dlang.org/spec/function.html#return-ref-parameters
         // Only look for errors if in module listed on command line
         if (p == sc.func)
@@ -1264,7 +1252,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
                 continue;
 
             auto pfunc = p.isFuncDeclaration();
-            if (pfunc && sc._module && sc._module.isRoot() &&
+            if (pfunc &&
                 /* This case comes up when the ReturnStatement of a __foreachbody is
                  * checked for escapes by the caller of __foreachbody. Skip it.
                  *
@@ -1284,7 +1272,8 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
                )
             {
                 // Only look for errors if in module listed on command line
-                if (global.params.useDIP1000 == FeatureState.enabled) // https://issues.dlang.org/show_bug.cgi?id=17029
+                // https://issues.dlang.org/show_bug.cgi?id=17029
+                if (global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe())
                 {
                     if (!gag)
                         error(e.loc, "scope variable `%s` may not be returned", v.toChars());
@@ -1398,7 +1387,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
             {
                 inferReturn(sc.func, v);        // infer addition of 'return'
             }
-            else if (sc._module && sc._module.isRoot())
+            else
             {
                 // https://dlang.org/spec/function.html#return-ref-parameters
                 // Only look for errors if in module listed on command line
@@ -1528,7 +1517,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false)
              * allowed, but CTFE can generate one out of a new expression,
              * but it'll be placed in static data so no need to check it.
              */
-            if (e.e1.op != TOK.structLiteral)
+            if (e.e1.op != EXP.structLiteral)
                 escapeByRef(e.e1, er, live);
         }
 
@@ -1768,7 +1757,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false)
                 }
             }
             // If 'this' is returned, check it too
-            if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
+            if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
             {
                 DotVarExp dve = e.e1.isDotVarExp();
                 FuncDeclaration fd = dve.var.isFuncDeclaration();
@@ -2024,7 +2013,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false)
                     }
                 }
                 // If 'this' is returned by ref, check it too
-                if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
+                if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
                 {
                     DotVarExp dve = e.e1.isDotVarExp();
 
@@ -2051,7 +2040,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false)
                     }
                 }
                 // If it's a delegate, check it too
-                if (e.e1.op == TOK.variable && t1.ty == Tdelegate)
+                if (e.e1.op == EXP.variable && t1.ty == Tdelegate)
                 {
                     escapeByValue(e.e1, er, live);
                 }
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index 2a623324454..e6b7e3014f1 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -60,6 +60,7 @@ import dmd.optimize;
 import dmd.root.ctfloat;
 import dmd.root.filename;
 import dmd.common.outbuffer;
+import dmd.root.optional;
 import dmd.root.rmem;
 import dmd.root.rootobject;
 import dmd.root.string;
@@ -116,7 +117,7 @@ enum ModifyFlags
 inout(Expression) firstComma(inout Expression e)
 {
     Expression ex = cast()e;
-    while (ex.op == TOK.comma)
+    while (ex.op == EXP.comma)
         ex = (cast(CommaExp)ex).e1;
     return cast(inout)ex;
 
@@ -133,7 +134,7 @@ inout(Expression) firstComma(inout Expression e)
 inout(Expression) lastComma(inout Expression e)
 {
     Expression ex = cast()e;
-    while (ex.op == TOK.comma)
+    while (ex.op == EXP.comma)
         ex = (cast(CommaExp)ex).e2;
     return cast(inout)ex;
 
@@ -286,7 +287,7 @@ extern (C++) void expandTuples(Expressions* exps)
         }
 
         // Inline expand all the tuples
-        while (arg.op == TOK.tuple)
+        while (arg.op == EXP.tuple)
         {
             TupleExp te = cast(TupleExp)arg;
             exps.remove(i); // remove arg
@@ -356,7 +357,7 @@ int expandAliasThisTuples(Expressions* exps, size_t starti = 0)
                 printf("expansion ->\n");
                 foreach (e; exps)
                 {
-                    printf("\texps[%d] e = %s %s\n", i, Token.tochars[e.op], e.toChars());
+                    printf("\texps[%d] e = %s %s\n", i, EXPtoString(e.op), e.toChars());
                 }
             }
             return cast(int)u;
@@ -528,15 +529,15 @@ extern (C++) struct UnionExp
     extern (C++) Expression copy()
     {
         Expression e = exp();
-        //if (e.size > sizeof(u)) printf("%s\n", Token::toChars(e.op));
+        //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
         assert(e.size <= u.sizeof);
         switch (e.op)
         {
-            case TOK.cantExpression:    return CTFEExp.cantexp;
-            case TOK.voidExpression:    return CTFEExp.voidexp;
-            case TOK.break_:            return CTFEExp.breakexp;
-            case TOK.continue_:         return CTFEExp.continueexp;
-            case TOK.goto_:             return CTFEExp.gotoexp;
+            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();
         }
     }
@@ -610,14 +611,14 @@ VarDeclaration expToVariable(Expression e)
     {
         switch (e.op)
         {
-            case TOK.variable:
+            case EXP.variable:
                 return (cast(VarExp)e).var.isVarDeclaration();
 
-            case TOK.dotVariable:
+            case EXP.dotVariable:
                 e = (cast(DotVarExp)e).e1;
                 continue;
 
-            case TOK.index:
+            case EXP.index:
             {
                 IndexExp ei = cast(IndexExp)e;
                 e = ei.e1;
@@ -627,7 +628,7 @@ VarDeclaration expToVariable(Expression e)
                 return null;
             }
 
-            case TOK.slice:
+            case EXP.slice:
             {
                 SliceExp ei = cast(SliceExp)e;
                 e = ei.e1;
@@ -637,8 +638,8 @@ VarDeclaration expToVariable(Expression e)
                 return null;
             }
 
-            case TOK.this_:
-            case TOK.super_:
+            case EXP.this_:
+            case EXP.super_:
                 return (cast(ThisExp)e).var.isVarDeclaration();
 
             default:
@@ -662,13 +663,13 @@ enum WANTexpand = 1;    // expand const/immutable variables if possible
  */
 extern (C++) abstract class Expression : ASTNode
 {
-    const TOK op;   // to minimize use of dynamic_cast
+    const EXP op;   // to minimize use of dynamic_cast
     ubyte size;     // # of bytes in Expression so we can copy() it
     ubyte parens;   // if this is a parenthesized expression
     Type type;      // !=null means that semantic() has been run
     Loc loc;        // file location
 
-    extern (D) this(const ref Loc loc, TOK op, int size)
+    extern (D) this(const ref Loc loc, EXP op, int size)
     {
         //printf("Expression::Expression(op = %d) this = %p\n", op, this);
         this.loc = loc;
@@ -678,12 +679,12 @@ extern (C++) abstract class Expression : ASTNode
 
     static void _init()
     {
-        CTFEExp.cantexp = new CTFEExp(TOK.cantExpression);
-        CTFEExp.voidexp = new CTFEExp(TOK.voidExpression);
-        CTFEExp.breakexp = new CTFEExp(TOK.break_);
-        CTFEExp.continueexp = new CTFEExp(TOK.continue_);
-        CTFEExp.gotoexp = new CTFEExp(TOK.goto_);
-        CTFEExp.showcontext = new CTFEExp(TOK.showCtfeContext);
+        CTFEExp.cantexp = new CTFEExp(EXP.cantExpression);
+        CTFEExp.voidexp = new CTFEExp(EXP.voidExpression);
+        CTFEExp.breakexp = new CTFEExp(EXP.break_);
+        CTFEExp.continueexp = new CTFEExp(EXP.continue_);
+        CTFEExp.gotoexp = new CTFEExp(EXP.goto_);
+        CTFEExp.showcontext = new CTFEExp(EXP.showCtfeContext);
     }
 
     /**
@@ -874,13 +875,13 @@ extern (C++) abstract class Expression : ASTNode
      */
     extern (D) static Expression extractLast(Expression e, out Expression e0)
     {
-        if (e.op != TOK.comma)
+        if (e.op != EXP.comma)
         {
             return e;
         }
 
         CommaExp ce = cast(CommaExp)e;
-        if (ce.e2.op != TOK.comma)
+        if (ce.e2.op != EXP.comma)
         {
             e0 = ce.e1;
             return ce.e2;
@@ -890,11 +891,11 @@ extern (C++) abstract class Expression : ASTNode
             e0 = e;
 
             Expression* pce = &ce.e2;
-            while ((cast(CommaExp)(*pce)).e2.op == TOK.comma)
+            while ((cast(CommaExp)(*pce)).e2.op == EXP.comma)
             {
                 pce = &(cast(CommaExp)(*pce)).e2;
             }
-            assert((*pce).op == TOK.comma);
+            assert((*pce).op == EXP.comma);
             ce = cast(CommaExp)(*pce);
             *pce = ce.e1;
 
@@ -918,14 +919,14 @@ extern (C++) abstract class Expression : ASTNode
 
     dinteger_t toInteger()
     {
-        //printf("Expression %s\n", Token::toChars(op));
+        //printf("Expression %s\n", EXPtoString(op).ptr);
         error("integer constant expression expected instead of `%s`", toChars());
         return 0;
     }
 
     uinteger_t toUInteger()
     {
-        //printf("Expression %s\n", Token::toChars(op));
+        //printf("Expression %s\n", EXPtoString(op).ptr);
         return cast(uinteger_t)toInteger();
     }
 
@@ -976,7 +977,7 @@ extern (C++) abstract class Expression : ASTNode
         else if (!loc.isValid())
             loc = e.loc;
 
-        if (e.op == TOK.type)
+        if (e.op == EXP.type)
             error("`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
         else
             error("`%s` is not an lvalue and cannot be modified", e.toChars());
@@ -1079,7 +1080,7 @@ extern (C++) abstract class Expression : ASTNode
 
     extern (D) final bool checkScalar()
     {
-        if (op == TOK.error)
+        if (op == EXP.error)
             return true;
         if (type.toBasetype().ty == Terror)
             return true;
@@ -1093,7 +1094,7 @@ extern (C++) abstract class Expression : ASTNode
 
     extern (D) final bool checkNoBool()
     {
-        if (op == TOK.error)
+        if (op == EXP.error)
             return true;
         if (type.toBasetype().ty == Terror)
             return true;
@@ -1107,7 +1108,7 @@ extern (C++) abstract class Expression : ASTNode
 
     extern (D) final bool checkIntegral()
     {
-        if (op == TOK.error)
+        if (op == EXP.error)
             return true;
         if (type.toBasetype().ty == Terror)
             return true;
@@ -1121,7 +1122,7 @@ extern (C++) abstract class Expression : ASTNode
 
     extern (D) final bool checkArithmetic()
     {
-        if (op == TOK.error)
+        if (op == EXP.error)
             return true;
         if (type.toBasetype().ty == Terror)
             return true;
@@ -1492,9 +1493,9 @@ extern (C++) abstract class Expression : ASTNode
 
     extern (D) final bool checkRightThis(Scope* sc)
     {
-        if (op == TOK.error)
+        if (op == EXP.error)
             return true;
-        if (op == TOK.variable && type.ty != Terror)
+        if (op == EXP.variable && type.ty != Terror)
         {
             VarExp ve = cast(VarExp)this;
             if (isNeedThisScope(sc, ve.var))
@@ -1513,7 +1514,7 @@ extern (C++) abstract class Expression : ASTNode
      * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
      * Returns true if error occurs.
      */
-    extern (D) final bool checkReadModifyWrite(TOK rmwOp, Expression ex = null)
+    extern (D) final bool checkReadModifyWrite(EXP rmwOp, Expression ex = null)
     {
         //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : "");
         if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass())
@@ -1522,13 +1523,13 @@ extern (C++) abstract class Expression : ASTNode
         // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
         switch (rmwOp)
         {
-        case TOK.plusPlus:
-        case TOK.prePlusPlus:
-            rmwOp = TOK.addAssign;
+        case EXP.plusPlus:
+        case EXP.prePlusPlus:
+            rmwOp = EXP.addAssign;
             break;
-        case TOK.minusMinus:
-        case TOK.preMinusMinus:
-            rmwOp = TOK.minAssign;
+        case EXP.minusMinus:
+        case EXP.preMinusMinus:
+            rmwOp = EXP.minAssign;
             break;
         default:
             break;
@@ -1536,7 +1537,7 @@ extern (C++) abstract class Expression : ASTNode
 
         error("read-modify-write operations are not allowed for `shared` variables");
         errorSupplemental("Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead",
-                          Token.toChars(rmwOp), toChars(), ex ? ex.toChars() : "1");
+                          EXPtoString(rmwOp).ptr, toChars(), ex ? ex.toChars() : "1");
         return true;
     }
 
@@ -1558,7 +1559,7 @@ extern (C++) abstract class Expression : ASTNode
         //printf("Expression::addressOf()\n");
         debug
         {
-            assert(op == TOK.error || isLvalue());
+            assert(op == EXP.error || isLvalue());
         }
         Expression e = new AddrExp(loc, this, type.pointerTo());
         return e;
@@ -1597,12 +1598,11 @@ extern (C++) abstract class Expression : ASTNode
         return .isConst(this);
     }
 
-    /********************************
-     * Does this expression statically evaluate to a boolean 'result' (true or false)?
-     */
-    bool isBool(bool result)
+    /// Statically evaluate this expression to a `bool` if possible
+    /// Returns: an optional thath either contains the value or is empty
+    Optional!bool toBool()
     {
-        return false;
+        return typeof(return)();
     }
 
     bool hasCode()
@@ -1612,127 +1612,127 @@ extern (C++) abstract class Expression : ASTNode
 
     final pure inout nothrow @nogc @safe
     {
-        inout(IntegerExp)   isIntegerExp() { return op == TOK.int64 ? cast(typeof(return))this : null; }
-        inout(ErrorExp)     isErrorExp() { return op == TOK.error ? cast(typeof(return))this : null; }
-        inout(VoidInitExp)  isVoidInitExp() { return op == TOK.void_ ? cast(typeof(return))this : null; }
-        inout(RealExp)      isRealExp() { return op == TOK.float64 ? cast(typeof(return))this : null; }
-        inout(ComplexExp)   isComplexExp() { return op == TOK.complex80 ? cast(typeof(return))this : null; }
-        inout(IdentifierExp) isIdentifierExp() { return op == TOK.identifier ? cast(typeof(return))this : null; }
-        inout(DollarExp)    isDollarExp() { return op == TOK.dollar ? cast(typeof(return))this : null; }
-        inout(DsymbolExp)   isDsymbolExp() { return op == TOK.dSymbol ? cast(typeof(return))this : null; }
-        inout(ThisExp)      isThisExp() { return op == TOK.this_ ? cast(typeof(return))this : null; }
-        inout(SuperExp)     isSuperExp() { return op == TOK.super_ ? cast(typeof(return))this : null; }
-        inout(NullExp)      isNullExp() { return op == TOK.null_ ? cast(typeof(return))this : null; }
-        inout(StringExp)    isStringExp() { return op == TOK.string_ ? cast(typeof(return))this : null; }
-        inout(TupleExp)     isTupleExp() { return op == TOK.tuple ? cast(typeof(return))this : null; }
-        inout(ArrayLiteralExp) isArrayLiteralExp() { return op == TOK.arrayLiteral ? cast(typeof(return))this : null; }
-        inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == TOK.assocArrayLiteral ? cast(typeof(return))this : null; }
-        inout(StructLiteralExp) isStructLiteralExp() { return op == TOK.structLiteral ? cast(typeof(return))this : null; }
-        inout(CompoundLiteralExp) isCompoundLiteralExp() { return op == TOK.compoundLiteral ? cast(typeof(return))this : null; }
-        inout(TypeExp)      isTypeExp() { return op == TOK.type ? cast(typeof(return))this : null; }
-        inout(ScopeExp)     isScopeExp() { return op == TOK.scope_ ? cast(typeof(return))this : null; }
-        inout(TemplateExp)  isTemplateExp() { return op == TOK.template_ ? cast(typeof(return))this : null; }
-        inout(NewExp) isNewExp() { return op == TOK.new_ ? cast(typeof(return))this : null; }
-        inout(NewAnonClassExp) isNewAnonClassExp() { return op == TOK.newAnonymousClass ? cast(typeof(return))this : null; }
-        inout(SymOffExp)    isSymOffExp() { return op == TOK.symbolOffset ? cast(typeof(return))this : null; }
-        inout(VarExp)       isVarExp() { return op == TOK.variable ? cast(typeof(return))this : null; }
-        inout(OverExp)      isOverExp() { return op == TOK.overloadSet ? cast(typeof(return))this : null; }
-        inout(FuncExp)      isFuncExp() { return op == TOK.function_ ? cast(typeof(return))this : null; }
-        inout(DeclarationExp) isDeclarationExp() { return op == TOK.declaration ? cast(typeof(return))this : null; }
-        inout(TypeidExp)    isTypeidExp() { return op == TOK.typeid_ ? cast(typeof(return))this : null; }
-        inout(TraitsExp)    isTraitsExp() { return op == TOK.traits ? cast(typeof(return))this : null; }
-        inout(HaltExp)      isHaltExp() { return op == TOK.halt ? cast(typeof(return))this : null; }
-        inout(IsExp)        isExp() { return op == TOK.is_ ? cast(typeof(return))this : null; }
-        inout(MixinExp)     isMixinExp() { return op == TOK.mixin_ ? cast(typeof(return))this : null; }
-        inout(ImportExp)    isImportExp() { return op == TOK.import_ ? cast(typeof(return))this : null; }
-        inout(AssertExp)    isAssertExp() { return op == TOK.assert_ ? cast(typeof(return))this : null; }
-        inout(DotIdExp)     isDotIdExp() { return op == TOK.dotIdentifier ? cast(typeof(return))this : null; }
-        inout(DotTemplateExp) isDotTemplateExp() { return op == TOK.dotTemplateDeclaration ? cast(typeof(return))this : null; }
-        inout(DotVarExp)    isDotVarExp() { return op == TOK.dotVariable ? cast(typeof(return))this : null; }
-        inout(DotTemplateInstanceExp) isDotTemplateInstanceExp() { return op == TOK.dotTemplateInstance ? cast(typeof(return))this : null; }
-        inout(DelegateExp)  isDelegateExp() { return op == TOK.delegate_ ? cast(typeof(return))this : null; }
-        inout(DotTypeExp)   isDotTypeExp() { return op == TOK.dotType ? cast(typeof(return))this : null; }
-        inout(CallExp)      isCallExp() { return op == TOK.call ? cast(typeof(return))this : null; }
-        inout(AddrExp)      isAddrExp() { return op == TOK.address ? cast(typeof(return))this : null; }
-        inout(PtrExp)       isPtrExp() { return op == TOK.star ? cast(typeof(return))this : null; }
-        inout(NegExp)       isNegExp() { return op == TOK.negate ? cast(typeof(return))this : null; }
-        inout(UAddExp)      isUAddExp() { return op == TOK.uadd ? cast(typeof(return))this : null; }
-        inout(ComExp)       isComExp() { return op == TOK.tilde ? cast(typeof(return))this : null; }
-        inout(NotExp)       isNotExp() { return op == TOK.not ? cast(typeof(return))this : null; }
-        inout(DeleteExp)    isDeleteExp() { return op == TOK.delete_ ? cast(typeof(return))this : null; }
-        inout(CastExp)      isCastExp() { return op == TOK.cast_ ? cast(typeof(return))this : null; }
-        inout(VectorExp)    isVectorExp() { return op == TOK.vector ? cast(typeof(return))this : null; }
-        inout(VectorArrayExp) isVectorArrayExp() { return op == TOK.vectorArray ? cast(typeof(return))this : null; }
-        inout(SliceExp)     isSliceExp() { return op == TOK.slice ? cast(typeof(return))this : null; }
-        inout(ArrayLengthExp) isArrayLengthExp() { return op == TOK.arrayLength ? cast(typeof(return))this : null; }
-        inout(ArrayExp)     isArrayExp() { return op == TOK.array ? cast(typeof(return))this : null; }
-        inout(DotExp)       isDotExp() { return op == TOK.dot ? cast(typeof(return))this : null; }
-        inout(CommaExp)     isCommaExp() { return op == TOK.comma ? cast(typeof(return))this : null; }
-        inout(IntervalExp)  isIntervalExp() { return op == TOK.interval ? cast(typeof(return))this : null; }
-        inout(DelegatePtrExp)     isDelegatePtrExp() { return op == TOK.delegatePointer ? cast(typeof(return))this : null; }
-        inout(DelegateFuncptrExp) isDelegateFuncptrExp() { return op == TOK.delegateFunctionPointer ? cast(typeof(return))this : null; }
-        inout(IndexExp)     isIndexExp() { return op == TOK.index ? cast(typeof(return))this : null; }
-        inout(PostExp)      isPostExp()  { return (op == TOK.plusPlus || op == TOK.minusMinus) ? cast(typeof(return))this : null; }
-        inout(PreExp)       isPreExp()   { return (op == TOK.prePlusPlus || op == TOK.preMinusMinus) ? cast(typeof(return))this : null; }
-        inout(AssignExp)    isAssignExp()    { return op == TOK.assign ? cast(typeof(return))this : null; }
-        inout(ConstructExp) isConstructExp() { return op == TOK.construct ? cast(typeof(return))this : null; }
-        inout(BlitExp)      isBlitExp()      { return op == TOK.blit ? cast(typeof(return))this : null; }
-        inout(AddAssignExp) isAddAssignExp() { return op == TOK.addAssign ? cast(typeof(return))this : null; }
-        inout(MinAssignExp) isMinAssignExp() { return op == TOK.minAssign ? cast(typeof(return))this : null; }
-        inout(MulAssignExp) isMulAssignExp() { return op == TOK.mulAssign ? cast(typeof(return))this : null; }
-
-        inout(DivAssignExp) isDivAssignExp() { return op == TOK.divAssign ? cast(typeof(return))this : null; }
-        inout(ModAssignExp) isModAssignExp() { return op == TOK.modAssign ? cast(typeof(return))this : null; }
-        inout(AndAssignExp) isAndAssignExp() { return op == TOK.andAssign ? cast(typeof(return))this : null; }
-        inout(OrAssignExp)  isOrAssignExp()  { return op == TOK.orAssign ? cast(typeof(return))this : null; }
-        inout(XorAssignExp) isXorAssignExp() { return op == TOK.xorAssign ? cast(typeof(return))this : null; }
-        inout(PowAssignExp) isPowAssignExp() { return op == TOK.powAssign ? cast(typeof(return))this : null; }
-
-        inout(ShlAssignExp)  isShlAssignExp()  { return op == TOK.leftShiftAssign ? cast(typeof(return))this : null; }
-        inout(ShrAssignExp)  isShrAssignExp()  { return op == TOK.rightShiftAssign ? cast(typeof(return))this : null; }
-        inout(UshrAssignExp) isUshrAssignExp() { return op == TOK.unsignedRightShiftAssign ? cast(typeof(return))this : null; }
-
-        inout(CatAssignExp) isCatAssignExp() { return op == TOK.concatenateAssign
+        inout(IntegerExp)   isIntegerExp() { return op == EXP.int64 ? cast(typeof(return))this : null; }
+        inout(ErrorExp)     isErrorExp() { return op == EXP.error ? cast(typeof(return))this : null; }
+        inout(VoidInitExp)  isVoidInitExp() { return op == EXP.void_ ? cast(typeof(return))this : null; }
+        inout(RealExp)      isRealExp() { return op == EXP.float64 ? cast(typeof(return))this : null; }
+        inout(ComplexExp)   isComplexExp() { return op == EXP.complex80 ? cast(typeof(return))this : null; }
+        inout(IdentifierExp) isIdentifierExp() { return op == EXP.identifier ? cast(typeof(return))this : null; }
+        inout(DollarExp)    isDollarExp() { return op == EXP.dollar ? cast(typeof(return))this : null; }
+        inout(DsymbolExp)   isDsymbolExp() { return op == EXP.dSymbol ? cast(typeof(return))this : null; }
+        inout(ThisExp)      isThisExp() { return op == EXP.this_ ? cast(typeof(return))this : null; }
+        inout(SuperExp)     isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
+        inout(NullExp)      isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
+        inout(StringExp)    isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
+        inout(TupleExp)     isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
+        inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
+        inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
+        inout(StructLiteralExp) isStructLiteralExp() { return op == EXP.structLiteral ? cast(typeof(return))this : null; }
+        inout(CompoundLiteralExp) isCompoundLiteralExp() { return op == EXP.compoundLiteral ? cast(typeof(return))this : null; }
+        inout(TypeExp)      isTypeExp() { return op == EXP.type ? cast(typeof(return))this : null; }
+        inout(ScopeExp)     isScopeExp() { return op == EXP.scope_ ? cast(typeof(return))this : null; }
+        inout(TemplateExp)  isTemplateExp() { return op == EXP.template_ ? cast(typeof(return))this : null; }
+        inout(NewExp) isNewExp() { return op == EXP.new_ ? cast(typeof(return))this : null; }
+        inout(NewAnonClassExp) isNewAnonClassExp() { return op == EXP.newAnonymousClass ? cast(typeof(return))this : null; }
+        inout(SymOffExp)    isSymOffExp() { return op == EXP.symbolOffset ? cast(typeof(return))this : null; }
+        inout(VarExp)       isVarExp() { return op == EXP.variable ? cast(typeof(return))this : null; }
+        inout(OverExp)      isOverExp() { return op == EXP.overloadSet ? cast(typeof(return))this : null; }
+        inout(FuncExp)      isFuncExp() { return op == EXP.function_ ? cast(typeof(return))this : null; }
+        inout(DeclarationExp) isDeclarationExp() { return op == EXP.declaration ? cast(typeof(return))this : null; }
+        inout(TypeidExp)    isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; }
+        inout(TraitsExp)    isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; }
+        inout(HaltExp)      isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; }
+        inout(IsExp)        isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
+        inout(MixinExp)     isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; }
+        inout(ImportExp)    isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; }
+        inout(AssertExp)    isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; }
+        inout(DotIdExp)     isDotIdExp() { return op == EXP.dotIdentifier ? cast(typeof(return))this : null; }
+        inout(DotTemplateExp) isDotTemplateExp() { return op == EXP.dotTemplateDeclaration ? cast(typeof(return))this : null; }
+        inout(DotVarExp)    isDotVarExp() { return op == EXP.dotVariable ? cast(typeof(return))this : null; }
+        inout(DotTemplateInstanceExp) isDotTemplateInstanceExp() { return op == EXP.dotTemplateInstance ? cast(typeof(return))this : null; }
+        inout(DelegateExp)  isDelegateExp() { return op == EXP.delegate_ ? cast(typeof(return))this : null; }
+        inout(DotTypeExp)   isDotTypeExp() { return op == EXP.dotType ? cast(typeof(return))this : null; }
+        inout(CallExp)      isCallExp() { return op == EXP.call ? cast(typeof(return))this : null; }
+        inout(AddrExp)      isAddrExp() { return op == EXP.address ? cast(typeof(return))this : null; }
+        inout(PtrExp)       isPtrExp() { return op == EXP.star ? cast(typeof(return))this : null; }
+        inout(NegExp)       isNegExp() { return op == EXP.negate ? cast(typeof(return))this : null; }
+        inout(UAddExp)      isUAddExp() { return op == EXP.uadd ? cast(typeof(return))this : null; }
+        inout(ComExp)       isComExp() { return op == EXP.tilde ? cast(typeof(return))this : null; }
+        inout(NotExp)       isNotExp() { return op == EXP.not ? cast(typeof(return))this : null; }
+        inout(DeleteExp)    isDeleteExp() { return op == EXP.delete_ ? cast(typeof(return))this : null; }
+        inout(CastExp)      isCastExp() { return op == EXP.cast_ ? cast(typeof(return))this : null; }
+        inout(VectorExp)    isVectorExp() { return op == EXP.vector ? cast(typeof(return))this : null; }
+        inout(VectorArrayExp) isVectorArrayExp() { return op == EXP.vectorArray ? cast(typeof(return))this : null; }
+        inout(SliceExp)     isSliceExp() { return op == EXP.slice ? cast(typeof(return))this : null; }
+        inout(ArrayLengthExp) isArrayLengthExp() { return op == EXP.arrayLength ? cast(typeof(return))this : null; }
+        inout(ArrayExp)     isArrayExp() { return op == EXP.array ? cast(typeof(return))this : null; }
+        inout(DotExp)       isDotExp() { return op == EXP.dot ? cast(typeof(return))this : null; }
+        inout(CommaExp)     isCommaExp() { return op == EXP.comma ? cast(typeof(return))this : null; }
+        inout(IntervalExp)  isIntervalExp() { return op == EXP.interval ? cast(typeof(return))this : null; }
+        inout(DelegatePtrExp)     isDelegatePtrExp() { return op == EXP.delegatePointer ? cast(typeof(return))this : null; }
+        inout(DelegateFuncptrExp) isDelegateFuncptrExp() { return op == EXP.delegateFunctionPointer ? cast(typeof(return))this : null; }
+        inout(IndexExp)     isIndexExp() { return op == EXP.index ? cast(typeof(return))this : null; }
+        inout(PostExp)      isPostExp()  { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; }
+        inout(PreExp)       isPreExp()   { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; }
+        inout(AssignExp)    isAssignExp()    { return op == EXP.assign ? cast(typeof(return))this : null; }
+        inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; }
+        inout(BlitExp)      isBlitExp()      { return op == EXP.blit ? cast(typeof(return))this : null; }
+        inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; }
+        inout(MinAssignExp) isMinAssignExp() { return op == EXP.minAssign ? cast(typeof(return))this : null; }
+        inout(MulAssignExp) isMulAssignExp() { return op == EXP.mulAssign ? cast(typeof(return))this : null; }
+
+        inout(DivAssignExp) isDivAssignExp() { return op == EXP.divAssign ? cast(typeof(return))this : null; }
+        inout(ModAssignExp) isModAssignExp() { return op == EXP.modAssign ? cast(typeof(return))this : null; }
+        inout(AndAssignExp) isAndAssignExp() { return op == EXP.andAssign ? cast(typeof(return))this : null; }
+        inout(OrAssignExp)  isOrAssignExp()  { return op == EXP.orAssign ? cast(typeof(return))this : null; }
+        inout(XorAssignExp) isXorAssignExp() { return op == EXP.xorAssign ? cast(typeof(return))this : null; }
+        inout(PowAssignExp) isPowAssignExp() { return op == EXP.powAssign ? cast(typeof(return))this : null; }
+
+        inout(ShlAssignExp)  isShlAssignExp()  { return op == EXP.leftShiftAssign ? cast(typeof(return))this : null; }
+        inout(ShrAssignExp)  isShrAssignExp()  { return op == EXP.rightShiftAssign ? cast(typeof(return))this : null; }
+        inout(UshrAssignExp) isUshrAssignExp() { return op == EXP.unsignedRightShiftAssign ? cast(typeof(return))this : null; }
+
+        inout(CatAssignExp) isCatAssignExp() { return op == EXP.concatenateAssign
                                                 ? cast(typeof(return))this
                                                 : null; }
 
-        inout(CatElemAssignExp) isCatElemAssignExp() { return op == TOK.concatenateElemAssign
+        inout(CatElemAssignExp) isCatElemAssignExp() { return op == EXP.concatenateElemAssign
                                                 ? cast(typeof(return))this
                                                 : null; }
 
-        inout(CatDcharAssignExp) isCatDcharAssignExp() { return op == TOK.concatenateDcharAssign
+        inout(CatDcharAssignExp) isCatDcharAssignExp() { return op == EXP.concatenateDcharAssign
                                                 ? cast(typeof(return))this
                                                 : null; }
 
-        inout(AddExp)      isAddExp() { return op == TOK.add ? cast(typeof(return))this : null; }
-        inout(MinExp)      isMinExp() { return op == TOK.min ? cast(typeof(return))this : null; }
-        inout(CatExp)      isCatExp() { return op == TOK.concatenate ? cast(typeof(return))this : null; }
-        inout(MulExp)      isMulExp() { return op == TOK.mul ? cast(typeof(return))this : null; }
-        inout(DivExp)      isDivExp() { return op == TOK.div ? cast(typeof(return))this : null; }
-        inout(ModExp)      isModExp() { return op == TOK.mod ? cast(typeof(return))this : null; }
-        inout(PowExp)      isPowExp() { return op == TOK.pow ? cast(typeof(return))this : null; }
-        inout(ShlExp)      isShlExp() { return op == TOK.leftShift ? cast(typeof(return))this : null; }
-        inout(ShrExp)      isShrExp() { return op == TOK.rightShift ? cast(typeof(return))this : null; }
-        inout(UshrExp)     isUshrExp() { return op == TOK.unsignedRightShift ? cast(typeof(return))this : null; }
-        inout(AndExp)      isAndExp() { return op == TOK.and ? cast(typeof(return))this : null; }
-        inout(OrExp)       isOrExp() { return op == TOK.or ? cast(typeof(return))this : null; }
-        inout(XorExp)      isXorExp() { return op == TOK.xor ? cast(typeof(return))this : null; }
-        inout(LogicalExp)  isLogicalExp() { return (op == TOK.andAnd || op == TOK.orOr) ? cast(typeof(return))this : null; }
-        //inout(CmpExp)    isCmpExp() { return op == TOK. ? cast(typeof(return))this : null; }
-        inout(InExp)       isInExp() { return op == TOK.in_ ? cast(typeof(return))this : null; }
-        inout(RemoveExp)   isRemoveExp() { return op == TOK.remove ? cast(typeof(return))this : null; }
-        inout(EqualExp)    isEqualExp() { return (op == TOK.equal || op == TOK.notEqual) ? cast(typeof(return))this : null; }
-        inout(IdentityExp) isIdentityExp() { return (op == TOK.identity || op == TOK.notIdentity) ? cast(typeof(return))this : null; }
-        inout(CondExp)     isCondExp() { return op == TOK.question ? cast(typeof(return))this : null; }
-        inout(GenericExp)  isGenericExp() { return op == TOK._Generic ? cast(typeof(return))this : null; }
+        inout(AddExp)      isAddExp() { return op == EXP.add ? cast(typeof(return))this : null; }
+        inout(MinExp)      isMinExp() { return op == EXP.min ? cast(typeof(return))this : null; }
+        inout(CatExp)      isCatExp() { return op == EXP.concatenate ? cast(typeof(return))this : null; }
+        inout(MulExp)      isMulExp() { return op == EXP.mul ? cast(typeof(return))this : null; }
+        inout(DivExp)      isDivExp() { return op == EXP.div ? cast(typeof(return))this : null; }
+        inout(ModExp)      isModExp() { return op == EXP.mod ? cast(typeof(return))this : null; }
+        inout(PowExp)      isPowExp() { return op == EXP.pow ? cast(typeof(return))this : null; }
+        inout(ShlExp)      isShlExp() { return op == EXP.leftShift ? cast(typeof(return))this : null; }
+        inout(ShrExp)      isShrExp() { return op == EXP.rightShift ? cast(typeof(return))this : null; }
+        inout(UshrExp)     isUshrExp() { return op == EXP.unsignedRightShift ? cast(typeof(return))this : null; }
+        inout(AndExp)      isAndExp() { return op == EXP.and ? cast(typeof(return))this : null; }
+        inout(OrExp)       isOrExp() { return op == EXP.or ? cast(typeof(return))this : null; }
+        inout(XorExp)      isXorExp() { return op == EXP.xor ? cast(typeof(return))this : null; }
+        inout(LogicalExp)  isLogicalExp() { return (op == EXP.andAnd || op == EXP.orOr) ? cast(typeof(return))this : null; }
+        //inout(CmpExp)    isCmpExp() { return op == EXP. ? cast(typeof(return))this : null; }
+        inout(InExp)       isInExp() { return op == EXP.in_ ? cast(typeof(return))this : null; }
+        inout(RemoveExp)   isRemoveExp() { return op == EXP.remove ? cast(typeof(return))this : null; }
+        inout(EqualExp)    isEqualExp() { return (op == EXP.equal || op == EXP.notEqual) ? cast(typeof(return))this : null; }
+        inout(IdentityExp) isIdentityExp() { return (op == EXP.identity || op == EXP.notIdentity) ? cast(typeof(return))this : null; }
+        inout(CondExp)     isCondExp() { return op == EXP.question ? cast(typeof(return))this : null; }
+        inout(GenericExp)  isGenericExp() { return op == EXP._Generic ? cast(typeof(return))this : null; }
         inout(DefaultInitExp)    isDefaultInitExp() { return isDefaultInitOp(op) ? cast(typeof(return))this: null; }
-        inout(FileInitExp)       isFileInitExp() { return (op == TOK.file || op == TOK.fileFullPath) ? cast(typeof(return))this : null; }
-        inout(LineInitExp)       isLineInitExp() { return op == TOK.line ? cast(typeof(return))this : null; }
-        inout(ModuleInitExp)     isModuleInitExp() { return op == TOK.moduleString ? cast(typeof(return))this : null; }
-        inout(FuncInitExp)       isFuncInitExp() { return op == TOK.functionString ? cast(typeof(return))this : null; }
-        inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == TOK.prettyFunction ? cast(typeof(return))this : null; }
-        inout(ClassReferenceExp) isClassReferenceExp() { return op == TOK.classReference ? cast(typeof(return))this : null; }
-        inout(ThrownExceptionExp) isThrownExceptionExp() { return op == TOK.thrownException ? cast(typeof(return))this : null; }
+        inout(FileInitExp)       isFileInitExp() { return (op == EXP.file || op == EXP.fileFullPath) ? cast(typeof(return))this : null; }
+        inout(LineInitExp)       isLineInitExp() { return op == EXP.line ? cast(typeof(return))this : null; }
+        inout(ModuleInitExp)     isModuleInitExp() { return op == EXP.moduleString ? cast(typeof(return))this : null; }
+        inout(FuncInitExp)       isFuncInitExp() { return op == EXP.functionString ? cast(typeof(return))this : null; }
+        inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == EXP.prettyFunction ? cast(typeof(return))this : null; }
+        inout(ClassReferenceExp) isClassReferenceExp() { return op == EXP.classReference ? cast(typeof(return))this : null; }
+        inout(ThrownExceptionExp) isThrownExceptionExp() { return op == EXP.thrownException ? cast(typeof(return))this : null; }
     }
 
     inout(BinAssignExp) isBinAssignExp() pure inout nothrow @nogc
@@ -1754,7 +1754,7 @@ extern (C++) final class IntegerExp : Expression
 
     extern (D) this(const ref Loc loc, dinteger_t value, Type type)
     {
-        super(loc, TOK.int64, __traits(classInstanceSize, IntegerExp));
+        super(loc, EXP.int64, __traits(classInstanceSize, IntegerExp));
         //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
         assert(type);
         if (!type.isscalar())
@@ -1770,7 +1770,7 @@ extern (C++) final class IntegerExp : Expression
 
     extern (D) this(dinteger_t value)
     {
-        super(Loc.initial, TOK.int64, __traits(classInstanceSize, IntegerExp));
+        super(Loc.initial, EXP.int64, __traits(classInstanceSize, IntegerExp));
         this.type = Type.tint32;
         this.value = cast(d_int32)value;
     }
@@ -1827,10 +1827,10 @@ extern (C++) final class IntegerExp : Expression
         return complex_t(toReal());
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
         bool r = toInteger() != 0;
-        return result ? r : !r;
+        return typeof(return)(r);
     }
 
     override Expression toLvalue(Scope* sc, Expression e)
@@ -1969,7 +1969,7 @@ extern (C++) final class ErrorExp : Expression
 {
     private extern (D) this()
     {
-        super(Loc.initial, TOK.error, __traits(classInstanceSize, ErrorExp));
+        super(Loc.initial, EXP.error, __traits(classInstanceSize, ErrorExp));
         type = Type.terror;
     }
 
@@ -2015,7 +2015,7 @@ extern (C++) final class VoidInitExp : Expression
 
     extern (D) this(VarDeclaration var)
     {
-        super(var.loc, TOK.void_, __traits(classInstanceSize, VoidInitExp));
+        super(var.loc, EXP.void_, __traits(classInstanceSize, VoidInitExp));
         this.var = var;
         this.type = var.type;
     }
@@ -2040,7 +2040,7 @@ extern (C++) final class RealExp : Expression
 
     extern (D) this(const ref Loc loc, real_t value, Type type)
     {
-        super(loc, TOK.float64, __traits(classInstanceSize, RealExp));
+        super(loc, EXP.float64, __traits(classInstanceSize, RealExp));
         //printf("RealExp::RealExp(%Lg)\n", value);
         this.value = value;
         this.type = type;
@@ -2096,9 +2096,9 @@ extern (C++) final class RealExp : Expression
         return complex_t(toReal(), toImaginary());
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return result ? cast(bool)value : !cast(bool)value;
+        return typeof(return)(!!value);
     }
 
     override void accept(Visitor v)
@@ -2115,7 +2115,7 @@ extern (C++) final class ComplexExp : Expression
 
     extern (D) this(const ref Loc loc, complex_t value, Type type)
     {
-        super(loc, TOK.complex80, __traits(classInstanceSize, ComplexExp));
+        super(loc, EXP.complex80, __traits(classInstanceSize, ComplexExp));
         this.value = value;
         this.type = type;
         //printf("ComplexExp::ComplexExp(%s)\n", toChars());
@@ -2171,12 +2171,9 @@ extern (C++) final class ComplexExp : Expression
         return value;
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        if (result)
-            return cast(bool)value;
-        else
-            return !value;
+        return typeof(return)(!!value);
     }
 
     override void accept(Visitor v)
@@ -2193,7 +2190,7 @@ extern (C++) class IdentifierExp : Expression
 
     extern (D) this(const ref Loc loc, Identifier ident)
     {
-        super(loc, TOK.identifier, __traits(classInstanceSize, IdentifierExp));
+        super(loc, EXP.identifier, __traits(classInstanceSize, IdentifierExp));
         this.ident = ident;
     }
 
@@ -2243,7 +2240,7 @@ extern (C++) final class DsymbolExp : Expression
 
     extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true)
     {
-        super(loc, TOK.dSymbol, __traits(classInstanceSize, DsymbolExp));
+        super(loc, EXP.dSymbol, __traits(classInstanceSize, DsymbolExp));
         this.s = s;
         this.hasOverloads = hasOverloads;
     }
@@ -2273,11 +2270,11 @@ extern (C++) class ThisExp : Expression
 
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.this_, __traits(classInstanceSize, ThisExp));
+        super(loc, EXP.this_, __traits(classInstanceSize, ThisExp));
         //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
     }
 
-    this(const ref Loc loc, const TOK tok)
+    this(const ref Loc loc, const EXP tok)
     {
         super(loc, tok, __traits(classInstanceSize, ThisExp));
         //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
@@ -2292,9 +2289,10 @@ extern (C++) class ThisExp : Expression
         return r;
     }
 
-    override final bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return result;
+        // `this` is never null (what about structs?)
+        return typeof(return)(true);
     }
 
     override final bool isLvalue()
@@ -2326,7 +2324,7 @@ extern (C++) final class SuperExp : ThisExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.super_);
+        super(loc, EXP.super_);
     }
 
     override void accept(Visitor v)
@@ -2342,7 +2340,7 @@ extern (C++) final class NullExp : Expression
 {
     extern (D) this(const ref Loc loc, Type type = null)
     {
-        super(loc, TOK.null_, __traits(classInstanceSize, NullExp));
+        super(loc, EXP.null_, __traits(classInstanceSize, NullExp));
         this.type = type;
     }
 
@@ -2350,7 +2348,7 @@ extern (C++) final class NullExp : Expression
     {
         if (auto e = o.isExpression())
         {
-            if (e.op == TOK.null_ && type.equals(e.type))
+            if (e.op == EXP.null_ && type.equals(e.type))
             {
                 return true;
             }
@@ -2358,9 +2356,10 @@ extern (C++) final class NullExp : Expression
         return false;
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return result ? false : true;
+        // null in any type is false
+        return typeof(return)(false);
     }
 
     override StringExp toStringExp()
@@ -2400,7 +2399,7 @@ extern (C++) final class StringExp : Expression
 
     extern (D) this(const ref Loc loc, const(void)[] string)
     {
-        super(loc, TOK.string_, __traits(classInstanceSize, StringExp));
+        super(loc, EXP.string_, __traits(classInstanceSize, StringExp));
         this.string = cast(char*)string.ptr; // note that this.string should be const
         this.len = string.length;
         this.sz = 1;                    // work around LDC bug #1286
@@ -2408,7 +2407,7 @@ extern (C++) final class StringExp : Expression
 
     extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix)
     {
-        super(loc, TOK.string_, __traits(classInstanceSize, StringExp));
+        super(loc, EXP.string_, __traits(classInstanceSize, StringExp));
         this.string = cast(char*)string.ptr; // note that this.string should be const
         this.len = len;
         this.sz = sz;
@@ -2681,9 +2680,11 @@ extern (C++) final class StringExp : Expression
         return cast(int)(len1 - len2);
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return result;
+        // Keep the old behaviour for this refactoring
+        // Should probably match language spec instead and check for length
+        return typeof(return)(true);
     }
 
     override bool isLvalue()
@@ -2810,7 +2811,7 @@ extern (C++) final class TupleExp : Expression
 
     extern (D) this(const ref Loc loc, Expression e0, Expressions* exps)
     {
-        super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp));
+        super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
         //printf("TupleExp(this = %p)\n", this);
         this.e0 = e0;
         this.exps = exps;
@@ -2818,14 +2819,14 @@ extern (C++) final class TupleExp : Expression
 
     extern (D) this(const ref Loc loc, Expressions* exps)
     {
-        super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp));
+        super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
         //printf("TupleExp(this = %p)\n", this);
         this.exps = exps;
     }
 
     extern (D) this(const ref Loc loc, TupleDeclaration tup)
     {
-        super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp));
+        super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
         this.exps = new Expressions();
 
         this.exps.reserve(tup.objects.dim);
@@ -2919,14 +2920,14 @@ extern (C++) final class ArrayLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, Type type, Expressions* elements)
     {
-        super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+        super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
         this.type = type;
         this.elements = elements;
     }
 
     extern (D) this(const ref Loc loc, Type type, Expression e)
     {
-        super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+        super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
         this.type = type;
         elements = new Expressions();
         elements.push(e);
@@ -2934,7 +2935,7 @@ extern (C++) final class ArrayLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements)
     {
-        super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
+        super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
         this.type = type;
         this.basis = basis;
         this.elements = elements;
@@ -3000,10 +3001,10 @@ extern (C++) final class ArrayLiteralExp : Expression
         return el ? el : basis;
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
         size_t dim = elements ? elements.dim : 0;
-        return result ? (dim != 0) : (dim == 0);
+        return typeof(return)(dim != 0);
     }
 
     override StringExp toStringExp()
@@ -3023,7 +3024,7 @@ extern (C++) final class ArrayLiteralExp : Expression
                 foreach (i; 0 .. elements.dim)
                 {
                     auto ch = this[i];
-                    if (ch.op != TOK.int64)
+                    if (ch.op != EXP.int64)
                         return null;
                     if (sz == 1)
                         buf.writeByte(cast(uint)ch.toInteger());
@@ -3079,7 +3080,7 @@ extern (C++) final class AssocArrayLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values)
     {
-        super(loc, TOK.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp));
+        super(loc, EXP.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp));
         assert(keys.dim == values.dim);
         this.keys = keys;
         this.values = values;
@@ -3119,10 +3120,10 @@ extern (C++) final class AssocArrayLiteralExp : Expression
         return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
         size_t dim = keys.dim;
-        return result ? (dim != 0) : (dim == 0);
+        return typeof(return)(dim != 0);
     }
 
     override void accept(Visitor v)
@@ -3172,7 +3173,7 @@ extern (C++) final class StructLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null)
     {
-        super(loc, TOK.structLiteral, __traits(classInstanceSize, StructLiteralExp));
+        super(loc, EXP.structLiteral, __traits(classInstanceSize, StructLiteralExp));
         this.sd = sd;
         if (!elements)
             elements = new Expressions();
@@ -3278,9 +3279,12 @@ extern (C++) final class StructLiteralExp : Expression
          */
         if (elements.dim)
         {
+            const sz = type.size();
+            if (sz == SIZE_INVALID)
+                return -1;
             foreach (i, v; sd.fields)
             {
-                if (offset == v.offset && type.size() == v.type.size())
+                if (offset == v.offset && sz == v.type.size())
                 {
                     /* context fields might not be filled. */
                     if (i >= sd.nonHiddenFields())
@@ -3348,7 +3352,7 @@ extern (C++) final class CompoundLiteralExp : Expression
 
     extern (D) this(const ref Loc loc, Type type_name, Initializer initializer)
     {
-        super(loc, TOK.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp));
+        super(loc, EXP.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp));
         super.type = type_name;
         this.initializer = initializer;
         //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars());
@@ -3367,7 +3371,7 @@ extern (C++) final class TypeExp : Expression
 {
     extern (D) this(const ref Loc loc, Type type)
     {
-        super(loc, TOK.type, __traits(classInstanceSize, TypeExp));
+        super(loc, EXP.type, __traits(classInstanceSize, TypeExp));
         //printf("TypeExp::TypeExp(%s)\n", type.toChars());
         this.type = type;
     }
@@ -3409,7 +3413,7 @@ extern (C++) final class ScopeExp : Expression
 
     extern (D) this(const ref Loc loc, ScopeDsymbol sds)
     {
-        super(loc, TOK.scope_, __traits(classInstanceSize, ScopeExp));
+        super(loc, EXP.scope_, __traits(classInstanceSize, ScopeExp));
         //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars());
         //static int count; if (++count == 38) *(char*)0=0;
         this.sds = sds;
@@ -3464,7 +3468,7 @@ extern (C++) final class TemplateExp : Expression
 
     extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null)
     {
-        super(loc, TOK.template_, __traits(classInstanceSize, TemplateExp));
+        super(loc, EXP.template_, __traits(classInstanceSize, TemplateExp));
         //printf("TemplateExp(): %s\n", td.toChars());
         this.td = td;
         this.fd = fd;
@@ -3519,7 +3523,7 @@ extern (C++) final class NewExp : Expression
 
     extern (D) this(const ref Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
     {
-        super(loc, TOK.new_, __traits(classInstanceSize, NewExp));
+        super(loc, EXP.new_, __traits(classInstanceSize, NewExp));
         this.thisexp = thisexp;
         this.newargs = newargs;
         this.newtype = newtype;
@@ -3558,7 +3562,7 @@ extern (C++) final class NewAnonClassExp : Expression
 
     extern (D) this(const ref Loc loc, Expression thisexp, Expressions* newargs, ClassDeclaration cd, Expressions* arguments)
     {
-        super(loc, TOK.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp));
+        super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp));
         this.thisexp = thisexp;
         this.newargs = newargs;
         this.cd = cd;
@@ -3584,7 +3588,7 @@ extern (C++) class SymbolExp : Expression
     Dsymbol originalScope; // original scope before inlining
     bool hasOverloads;
 
-    extern (D) this(const ref Loc loc, TOK op, int size, Declaration var, bool hasOverloads)
+    extern (D) this(const ref Loc loc, EXP op, int size, Declaration var, bool hasOverloads)
     {
         super(loc, op, size);
         assert(var);
@@ -3615,13 +3619,13 @@ extern (C++) final class SymOffExp : SymbolExp
                 .error(loc, "need `this` for address of `%s`", v.toChars());
             hasOverloads = false;
         }
-        super(loc, TOK.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads);
+        super(loc, EXP.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads);
         this.offset = offset;
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return result ? true : false;
+        return typeof(return)(true);
     }
 
     override void accept(Visitor v)
@@ -3641,7 +3645,7 @@ extern (C++) final class VarExp : SymbolExp
         if (var.isVarDeclaration())
             hasOverloads = false;
 
-        super(loc, TOK.variable, __traits(classInstanceSize, VarExp), var, hasOverloads);
+        super(loc, EXP.variable, __traits(classInstanceSize, VarExp), var, hasOverloads);
         //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
         //if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
         this.type = var.type;
@@ -3725,7 +3729,7 @@ extern (C++) final class OverExp : Expression
 
     extern (D) this(const ref Loc loc, OverloadSet s)
     {
-        super(loc, TOK.overloadSet, __traits(classInstanceSize, OverExp));
+        super(loc, EXP.overloadSet, __traits(classInstanceSize, OverExp));
         //printf("OverExp(this = %p, '%s')\n", this, var.toChars());
         vars = s;
         type = Type.tvoid;
@@ -3755,11 +3759,11 @@ extern (C++) final class FuncExp : Expression
 {
     FuncLiteralDeclaration fd;
     TemplateDeclaration td;
-    TOK tok;
+    TOK tok;  // TOK.reserved, TOK.delegate_, TOK.function_
 
     extern (D) this(const ref Loc loc, Dsymbol s)
     {
-        super(loc, TOK.function_, __traits(classInstanceSize, FuncExp));
+        super(loc, EXP.function_, __traits(classInstanceSize, FuncExp));
         this.td = s.isTemplateDeclaration();
         this.fd = s.isFuncLiteralDeclaration();
         if (td)
@@ -3927,7 +3931,7 @@ extern (C++) final class FuncExp : Expression
             // Reset inference target for the later re-semantic
             fd.treq = null;
 
-            if (ex.op == TOK.error)
+            if (ex.op == EXP.error)
                 return MATCH.nomatch;
             if (auto ef = ex.isFuncExp())
                 return ef.matchType(to, sc, presult, flag);
@@ -4057,7 +4061,7 @@ extern (C++) final class DeclarationExp : Expression
 
     extern (D) this(const ref Loc loc, Dsymbol declaration)
     {
-        super(loc, TOK.declaration, __traits(classInstanceSize, DeclarationExp));
+        super(loc, EXP.declaration, __traits(classInstanceSize, DeclarationExp));
         this.declaration = declaration;
     }
 
@@ -4090,7 +4094,7 @@ extern (C++) final class TypeidExp : Expression
 
     extern (D) this(const ref Loc loc, RootObject o)
     {
-        super(loc, TOK.typeid_, __traits(classInstanceSize, TypeidExp));
+        super(loc, EXP.typeid_, __traits(classInstanceSize, TypeidExp));
         this.obj = o;
     }
 
@@ -4115,7 +4119,7 @@ extern (C++) final class TraitsExp : Expression
 
     extern (D) this(const ref Loc loc, Identifier ident, Objects* args)
     {
-        super(loc, TOK.traits, __traits(classInstanceSize, TraitsExp));
+        super(loc, EXP.traits, __traits(classInstanceSize, TraitsExp));
         this.ident = ident;
         this.args = args;
     }
@@ -4137,7 +4141,7 @@ extern (C++) final class HaltExp : Expression
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.halt, __traits(classInstanceSize, HaltExp));
+        super(loc, EXP.halt, __traits(classInstanceSize, HaltExp));
     }
 
     override void accept(Visitor v)
@@ -4161,7 +4165,7 @@ extern (C++) final class IsExp : Expression
 
     extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters)
     {
-        super(loc, TOK.is_, __traits(classInstanceSize, IsExp));
+        super(loc, EXP.is_, __traits(classInstanceSize, IsExp));
         this.targ = targ;
         this.id = id;
         this.tok = tok;
@@ -4196,7 +4200,7 @@ extern (C++) abstract class UnaExp : Expression
     Expression e1;
     Type att1;      // Save alias this type to detect recursion
 
-    extern (D) this(const ref Loc loc, TOK op, int size, Expression e1)
+    extern (D) this(const ref Loc loc, EXP op, int size, Expression e1)
     {
         super(loc, op, size);
         this.e1 = e1;
@@ -4221,13 +4225,13 @@ extern (C++) abstract class UnaExp : Expression
         if (e1.type.toBasetype() == Type.terror)
             return e1;
 
-        if (e1.op == TOK.type)
+        if (e1.op == EXP.type)
         {
-            error("incompatible type for `%s(%s)`: cannot use `%s` with types", Token.toChars(op), e1.toChars(), Token.toChars(op));
+            error("incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(op).ptr, e1.toChars(), EXPtoString(op).ptr);
         }
         else
         {
-            error("incompatible type for `%s(%s)`: `%s`", Token.toChars(op), e1.toChars(), e1.type.toChars());
+            error("incompatible type for `%s(%s)`: `%s`", EXPtoString(op).ptr, e1.toChars(), e1.type.toChars());
         }
         return ErrorExp.get();
     }
@@ -4257,7 +4261,7 @@ extern (C++) abstract class UnaExp : Expression
 }
 
 alias fp_t = UnionExp function(const ref Loc loc, Type, Expression, Expression);
-alias fp2_t = bool function(const ref Loc loc, TOK, Expression, Expression);
+alias fp2_t = bool function(const ref Loc loc, EXP, Expression, Expression);
 
 /***********************************************************
  */
@@ -4268,7 +4272,7 @@ extern (C++) abstract class BinExp : Expression
     Type att1;      // Save alias this type to detect recursion
     Type att2;      // Save alias this type to detect recursion
 
-    extern (D) this(const ref Loc loc, TOK op, int size, Expression e1, Expression e2)
+    extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2)
     {
         super(loc, op, size);
         this.e1 = e1;
@@ -4298,22 +4302,22 @@ extern (C++) abstract class BinExp : Expression
             return e2;
 
         // CondExp uses 'a ? b : c' but we're comparing 'b : c'
-        TOK thisOp = (op == TOK.question) ? TOK.colon : op;
-        if (e1.op == TOK.type || e2.op == TOK.type)
+        const(char)* thisOp = (op == EXP.question) ? ":" : EXPtoString(op).ptr;
+        if (e1.op == EXP.type || e2.op == EXP.type)
         {
             error("incompatible types for `(%s) %s (%s)`: cannot use `%s` with types",
-                e1.toChars(), Token.toChars(thisOp), e2.toChars(), Token.toChars(op));
+                e1.toChars(), thisOp, e2.toChars(), EXPtoString(op).ptr);
         }
         else if (e1.type.equals(e2.type))
         {
             error("incompatible types for `(%s) %s (%s)`: both operands are of type `%s`",
-                e1.toChars(), Token.toChars(thisOp), e2.toChars(), e1.type.toChars());
+                e1.toChars(), thisOp, e2.toChars(), e1.type.toChars());
         }
         else
         {
             auto ts = toAutoQualChars(e1.type, e2.type);
             error("incompatible types for `(%s) %s (%s)`: `%s` and `%s`",
-                e1.toChars(), Token.toChars(thisOp), e2.toChars(), ts[0], ts[1]);
+                e1.toChars(), thisOp, e2.toChars(), ts[0], ts[1]);
         }
         return ErrorExp.get();
     }
@@ -4327,22 +4331,22 @@ extern (C++) abstract class BinExp : Expression
         // T opAssign floating yields a floating. Prevent truncating conversions (float to int).
         // See issue 3841.
         // Should we also prevent double to float (type.isfloating() && type.size() < t2.size()) ?
-        if (op == TOK.addAssign || op == TOK.minAssign ||
-            op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign ||
-            op == TOK.powAssign)
+        if (op == EXP.addAssign || op == EXP.minAssign ||
+            op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign ||
+            op == EXP.powAssign)
         {
             if ((type.isintegral() && t2.isfloating()))
             {
-                warning("`%s %s %s` is performing truncating conversion", type.toChars(), Token.toChars(op), t2.toChars());
+                warning("`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars());
             }
         }
 
         // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
-        if (op == TOK.mulAssign || op == TOK.divAssign || op == TOK.modAssign)
+        if (op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign)
         {
             // Any multiplication by an imaginary or complex number yields a complex result.
             // r *= c, i*=c, r*=i, i*=i are all forbidden operations.
-            const(char)* opstr = Token.toChars(op);
+            const(char)* opstr = EXPtoString(op).ptr;
             if (t1.isreal() && t2.iscomplex())
             {
                 error("`%s %s %s` is undefined. Did you mean `%s %s %s.re`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
@@ -4361,13 +4365,13 @@ extern (C++) abstract class BinExp : Expression
         }
 
         // generate an error if this is a nonsensical += or -=, eg real += imaginary
-        if (op == TOK.addAssign || op == TOK.minAssign)
+        if (op == EXP.addAssign || op == EXP.minAssign)
         {
             // Addition or subtraction of a real and an imaginary is a complex result.
             // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
             if ((t1.isreal() && (t2.isimaginary() || t2.iscomplex())) || (t1.isimaginary() && (t2.isreal() || t2.iscomplex())))
             {
-                error("`%s %s %s` is undefined (result is complex)", t1.toChars(), Token.toChars(op), t2.toChars());
+                error("`%s %s %s` is undefined (result is complex)", t1.toChars(), EXPtoString(op).ptr, t2.toChars());
                 return ErrorExp.get();
             }
             if (type.isreal() || type.isimaginary())
@@ -4376,7 +4380,7 @@ extern (C++) abstract class BinExp : Expression
                 e2 = e2.castTo(sc, t1);
             }
         }
-        if (op == TOK.mulAssign)
+        if (op == EXP.mulAssign)
         {
             if (t2.isfloating())
             {
@@ -4413,7 +4417,7 @@ extern (C++) abstract class BinExp : Expression
                 }
             }
         }
-        else if (op == TOK.divAssign)
+        else if (op == EXP.divAssign)
         {
             if (t2.isimaginary())
             {
@@ -4454,7 +4458,7 @@ extern (C++) abstract class BinExp : Expression
                 }
             }
         }
-        else if (op == TOK.modAssign)
+        else if (op == EXP.modAssign)
         {
             if (t2.iscomplex())
             {
@@ -4558,7 +4562,7 @@ extern (C++) abstract class BinExp : Expression
  */
 extern (C++) class BinAssignExp : BinExp
 {
-    extern (D) this(const ref Loc loc, TOK op, int size, Expression e1, Expression e2)
+    extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2)
     {
         super(loc, op, size, e1, e2);
     }
@@ -4600,7 +4604,7 @@ extern (C++) final class MixinExp : Expression
 
     extern (D) this(const ref Loc loc, Expressions* exps)
     {
-        super(loc, TOK.mixin_, __traits(classInstanceSize, MixinExp));
+        super(loc, EXP.mixin_, __traits(classInstanceSize, MixinExp));
         this.exps = exps;
     }
 
@@ -4643,7 +4647,7 @@ extern (C++) final class ImportExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.import_, __traits(classInstanceSize, ImportExp), e);
+        super(loc, EXP.import_, __traits(classInstanceSize, ImportExp), e);
     }
 
     override void accept(Visitor v)
@@ -4661,7 +4665,7 @@ extern (C++) final class AssertExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Expression msg = null)
     {
-        super(loc, TOK.assert_, __traits(classInstanceSize, AssertExp), e);
+        super(loc, EXP.assert_, __traits(classInstanceSize, AssertExp), e);
         this.msg = msg;
     }
 
@@ -4687,7 +4691,7 @@ extern (C++) final class DotIdExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Identifier ident)
     {
-        super(loc, TOK.dotIdentifier, __traits(classInstanceSize, DotIdExp), e);
+        super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e);
         this.ident = ident;
     }
 
@@ -4711,7 +4715,7 @@ extern (C++) final class DotTemplateExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td)
     {
-        super(loc, TOK.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e);
+        super(loc, EXP.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e);
         this.td = td;
     }
 
@@ -4745,7 +4749,7 @@ extern (C++) final class DotVarExp : UnaExp
         if (var.isVarDeclaration())
             hasOverloads = false;
 
-        super(loc, TOK.dotVariable, __traits(classInstanceSize, DotVarExp), e);
+        super(loc, EXP.dotVariable, __traits(classInstanceSize, DotVarExp), e);
         //printf("DotVarExp()\n");
         this.var = var;
         this.hasOverloads = hasOverloads;
@@ -4753,7 +4757,7 @@ extern (C++) final class DotVarExp : UnaExp
 
     override bool isLvalue()
     {
-        if (e1.op != TOK.structLiteral)
+        if (e1.op != EXP.structLiteral)
             return true;
         auto vd = var.isVarDeclaration();
         return !(vd && vd.isField());
@@ -4772,7 +4776,7 @@ extern (C++) final class DotVarExp : UnaExp
         }
         if (!isLvalue())
             return Expression.toLvalue(sc, e);
-        if (e1.op == TOK.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
+        if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
         {
             if (VarDeclaration vd = var.isVarDeclaration())
             {
@@ -4826,14 +4830,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs)
     {
-        super(loc, TOK.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
+        super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
         //printf("DotTemplateInstanceExp()\n");
         this.ti = new TemplateInstance(loc, name, tiargs);
     }
 
     extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti)
     {
-        super(loc, TOK.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
+        super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
         this.ti = ti;
     }
 
@@ -4853,29 +4857,29 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
 
         Expression e = new DotIdExp(loc, e1, ti.name);
         e = e.expressionSemantic(sc);
-        if (e.op == TOK.dot)
+        if (e.op == EXP.dot)
             e = (cast(DotExp)e).e2;
 
         Dsymbol s = null;
         switch (e.op)
         {
-        case TOK.overloadSet:
+        case EXP.overloadSet:
             s = (cast(OverExp)e).vars;
             break;
 
-        case TOK.dotTemplateDeclaration:
+        case EXP.dotTemplateDeclaration:
             s = (cast(DotTemplateExp)e).td;
             break;
 
-        case TOK.scope_:
+        case EXP.scope_:
             s = (cast(ScopeExp)e).sds;
             break;
 
-        case TOK.dotVariable:
+        case EXP.dotVariable:
             s = (cast(DotVarExp)e).var;
             break;
 
-        case TOK.variable:
+        case EXP.variable:
             s = (cast(VarExp)e).var;
             break;
 
@@ -4926,7 +4930,7 @@ extern (C++) final class DelegateExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null)
     {
-        super(loc, TOK.delegate_, __traits(classInstanceSize, DelegateExp), e);
+        super(loc, EXP.delegate_, __traits(classInstanceSize, DelegateExp), e);
         this.func = f;
         this.hasOverloads = hasOverloads;
         this.vthis2 = vthis2;
@@ -4946,7 +4950,7 @@ extern (C++) final class DotTypeExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Dsymbol s)
     {
-        super(loc, TOK.dotType, __traits(classInstanceSize, DotTypeExp), e);
+        super(loc, EXP.dotType, __traits(classInstanceSize, DotTypeExp), e);
         this.sym = s;
     }
 
@@ -4969,18 +4973,18 @@ extern (C++) final class CallExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Expressions* exps)
     {
-        super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
         this.arguments = exps;
     }
 
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
     }
 
     extern (D) this(const ref Loc loc, Expression e, Expression earg1)
     {
-        super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
         this.arguments = new Expressions();
         if (earg1)
             this.arguments.push(earg1);
@@ -4988,7 +4992,7 @@ extern (C++) final class CallExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2)
     {
-        super(loc, TOK.call, __traits(classInstanceSize, CallExp), e);
+        super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
         auto arguments = new Expressions(2);
         (*arguments)[0] = earg1;
         (*arguments)[1] = earg2;
@@ -5143,7 +5147,7 @@ extern (C++) final class AddrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.address, __traits(classInstanceSize, AddrExp), e);
+        super(loc, EXP.address, __traits(classInstanceSize, AddrExp), e);
     }
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
@@ -5164,14 +5168,14 @@ extern (C++) final class PtrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.star, __traits(classInstanceSize, PtrExp), e);
+        super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e);
         //if (e.type)
         //  type = ((TypePointer *)e.type).next;
     }
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
     {
-        super(loc, TOK.star, __traits(classInstanceSize, PtrExp), e);
+        super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e);
         type = t;
     }
 
@@ -5216,7 +5220,7 @@ extern (C++) final class NegExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.negate, __traits(classInstanceSize, NegExp), e);
+        super(loc, EXP.negate, __traits(classInstanceSize, NegExp), e);
     }
 
     override void accept(Visitor v)
@@ -5231,7 +5235,7 @@ extern (C++) final class UAddExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.uadd, __traits(classInstanceSize, UAddExp), e);
+        super(loc, EXP.uadd, __traits(classInstanceSize, UAddExp), e);
     }
 
     override void accept(Visitor v)
@@ -5246,7 +5250,7 @@ extern (C++) final class ComExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.tilde, __traits(classInstanceSize, ComExp), e);
+        super(loc, EXP.tilde, __traits(classInstanceSize, ComExp), e);
     }
 
     override void accept(Visitor v)
@@ -5261,7 +5265,7 @@ extern (C++) final class NotExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e)
     {
-        super(loc, TOK.not, __traits(classInstanceSize, NotExp), e);
+        super(loc, EXP.not, __traits(classInstanceSize, NotExp), e);
     }
 
     override void accept(Visitor v)
@@ -5278,7 +5282,7 @@ extern (C++) final class DeleteExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, bool isRAII)
     {
-        super(loc, TOK.delete_, __traits(classInstanceSize, DeleteExp), e);
+        super(loc, EXP.delete_, __traits(classInstanceSize, DeleteExp), e);
         this.isRAII = isRAII;
     }
 
@@ -5298,7 +5302,7 @@ extern (C++) final class CastExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
     {
-        super(loc, TOK.cast_, __traits(classInstanceSize, CastExp), e);
+        super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e);
         this.to = t;
     }
 
@@ -5306,7 +5310,7 @@ extern (C++) final class CastExp : UnaExp
      */
     extern (D) this(const ref Loc loc, Expression e, ubyte mod)
     {
-        super(loc, TOK.cast_, __traits(classInstanceSize, CastExp), e);
+        super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e);
         this.mod = mod;
     }
 
@@ -5360,7 +5364,7 @@ extern (C++) final class VectorExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e, Type t)
     {
-        super(loc, TOK.vector, __traits(classInstanceSize, VectorExp), e);
+        super(loc, EXP.vector, __traits(classInstanceSize, VectorExp), e);
         assert(t.ty == Tvector);
         to = cast(TypeVector)t;
     }
@@ -5396,7 +5400,7 @@ extern (C++) final class VectorArrayExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, TOK.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1);
+        super(loc, EXP.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1);
     }
 
     override bool isLvalue()
@@ -5434,14 +5438,14 @@ extern (C++) final class SliceExp : UnaExp
     /************************************************************/
     extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie)
     {
-        super(loc, TOK.slice, __traits(classInstanceSize, SliceExp), e1);
+        super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1);
         this.upr = ie ? ie.upr : null;
         this.lwr = ie ? ie.lwr : null;
     }
 
     extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr)
     {
-        super(loc, TOK.slice, __traits(classInstanceSize, SliceExp), e1);
+        super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1);
         this.upr = upr;
         this.lwr = lwr;
     }
@@ -5473,9 +5477,9 @@ extern (C++) final class SliceExp : UnaExp
         return this;
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return e1.isBool(result);
+        return e1.toBool();
     }
 
     override void accept(Visitor v)
@@ -5490,7 +5494,7 @@ extern (C++) final class ArrayLengthExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, TOK.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1);
+        super(loc, EXP.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1);
     }
 
     override void accept(Visitor v)
@@ -5513,7 +5517,7 @@ extern (C++) final class ArrayExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expression index = null)
     {
-        super(loc, TOK.array, __traits(classInstanceSize, ArrayExp), e1);
+        super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1);
         arguments = new Expressions();
         if (index)
             arguments.push(index);
@@ -5521,7 +5525,7 @@ extern (C++) final class ArrayExp : UnaExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expressions* args)
     {
-        super(loc, TOK.array, __traits(classInstanceSize, ArrayExp), e1);
+        super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1);
         arguments = args;
     }
 
@@ -5558,7 +5562,7 @@ extern (C++) final class DotExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.dot, __traits(classInstanceSize, DotExp), e1, e2);
+        super(loc, EXP.dot, __traits(classInstanceSize, DotExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -5584,7 +5588,7 @@ extern (C++) final class CommaExp : BinExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true)
     {
-        super(loc, TOK.comma, __traits(classInstanceSize, CommaExp), e1, e2);
+        super(loc, EXP.comma, __traits(classInstanceSize, CommaExp), e1, e2);
         allowCommaExp = isGenerated = generated;
     }
 
@@ -5605,9 +5609,9 @@ extern (C++) final class CommaExp : BinExp
         return this;
     }
 
-    override bool isBool(bool result)
+    override Optional!bool toBool()
     {
-        return e2.isBool(result);
+        return e2.toBool();
     }
 
     override Expression addDtorHook(Scope* sc)
@@ -5653,7 +5657,7 @@ extern (C++) final class IntervalExp : Expression
 
     extern (D) this(const ref Loc loc, Expression lwr, Expression upr)
     {
-        super(loc, TOK.interval, __traits(classInstanceSize, IntervalExp));
+        super(loc, EXP.interval, __traits(classInstanceSize, IntervalExp));
         this.lwr = lwr;
         this.upr = upr;
     }
@@ -5673,7 +5677,7 @@ extern (C++) final class DelegatePtrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, TOK.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1);
+        super(loc, EXP.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1);
     }
 
     override bool isLvalue()
@@ -5709,7 +5713,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
 {
     extern (D) this(const ref Loc loc, Expression e1)
     {
-        super(loc, TOK.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1);
+        super(loc, EXP.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1);
     }
 
     override bool isLvalue()
@@ -5750,7 +5754,7 @@ extern (C++) final class IndexExp : BinExp
 
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.index, __traits(classInstanceSize, IndexExp), e1, e2);
+        super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2);
         //printf("IndexExp::IndexExp('%s')\n", toChars());
     }
 
@@ -5763,10 +5767,10 @@ extern (C++) final class IndexExp : BinExp
 
     override bool isLvalue()
     {
-        if (e1.op == TOK.assocArrayLiteral)
+        if (e1.op == EXP.assocArrayLiteral)
             return false;
         if (e1.type.ty == Tsarray ||
-            (e1.op == TOK.index && e1.type.ty != Tarray))
+            (e1.op == EXP.index && e1.type.ty != Tarray))
         {
             return e1.isLvalue();
         }
@@ -5784,7 +5788,7 @@ extern (C++) final class IndexExp : BinExp
     {
         //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
         Expression ex = markSettingAAElem();
-        if (ex.op == TOK.error)
+        if (ex.op == EXP.error)
             return ex;
 
         return Expression.modifiableLvalue(sc, e);
@@ -5805,7 +5809,7 @@ extern (C++) final class IndexExp : BinExp
             if (auto ie = e1.isIndexExp())
             {
                 Expression ex = ie.markSettingAAElem();
-                if (ex.op == TOK.error)
+                if (ex.op == EXP.error)
                     return ex;
                 assert(ex == e1);
             }
@@ -5824,10 +5828,10 @@ extern (C++) final class IndexExp : BinExp
  */
 extern (C++) final class PostExp : BinExp
 {
-    extern (D) this(TOK op, const ref Loc loc, Expression e)
+    extern (D) this(EXP op, const ref Loc loc, Expression e)
     {
         super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1);
-        assert(op == TOK.minusMinus || op == TOK.plusPlus);
+        assert(op == EXP.minusMinus || op == EXP.plusPlus);
     }
 
     override void accept(Visitor v)
@@ -5841,10 +5845,10 @@ extern (C++) final class PostExp : BinExp
  */
 extern (C++) final class PreExp : UnaExp
 {
-    extern (D) this(TOK op, const ref Loc loc, Expression e)
+    extern (D) this(EXP op, const ref Loc loc, Expression e)
     {
         super(loc, op, __traits(classInstanceSize, PreExp), e);
-        assert(op == TOK.preMinusMinus || op == TOK.prePlusPlus);
+        assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus);
     }
 
     override void accept(Visitor v)
@@ -5867,13 +5871,13 @@ extern (C++) class AssignExp : BinExp
     MemorySet memset;
 
     /************************************************************/
-    /* op can be TOK.assign, TOK.construct, or TOK.blit */
+    /* op can be EXP.assign, EXP.construct, or EXP.blit */
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.assign, __traits(classInstanceSize, AssignExp), e1, e2);
+        super(loc, EXP.assign, __traits(classInstanceSize, AssignExp), e1, e2);
     }
 
-    this(const ref Loc loc, TOK tok, Expression e1, Expression e2)
+    this(const ref Loc loc, EXP tok, Expression e1, Expression e2)
     {
         super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2);
     }
@@ -5882,7 +5886,7 @@ extern (C++) class AssignExp : BinExp
     {
         // Array-op 'x[] = y[]' should make an rvalue.
         // Setting array length 'x.length = v' should make an rvalue.
-        if (e1.op == TOK.slice || e1.op == TOK.arrayLength)
+        if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
         {
             return false;
         }
@@ -5891,7 +5895,7 @@ extern (C++) class AssignExp : BinExp
 
     override final Expression toLvalue(Scope* sc, Expression ex)
     {
-        if (e1.op == TOK.slice || e1.op == TOK.arrayLength)
+        if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
         {
             return Expression.toLvalue(sc, ex);
         }
@@ -5915,7 +5919,7 @@ extern (C++) final class ConstructExp : AssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.construct, e1, e2);
+        super(loc, EXP.construct, e1, e2);
     }
 
     // Internal use only. If `v` is a reference variable, the assignment
@@ -5925,7 +5929,7 @@ extern (C++) final class ConstructExp : AssignExp
         auto ve = new VarExp(loc, v);
         assert(v.type && ve.type);
 
-        super(loc, TOK.construct, ve, e2);
+        super(loc, EXP.construct, ve, e2);
 
         if (v.isReference())
             memset = MemorySet.referenceInit;
@@ -5943,7 +5947,7 @@ extern (C++) final class BlitExp : AssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.blit, e1, e2);
+        super(loc, EXP.blit, e1, e2);
     }
 
     // Internal use only. If `v` is a reference variable, the assinment
@@ -5953,7 +5957,7 @@ extern (C++) final class BlitExp : AssignExp
         auto ve = new VarExp(loc, v);
         assert(v.type && ve.type);
 
-        super(loc, TOK.blit, ve, e2);
+        super(loc, EXP.blit, ve, e2);
 
         if (v.isReference())
             memset = MemorySet.referenceInit;
@@ -5971,7 +5975,7 @@ extern (C++) final class AddAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2);
+        super(loc, EXP.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -5986,7 +5990,7 @@ extern (C++) final class MinAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2);
+        super(loc, EXP.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6001,7 +6005,7 @@ extern (C++) final class MulAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2);
+        super(loc, EXP.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6016,7 +6020,7 @@ extern (C++) final class DivAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2);
+        super(loc, EXP.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6031,7 +6035,7 @@ extern (C++) final class ModAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2);
+        super(loc, EXP.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6046,7 +6050,7 @@ extern (C++) final class AndAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2);
+        super(loc, EXP.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6061,7 +6065,7 @@ extern (C++) final class OrAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2);
+        super(loc, EXP.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6076,7 +6080,7 @@ extern (C++) final class XorAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2);
+        super(loc, EXP.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6091,7 +6095,7 @@ extern (C++) final class PowAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2);
+        super(loc, EXP.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6106,7 +6110,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2);
+        super(loc, EXP.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6121,7 +6125,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2);
+        super(loc, EXP.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6136,7 +6140,7 @@ extern (C++) final class UshrAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2);
+        super(loc, EXP.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6148,21 +6152,21 @@ extern (C++) final class UshrAssignExp : BinAssignExp
 /***********************************************************
  * The ~= operator. It can have one of the following operators:
  *
- * TOK.concatenateAssign      - appending T[] to T[]
- * TOK.concatenateElemAssign  - appending T to T[]
- * TOK.concatenateDcharAssign - appending dchar to T[]
+ * EXP.concatenateAssign      - appending T[] to T[]
+ * EXP.concatenateElemAssign  - appending T to T[]
+ * EXP.concatenateDcharAssign - appending dchar to T[]
  *
- * The parser initially sets it to TOK.concatenateAssign, and semantic() later decides which
+ * The parser initially sets it to EXP.concatenateAssign, and semantic() later decides which
  * of the three it will be set to.
  */
 extern (C++) class CatAssignExp : BinAssignExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2);
+        super(loc, EXP.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2);
     }
 
-    extern (D) this(const ref Loc loc, TOK tok, Expression e1, Expression e2)
+    extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2)
     {
         super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2);
     }
@@ -6178,7 +6182,7 @@ extern (C++) final class CatElemAssignExp : CatAssignExp
 {
     extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2)
     {
-        super(loc, TOK.concatenateElemAssign, e1, e2);
+        super(loc, EXP.concatenateElemAssign, e1, e2);
         this.type = type;
     }
 
@@ -6193,7 +6197,7 @@ extern (C++) final class CatDcharAssignExp : CatAssignExp
 {
     extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2)
     {
-        super(loc, TOK.concatenateDcharAssign, e1, e2);
+        super(loc, EXP.concatenateDcharAssign, e1, e2);
         this.type = type;
     }
 
@@ -6210,7 +6214,7 @@ extern (C++) final class AddExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.add, __traits(classInstanceSize, AddExp), e1, e2);
+        super(loc, EXP.add, __traits(classInstanceSize, AddExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6225,7 +6229,7 @@ extern (C++) final class MinExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.min, __traits(classInstanceSize, MinExp), e1, e2);
+        super(loc, EXP.min, __traits(classInstanceSize, MinExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6241,7 +6245,7 @@ extern (C++) final class CatExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.concatenate, __traits(classInstanceSize, CatExp), e1, e2);
+        super(loc, EXP.concatenate, __traits(classInstanceSize, CatExp), e1, e2);
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -6264,7 +6268,7 @@ extern (C++) final class MulExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.mul, __traits(classInstanceSize, MulExp), e1, e2);
+        super(loc, EXP.mul, __traits(classInstanceSize, MulExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6280,7 +6284,7 @@ extern (C++) final class DivExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.div, __traits(classInstanceSize, DivExp), e1, e2);
+        super(loc, EXP.div, __traits(classInstanceSize, DivExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6296,7 +6300,7 @@ extern (C++) final class ModExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.mod, __traits(classInstanceSize, ModExp), e1, e2);
+        super(loc, EXP.mod, __traits(classInstanceSize, ModExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6312,7 +6316,7 @@ extern (C++) final class PowExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.pow, __traits(classInstanceSize, PowExp), e1, e2);
+        super(loc, EXP.pow, __traits(classInstanceSize, PowExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6327,7 +6331,7 @@ extern (C++) final class ShlExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.leftShift, __traits(classInstanceSize, ShlExp), e1, e2);
+        super(loc, EXP.leftShift, __traits(classInstanceSize, ShlExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6342,7 +6346,7 @@ extern (C++) final class ShrExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.rightShift, __traits(classInstanceSize, ShrExp), e1, e2);
+        super(loc, EXP.rightShift, __traits(classInstanceSize, ShrExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6357,7 +6361,7 @@ extern (C++) final class UshrExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2);
+        super(loc, EXP.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6372,7 +6376,7 @@ extern (C++) final class AndExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.and, __traits(classInstanceSize, AndExp), e1, e2);
+        super(loc, EXP.and, __traits(classInstanceSize, AndExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6387,7 +6391,7 @@ extern (C++) final class OrExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.or, __traits(classInstanceSize, OrExp), e1, e2);
+        super(loc, EXP.or, __traits(classInstanceSize, OrExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6402,7 +6406,7 @@ extern (C++) final class XorExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.xor, __traits(classInstanceSize, XorExp), e1, e2);
+        super(loc, EXP.xor, __traits(classInstanceSize, XorExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6417,10 +6421,10 @@ extern (C++) final class XorExp : BinExp
  */
 extern (C++) final class LogicalExp : BinExp
 {
-    extern (D) this(const ref Loc loc, TOK op, Expression e1, Expression e2)
+    extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2)
     {
         super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2);
-        assert(op == TOK.andAnd || op == TOK.orOr);
+        assert(op == EXP.andAnd || op == EXP.orOr);
     }
 
     override void accept(Visitor v)
@@ -6431,16 +6435,16 @@ extern (C++) final class LogicalExp : BinExp
 
 /***********************************************************
  * `op` is one of:
- *      TOK.lessThan, TOK.lessOrEqual, TOK.greaterThan, TOK.greaterOrEqual
+ *      EXP.lessThan, EXP.lessOrEqual, EXP.greaterThan, EXP.greaterOrEqual
  *
  * http://dlang.org/spec/expression.html#relation_expressions
  */
 extern (C++) final class CmpExp : BinExp
 {
-    extern (D) this(TOK op, const ref Loc loc, Expression e1, Expression e2)
+    extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
     {
         super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2);
-        assert(op == TOK.lessThan || op == TOK.lessOrEqual || op == TOK.greaterThan || op == TOK.greaterOrEqual);
+        assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual);
     }
 
     override void accept(Visitor v)
@@ -6455,7 +6459,7 @@ extern (C++) final class InExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.in_, __traits(classInstanceSize, InExp), e1, e2);
+        super(loc, EXP.in_, __traits(classInstanceSize, InExp), e1, e2);
     }
 
     override void accept(Visitor v)
@@ -6471,7 +6475,7 @@ extern (C++) final class RemoveExp : BinExp
 {
     extern (D) this(const ref Loc loc, Expression e1, Expression e2)
     {
-        super(loc, TOK.remove, __traits(classInstanceSize, RemoveExp), e1, e2);
+        super(loc, EXP.remove, __traits(classInstanceSize, RemoveExp), e1, e2);
         type = Type.tbool;
     }
 
@@ -6484,16 +6488,16 @@ extern (C++) final class RemoveExp : BinExp
 /***********************************************************
  * `==` and `!=`
  *
- * TOK.equal and TOK.notEqual
+ * EXP.equal and EXP.notEqual
  *
  * http://dlang.org/spec/expression.html#equality_expressions
  */
 extern (C++) final class EqualExp : BinExp
 {
-    extern (D) this(TOK op, const ref Loc loc, Expression e1, Expression e2)
+    extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
     {
         super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2);
-        assert(op == TOK.equal || op == TOK.notEqual);
+        assert(op == EXP.equal || op == EXP.notEqual);
     }
 
     override void accept(Visitor v)
@@ -6505,16 +6509,16 @@ extern (C++) final class EqualExp : BinExp
 /***********************************************************
  * `is` and `!is`
  *
- * TOK.identity and TOK.notIdentity
+ * EXP.identity and EXP.notIdentity
  *
  *  http://dlang.org/spec/expression.html#identity_expressions
  */
 extern (C++) final class IdentityExp : BinExp
 {
-    extern (D) this(TOK op, const ref Loc loc, Expression e1, Expression e2)
+    extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
     {
         super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2);
-        assert(op == TOK.identity || op == TOK.notIdentity);
+        assert(op == EXP.identity || op == EXP.notIdentity);
     }
 
     override void accept(Visitor v)
@@ -6534,7 +6538,7 @@ extern (C++) final class CondExp : BinExp
 
     extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2)
     {
-        super(loc, TOK.question, __traits(classInstanceSize, CondExp), e1, e2);
+        super(loc, EXP.question, __traits(classInstanceSize, CondExp), e1, e2);
         this.econd = econd;
     }
 
@@ -6623,9 +6627,9 @@ extern (C++) final class CondExp : BinExp
                         //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
                         Expression ve = new VarExp(vcond.loc, vcond);
                         if (isThen)
-                            v.edtor = new LogicalExp(v.edtor.loc, TOK.andAnd, ve, v.edtor);
+                            v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor);
                         else
-                            v.edtor = new LogicalExp(v.edtor.loc, TOK.orOr, ve, v.edtor);
+                            v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor);
                         v.edtor = v.edtor.expressionSemantic(sc);
                         //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
                     }
@@ -6649,18 +6653,18 @@ extern (C++) final class CondExp : BinExp
 }
 
 /// Returns: if this token is the `op` for a derived `DefaultInitExp` class.
-bool isDefaultInitOp(TOK op) pure nothrow @safe @nogc
+bool isDefaultInitOp(EXP op) pure nothrow @safe @nogc
 {
-    return  op == TOK.prettyFunction    || op == TOK.functionString ||
-            op == TOK.line              || op == TOK.moduleString   ||
-            op == TOK.file              || op == TOK.fileFullPath   ;
+    return  op == EXP.prettyFunction    || op == EXP.functionString ||
+            op == EXP.line              || op == EXP.moduleString   ||
+            op == EXP.file              || op == EXP.fileFullPath   ;
 }
 
 /***********************************************************
  */
 extern (C++) class DefaultInitExp : Expression
 {
-    extern (D) this(const ref Loc loc, TOK op, int size)
+    extern (D) this(const ref Loc loc, EXP op, int size)
     {
         super(loc, op, size);
     }
@@ -6675,7 +6679,7 @@ extern (C++) class DefaultInitExp : Expression
  */
 extern (C++) final class FileInitExp : DefaultInitExp
 {
-    extern (D) this(const ref Loc loc, TOK tok)
+    extern (D) this(const ref Loc loc, EXP tok)
     {
         super(loc, tok, __traits(classInstanceSize, FileInitExp));
     }
@@ -6684,7 +6688,7 @@ extern (C++) final class FileInitExp : DefaultInitExp
     {
         //printf("FileInitExp::resolve() %s\n", toChars());
         const(char)* s;
-        if (op == TOK.fileFullPath)
+        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();
@@ -6707,7 +6711,7 @@ extern (C++) final class LineInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.line, __traits(classInstanceSize, LineInitExp));
+        super(loc, EXP.line, __traits(classInstanceSize, LineInitExp));
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -6729,7 +6733,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.moduleString, __traits(classInstanceSize, ModuleInitExp));
+        super(loc, EXP.moduleString, __traits(classInstanceSize, ModuleInitExp));
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -6753,7 +6757,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.functionString, __traits(classInstanceSize, FuncInitExp));
+        super(loc, EXP.functionString, __traits(classInstanceSize, FuncInitExp));
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -6783,7 +6787,7 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp
 {
     extern (D) this(const ref Loc loc)
     {
-        super(loc, TOK.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp));
+        super(loc, EXP.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp));
     }
 
     override Expression resolveLoc(const ref Loc loc, Scope* sc)
@@ -6828,7 +6832,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression
 
     extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration)
     {
-        super(loc, TOK.objcClassReference,
+        super(loc, EXP.objcClassReference,
             __traits(classInstanceSize, ObjcClassReferenceExp));
         this.classDeclaration = classDeclaration;
         type = objc.getRuntimeMetaclass(classDeclaration).getType();
@@ -6852,7 +6856,7 @@ extern (C++) final class GenericExp : Expression
 
     extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps)
     {
-        super(loc, TOK._Generic, __traits(classInstanceSize, GenericExp));
+        super(loc, EXP._Generic, __traits(classInstanceSize, GenericExp));
         this.cntlExp = cntlExp;
         this.types = types;
         this.exps = exps;
@@ -6883,18 +6887,18 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag
 {
     switch(exp.op)
     {
-        case TOK.variable:
+        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 TOK.dotVariable:
+        case EXP.dotVariable:
             auto dotVarExp = cast(DotVarExp)exp;
 
             //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
-            if (dotVarExp.e1.op == TOK.this_)
+            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
@@ -6925,13 +6929,13 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag
                             onlyUnion = false;
 
                         // Another DotVarExp left?
-                        if (!dve.e1 || dve.e1.op != TOK.dotVariable)
+                        if (!dve.e1 || dve.e1.op != EXP.dotVariable)
                             break;
 
                         dve = cast(DotVarExp) dve.e1;
                     }
 
-                    if (dve.e1.op == TOK.this_)
+                    if (dve.e1.op == EXP.this_)
                     {
                         scope v = dve.var.isVarDeclaration();
                         /* if v is a struct member field with no initializer, no default construction
@@ -6968,7 +6972,7 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag
             //printf("\te1 = %s\n", e1.toChars());
             return dotVarExp.e1.checkModifiable(sc, flag);
 
-        case TOK.star:
+        case EXP.star:
             auto ptrExp = cast(PtrExp)exp;
             if (auto se = ptrExp.e1.isSymOffExp())
             {
@@ -6980,33 +6984,33 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag
             }
             return Modifiable.yes;
 
-        case TOK.slice:
+        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 == TOK.index && e1.type.ty != Tarray) || e1.op == TOK.slice)
+            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 TOK.comma:
+        case EXP.comma:
             return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
 
-        case TOK.index:
+        case EXP.index:
             auto indexExp = cast(IndexExp)exp;
             auto e1 = indexExp.e1;
             if (e1.type.ty == Tsarray ||
                 e1.type.ty == Taarray ||
-                (e1.op == TOK.index && e1.type.ty != Tarray) ||
-                e1.op == TOK.slice)
+                (e1.op == EXP.index && e1.type.ty != Tarray) ||
+                e1.op == EXP.slice)
             {
                 return e1.checkModifiable(sc, flag);
             }
             return Modifiable.yes;
 
-        case TOK.question:
+        case EXP.question:
             auto condExp = cast(CondExp)exp;
             if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
                 && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index 691364cb265..411822cb9d6 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -18,6 +18,7 @@
 #include "tokens.h"
 
 #include "root/dcompat.h"
+#include "root/optional.h"
 
 class Type;
 class TypeVector;
@@ -76,7 +77,7 @@ enum class ModifyFlags
 class Expression : public ASTNode
 {
 public:
-    TOK op;                     // to minimize use of dynamic_cast
+    EXP op;                     // to minimize use of dynamic_cast
     unsigned char size;         // # of bytes in Expression so we can copy() it
     unsigned char parens;       // if this is a parenthesized expression
     Type *type;                 // !=NULL means that semantic() has been run
@@ -121,8 +122,7 @@ public:
     // A compile-time result is required. Give an error if not possible
     Expression *ctfeInterpret();
     int isConst();
-    virtual bool isBool(bool result);
-
+    virtual Optional<bool> toBool();
     virtual bool hasCode()
     {
         return true;
@@ -249,7 +249,7 @@ public:
     real_t toReal();
     real_t toImaginary();
     complex_t toComplex();
-    bool isBool(bool result);
+    Optional<bool> toBool();
     Expression *toLvalue(Scope *sc, Expression *e);
     void accept(Visitor *v) { v->visit(this); }
     dinteger_t getInteger() { return value; }
@@ -280,7 +280,7 @@ public:
     real_t toReal();
     real_t toImaginary();
     complex_t toComplex();
-    bool isBool(bool result);
+    Optional<bool> toBool();
     void accept(Visitor *v) { v->visit(this); }
 };
 
@@ -297,7 +297,7 @@ public:
     real_t toReal();
     real_t toImaginary();
     complex_t toComplex();
-    bool isBool(bool result);
+    Optional<bool> toBool();
     void accept(Visitor *v) { v->visit(this); }
 };
 
@@ -336,7 +336,7 @@ public:
     VarDeclaration *var;
 
     ThisExp *syntaxCopy();
-    bool isBool(bool result);
+    Optional<bool> toBool();
     bool isLvalue();
     Expression *toLvalue(Scope *sc, Expression *e);
 
@@ -353,7 +353,7 @@ class NullExp : public Expression
 {
 public:
     bool equals(const RootObject *o) const;
-    bool isBool(bool result);
+    Optional<bool> toBool();
     StringExp *toStringExp();
     void accept(Visitor *v) { v->visit(this); }
 };
@@ -374,7 +374,7 @@ public:
     bool equals(const RootObject *o) const;
     StringExp *toStringExp();
     StringExp *toUTF8(Scope *sc);
-    bool isBool(bool result);
+    Optional<bool> toBool();
     bool isLvalue();
     Expression *toLvalue(Scope *sc, Expression *e);
     Expression *modifiableLvalue(Scope *sc, Expression *e);
@@ -420,7 +420,7 @@ public:
     bool equals(const RootObject *o) const;
     Expression *getElement(d_size_t i); // use opIndex instead
     Expression *opIndex(d_size_t i);
-    bool isBool(bool result);
+    Optional<bool> toBool();
     StringExp *toStringExp();
 
     void accept(Visitor *v) { v->visit(this); }
@@ -435,7 +435,7 @@ public:
 
     bool equals(const RootObject *o) const;
     AssocArrayLiteralExp *syntaxCopy();
-    bool isBool(bool result);
+    Optional<bool> toBool();
 
     void accept(Visitor *v) { v->visit(this); }
 };
@@ -567,7 +567,7 @@ class SymOffExp : public SymbolExp
 public:
     dinteger_t offset;
 
-    bool isBool(bool result);
+    Optional<bool> toBool();
 
     void accept(Visitor *v) { v->visit(this); }
 };
@@ -926,7 +926,7 @@ public:
     bool isLvalue();
     Expression *toLvalue(Scope *sc, Expression *e);
     Expression *modifiableLvalue(Scope *sc, Expression *e);
-    bool isBool(bool result);
+    Optional<bool> toBool();
 
     void accept(Visitor *v) { v->visit(this); }
 };
@@ -997,7 +997,7 @@ public:
     bool isLvalue();
     Expression *toLvalue(Scope *sc, Expression *e);
     Expression *modifiableLvalue(Scope *sc, Expression *e);
-    bool isBool(bool result);
+    Optional<bool> toBool();
     Expression *addDtorHook(Scope *sc);
     void accept(Visitor *v) { v->visit(this); }
 };
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 8e152d6a1fc..ec2bce47b47 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -108,7 +108,7 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps)
 
         // allowed to contain types as well as expressions
         auto e4 = ctfeInterpretForPragmaMsg(e3);
-        if (!e4 || e4.op == TOK.error)
+        if (!e4 || e4.op == EXP.error)
             return true;
 
         // expand tuple
@@ -153,14 +153,14 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s)
     exp = resolveProperties(sc, exp);
     sc = sc.endCTFE();
 
-    if (exp.op == TOK.error)
+    if (exp.op == EXP.error)
         return null;
 
     auto e = exp;
     if (exp.type.isString())
     {
         e = e.ctfeInterpret();
-        if (e.op == TOK.error)
+        if (e.op == EXP.error)
             return null;
     }
 
@@ -215,7 +215,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
         if (i == 0)
             *pe0 = extractOpDollarSideEffect(sc, ae);
 
-        if (e.op == TOK.interval && !(slice && slice.isTemplateDeclaration()))
+        if (e.op == EXP.interval && !(slice && slice.isTemplateDeclaration()))
         {
         Lfallback:
             if (ae.arguments.dim == 1)
@@ -273,7 +273,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
             ae.error("`%s` has no value", e.toChars());
             e = ErrorExp.get();
         }
-        if (e.op == TOK.error)
+        if (e.op == EXP.error)
             return e;
 
         (*ae.arguments)[i] = e;
@@ -342,9 +342,9 @@ bool arrayExpressionSemantic(Expressions* exps, Scope* sc, bool preserveErrors =
             if (e)
             {
                 auto e2 = e.expressionSemantic(sc);
-                if (e2.op == TOK.error)
+                if (e2.op == EXP.error)
                     err = true;
-                if (preserveErrors || e2.op != TOK.error)
+                if (preserveErrors || e2.op != EXP.error)
                     e = e2;
             }
         }
@@ -531,7 +531,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce)
         {
             if (Expression ey = die.semanticY(sc, 1))
             {
-                if (ey.op == TOK.error)
+                if (ey.op == EXP.error)
                     return ey;
                 ce.e1 = ey;
                 if (isDotOpDispatch(ey))
@@ -1024,7 +1024,7 @@ L1:
     Type t = e1.type.toBasetype();
     //printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars());
 
-    if (e1.op == TOK.objcClassReference)
+    if (e1.op == EXP.objcClassReference)
     {
         // We already have an Objective-C class reference, just use that as 'this'.
         return e1;
@@ -1040,7 +1040,7 @@ L1:
      * class A { inc(alias m)() { ++m; } } // `m` needs `this` of `B`
      * class B {int m; inc() { new A().inc!m(); } }
      */
-    if (e1.op == TOK.this_)
+    if (e1.op == EXP.this_)
     {
         FuncDeclaration f = hasThis(sc);
         if (f && f.isThis2)
@@ -1051,7 +1051,7 @@ L1:
                 e1 = new PtrExp(loc, e1);
                 e1 = new IndexExp(loc, e1, IntegerExp.literal!1);
                 e1 = getThisSkipNestedFuncs(loc, sc, f.toParent2(), ad, e1, t, var);
-                if (e1.op == TOK.error)
+                if (e1.op == EXP.error)
                     return e1;
                 goto L1;
             }
@@ -1089,7 +1089,7 @@ L1:
                 // Skip up over nested functions, and get the enclosing
                 // class type.
                 e1 = getThisSkipNestedFuncs(loc, sc, tcd.toParentP(ad), ad, e1, t, var);
-                if (e1.op == TOK.error)
+                if (e1.op == EXP.error)
                     return e1;
                 goto L1;
             }
@@ -1110,7 +1110,7 @@ L1:
  */
 private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null)
 {
-    //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null);
+    //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null);
     Loc loc = e1.loc;
 
     OverloadSet os;
@@ -1138,7 +1138,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
         if (e2)
         {
             e2 = e2.expressionSemantic(sc);
-            if (e2.op == TOK.error)
+            if (e2.op == EXP.error)
                 return ErrorExp.get();
             e2 = resolveProperties(sc, e2);
 
@@ -1233,7 +1233,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
         tthis = null;
         goto Lfd;
     }
-    else if (e1.op == TOK.dotVariable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(DotVarExp)e1).var.isOverDeclaration()))
+    else if (e1.op == EXP.dotVariable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(DotVarExp)e1).var.isOverDeclaration()))
     {
         DotVarExp dve = cast(DotVarExp)e1;
         s = dve.var;
@@ -1241,11 +1241,11 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
         tthis = dve.e1.type;
         goto Lfd;
     }
-    else if (sc && sc.flags & SCOPE.Cfile && e1.op == TOK.variable && !e2)
+    else if (sc && sc.flags & SCOPE.Cfile && e1.op == EXP.variable && !e2)
     {
         // ImportC: do not implicitly call function if no ( ) are present
     }
-    else if (e1.op == TOK.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration()))
+    else if (e1.op == EXP.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration()))
     {
         s = (cast(VarExp)e1).var;
         tiargs = null;
@@ -1255,7 +1255,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
         if (e2)
         {
             e2 = e2.expressionSemantic(sc);
-            if (e2.op == TOK.error)
+            if (e2.op == EXP.error)
                 return ErrorExp.get();
             e2 = resolveProperties(sc, e2);
 
@@ -1316,7 +1316,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
         if (e2)
             goto Leprop;
     }
-    if (e1.op == TOK.variable)
+    if (e1.op == EXP.variable)
     {
         VarExp ve = cast(VarExp)e1;
         VarDeclaration v = ve.var.isVarDeclaration();
@@ -1326,7 +1326,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
     if (e2)
         return null;
 
-    if (e1.type && e1.op != TOK.type) // function type is not a property
+    if (e1.type && e1.op != EXP.type) // function type is not a property
     {
         /* Look for e1 being a lazy parameter; rewrite as delegate call
          * only if the symbol wasn't already treated as a delegate
@@ -1337,13 +1337,13 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
                 Expression e = new CallExp(loc, e1);
                 return e.expressionSemantic(sc);
         }
-        else if (e1.op == TOK.dotVariable)
+        else if (e1.op == EXP.dotVariable)
         {
             // Check for reading overlapped pointer field in @safe code.
             if (checkUnsafeAccess(sc, e1, true, true))
                 return ErrorExp.get();
         }
-        else if (e1.op == TOK.call)
+        else if (e1.op == EXP.call)
         {
             CallExp ce = cast(CallExp)e1;
             // Check for reading overlapped pointer field in @safe code.
@@ -1409,7 +1409,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
             t0 = Type.terror;
             continue;
         }
-        if (e.op == TOK.type)
+        if (e.op == EXP.type)
         {
             foundType = true; // do not break immediately, there might be more errors
             e.checkValue(); // report an error "type T has no value"
@@ -1440,9 +1440,9 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
             condexp.e2 = e;
             condexp.loc = e.loc;
             Expression ex = condexp.expressionSemantic(sc);
-            if (ex.op == TOK.error)
+            if (ex.op == EXP.error)
                 e = ex;
-            else if (e.op == TOK.function_ || e.op == TOK.delegate_)
+            else if (e.op == EXP.function_ || e.op == EXP.delegate_)
             {
                 // https://issues.dlang.org/show_bug.cgi?id=21285
                 // Functions and delegates don't convert correctly with castTo below
@@ -1458,7 +1458,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
         }
         e0 = e;
         t0 = e.type;
-        if (e.op != TOK.error)
+        if (e.op != EXP.error)
             exps[i] = e;
     }
 
@@ -1477,7 +1477,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
             continue;
 
         e = e.implicitCastTo(sc, t0);
-        if (e.op == TOK.error)
+        if (e.op == EXP.error)
         {
             /* https://issues.dlang.org/show_bug.cgi?id=13024
              * a workaround for the bug in typeMerge -
@@ -1491,52 +1491,52 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
     return t0;
 }
 
-private Expression opAssignToOp(const ref Loc loc, TOK op, Expression e1, Expression e2)
+private Expression opAssignToOp(const ref Loc loc, EXP op, Expression e1, Expression e2)
 {
     Expression e;
     switch (op)
     {
-    case TOK.addAssign:
+    case EXP.addAssign:
         e = new AddExp(loc, e1, e2);
         break;
 
-    case TOK.minAssign:
+    case EXP.minAssign:
         e = new MinExp(loc, e1, e2);
         break;
 
-    case TOK.mulAssign:
+    case EXP.mulAssign:
         e = new MulExp(loc, e1, e2);
         break;
 
-    case TOK.divAssign:
+    case EXP.divAssign:
         e = new DivExp(loc, e1, e2);
         break;
 
-    case TOK.modAssign:
+    case EXP.modAssign:
         e = new ModExp(loc, e1, e2);
         break;
 
-    case TOK.andAssign:
+    case EXP.andAssign:
         e = new AndExp(loc, e1, e2);
         break;
 
-    case TOK.orAssign:
+    case EXP.orAssign:
         e = new OrExp(loc, e1, e2);
         break;
 
-    case TOK.xorAssign:
+    case EXP.xorAssign:
         e = new XorExp(loc, e1, e2);
         break;
 
-    case TOK.leftShiftAssign:
+    case EXP.leftShiftAssign:
         e = new ShlExp(loc, e1, e2);
         break;
 
-    case TOK.rightShiftAssign:
+    case EXP.rightShiftAssign:
         e = new ShrExp(loc, e1, e2);
         break;
 
-    case TOK.unsignedRightShiftAssign:
+    case EXP.unsignedRightShiftAssign:
         e = new UshrExp(loc, e1, e2);
         break;
 
@@ -1559,9 +1559,9 @@ private Expression rewriteOpAssign(BinExp exp)
 {
     Expression e;
 
-    assert(exp.e1.op == TOK.arrayLength);
+    assert(exp.e1.op == EXP.arrayLength);
     ArrayLengthExp ale = cast(ArrayLengthExp)exp.e1;
-    if (ale.e1.op == TOK.variable)
+    if (ale.e1.op == EXP.variable)
     {
         e = opAssignToOp(exp.loc, exp.op, ale, exp.e2);
         e = new AssignExp(exp.loc, ale.syntaxCopy(), e);
@@ -1604,12 +1604,12 @@ private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool repo
             Expression arg = (*exps)[i];
             arg = resolveProperties(sc, arg);
             arg = arg.arrayFuncConv(sc);
-            if (arg.op == TOK.type)
+            if (arg.op == EXP.type)
             {
                 // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
                 arg = resolveAliasThis(sc, arg);
 
-                if (arg.op == TOK.type)
+                if (arg.op == EXP.type)
                 {
                     if (reportErrors)
                     {
@@ -2056,7 +2056,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                 /* Argument value cannot escape from the called function.
                  */
                 Expression a = arg;
-                if (a.op == TOK.cast_)
+                if (a.op == EXP.cast_)
                     a = (cast(CastExp)a).e1;
 
                 ArrayLiteralExp ale;
@@ -2071,7 +2071,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                     arg = CommaExp.combine(declareTmp, castToSlice);
                     arg = arg.expressionSemantic(sc);
                 }
-                else if (a.op == TOK.function_)
+                else if (a.op == EXP.function_)
                 {
                     /* Function literals can only appear once, so if this
                      * appearance was scoped, there cannot be any others.
@@ -2079,14 +2079,14 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                     FuncExp fe = cast(FuncExp)a;
                     fe.fd.tookAddressOf = 0;
                 }
-                else if (a.op == TOK.delegate_)
+                else if (a.op == EXP.delegate_)
                 {
                     /* For passing a delegate to a scoped parameter,
                      * this doesn't count as taking the address of it.
                      * We only worry about 'escaping' references to the function.
                      */
                     DelegateExp de = cast(DelegateExp)a;
-                    if (de.e1.op == TOK.variable)
+                    if (de.e1.op == EXP.variable)
                     {
                         VarExp ve = cast(VarExp)de.e1;
                         FuncDeclaration f = ve.var.isFuncDeclaration();
@@ -2185,7 +2185,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                 //arg = callCpCtor(sc, arg);
             }
             // Give error for overloaded function addresses
-            if (arg.op == TOK.symbolOffset)
+            if (arg.op == EXP.symbolOffset)
             {
                 SymOffExp se = cast(SymOffExp)arg;
                 if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique())
@@ -2357,7 +2357,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
                         // edtor => (__gate || edtor)
                         assert(tmp.edtor);
                         Expression e = tmp.edtor;
-                        e = new LogicalExp(e.loc, TOK.orOr, new VarExp(e.loc, gate), e);
+                        e = new LogicalExp(e.loc, EXP.orOr, new VarExp(e.loc, gate), e);
                         tmp.edtor = e.expressionSemantic(sc);
                         //printf("edtor: %s\n", tmp.edtor.toChars());
                     }
@@ -2766,7 +2766,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     }
                 }
                 // Try Type.opDispatch (so the static version)
-                else if (ss.withstate.exp && ss.withstate.exp.op == TOK.type)
+                else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type)
                 {
                     if (Type t = ss.withstate.exp.isTypeExp().type)
                     {
@@ -3077,7 +3077,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 exp.error("`%s` has no value", e.toChars());
                 err = true;
             }
-            else if (e.op == TOK.error)
+            else if (e.op == EXP.error)
                 err = true;
             else
                 (*exp.exps)[i] = e;
@@ -3110,7 +3110,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         if (e.basis)
             e.basis = e.basis.expressionSemantic(sc);
-        if (arrayExpressionSemantic(e.elements, sc) || (e.basis && e.basis.op == TOK.error))
+        if (arrayExpressionSemantic(e.elements, sc) || (e.basis && e.basis.op == EXP.error))
             return setError();
 
         expandTuples(e.elements);
@@ -3474,7 +3474,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (exp.thisexp)
         {
             exp.thisexp = exp.thisexp.expressionSemantic(sc);
-            if (exp.thisexp.op == TOK.error)
+            if (exp.thisexp.op == EXP.error)
                 return setError();
 
             cdthis = exp.thisexp.type.isClassHandle();
@@ -3619,7 +3619,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                         }
 
                         exp.thisexp = exp.thisexp.expressionSemantic(sc);
-                        if (exp.thisexp.op == TOK.error)
+                        if (exp.thisexp.op == EXP.error)
                             return setError();
                         cdthis = exp.thisexp.type.isClassHandle();
                     }
@@ -3666,9 +3666,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (AggregateDeclaration ad2 = cd.isMember2())
                 {
                     Expression te = new ThisExp(exp.loc).expressionSemantic(sc);
-                    if (te.op != TOK.error)
+                    if (te.op != EXP.error)
                         te = getRightThis(exp.loc, sc, ad2, te, cd);
-                    if (te.op == TOK.error)
+                    if (te.op == EXP.error)
                     {
                         exp.error("need `this` of type `%s` needed to `new` nested class `%s`", ad2.toChars(), cd.toChars());
                         return setError();
@@ -3764,7 +3764,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
 
-            if (sd.ctor && nargs)
+            if (sd.hasRegularCtor() && nargs)
             {
                 FuncDeclaration f = resolveFuncCall(exp.loc, sc, sd.ctor, null, tb, exp.arguments, FuncResolveFlag.standard);
                 if (!f || f.errors)
@@ -3842,10 +3842,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 Expression arg = (*exp.arguments)[i];
                 arg = resolveProperties(sc, arg);
                 arg = arg.implicitCastTo(sc, Type.tsize_t);
-                if (arg.op == TOK.error)
+                if (arg.op == EXP.error)
                     return setError();
                 arg = arg.optimize(WANTvalue);
-                if (arg.op == TOK.int64 && cast(sinteger_t)arg.toInteger() < 0)
+                if (arg.op == EXP.int64 && cast(sinteger_t)arg.toInteger() < 0)
                 {
                     exp.error("negative array index `%s`", arg.toChars());
                     return setError();
@@ -4104,7 +4104,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 *   auto foo(void function() fp) { return 1; }
                 *   assert(foo({}) == 1);
                 *
-                * So, should keep fd.tok == TOKreserve if fd.treq == NULL.
+                * So, should keep fd.tok == TOK.reserve if fd.treq == NULL.
                 */
             if (exp.fd.treq && exp.fd.treq.ty == Tpointer)
             {
@@ -4135,7 +4135,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             for (size_t k = 0; k < arguments.dim; k++)
             {
                 Expression checkarg = (*arguments)[k];
-                if (checkarg.op == TOK.error)
+                if (checkarg.op == EXP.error)
                     return checkarg;
             }
 
@@ -4213,7 +4213,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Type tthis = null;
         Expression e1org = exp.e1;
 
-        if (exp.e1.op == TOK.comma)
+        if (exp.e1.op == EXP.comma)
         {
             /* Rewrite (a,b)(args) as (a,(b(args)))
              */
@@ -4223,14 +4223,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = ce.expressionSemantic(sc);
             return;
         }
-        if (exp.e1.op == TOK.delegate_)
+        if (exp.e1.op == EXP.delegate_)
         {
             DelegateExp de = cast(DelegateExp)exp.e1;
             exp.e1 = new DotVarExp(de.loc, de.e1, de.func, de.hasOverloads);
             visit(exp);
             return;
         }
-        if (exp.e1.op == TOK.function_)
+        if (exp.e1.op == EXP.function_)
         {
             if (arrayExpressionSemantic(exp.arguments, sc) || preFunctionParameters(sc, exp.arguments))
                 return setError();
@@ -4238,7 +4238,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // Run e1 semantic even if arguments have any errors
             FuncExp fe = cast(FuncExp)exp.e1;
             exp.e1 = callExpSemantic(fe, sc, exp.arguments);
-            if (exp.e1.op == TOK.error)
+            if (exp.e1.op == EXP.error)
             {
                 result = exp.e1;
                 return;
@@ -4254,7 +4254,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         /* This recognizes:
          *  foo!(tiargs)(funcargs)
          */
-        if (exp.e1.op == TOK.scope_)
+        if (exp.e1.op == EXP.scope_)
         {
             ScopeExp se = cast(ScopeExp)exp.e1;
             TemplateInstance ti = se.sds.isTemplateInstance();
@@ -4288,7 +4288,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 else
                 {
                     Expression e1x = exp.e1.expressionSemantic(sc);
-                    if (e1x.op == TOK.error)
+                    if (e1x.op == EXP.error)
                     {
                         result = e1x;
                         return;
@@ -4302,7 +4302,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
          *  expr.foo!(tiargs)(funcargs)
          */
     Ldotti:
-        if (exp.e1.op == TOK.dotTemplateInstance)
+        if (exp.e1.op == EXP.dotTemplateInstance)
         {
             DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)exp.e1;
             TemplateInstance ti = se.ti;
@@ -4330,7 +4330,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 else
                 {
                     Expression e1x = exp.e1.expressionSemantic(sc);
-                    if (e1x.op == TOK.error)
+                    if (e1x.op == EXP.error)
                     {
                         result = e1x;
                         return;
@@ -4343,13 +4343,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
     Lagain:
         //printf("Lagain: %s\n", toChars());
         exp.f = null;
-        if (exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_)
+        if (exp.e1.op == EXP.this_ || exp.e1.op == EXP.super_)
         {
             // semantic() run later for these
         }
         else
         {
-            if (exp.e1.op == TOK.dotIdentifier)
+            if (exp.e1.op == EXP.dotIdentifier)
             {
                 DotIdExp die = cast(DotIdExp)exp.e1;
                 exp.e1 = die.expressionSemantic(sc);
@@ -4357,7 +4357,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  * We handle such earlier, so go back.
                  * Note that in the rewrite, we carefully did not run semantic() on e1
                  */
-                if (exp.e1.op == TOK.dotTemplateInstance)
+                if (exp.e1.op == EXP.dotTemplateInstance)
                 {
                     goto Ldotti;
                 }
@@ -4382,7 +4382,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
             /* Look for e1 being a lazy parameter
              */
-            if (exp.e1.op == TOK.variable)
+            if (exp.e1.op == EXP.variable)
             {
                 VarExp ve = cast(VarExp)exp.e1;
                 if (ve.var.storage_class & STC.lazy_)
@@ -4400,29 +4400,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return setError();
             }
 
-            if (exp.e1.op == TOK.symbolOffset && (cast(SymOffExp)exp.e1).hasOverloads)
+            if (exp.e1.op == EXP.symbolOffset && (cast(SymOffExp)exp.e1).hasOverloads)
             {
                 SymOffExp se = cast(SymOffExp)exp.e1;
                 exp.e1 = new VarExp(se.loc, se.var, true);
                 exp.e1 = exp.e1.expressionSemantic(sc);
             }
-            else if (exp.e1.op == TOK.dot)
+            else if (exp.e1.op == EXP.dot)
             {
                 DotExp de = cast(DotExp)exp.e1;
 
-                if (de.e2.op == TOK.overloadSet)
+                if (de.e2.op == EXP.overloadSet)
                 {
                     ethis = de.e1;
                     tthis = de.e1.type;
                     exp.e1 = de.e2;
                 }
             }
-            else if (exp.e1.op == TOK.star && exp.e1.type.ty == Tfunction)
+            else if (exp.e1.op == EXP.star && exp.e1.type.ty == Tfunction)
             {
                 // Rewrite (*fp)(arguments) to fp(arguments)
                 exp.e1 = (cast(PtrExp)exp.e1).e1;
             }
-            else if (exp.e1.op == TOK.type && (sc && sc.flags & SCOPE.Cfile))
+            else if (exp.e1.op == EXP.type && (sc && sc.flags & SCOPE.Cfile))
             {
                 const numArgs = exp.arguments ? exp.arguments.length : 0;
                 if (e1org.parens && numArgs >= 1)
@@ -4480,7 +4480,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         Type t1 = exp.e1.type ? exp.e1.type.toBasetype() : null;
 
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
@@ -4515,7 +4515,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
 
                 // First look for constructor
-                if (exp.e1.op == TOK.type && sd.ctor)
+                if (exp.e1.op == EXP.type && sd.ctor)
                 {
                     if (!sd.noDefaultCtor && !(exp.arguments && exp.arguments.dim))
                         goto Lx;
@@ -4566,7 +4566,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (search_function(sd, Id.call))
                     goto L1;
                 // overload of opCall, therefore it's a call
-                if (exp.e1.op != TOK.type)
+                if (exp.e1.op != EXP.type)
                 {
                     if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type))
                     {
@@ -4595,7 +4595,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 result = e;
                 return;
             }
-            else if (exp.e1.op == TOK.type && t1.isscalar())
+            else if (exp.e1.op == EXP.type && t1.isscalar())
             {
                 Expression e;
 
@@ -4659,7 +4659,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         bool isSuper = false;
-        if (exp.e1.op == TOK.dotVariable && t1.ty == Tfunction || exp.e1.op == TOK.dotTemplateDeclaration)
+        if (exp.e1.op == EXP.dotVariable && t1.ty == Tfunction || exp.e1.op == EXP.dotTemplateDeclaration)
         {
             UnaExp ue = cast(UnaExp)exp.e1;
 
@@ -4667,7 +4667,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             DotVarExp dve;
             DotTemplateExp dte;
             Dsymbol s;
-            if (exp.e1.op == TOK.dotVariable)
+            if (exp.e1.op == EXP.dotVariable)
             {
                 dve = cast(DotVarExp)exp.e1;
                 dte = null;
@@ -4703,7 +4703,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             {
                 AggregateDeclaration ad = exp.f.toParentLocal().isAggregateDeclaration();
                 ue.e1 = getRightThis(exp.loc, sc, ad, ue.e1, exp.f);
-                if (ue.e1.op == TOK.error)
+                if (ue.e1.op == EXP.error)
                 {
                     result = ue.e1;
                     return;
@@ -4720,7 +4720,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             /* Cannot call public functions from inside invariant
              * (because then the invariant would have infinite recursion)
              */
-            if (sc.func && sc.func.isInvariantDeclaration() && ue.e1.op == TOK.this_ && exp.f.addPostInvariant())
+            if (sc.func && sc.func.isInvariantDeclaration() && ue.e1.op == EXP.this_ && exp.f.addPostInvariant())
             {
                 exp.error("cannot call `public`/`export` function `%s` from invariant", exp.f.toChars());
                 return setError();
@@ -4737,7 +4737,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             {
                 if (ue1old.checkRightThis(sc))
                     return setError();
-                if (exp.e1.op == TOK.dotVariable)
+                if (exp.e1.op == EXP.dotVariable)
                 {
                     dve.var = exp.f;
                     exp.e1.type = exp.f.type;
@@ -4746,7 +4746,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 {
                     exp.e1 = new DotVarExp(exp.loc, dte.e1, exp.f, false);
                     exp.e1 = exp.e1.expressionSemantic(sc);
-                    if (exp.e1.op == TOK.error)
+                    if (exp.e1.op == EXP.error)
                         return setError();
                     ue = cast(UnaExp)exp.e1;
                 }
@@ -4764,12 +4764,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 ClassDeclaration cd = ue.e1.type.isClassHandle();
                 if (ad && cd && ad.isClassDeclaration())
                 {
-                    if (ue.e1.op == TOK.dotType)
+                    if (ue.e1.op == EXP.dotType)
                     {
                         ue.e1 = (cast(DotTypeExp)ue.e1).e1;
                         exp.directcall = true;
                     }
-                    else if (ue.e1.op == TOK.super_)
+                    else if (ue.e1.op == EXP.super_)
                         exp.directcall = true;
                     else if ((cd.storage_class & STC.final_) != 0) // https://issues.dlang.org/show_bug.cgi?id=14211
                         exp.directcall = true;
@@ -4791,12 +4791,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
             t1 = exp.e1.type;
         }
-        else if (exp.e1.op == TOK.super_ || exp.e1.op == TOK.this_)
+        else if (exp.e1.op == EXP.super_ || exp.e1.op == EXP.this_)
         {
             auto ad = sc.func ? sc.func.isThis() : null;
             auto cd = ad ? ad.isClassDeclaration() : null;
 
-            isSuper = exp.e1.op == TOK.super_;
+            isSuper = exp.e1.op == EXP.super_;
             if (isSuper)
             {
                 // Base class constructor call
@@ -4858,7 +4858,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             exp.e1 = new DotVarExp(exp.e1.loc, exp.e1, exp.f, false);
             exp.e1 = exp.e1.expressionSemantic(sc);
             // https://issues.dlang.org/show_bug.cgi?id=21095
-            if (exp.e1.op == TOK.error)
+            if (exp.e1.op == EXP.error)
                 return setError();
             t1 = exp.e1.type;
 
@@ -4870,7 +4870,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 return setError();
             }
         }
-        else if (exp.e1.op == TOK.overloadSet)
+        else if (exp.e1.op == EXP.overloadSet)
         {
             auto os = (cast(OverExp)exp.e1).vars;
             exp.f = resolveOverloadSet(exp.loc, sc, os, tiargs, tthis, exp.arguments);
@@ -4897,7 +4897,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             const(char)* p;
             Dsymbol s;
             exp.f = null;
-            if (exp.e1.op == TOK.function_)
+            if (exp.e1.op == EXP.function_)
             {
                 // function literal that direct called is always inferred.
                 assert((cast(FuncExp)exp.e1).fd);
@@ -4917,7 +4917,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 tf = tfx;
                 p = "function pointer";
             }
-            else if (exp.e1.op == TOK.dotVariable && (cast(DotVarExp)exp.e1).var.isOverDeclaration())
+            else if (exp.e1.op == EXP.dotVariable && (cast(DotVarExp)exp.e1).var.isOverDeclaration())
             {
                 DotVarExp dve = cast(DotVarExp)exp.e1;
                 exp.f = resolveFuncCall(exp.loc, sc, dve.var, tiargs, dve.e1.type, exp.arguments, FuncResolveFlag.overloadOnly);
@@ -4935,12 +4935,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 result = e.expressionSemantic(sc);
                 return;
             }
-            else if (exp.e1.op == TOK.variable && (cast(VarExp)exp.e1).var.isOverDeclaration())
+            else if (exp.e1.op == EXP.variable && (cast(VarExp)exp.e1).var.isOverDeclaration())
             {
                 s = (cast(VarExp)exp.e1).var;
                 goto L2;
             }
-            else if (exp.e1.op == TOK.template_)
+            else if (exp.e1.op == EXP.template_)
             {
                 s = (cast(TemplateExp)exp.e1).td;
             L2:
@@ -5031,7 +5031,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
             t1 = tf;
         }
-        else if (exp.e1.op == TOK.variable)
+        else if (exp.e1.op == EXP.variable)
         {
             // Do overload resolution
             VarExp ve = cast(VarExp)exp.e1;
@@ -5188,9 +5188,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (AggregateDeclaration ad2 = exp.f.isMember2())
             {
                 Expression te = new ThisExp(exp.loc).expressionSemantic(sc);
-                if (te.op != TOK.error)
+                if (te.op != EXP.error)
                     te = getRightThis(exp.loc, sc, ad2, te, exp.f);
-                if (te.op == TOK.error)
+                if (te.op == EXP.error)
                 {
                     exp.error("need `this` of type `%s` to call function `%s`", ad2.toChars(), exp.f.toChars());
                     return setError();
@@ -5368,7 +5368,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 ea = ea.expressionSemantic(sc);
             ea = resolveProperties(sc, ea);
             ta = ea.type;
-            if (ea.op == TOK.type)
+            if (ea.op == EXP.type)
                 ea = null;
         }
 
@@ -5644,7 +5644,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                         /* If one of the default arguments was an error,
                            don't return an invalid tuple
                          */
-                        if (e.tok2 == TOK.parameters && arg.defaultArg && arg.defaultArg.op == TOK.error)
+                        if (e.tok2 == TOK.parameters && arg.defaultArg && arg.defaultArg.op == EXP.error)
                             return setError();
                         args.push(new Parameter(arg.storageClass, arg.type, (e.tok2 == TOK.parameters) ? arg.ident : null, (e.tok2 == TOK.parameters) ? arg.defaultArg : null, arg.userAttribDecl));
                     }
@@ -5819,7 +5819,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (exp.e1.op == TOK.arrayLength)
+        if (exp.e1.op == EXP.arrayLength)
         {
             // arr.length op= e2;
             e = rewriteOpAssign(exp);
@@ -5827,12 +5827,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = e;
             return;
         }
-        if (exp.e1.op == TOK.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
+        if (exp.e1.op == EXP.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
         {
             if (checkNonAssignmentArrayOp(exp.e1))
                 return setError();
 
-            if (exp.e1.op == TOK.slice)
+            if (exp.e1.op == EXP.slice)
                 (cast(SliceExp)exp.e1).arrayop = true;
 
             // T[] op= ...
@@ -5869,16 +5869,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             exp.e1.checkSharedAccess(sc))
             return setError();
 
-        int arith = (exp.op == TOK.addAssign || exp.op == TOK.minAssign || exp.op == TOK.mulAssign || exp.op == TOK.divAssign || exp.op == TOK.modAssign || exp.op == TOK.powAssign);
-        int bitwise = (exp.op == TOK.andAssign || exp.op == TOK.orAssign || exp.op == TOK.xorAssign);
-        int shift = (exp.op == TOK.leftShiftAssign || exp.op == TOK.rightShiftAssign || exp.op == TOK.unsignedRightShiftAssign);
+        int arith = (exp.op == EXP.addAssign || exp.op == EXP.minAssign || exp.op == EXP.mulAssign || exp.op == EXP.divAssign || exp.op == EXP.modAssign || exp.op == EXP.powAssign);
+        int bitwise = (exp.op == EXP.andAssign || exp.op == EXP.orAssign || exp.op == EXP.xorAssign);
+        int shift = (exp.op == EXP.leftShiftAssign || exp.op == EXP.rightShiftAssign || exp.op == EXP.unsignedRightShiftAssign);
 
         if (bitwise && exp.type.toBasetype().ty == Tbool)
             exp.e2 = exp.e2.implicitCastTo(sc, exp.type);
         else if (exp.checkNoBool())
             return setError();
 
-        if ((exp.op == TOK.addAssign || exp.op == TOK.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isintegral())
+        if ((exp.op == EXP.addAssign || exp.op == EXP.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isintegral())
         {
             result = scaleFactor(exp, sc);
             return;
@@ -5907,17 +5907,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (exp.e1.op == TOK.error || exp.e2.op == TOK.error)
+        if (exp.e1.op == EXP.error || exp.e2.op == EXP.error)
             return setError();
 
         e = exp.checkOpAssignTypes(sc);
-        if (e.op == TOK.error)
+        if (e.op == EXP.error)
         {
             result = e;
             return;
         }
 
-        assert(e.op == TOK.assign || e == exp);
+        assert(e.op == EXP.assign || e == exp);
         result = (cast(BinExp)e).reorderSettingAAElem(sc);
     }
 
@@ -6209,9 +6209,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             Objects* tiargs;
             Loc loc = exp.e1.loc;
 
-            const tok = exp.e1.op;
+            const op = exp.e1.op;
             bool isEqualsCallExpression;
-            if (tok == TOK.call)
+            if (op == EXP.call)
             {
                 const callExp = cast(CallExp) exp.e1;
 
@@ -6225,11 +6225,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                                              callExpIdent == Id.eq;
                 }
             }
-            if (tok == TOK.equal || tok == TOK.notEqual ||
-                tok == TOK.lessThan || tok == TOK.greaterThan ||
-                tok == TOK.lessOrEqual || tok == TOK.greaterOrEqual ||
-                tok == TOK.identity || tok == TOK.notIdentity ||
-                tok == TOK.in_ ||
+            if (op == EXP.equal || op == EXP.notEqual ||
+                op == EXP.lessThan || op == EXP.greaterThan ||
+                op == EXP.lessOrEqual || op == EXP.greaterOrEqual ||
+                op == EXP.identity || op == EXP.notIdentity ||
+                op == EXP.in_ ||
                 isEqualsCallExpression)
             {
                 es = new Expressions(3);
@@ -6269,7 +6269,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
 
                 // template args
-                Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : Token.toString(exp.e1.op));
+                Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : EXPtoString(exp.e1.op));
                 comp = comp.expressionSemantic(sc);
                 (*es)[0] = comp;
                 (*tiargs)[0] = (*es)[1].type;
@@ -6277,7 +6277,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
             // Format exp.e1 before any additional boolean conversion
             // Ignore &&/|| because "assert(...) failed" is more informative than "false != true"
-            else if (tok != TOK.andAnd && tok != TOK.orOr)
+            else if (op != EXP.andAnd && op != EXP.orOr)
             {
                 es = new Expressions(2);
                 tiargs = new Objects(1);
@@ -6343,7 +6343,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         exp.e1 = exp.e1.optimize(WANTvalue);
         exp.e1 = exp.e1.toBoolean(sc);
 
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
@@ -6358,7 +6358,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             checkParamArgumentEscape(sc, null, null, exp.msg, true, false);
         }
 
-        if (exp.msg && exp.msg.op == TOK.error)
+        if (exp.msg && exp.msg.op == EXP.error)
         {
             result = exp.msg;
             return;
@@ -6369,7 +6369,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (f1 || f2)
             return setError();
 
-        if (exp.e1.isBool(false))
+        if (exp.e1.toBool().hasValue(false))
         {
             /* This is an `assert(0)` which means halt program execution
              */
@@ -6688,7 +6688,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         AggregateDeclaration ad = f.toParentLocal().isAggregateDeclaration();
         if (f.needThis())
             e.e1 = getRightThis(e.loc, sc, ad, e.e1, f);
-        if (e.e1.op == TOK.error)
+        if (e.e1.op == EXP.error)
             return setError();
 
         /* A delegate takes the address of e.e1 in order to set the .ptr field
@@ -6731,9 +6731,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (AggregateDeclaration ad2 = f.isMember2())
             {
                 Expression te = new ThisExp(e.loc).expressionSemantic(sc);
-                if (te.op != TOK.error)
+                if (te.op != EXP.error)
                     te = getRightThis(e.loc, sc, ad2, te, f);
-                if (te.op == TOK.error)
+                if (te.op == EXP.error)
                 {
                     e.error("need `this` of type `%s` to make delegate from function `%s`", ad2.toChars(), f.toChars());
                     return setError();
@@ -6806,9 +6806,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
-        int wasCond = exp.e1.op == TOK.question;
+        int wasCond = exp.e1.op == EXP.question;
 
-        if (exp.e1.op == TOK.dotTemplateInstance)
+        if (exp.e1.op == EXP.dotTemplateInstance)
         {
             DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)exp.e1;
             TemplateInstance ti = dti.ti;
@@ -6827,7 +6827,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
         }
-        else if (exp.e1.op == TOK.scope_)
+        else if (exp.e1.op == EXP.scope_)
         {
             TemplateInstance ti = (cast(ScopeExp)exp.e1).sds.isTemplateInstance();
             if (ti)
@@ -6891,7 +6891,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         exp.e1 = exp.e1.toLvalue(sc, null);
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
@@ -6913,7 +6913,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         else if (!exp.e1.type.deco)
         {
-            if (exp.e1.op == TOK.variable)
+            if (exp.e1.op == EXP.variable)
             {
                 VarExp ve = cast(VarExp)exp.e1;
                 Declaration d = ve.var;
@@ -6927,7 +6927,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         exp.type = exp.e1.type.pointerTo();
 
         // See if this should really be a delegate
-        if (exp.e1.op == TOK.dotVariable)
+        if (exp.e1.op == EXP.dotVariable)
         {
             DotVarExp dve = cast(DotVarExp)exp.e1;
             FuncDeclaration f = dve.var.isFuncDeclaration();
@@ -6961,7 +6961,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
         }
-        else if (exp.e1.op == TOK.variable)
+        else if (exp.e1.op == EXP.variable)
         {
             VarExp ve = cast(VarExp)exp.e1;
             VarDeclaration v = ve.var.isVarDeclaration();
@@ -7023,7 +7023,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
         }
-        else if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && global.params.useDIP1000 == FeatureState.enabled)
+        else if ((exp.e1.op == EXP.this_ || exp.e1.op == EXP.super_) && global.params.useDIP1000 == FeatureState.enabled)
         {
             if (VarDeclaration v = expToVariable(exp.e1))
             {
@@ -7031,20 +7031,21 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return setError();
             }
         }
-        else if (exp.e1.op == TOK.call)
+        else if (exp.e1.op == EXP.call)
         {
             CallExp ce = cast(CallExp)exp.e1;
             if (ce.e1.type.ty == Tfunction)
             {
                 TypeFunction tf = cast(TypeFunction)ce.e1.type;
-                if (tf.isref && sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
+                if (tf.isref && sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_)
+                    && tf.next.hasPointers() && sc.func.setUnsafe())
                 {
                     exp.error("cannot take address of `ref return` of `%s()` in `@safe` function `%s`",
                         ce.e1.toChars(), sc.func.toChars());
                 }
             }
         }
-        else if (exp.e1.op == TOK.index)
+        else if (exp.e1.op == EXP.index)
         {
             /* For:
              *   int[3] a;
@@ -7064,12 +7065,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             /* a ? b : c was transformed to *(a ? &b : &c), but we still
              * need to do safety checks
              */
-            assert(exp.e1.op == TOK.star);
+            assert(exp.e1.op == EXP.star);
             PtrExp pe = cast(PtrExp)exp.e1;
-            assert(pe.e1.op == TOK.question);
+            assert(pe.e1.op == EXP.question);
             CondExp ce = cast(CondExp)pe.e1;
-            assert(ce.e1.op == TOK.address);
-            assert(ce.e2.op == TOK.address);
+            assert(ce.e1.op == EXP.address);
+            assert(ce.e2.op == EXP.address);
 
             // Re-run semantic on the address expressions only
             ce.e1.type = null;
@@ -7270,7 +7271,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-        if (e.e1.op == TOK.type)
+        if (e.e1.op == EXP.type)
             e.e1 = resolveAliasThis(sc, e.e1);
 
         e.e1 = resolveProperties(sc, e.e1);
@@ -7313,7 +7314,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         exp.e1 = resolveProperties(sc, exp.e1);
         exp.e1 = exp.e1.modifiableLvalue(sc, null);
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
@@ -7405,8 +7406,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         if ((sc && sc.flags & SCOPE.Cfile) &&
             exp.to && exp.to.ty == Tident &&
-            (exp.e1.op == TOK.address || exp.e1.op == TOK.star ||
-             exp.e1.op == TOK.uadd || exp.e1.op == TOK.negate))
+            (exp.e1.op == EXP.address || exp.e1.op == EXP.star ||
+             exp.e1.op == EXP.uadd || exp.e1.op == EXP.negate))
         {
             /* Ambiguous cases arise from CParser if type-name is just an identifier.
              *   ( identifier ) cast-expression
@@ -7457,11 +7458,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-        if (exp.e1.op == TOK.type)
+        if (exp.e1.op == EXP.type)
             exp.e1 = resolveAliasThis(sc, exp.e1);
 
         auto e1x = resolveProperties(sc, exp.e1);
-        if (e1x.op == TOK.error)
+        if (e1x.op == EXP.error)
         {
             result = e1x;
             return;
@@ -7557,7 +7558,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         Expression ex = exp.e1.castTo(sc, exp.to);
-        if (ex.op == TOK.error)
+        if (ex.op == EXP.error)
         {
             result = ex;
             return;
@@ -7591,16 +7592,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
 
-            if(t1b.ty == Tarray && exp.e1.op != TOK.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0)
+            if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0)
             {
                 auto tFrom = t1b.nextOf();
                 auto tTo = tob.nextOf();
 
                 // https://issues.dlang.org/show_bug.cgi?id=20130
-                if (exp.e1.op != TOK.string_ || !ex.isStringExp)
+                if (exp.e1.op != EXP.string_ || !ex.isStringExp)
                 {
                     const uint fromSize = cast(uint)tFrom.size();
                     const uint toSize = cast(uint)tTo.size();
+                    if (fromSize == SIZE_INVALID || toSize == SIZE_INVALID)
+                        return setError();
 
                     // If array element sizes do not match, we must adjust the dimensions
                     if (fromSize != toSize)
@@ -7664,7 +7667,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         exp.e1 = exp.e1.expressionSemantic(sc);
         exp.type = exp.to.typeSemantic(exp.loc, sc);
-        if (exp.e1.op == TOK.error || exp.type.ty == Terror)
+        if (exp.e1.op == EXP.error || exp.type.ty == Terror)
         {
             result = exp.e1;
             return;
@@ -7687,7 +7690,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         exp.e1 = exp.e1.optimize(WANTvalue);
         bool res;
-        if (exp.e1.op == TOK.arrayLiteral)
+        if (exp.e1.op == EXP.arrayLiteral)
         {
             foreach (i; 0 .. exp.dim)
             {
@@ -7712,7 +7715,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             unaSemantic(e, sc);
             e.e1 = resolveProperties(sc, e.e1);
 
-            if (e.e1.op == TOK.error)
+            if (e.e1.op == EXP.error)
             {
                 result = e.e1;
                 return;
@@ -7742,7 +7745,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
         exp.e1 = resolveProperties(sc, exp.e1);
-        if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+        if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple)
         {
             if (exp.lwr || exp.upr)
             {
@@ -7755,7 +7758,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         if (!exp.lwr && !exp.upr)
         {
-            if (exp.e1.op == TOK.arrayLiteral)
+            if (exp.e1.op == EXP.arrayLiteral)
             {
                 // Convert [a,b,c][] to [a,b,c]
                 Type t1b = exp.e1.type.toBasetype();
@@ -7768,7 +7771,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 result = e;
                 return;
             }
-            if (exp.e1.op == TOK.slice)
+            if (exp.e1.op == EXP.slice)
             {
                 // Convert e[][] to e[]
                 SliceExp se = cast(SliceExp)exp.e1;
@@ -7785,7 +7788,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 return;
             }
         }
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
@@ -7824,10 +7827,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  */
                 if (VarDeclaration v = expToVariable(exp.e1))
                 {
-                    if (exp.e1.op == TOK.dotVariable)
+                    if (exp.e1.op == EXP.dotVariable)
                     {
                         DotVarExp dve = cast(DotVarExp)exp.e1;
-                        if ((dve.e1.op == TOK.this_ || dve.e1.op == TOK.super_) &&
+                        if ((dve.e1.op == EXP.this_ || dve.e1.op == EXP.super_) &&
                             !(v.storage_class & STC.ref_))
                         {
                             // because it's a class
@@ -7912,13 +7915,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             TupleExp te;
             TypeTuple tup;
             size_t length;
-            if (exp.e1.op == TOK.tuple) // slicing an expression tuple
+            if (exp.e1.op == EXP.tuple) // slicing an expression tuple
             {
                 te = cast(TupleExp)exp.e1;
                 tup = null;
                 length = te.exps.dim;
             }
-            else if (exp.e1.op == TOK.type) // slicing a type tuple
+            else if (exp.e1.op == EXP.type) // slicing a type tuple
             {
                 te = null;
                 tup = cast(TypeTuple)t1b;
@@ -7936,7 +7939,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             size_t j1 = cast(size_t)i1;
             size_t j2 = cast(size_t)i2;
             Expression e;
-            if (exp.e1.op == TOK.tuple)
+            if (exp.e1.op == EXP.tuple)
             {
                 auto exps = new Expressions(j2 - j1);
                 for (size_t i = 0; i < j2 - j1; i++)
@@ -7982,29 +7985,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 Expression el = new ArrayLengthExp(exp.loc, exp.e1);
                 el = el.expressionSemantic(sc);
                 el = el.optimize(WANTvalue);
-                if (el.op == TOK.int64)
+                if (el.op == EXP.int64)
                 {
                     // Array length is known at compile-time. Upper is in bounds if it fits length.
                     dinteger_t length = el.toInteger();
                     auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length));
                     exp.upperIsInBounds = bounds.contains(uprRange);
-                    if (exp.lwr.op == TOK.int64 && exp.upr.op == TOK.int64 && exp.lwr.toInteger() > exp.upr.toInteger())
+                    if (exp.lwr.op == EXP.int64 && exp.upr.op == EXP.int64 && exp.lwr.toInteger() > exp.upr.toInteger())
                     {
                         exp.error("in slice `%s[%llu .. %llu]`, lower bound is greater than upper bound", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger());
                         return setError();
                     }
-                    if (exp.upr.op == TOK.int64 && exp.upr.toInteger() > length)
+                    if (exp.upr.op == EXP.int64 && exp.upr.toInteger() > length)
                     {
                         exp.error("in slice `%s[%llu .. %llu]`, upper bound is greater than array length `%llu`", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger(), length);
                         return setError();
                     }
                 }
-                else if (exp.upr.op == TOK.int64 && exp.upr.toInteger() == 0)
+                else if (exp.upr.op == EXP.int64 && exp.upr.toInteger() == 0)
                 {
                     // Upper slice expression is '0'. Value is always in bounds.
                     exp.upperIsInBounds = true;
                 }
-                else if (exp.upr.op == TOK.variable && (cast(VarExp)exp.upr).var.ident == Id.dollar)
+                else if (exp.upr.op == EXP.variable && (cast(VarExp)exp.upr).var.ident == Id.dollar)
                 {
                     // Upper slice expression is '$'. Value is always in bounds.
                     exp.upperIsInBounds = true;
@@ -8069,7 +8072,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         if (isAggregate(exp.e1.type))
             exp.error("no `[]` operator overload for type `%s`", exp.e1.type.toChars());
-        else if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+        else if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple)
             exp.error("static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars());
         else if (isIndexableNonAggregate(exp.e1.type))
             exp.error("only one index allowed to index `%s`", exp.e1.type.toChars());
@@ -8090,17 +8093,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         exp.e1 = exp.e1.expressionSemantic(sc);
         exp.e2 = exp.e2.expressionSemantic(sc);
 
-        if (exp.e1.op == TOK.type)
+        if (exp.e1.op == EXP.type)
         {
             result = exp.e2;
             return;
         }
-        if (exp.e2.op == TOK.type)
+        if (exp.e2.op == EXP.type)
         {
             result = exp.e2;
             return;
         }
-        if (exp.e2.op == TOK.template_)
+        if (exp.e2.op == EXP.template_)
         {
             auto td = (cast(TemplateExp)exp.e2).td;
             Expression e = new DotTemplateExp(exp.loc, exp.e1, td);
@@ -8169,12 +8172,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         ue = ue.expressionSemantic(sc);
         ue = resolveProperties(sc, ue);
 
-        if (le.op == TOK.error)
+        if (le.op == EXP.error)
         {
             result = le;
             return;
         }
-        if (ue.op == TOK.error)
+        if (ue.op == EXP.error)
         {
             result = ue;
             return;
@@ -8198,7 +8201,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             unaSemantic(e, sc);
             e.e1 = resolveProperties(sc, e.e1);
 
-            if (e.e1.op == TOK.error)
+            if (e.e1.op == EXP.error)
             {
                 result = e.e1;
                 return;
@@ -8218,7 +8221,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             unaSemantic(e, sc);
             e.e1 = resolveProperties(sc, e.e1);
-            if (e.e1.op == TOK.error)
+            if (e.e1.op == EXP.error)
             {
                 result = e.e1;
                 return;
@@ -8244,12 +8247,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (!exp.e1.type)
             exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
         assert(exp.e1.type); // semantic() should already be run on it
-        if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
+        if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple)
         {
             exp.e2 = exp.e2.expressionSemantic(sc);
             exp.e2 = resolveProperties(sc, exp.e2);
             Type nt;
-            if (exp.e2.op == TOK.type)
+            if (exp.e2.op == EXP.type)
                 nt = new TypeAArray(exp.e1.type, exp.e2.type);
             else
                 nt = new TypeSArray(exp.e1.type, exp.e2);
@@ -8257,7 +8260,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = e.expressionSemantic(sc);
             return;
         }
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
@@ -8294,7 +8297,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         exp.e2 = resolveProperties(sc, exp.e2);
         if (t1b.ty == Ttuple)
             sc = sc.endCTFE();
-        if (exp.e2.op == TOK.tuple)
+        if (exp.e2.op == EXP.tuple)
         {
             TupleExp te = cast(TupleExp)exp.e2;
             if (te.exps && te.exps.dim == 1)
@@ -8320,7 +8323,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (exp.e2.type == Type.terror)
                 return setError();
             exp.e2 = exp.e2.optimize(WANTvalue);
-            if (exp.e2.op == TOK.int64 && exp.e2.toInteger() == 0)
+            if (exp.e2.op == EXP.int64 && exp.e2.toInteger() == 0)
             {
             }
             else if (sc.func && !(sc.flags & SCOPE.debug_) && sc.func.setUnsafe())
@@ -8378,13 +8381,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 TupleExp te;
                 TypeTuple tup;
                 size_t length;
-                if (exp.e1.op == TOK.tuple)
+                if (exp.e1.op == EXP.tuple)
                 {
                     te = cast(TupleExp)exp.e1;
                     tup = null;
                     length = te.exps.dim;
                 }
-                else if (exp.e1.op == TOK.type)
+                else if (exp.e1.op == EXP.type)
                 {
                     te = null;
                     tup = cast(TypeTuple)t1b;
@@ -8399,7 +8402,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return setError();
                 }
                 Expression e;
-                if (exp.e1.op == TOK.tuple)
+                if (exp.e1.op == EXP.tuple)
                 {
                     e = (*te.exps)[cast(size_t)index];
                     e = Expression.combine(te.e0, e);
@@ -8422,7 +8425,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             Expression el = new ArrayLengthExp(exp.loc, exp.e1);
             el = el.expressionSemantic(sc);
             el = el.optimize(WANTvalue);
-            if (el.op == TOK.int64)
+            if (el.op == EXP.int64)
             {
                 exp.e2 = exp.e2.optimize(WANTvalue);
                 dinteger_t length = el.toInteger();
@@ -8455,7 +8458,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
         Expression e1x = resolveProperties(sc, exp.e1);
-        if (e1x.op == TOK.error)
+        if (e1x.op == EXP.error)
         {
             result = e1x;
             return;
@@ -8472,15 +8475,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (exp.e1.checkReadModifyWrite(exp.op))
             return setError();
 
-        if (exp.e1.op == TOK.slice)
+        if (exp.e1.op == EXP.slice)
         {
-            const(char)* s = exp.op == TOK.plusPlus ? "increment" : "decrement";
+            const(char)* s = exp.op == EXP.plusPlus ? "increment" : "decrement";
             exp.error("cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toChars(), s);
             return setError();
         }
 
         Type t1 = exp.e1.type.toBasetype();
-        if (t1.ty == Tclass || t1.ty == Tstruct || exp.e1.op == TOK.arrayLength)
+        if (t1.ty == Tclass || t1.ty == Tstruct || exp.e1.op == EXP.arrayLength)
         {
             /* Check for operator overloading,
              * but rewrite in terms of ++e instead of e++
@@ -8489,7 +8492,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             /* If e1 is not trivial, take a reference to it
              */
             Expression de = null;
-            if (exp.e1.op != TOK.variable && exp.e1.op != TOK.arrayLength)
+            if (exp.e1.op != EXP.variable && exp.e1.op != EXP.arrayLength)
             {
                 // ref v = e1;
                 auto v = copyToTemp(STC.ref_, "__postref", exp.e1);
@@ -8504,7 +8507,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             Expression ea = new DeclarationExp(exp.loc, tmp);
 
             Expression eb = exp.e1.syntaxCopy();
-            eb = new PreExp(exp.op == TOK.plusPlus ? TOK.prePlusPlus : TOK.preMinusMinus, exp.loc, eb);
+            eb = new PreExp(exp.op == EXP.plusPlus ? EXP.prePlusPlus : EXP.preMinusMinus, exp.loc, eb);
 
             Expression ec = new VarExp(exp.loc, tmp);
 
@@ -8547,7 +8550,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // Rewrite as e1+=1 or e1-=1
-        if (exp.op == TOK.prePlusPlus)
+        if (exp.op == EXP.prePlusPlus)
             e = new AddAssignExp(exp.loc, exp.e1, IntegerExp.literal!1);
         else
             e = new MinAssignExp(exp.loc, exp.e1, IntegerExp.literal!1);
@@ -8596,8 +8599,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             printf("AssignExp::semantic('%s')\n", exp.toChars());
         }
-        //printf("exp.e1.op = %d, '%s'\n", exp.e1.op, Token.toChars(exp.e1.op));
-        //printf("exp.e2.op = %d, '%s'\n", exp.e2.op, Token.toChars(exp.e2.op));
+        //printf("exp.e1.op = %d, '%s'\n", exp.e1.op, EXPtoString(exp.e1.op).ptr);
+        //printf("exp.e2.op = %d, '%s'\n", exp.e2.op, EXPtoString(exp.e2.op).ptr);
 
         void setResult(Expression e, int line = __LINE__)
         {
@@ -8640,17 +8643,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
             const(bool) maybeSlice =
                 (ae.arguments.dim == 0 ||
-                 ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval);
+                 ae.arguments.dim == 1 && (*ae.arguments)[0].op == EXP.interval);
 
             IntervalExp ie = null;
             if (maybeSlice && ae.arguments.dim)
             {
-                assert((*ae.arguments)[0].op == TOK.interval);
+                assert((*ae.arguments)[0].op == EXP.interval);
                 ie = cast(IntervalExp)(*ae.arguments)[0];
             }
             while (true)
             {
-                if (ae.e1.op == TOK.error)
+                if (ae.e1.op == EXP.error)
                     return setResult(ae.e1);
 
                 Expression e0 = null;
@@ -8667,11 +8670,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     res = resolveOpDollar(sc, ae, &e0);
                     if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j)
                         goto Lfallback;
-                    if (res.op == TOK.error)
+                    if (res.op == EXP.error)
                         return setResult(res);
 
                     res = exp.e2.expressionSemantic(sc);
-                    if (res.op == TOK.error)
+                    if (res.op == EXP.error)
                         return setResult(res);
                     exp.e2 = res;
 
@@ -8695,11 +8698,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 {
                     // Deal with $
                     res = resolveOpDollar(sc, ae, ie, &e0);
-                    if (res.op == TOK.error)
+                    if (res.op == EXP.error)
                         return setResult(res);
 
                     res = exp.e2.expressionSemantic(sc);
-                    if (res.op == TOK.error)
+                    if (res.op == EXP.error)
                         return setResult(res);
 
                     exp.e2 = res;
@@ -8826,9 +8829,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (!t1.isTypeSArray())
                 e2x = e2x.arrayFuncConv(sc);
             e2x = resolveProperties(sc, e2x);
-            if (e2x.op == TOK.type)
+            if (e2x.op == EXP.type)
                 e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
-            if (e2x.op == TOK.error)
+            if (e2x.op == EXP.error)
                 return setResult(e2x);
             // We delay checking the value for structs/classes as these might have
             // an opAssign defined.
@@ -8844,7 +8847,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             Expression e2x = exp.e2;
 
         Ltupleassign:
-            if (exp.e1.op == TOK.tuple && e2x.op == TOK.tuple)
+            if (exp.e1.op == EXP.tuple && e2x.op == EXP.tuple)
             {
                 TupleExp tup1 = cast(TupleExp)exp.e1;
                 TupleExp tup2 = cast(TupleExp)e2x;
@@ -8877,7 +8880,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
             /* Look for form: e1 = e2.aliasthis.
              */
-            if (exp.e1.op == TOK.tuple)
+            if (exp.e1.op == EXP.tuple)
             {
                 TupleDeclaration td = isAliasThisTuple(e2x);
                 if (!td)
@@ -8915,7 +8918,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
                 e2x = new TupleExp(e2x.loc, e0, iexps);
                 e2x = e2x.expressionSemantic(sc);
-                if (e2x.op == TOK.error)
+                if (e2x.op == EXP.error)
                 {
                     result = e2x;
                     return;
@@ -8929,7 +8932,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         /* Inside constructor, if this is the first assignment of object field,
          * rewrite this to initializing the field.
          */
-        if (exp.op == TOK.assign
+        if (exp.op == EXP.assign
             && exp.e1.checkModifiable(sc) == Modifiable.initialization)
         {
             //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars());
@@ -8942,20 +8945,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (auto ie1 = exp.e1.isIndexExp())
             {
                 Expression e1x = ie1.markSettingAAElem();
-                if (e1x.op == TOK.error)
+                if (e1x.op == EXP.error)
                 {
                     result = e1x;
                     return;
                 }
             }
         }
-        else if (exp.op == TOK.construct && exp.e1.op == TOK.variable &&
+        else if (exp.op == EXP.construct && exp.e1.op == EXP.variable &&
                  (cast(VarExp)exp.e1).var.storage_class & (STC.out_ | STC.ref_))
         {
             exp.memset = MemorySet.referenceInit;
         }
 
-        if (exp.op == TOK.assign)  // skip TOK.blit and TOK.construct, which are initializations
+        if (exp.op == EXP.assign)  // skip EXP.blit and EXP.construct, which are initializations
         {
             exp.e1.checkSharedAccess(sc);
             checkUnsafeAccess(sc, exp.e1, false, true);
@@ -8977,7 +8980,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             auto e2x = exp.e2;
             auto sd = (cast(TypeStruct)t1).sym;
 
-            if (exp.op == TOK.construct)
+            if (exp.op == EXP.construct)
             {
                 Type t2 = e2x.type.toBasetype();
                 if (t2.ty == Tstruct && sd == (cast(TypeStruct)t2).sym)
@@ -8992,12 +8995,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     // Look for the form from last of comma chain.
                     auto e2y = lastComma(e2x);
 
-                    CallExp ce = (e2y.op == TOK.call) ? cast(CallExp)e2y : null;
-                    DotVarExp dve = (ce && ce.e1.op == TOK.dotVariable)
+                    CallExp ce = (e2y.op == EXP.call) ? cast(CallExp)e2y : null;
+                    DotVarExp dve = (ce && ce.e1.op == EXP.dotVariable)
                         ? cast(DotVarExp)ce.e1 : null;
                     if (sd.ctor && ce && dve && dve.var.isCtorDeclaration() &&
                         // https://issues.dlang.org/show_bug.cgi?id=19389
-                        dve.e1.op != TOK.dotVariable &&
+                        dve.e1.op != EXP.dotVariable &&
                         e2y.type.implicitConvTo(t1))
                     {
                         /* Look for form of constructor call which is:
@@ -9009,7 +9012,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                          * initializer
                          */
                         Expression einit = getInitExp(sd, exp.loc, sc, t1);
-                        if (einit.op == TOK.error)
+                        if (einit.op == EXP.error)
                         {
                             result = einit;
                             return;
@@ -9043,7 +9046,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     // a temporary created and an extra destructor call.
                     // AST will be rewritten to:
                     // a ? e1 = 0, e1.this(1) : ...; -> blitting plus construction
-                    if (e2x.op == TOK.question)
+                    if (e2x.op == EXP.question)
                     {
                         /* Rewrite as:
                          *  a ? e1 = b : e1 = c;
@@ -9158,7 +9161,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                          * Foo f = new Foo2(0); is a valid expression if Foo has a constructor
                          * which receives an instance of a Foo2 class
                          */
-                        if (exp.e2.op == TOK.new_)
+                        if (exp.e2.op == EXP.new_)
                         {
                             auto newExp = cast(NewExp)(exp.e2);
                             if (newExp.newtype && newExp.newtype == t1)
@@ -9193,7 +9196,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
                         e2x = e2x.expressionSemantic(sc);
                         e2x = resolveProperties(sc, e2x);
-                        if (e2x.op == TOK.error)
+                        if (e2x.op == EXP.error)
                         {
                             result = e2x;
                             return;
@@ -9216,9 +9219,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     }
                 }
             }
-            else if (exp.op == TOK.assign)
+            else if (exp.op == EXP.assign)
             {
-                if (e1x.op == TOK.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
+                if (e1x.op == EXP.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
                 {
                     /*
                      * Rewrite:
@@ -9233,12 +9236,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                      */
                     // ensure we keep the expr modifiable
                     Expression esetting = (cast(IndexExp)e1x).markSettingAAElem();
-                    if (esetting.op == TOK.error)
+                    if (esetting.op == EXP.error)
                     {
                         result = esetting;
                         return;
                     }
-                    assert(esetting.op == TOK.index);
+                    assert(esetting.op == EXP.index);
                     IndexExp ie = cast(IndexExp) esetting;
                     Type t2 = e2x.type.toBasetype();
 
@@ -9279,7 +9282,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
                             ey = new ConstructExp(exp.loc, ex, ey);
                             ey = ey.expressionSemantic(sc);
-                            if (ey.op == TOK.error)
+                            if (ey.op == EXP.error)
                             {
                                 result = ey;
                                 return;
@@ -9290,7 +9293,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                             // The whole expression should have the common type
                             // of opAssign() return and assigned AA entry.
                             // Even if there's no common type, expression should be typed as void.
-                            if (!typeMerge(sc, TOK.question, ex, ey))
+                            if (!typeMerge(sc, EXP.question, ex, ey))
                             {
                                 ex = new CastExp(ex.loc, ex, Type.tvoid);
                                 ey = new CastExp(ey.loc, ey, Type.tvoid);
@@ -9314,7 +9317,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
             else
-                assert(exp.op == TOK.blit);
+                assert(exp.op == EXP.blit);
 
             if (e2x.checkValue())
                 return setError();
@@ -9325,7 +9328,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         else if (t1.ty == Tclass)
         {
             // Disallow assignment operator overloads for same type
-            if (exp.op == TOK.assign && !exp.e2.implicitConvTo(exp.e1.type))
+            if (exp.op == EXP.assign && !exp.e2.implicitConvTo(exp.e1.type))
             {
                 Expression e = exp.op_overload(sc);
                 if (e)
@@ -9340,13 +9343,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         else if (t1.ty == Tsarray)
         {
             // SliceExp cannot have static array type without context inference.
-            assert(exp.e1.op != TOK.slice);
+            assert(exp.e1.op != EXP.slice);
             Expression e1x = exp.e1;
             Expression e2x = exp.e2;
 
             if (e2x.implicitConvTo(e1x.type))
             {
-                if (exp.op != TOK.blit && (e2x.op == TOK.slice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == TOK.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != TOK.slice && e2x.isLvalue()))
+                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))
                         return setError();
@@ -9392,7 +9395,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
                 // May be block or element-wise assignment, so
                 // convert e1 to e1[]
-                if (exp.op != TOK.assign)
+                if (exp.op != EXP.assign)
                 {
                     // If multidimensional static array, treat as one large array
                     //
@@ -9435,9 +9438,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 sle.arrayop = true;
                 e1x = sle.expressionSemantic(sc);
             }
-            if (e1x.op == TOK.error)
+            if (e1x.op == EXP.error)
                 return setResult(e1x);
-            if (e2x.op == TOK.error)
+            if (e2x.op == EXP.error)
                 return setResult(e2x);
 
             exp.e1 = e1x;
@@ -9451,7 +9454,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);
-            if (ale1x.op == TOK.error)
+            if (ale1x.op == EXP.error)
                 return setResult(ale1x);
             ale.e1 = ale1x;
 
@@ -9466,7 +9469,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             auto lc = lastComma(exp.e2);
             lc = lc.optimize(WANTvalue);
             // use slice expression when arr.length = 0 to avoid runtime call
-            if(lc.op == TOK.int64 && lc.toInteger() == 0)
+            if(lc.op == EXP.int64 && lc.toInteger() == 0)
             {
                 Expression se = new SliceExp(ale.loc, ale.e1, lc, lc);
                 Expression as = new AssignExp(ale.loc, ale.e1, se);
@@ -9507,7 +9510,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             Type tn = se.type.nextOf();
             const fun = sc.func;
-            if (exp.op == TOK.assign && !tn.isMutable() &&
+            if (exp.op == EXP.assign && !tn.isMutable() &&
                 // allow modifiation in module ctor, see
                 // https://issues.dlang.org/show_bug.cgi?id=9884
                 (!fun || (fun && !fun.isStaticCtorDeclaration())))
@@ -9516,7 +9519,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 return setError();
             }
 
-            if (exp.op == TOK.assign && !tn.baseElemOf().isAssignable())
+            if (exp.op == EXP.assign && !tn.baseElemOf().isAssignable())
             {
                 exp.error("slice `%s` is not mutable, struct `%s` has immutable members",
                     exp.e1.toChars(), tn.baseElemOf().toChars());
@@ -9525,18 +9528,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
 
             // For conditional operator, both branches need conversion.
-            while (se.e1.op == TOK.slice)
+            while (se.e1.op == EXP.slice)
                 se = cast(SliceExp)se.e1;
-            if (se.e1.op == TOK.question && se.e1.type.toBasetype().ty == Tsarray)
+            if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray)
             {
                 se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
-                if (se.e1.op == TOK.error)
+                if (se.e1.op == EXP.error)
                     return setResult(se.e1);
             }
         }
         else
         {
-            if (t1.ty == Tsarray && exp.op == TOK.assign)
+            if (t1.ty == Tsarray && exp.op == EXP.assign)
             {
                 Type tn = exp.e1.type.nextOf();
                 if (tn && !tn.baseElemOf().isAssignable())
@@ -9552,12 +9555,12 @@ 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 == TOK.assign)
+            if (exp.op == EXP.assign)
                 e1x = e1x.modifiableLvalue(sc, e1old);
 
             e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
 
-            if (e1x.op == TOK.error)
+            if (e1x.op == EXP.error)
             {
                 result = e1x;
                 return;
@@ -9576,18 +9579,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         while (telem.ty == Tarray)
             telem = telem.nextOf();
 
-        if (exp.e1.op == TOK.slice && t1.nextOf() &&
-            (telem.ty != Tvoid || e2x.op == TOK.null_) &&
+        if (exp.e1.op == EXP.slice && t1.nextOf() &&
+            (telem.ty != Tvoid || e2x.op == EXP.null_) &&
             e2x.implicitConvTo(t1.nextOf()))
         {
             // Check for block assignment. If it is of type void[], void[][], etc,
             // '= 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 != TOK.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf()))
+            if (exp.op != EXP.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf()))
                 return setError();
         }
-        else if (exp.e1.op == TOK.slice &&
+        else if (exp.e1.op == EXP.slice &&
                  (t2.ty == Tarray || t2.ty == Tsarray) &&
                  t2.nextOf().implicitConvTo(t1.nextOf()))
         {
@@ -9616,24 +9619,24 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
 
-            if (exp.op != TOK.blit &&
-                (e2x.op == TOK.slice && (cast(UnaExp)e2x).e1.isLvalue() ||
-                 e2x.op == TOK.cast_ && (cast(UnaExp)e2x).e1.isLvalue() ||
-                 e2x.op != TOK.slice && e2x.isLvalue()))
+            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 (exp.e1.checkPostblit(sc, t1.nextOf()))
                     return setError();
             }
 
-            if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == TOK.assign &&
-                e2x.op != TOK.slice && e2x.op != TOK.assign &&
-                e2x.op != TOK.arrayLiteral && e2x.op != TOK.string_ &&
-                !(e2x.op == TOK.add || e2x.op == TOK.min ||
-                  e2x.op == TOK.mul || e2x.op == TOK.div ||
-                  e2x.op == TOK.mod || e2x.op == TOK.xor ||
-                  e2x.op == TOK.and || e2x.op == TOK.or ||
-                  e2x.op == TOK.pow ||
-                  e2x.op == TOK.tilde || e2x.op == TOK.negate))
+            if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == EXP.assign &&
+                e2x.op != EXP.slice && e2x.op != EXP.assign &&
+                e2x.op != EXP.arrayLiteral && e2x.op != EXP.string_ &&
+                !(e2x.op == EXP.add || e2x.op == EXP.min ||
+                  e2x.op == EXP.mul || e2x.op == EXP.div ||
+                  e2x.op == EXP.mod || e2x.op == EXP.xor ||
+                  e2x.op == EXP.and || e2x.op == EXP.or ||
+                  e2x.op == EXP.pow ||
+                  e2x.op == EXP.tilde || e2x.op == EXP.negate))
             {
                 const(char)* e1str = exp.e1.toChars();
                 const(char)* e2str = e2x.toChars();
@@ -9673,7 +9676,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  * elements need to be const at best. So we should give a chance
                  * to change code unit size for polysemous string literal.
                  */
-                if (e2x.op == TOK.string_)
+                if (e2x.op == EXP.string_)
                     e2x = e2x.implicitCastTo(sc, exp.e1.type.constOf());
                 else
                     e2x = e2x.implicitCastTo(sc, exp.e1.type);
@@ -9689,19 +9692,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         else
         {
-            if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == TOK.assign &&
+            if (0 && global.params.warnings != DiagnosticReporting.off && !global.gag && exp.op == EXP.assign &&
                 t1.ty == Tarray && t2.ty == Tsarray &&
-                e2x.op != TOK.slice &&
+                e2x.op != EXP.slice &&
                 t2.implicitConvTo(t1))
             {
                 // Disallow ar[] = sa (Converted to ar[] = sa[])
                 // Disallow da   = sa (Converted to da   = sa[])
                 const(char)* e1str = exp.e1.toChars();
                 const(char)* e2str = e2x.toChars();
-                const(char)* atypestr = exp.e1.op == TOK.slice ? "element-wise" : "slice";
+                const(char)* atypestr = exp.e1.op == EXP.slice ? "element-wise" : "slice";
                 exp.warning("explicit %s assignment `%s = (%s)[]` is better than `%s = %s`", atypestr, e1str, e2str, e1str, e2str);
             }
-            if (exp.op == TOK.blit)
+            if (exp.op == EXP.blit)
                 e2x = e2x.castTo(sc, exp.e1.type);
             else
             {
@@ -9711,7 +9714,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
                 // If the implicit cast has failed and the assign expression is
                 // the initialization of a struct member field
-                if (e2x.op == TOK.error && exp.op == TOK.construct && t1.ty == Tstruct)
+                if (e2x.op == EXP.error && exp.op == EXP.construct && t1.ty == Tstruct)
                 {
                     scope sd = (cast(TypeStruct)t1).sym;
                     Dsymbol opAssign = search_function(sd, Id.assign);
@@ -9729,7 +9732,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
             }
         }
-        if (e2x.op == TOK.error)
+        if (e2x.op == EXP.error)
         {
             result = e2x;
             return;
@@ -9743,11 +9746,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             // Look for valid array operations
             if (exp.memset != MemorySet.blockAssign &&
-                exp.e1.op == TOK.slice &&
+                exp.e1.op == EXP.slice &&
                 (isUnaArrayOp(exp.e2.op) || isBinArrayOp(exp.e2.op)))
             {
                 exp.type = exp.e1.type;
-                if (exp.op == TOK.construct) // https://issues.dlang.org/show_bug.cgi?id=10282
+                if (exp.op == EXP.construct) // https://issues.dlang.org/show_bug.cgi?id=10282
                                         // tweak mutability of e1 element
                     exp.e1.type = exp.e1.type.nextOf().mutableOf().arrayOf();
                 result = arrayOp(exp, sc);
@@ -9756,7 +9759,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
             // Drop invalid array operations in e2
             //  d = a[] + b[], d = (a[] + b[])[0..2], etc
-            if (checkNonAssignmentArrayOp(exp.e2, exp.memset != MemorySet.blockAssign && exp.op == TOK.assign))
+            if (checkNonAssignmentArrayOp(exp.e2, exp.memset != MemorySet.blockAssign && exp.op == EXP.assign))
                 return setError();
 
             // Remains valid array assignments
@@ -9766,7 +9769,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         /* Don't allow assignment to classes that were allocated on the stack with:
          *      scope Class c = new Class();
          */
-        if (exp.e1.op == TOK.variable && exp.op == TOK.assign)
+        if (exp.e1.op == EXP.variable && exp.op == EXP.assign)
         {
             VarExp ve = cast(VarExp)exp.e1;
             VarDeclaration vd = ve.var.isVarDeclaration();
@@ -9777,14 +9780,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
-        if (exp.e1.op == TOK.variable && (cast(VarExp)exp.e1).var.ident == Id.ctfe)
+        if (exp.e1.op == EXP.variable && (cast(VarExp)exp.e1).var.ident == Id.ctfe)
         {
             exp.error("cannot modify compiler-generated variable `__ctfe`");
         }
 
         exp.type = exp.e1.type;
         assert(exp.type);
-        auto res = exp.op == TOK.assign ? exp.reorderSettingAAElem(sc) : exp;
+        auto res = exp.op == EXP.assign ? exp.reorderSettingAAElem(sc) : exp;
         Expression tmp;
         /* https://issues.dlang.org/show_bug.cgi?id=22366
          *
@@ -9792,6 +9795,84 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
          * `checkAssignExp` expects only AssignExps.
          */
         checkAssignEscape(sc, Expression.extractLast(res, tmp), false);
+
+        if (auto ae = res.isConstructExp())
+        {
+            Type t1b = ae.e1.type.toBasetype();
+            if (t1b.ty != Tsarray && t1b.ty != Tarray)
+                return setResult(res);
+
+            /* Do not lower Rvalues and references, as they need to be moved,
+             * not copied.
+             * Skip the lowering when the RHS is an array literal, as e2ir
+             * already handles such cases more elegantly.
+             */
+            const isArrayCtor =
+                (ae.e1.isSliceExp || ae.e1.type.ty == Tsarray) &&
+                ae.e2.isLvalue &&
+                !(ae.e1.isVarExp &&
+                    ae.e1.isVarExp.var.isVarDeclaration.isReference) &&
+                (ae.e2.isVarExp ||
+                    ae.e2.isSliceExp ||
+                    (ae.e2.type.ty == Tsarray && !ae.e2.isArrayLiteralExp)) &&
+                ae.e1.type.nextOf &&
+                ae.e2.type.nextOf &&
+                ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf);
+
+            const isArraySetCtor =
+                (ae.e1.isSliceExp || ae.e1.type.ty == Tsarray) &&
+                ae.e2.isLvalue &&
+                (ae.e2.type.ty == Tstruct || ae.e2.type.ty == Tsarray) &&
+                ae.e1.type.nextOf &&
+                ae.e1.type.nextOf.equivalent(ae.e2.type);
+
+            if (isArrayCtor || isArraySetCtor)
+            {
+                const ts = t1b.nextOf().baseElemOf().isTypeStruct();
+                if (!ts || (!ts.sym.postblit && !ts.sym.dtor))
+                    return setResult(res);
+
+                auto func = isArrayCtor ? Id._d_arrayctor : Id._d_arraysetctor;
+                const other = isArrayCtor ? "other array" : "value";
+                if (!verifyHookExist(exp.loc, *sc, func, "construct array with " ~ other, Id.object))
+                    return setError();
+
+                // Lower to object._d_array{,set}ctor(e1, e2)
+                Expression id = new IdentifierExp(exp.loc, Id.empty);
+                id = new DotIdExp(exp.loc, id, Id.object);
+                id = new DotIdExp(exp.loc, id, func);
+                id = id.expressionSemantic(sc);
+
+                auto arguments = new Expressions();
+                arguments.push(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf).expressionSemantic(sc));
+                if (isArrayCtor)
+                {
+                    arguments.push(new CastExp(ae.loc, ae.e2, ae.e2.type.nextOf.arrayOf).expressionSemantic(sc));
+                    Expression ce = new CallExp(exp.loc, id, arguments);
+                    res = ce.expressionSemantic(sc);
+                }
+                else
+                {
+                    Expression e0;
+                    // If ae.e2 is not a variable, construct a temp variable, as _d_arraysetctor requires `ref` access
+                    if (!ae.e2.isVarExp)
+                    {
+                        auto vd = copyToTemp(STC.scope_, "__setctor", ae.e2);
+                        e0 = new DeclarationExp(vd.loc, vd).expressionSemantic(sc);
+                        arguments.push(new VarExp(vd.loc, vd).expressionSemantic(sc));
+                    }
+                    else
+                        arguments.push(ae.e2);
+
+                    Expression ce = new CallExp(exp.loc, id, arguments);
+                    res = Expression.combine(e0, ce).expressionSemantic(sc);
+                }
+
+                if (global.params.verbose)
+                    message("lowered   %s =>\n          %s", exp.toChars(), res.toChars());
+            }
+        }
+
         return setResult(res);
     }
 
@@ -9814,7 +9895,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return setError();
 
         assert(exp.e1.type && exp.e2.type);
-        if (exp.e1.op == TOK.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
+        if (exp.e1.op == EXP.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray)
         {
             if (checkNonAssignmentArrayOp(exp.e1))
                 return setError();
@@ -9855,7 +9936,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             e = Expression.extractLast(e, e0);
             assert(e == exp);
 
-            if (exp.e1.op == TOK.variable)
+            if (exp.e1.op == EXP.variable)
             {
                 // Rewrite: e1 = e1 ^^ e2
                 e = new PowExp(exp.loc, exp.e1.syntaxCopy(), exp.e2);
@@ -9895,7 +9976,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (exp.e1.op == TOK.slice)
+        if (exp.e1.op == EXP.slice)
         {
             SliceExp se = cast(SliceExp)exp.e1;
             if (se.e1.type.toBasetype().ty == Tsarray)
@@ -9906,12 +9987,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1);
-        if (exp.e1.op == TOK.error)
+        if (exp.e1.op == EXP.error)
         {
             result = exp.e1;
             return;
         }
-        if (exp.e2.op == TOK.error)
+        if (exp.e2.op == EXP.error)
         {
             result = exp.e2;
             return;
@@ -9925,9 +10006,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Type tb2 = exp.e2.type.toBasetype();
 
         /* Possibilities:
-         * TOK.concatenateAssign: appending T[] to T[]
-         * TOK.concatenateElemAssign: appending T to T[]
-         * TOK.concatenateDcharAssign: appending dchar to T[]
+         * EXP.concatenateAssign: appending T[] to T[]
+         * EXP.concatenateElemAssign: appending T to T[]
+         * EXP.concatenateDcharAssign: appending dchar to T[]
          */
         if ((tb1.ty == Tarray) &&
             (tb2.ty == Tarray || tb2.ty == Tsarray) &&
@@ -9935,8 +10016,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
              (tb2.nextOf().implicitConvTo(tb1next) &&
               (tb2.nextOf().size(Loc.initial) == tb1next.size(Loc.initial)))))
         {
-            // TOK.concatenateAssign
-            assert(exp.op == TOK.concatenateAssign);
+            // EXP.concatenateAssign
+            assert(exp.op == EXP.concatenateAssign);
             if (exp.e1.checkPostblit(sc, tb1next))
                 return setError();
 
@@ -10033,7 +10114,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         exp.type = exp.e1.type;
         auto res = exp.reorderSettingAAElem(sc);
-        if ((exp.op == TOK.concatenateElemAssign || exp.op == TOK.concatenateDcharAssign) &&
+        if ((exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) &&
             global.params.useDIP1000 == FeatureState.enabled)
             checkAssignEscape(sc, res, false);
         result = res;
@@ -10227,6 +10308,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 {
                     e = new IntegerExp(exp.loc, 0, Type.tptrdiff_t);
                 }
+                else if (stride == cast(d_int64)SIZE_INVALID)
+                    e = ErrorExp.get();
                 else
                 {
                     e = new DivExp(exp.loc, exp, new IntegerExp(Loc.initial, stride, Type.tptrdiff_t));
@@ -10336,7 +10419,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Type tb2next = tb2.nextOf();
 
         // Check for: array ~ array
-        if (tb1next && tb2next && (tb1next.implicitConvTo(tb2next) >= MATCH.constant || tb2next.implicitConvTo(tb1next) >= MATCH.constant || exp.e1.op == TOK.arrayLiteral && exp.e1.implicitConvTo(tb2) || exp.e2.op == TOK.arrayLiteral && exp.e2.implicitConvTo(tb1)))
+        if (tb1next && tb2next && (tb1next.implicitConvTo(tb2next) >= MATCH.constant || tb2next.implicitConvTo(tb1next) >= MATCH.constant || exp.e1.op == EXP.arrayLiteral && exp.e1.implicitConvTo(tb2) || exp.e2.op == EXP.arrayLiteral && exp.e2.implicitConvTo(tb1)))
         {
             /* https://issues.dlang.org/show_bug.cgi?id=9248
              * Here to avoid the case of:
@@ -10360,14 +10443,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         // Check for: array ~ element
         if ((tb1.ty == Tsarray || tb1.ty == Tarray) && tb2.ty != Tvoid)
         {
-            if (exp.e1.op == TOK.arrayLiteral)
+            if (exp.e1.op == EXP.arrayLiteral)
             {
                 exp.e2 = doCopyOrMove(sc, exp.e2);
                 // https://issues.dlang.org/show_bug.cgi?id=14686
                 // Postblit call appears in AST, and this is
                 // finally translated  to an ArrayLiteralExp in below optimize().
             }
-            else if (exp.e1.op == TOK.string_)
+            else if (exp.e1.op == EXP.string_)
             {
                 // No postblit call exists on character (integer) value.
             }
@@ -10378,7 +10461,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 // Postblit call will be done in runtime helper function
             }
 
-            if (exp.e1.op == TOK.arrayLiteral && exp.e1.implicitConvTo(tb2.arrayOf()))
+            if (exp.e1.op == EXP.arrayLiteral && exp.e1.implicitConvTo(tb2.arrayOf()))
             {
                 exp.e1 = exp.e1.implicitCastTo(sc, tb2.arrayOf());
                 exp.type = tb2.arrayOf();
@@ -10403,11 +10486,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         // Check for: element ~ array
         if ((tb2.ty == Tsarray || tb2.ty == Tarray) && tb1.ty != Tvoid)
         {
-            if (exp.e2.op == TOK.arrayLiteral)
+            if (exp.e2.op == EXP.arrayLiteral)
             {
                 exp.e1 = doCopyOrMove(sc, exp.e1);
             }
-            else if (exp.e2.op == TOK.string_)
+            else if (exp.e2.op == EXP.string_)
             {
             }
             else
@@ -10416,7 +10499,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     return setError();
             }
 
-            if (exp.e2.op == TOK.arrayLiteral && exp.e2.implicitConvTo(tb1.arrayOf()))
+            if (exp.e2.op == EXP.arrayLiteral && exp.e2.implicitConvTo(tb1.arrayOf()))
             {
                 exp.e2 = exp.e2.implicitCastTo(sc, tb1.arrayOf());
                 exp.type = tb1.arrayOf();
@@ -10444,11 +10527,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             Type t1 = tb1next.mutableOf().constOf().arrayOf();
             Type t2 = tb2next.mutableOf().constOf().arrayOf();
-            if (exp.e1.op == TOK.string_ && !(cast(StringExp)exp.e1).committed)
+            if (exp.e1.op == EXP.string_ && !(cast(StringExp)exp.e1).committed)
                 exp.e1.type = t1;
             else
                 exp.e1 = exp.e1.castTo(sc, t1);
-            if (exp.e2.op == TOK.string_ && !(cast(StringExp)exp.e2).committed)
+            if (exp.e2.op == EXP.string_ && !(cast(StringExp)exp.e2).committed)
                 exp.e2.type = t2;
             else
                 exp.e2 = exp.e2.castTo(sc, t2);
@@ -10804,7 +10887,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         // First, attempt to fold the expression.
         e = exp.optimize(WANTvalue);
-        if (e.op != TOK.pow)
+        if (e.op != EXP.pow)
         {
             e = e.expressionSemantic(sc);
             result = e;
@@ -10819,7 +10902,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         e = new ScopeExp(exp.loc, mmath);
 
-        if (exp.e2.op == TOK.float64 && exp.e2.toReal() == CTFloat.half)
+        if (exp.e2.op == EXP.float64 && exp.e2.toReal() == CTFloat.half)
         {
             // Replace e1 ^^ 0.5 with .std.math.sqrt(e1)
             e = new CallExp(exp.loc, new DotIdExp(exp.loc, e, Id._sqrt), exp.e1);
@@ -11126,7 +11209,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Expression e1x = exp.e1.expressionSemantic(sc);
 
         // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-        if (e1x.op == TOK.type)
+        if (e1x.op == EXP.type)
             e1x = resolveAliasThis(sc, e1x);
 
         e1x = resolveProperties(sc, e1x);
@@ -11137,9 +11220,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             /* If in static if, don't evaluate e2 if we don't have to.
              */
             e1x = e1x.optimize(WANTvalue);
-            if (e1x.isBool(exp.op == TOK.orOr))
+            if (e1x.toBool().hasValue(exp.op == EXP.orOr))
             {
-                result = IntegerExp.createBool(exp.op == TOK.orOr);
+                result = IntegerExp.createBool(exp.op == EXP.orOr);
                 return;
             }
         }
@@ -11150,7 +11233,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         ctorflow.freeFieldinit();
 
         // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-        if (e2x.op == TOK.type)
+        if (e2x.op == EXP.type)
             e2x = resolveAliasThis(sc, e2x);
 
         e2x = resolveProperties(sc, e2x);
@@ -11164,17 +11247,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (e2x.type.ty != Tvoid)
             e2x = e2x.toBoolean(sc);
 
-        if (e2x.op == TOK.type || e2x.op == TOK.scope_)
+        if (e2x.op == EXP.type || e2x.op == EXP.scope_)
         {
             exp.error("`%s` is not an expression", exp.e2.toChars());
             return setError();
         }
-        if (e1x.op == TOK.error || e1x.type.ty == Tnoreturn)
+        if (e1x.op == EXP.error || e1x.type.ty == Tnoreturn)
         {
             result = e1x;
             return;
         }
-        if (e2x.op == TOK.error)
+        if (e2x.op == EXP.error)
         {
             result = e2x;
             return;
@@ -11213,13 +11296,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         Type t1 = exp.e1.type.toBasetype();
         Type t2 = exp.e2.type.toBasetype();
-        if (t1.ty == Tclass && exp.e2.op == TOK.null_ || t2.ty == Tclass && exp.e1.op == TOK.null_)
+        if (t1.ty == Tclass && exp.e2.op == EXP.null_ || t2.ty == Tclass && exp.e1.op == EXP.null_)
         {
             exp.error("do not use `null` when comparing class types");
             return setError();
         }
 
-        TOK cmpop;
+        EXP cmpop;
         if (auto e = exp.op_overload(sc, &cmpop))
         {
             if (!e.type.isscalar() && e.type.equals(exp.e1.type))
@@ -11227,7 +11310,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 exp.error("recursive `opCmp` expansion");
                 return setError();
             }
-            if (e.op == TOK.call)
+            if (e.op == EXP.call)
             {
                 e = new CmpExp(cmpop, exp.loc, e, IntegerExp.literal!0);
                 e = e.expressionSemantic(sc);
@@ -11298,7 +11381,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         else if (t1.ty == Taarray || t2.ty == Taarray)
         {
-            exp.error("`%s` is not defined for associative arrays", Token.toChars(exp.op));
+            exp.error("`%s` is not defined for associative arrays", EXPtoString(exp.op).ptr);
             return setError();
         }
         else if (!target.isVectorOpSupported(t1, exp.op, t2))
@@ -11402,7 +11485,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = e;
             return;
         }
-        if (exp.e1.op == TOK.type || exp.e2.op == TOK.type)
+        if (exp.e1.op == EXP.type || exp.e2.op == EXP.type)
         {
             /* https://issues.dlang.org/show_bug.cgi?id=12520
              * empty tuples are represented as types so special cases are added
@@ -11411,7 +11494,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             static auto extractTypeTupAndExpTup(Expression e)
             {
                 static struct Result { bool ttEmpty; bool te; }
-                auto tt = e.op == TOK.type ? e.isTypeExp().type.isTypeTuple() : null;
+                auto tt = e.op == EXP.type ? e.isTypeExp().type.isTypeTuple() : null;
                 return Result(tt && (!tt.arguments || !tt.arguments.dim), e.isTupleExp() !is null);
             }
             auto tups1 = extractTypeTupAndExpTup(exp.e1);
@@ -11419,19 +11502,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // AliasSeq!() == AliasSeq!(<at least a value>)
             if (tups1.ttEmpty && tups2.te)
             {
-                result = IntegerExp.createBool(exp.op != TOK.equal);
+                result = IntegerExp.createBool(exp.op != EXP.equal);
                 return;
             }
             // AliasSeq!(<at least a value>) == AliasSeq!()
             else if (tups1.te && tups2.ttEmpty)
             {
-                result = IntegerExp.createBool(exp.op != TOK.equal);
+                result = IntegerExp.createBool(exp.op != EXP.equal);
                 return;
             }
             // AliasSeq!() == AliasSeq!()
             else if (tups1.ttEmpty && tups2.ttEmpty)
             {
-                result = IntegerExp.createBool(exp.op == TOK.equal);
+                result = IntegerExp.createBool(exp.op == EXP.equal);
                 return;
             }
             // otherwise, two types are really not comparable
@@ -11451,18 +11534,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
          * comparing the addresses of two statics. If so, we can just see
          * if they are the same symbol.
          */
-        if (exp.e1.op == TOK.address && exp.e2.op == TOK.address)
+        if (exp.e1.op == EXP.address && exp.e2.op == EXP.address)
         {
             AddrExp ae1 = cast(AddrExp)exp.e1;
             AddrExp ae2 = cast(AddrExp)exp.e2;
-            if (ae1.e1.op == TOK.variable && ae2.e1.op == TOK.variable)
+            if (ae1.e1.op == EXP.variable && ae2.e1.op == EXP.variable)
             {
                 VarExp ve1 = cast(VarExp)ae1.e1;
                 VarExp ve2 = cast(VarExp)ae2.e1;
                 if (ve1.var == ve2.var)
                 {
                     // They are the same, result is 'true' for ==, 'false' for !=
-                    result = IntegerExp.createBool(exp.op == TOK.equal);
+                    result = IntegerExp.createBool(exp.op == EXP.equal);
                     return;
                 }
             }
@@ -11555,7 +11638,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             (*arguments)[1] = exp.e2;
 
             __equals = new CallExp(exp.loc, __equals, arguments);
-            if (exp.op == TOK.notEqual)
+            if (exp.op == EXP.notEqual)
             {
                 __equals = new NotExp(exp.loc, __equals);
             }
@@ -11611,7 +11694,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (f1 || f2)
             return setError();
 
-        if (exp.e1.op == TOK.type || exp.e2.op == TOK.type)
+        if (exp.e1.op == EXP.type || exp.e2.op == EXP.type)
         {
             result = exp.incompatibleTypes();
             return;
@@ -11634,9 +11717,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (exp.e1.op == TOK.call)
+        if (exp.e1.op == EXP.call)
             exp.e1 = (cast(CallExp)exp.e1).addDtorHook(sc);
-        if (exp.e2.op == TOK.call)
+        if (exp.e2.op == EXP.call)
             exp.e2 = (cast(CallExp)exp.e2).addDtorHook(sc);
 
         if (exp.e1.type.toBasetype().ty == Tsarray ||
@@ -11660,7 +11743,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (exp.econd.op == TOK.dotIdentifier)
+        if (exp.econd.op == EXP.dotIdentifier)
             (cast(DotIdExp)exp.econd).noderef = true;
 
         Expression ec = exp.econd.expressionSemantic(sc);
@@ -11679,7 +11762,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         sc.merge(exp.loc, ctorflow1);
         ctorflow1.freeFieldinit();
 
-        if (ec.op == TOK.error)
+        if (ec.op == EXP.error)
         {
             result = ec;
             return;
@@ -11688,7 +11771,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return setError();
         exp.econd = ec;
 
-        if (e1x.op == TOK.error)
+        if (e1x.op == EXP.error)
         {
             result = e1x;
             return;
@@ -11697,7 +11780,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return setError();
         exp.e1 = e1x;
 
-        if (e2x.op == TOK.error)
+        if (e2x.op == EXP.error)
         {
             result = e2x;
             return;
@@ -11957,7 +12040,7 @@ Expression unaSemantic(UnaExp e, Scope* sc)
         printf("UnaExp::semantic('%s')\n", e.toChars());
     }
     Expression e1x = e.e1.expressionSemantic(sc);
-    if (e1x.op == TOK.error)
+    if (e1x.op == EXP.error)
         return e1x;
     e.e1 = e1x;
     return null;
@@ -11977,14 +12060,14 @@ Expression binSemantic(BinExp e, Scope* sc)
     Expression e2x = e.e2.expressionSemantic(sc);
 
     // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-    if (e1x.op == TOK.type)
+    if (e1x.op == EXP.type)
         e1x = resolveAliasThis(sc, e1x);
-    if (e2x.op == TOK.type)
+    if (e2x.op == EXP.type)
         e2x = resolveAliasThis(sc, e2x);
 
-    if (e1x.op == TOK.error)
+    if (e1x.op == EXP.error)
         return e1x;
-    if (e2x.op == TOK.error)
+    if (e2x.op == EXP.error)
         return e2x;
     e.e1 = e1x;
     e.e2 = e2x;
@@ -11997,9 +12080,9 @@ Expression binSemanticProp(BinExp e, Scope* sc)
         return ex;
     Expression e1x = resolveProperties(sc, e.e1);
     Expression e2x = resolveProperties(sc, e.e2);
-    if (e1x.op == TOK.error)
+    if (e1x.op == EXP.error)
         return e1x;
-    if (e2x.op == TOK.error)
+    if (e2x.op == EXP.error)
         return e2x;
     e.e1 = e1x;
     e.e2 = e2x;
@@ -12049,11 +12132,11 @@ Expression semanticX(DotIdExp exp, Scope* sc)
         Dsymbol ds;
         switch (exp.e1.op)
         {
-            case TOK.scope_:      return dotMangleof(exp.loc, sc, exp.e1.isScopeExp().sds);
-            case TOK.variable:    return dotMangleof(exp.loc, sc, exp.e1.isVarExp().var);
-            case TOK.dotVariable: return dotMangleof(exp.loc, sc, exp.e1.isDotVarExp().var);
-            case TOK.overloadSet: return dotMangleof(exp.loc, sc, exp.e1.isOverExp().vars);
-            case TOK.template_:
+            case EXP.scope_:      return dotMangleof(exp.loc, sc, exp.e1.isScopeExp().sds);
+            case EXP.variable:    return dotMangleof(exp.loc, sc, exp.e1.isVarExp().var);
+            case EXP.dotVariable: return dotMangleof(exp.loc, sc, exp.e1.isDotVarExp().var);
+            case EXP.overloadSet: return dotMangleof(exp.loc, sc, exp.e1.isOverExp().vars);
+            case EXP.template_:
             {
                 TemplateExp te = exp.e1.isTemplateExp();
                 return dotMangleof(exp.loc, sc, ds = te.fd ? te.fd.isDsymbol() : te.td);
@@ -12480,9 +12563,9 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
     }
     assert(e);
 
-    if (e.op == TOK.error)
+    if (e.op == EXP.error)
         return e;
-    if (e.op == TOK.dotVariable)
+    if (e.op == EXP.dotVariable)
     {
         DotVarExp dve = cast(DotVarExp)e;
         if (FuncDeclaration fd = dve.var.isFuncDeclaration())
@@ -12516,7 +12599,7 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
                    .expressionSemantic(sc);
         }
     }
-    else if (e.op == TOK.variable)
+    else if (e.op == EXP.variable)
     {
         VarExp ve = cast(VarExp)e;
         if (FuncDeclaration fd = ve.var.isFuncDeclaration())
@@ -12535,7 +12618,7 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
         }
     }
 
-    if (e.op == TOK.dotTemplateDeclaration)
+    if (e.op == EXP.dotTemplateDeclaration)
     {
         DotTemplateExp dte = cast(DotTemplateExp)e;
         exp.e1 = dte.e1; // pull semantic() result
@@ -12560,17 +12643,17 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
         return new DotExp(exp.loc, exp.e1, new ScopeExp(exp.loc, exp.ti))
                .expressionSemantic(sc);
     }
-    else if (e.op == TOK.template_)
+    else if (e.op == EXP.template_)
     {
         exp.ti.tempdecl = (cast(TemplateExp)e).td;
         return new ScopeExp(exp.loc, exp.ti)
                .expressionSemantic(sc);
     }
-    else if (e.op == TOK.dot)
+    else if (e.op == EXP.dot)
     {
         DotExp de = cast(DotExp)e;
 
-        if (de.e2.op == TOK.overloadSet)
+        if (de.e2.op == EXP.overloadSet)
         {
             if (!exp.findTempDecl(sc) || !exp.ti.semanticTiargs(sc))
             {
@@ -12593,7 +12676,7 @@ Expression semanticY(DotTemplateInstanceExp exp, Scope* sc, int flag)
                    .expressionSemantic(sc);
         }
     }
-    else if (e.op == TOK.overloadSet)
+    else if (e.op == EXP.overloadSet)
     {
         OverExp oe = cast(OverExp)e;
         exp.ti.tempdecl = oe.vars;
@@ -12873,7 +12956,7 @@ Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, Aggre
         }
         s = s.toParent2();
     }
-    if (n > 1 || e1.op == TOK.index)
+    if (n > 1 || e1.op == EXP.index)
         e1 = e1.expressionSemantic(sc);
     if (s && e1.type.equivalent(Type.tvoidptr))
     {
@@ -12967,7 +13050,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
         e = resolveProperties(sc, e);
         if (i >= nfields)
         {
-            if (i <= sd.fields.dim && e.op == TOK.null_)
+            if (i <= sd.fields.dim && e.op == EXP.null_)
             {
                 // CTFE sometimes creates null as hidden pointer; we'll allow this.
                 continue;
@@ -12988,7 +13071,10 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
             }
             return false;
         }
-        offset = cast(uint)(v.offset + v.type.size());
+        const vsize = v.type.size();
+        if (vsize == SIZE_INVALID)
+            return false;
+        offset = cast(uint)(v.offset + vsize);
 
         Type t = v.type;
         if (stype)
@@ -13015,7 +13101,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
          * Allow this by doing an explicit cast, which will lengthen the string
          * literal.
          */
-        if (e.op == TOK.string_ && tb.ty == Tsarray)
+        if (e.op == EXP.string_ && tb.ty == Tsarray)
         {
             StringExp se = cast(StringExp)e;
             Type typeb = se.type.toBasetype();
@@ -13042,7 +13128,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
 
         e = e.implicitCastTo(sc, t);
     L1:
-        if (e.op == TOK.error)
+        if (e.op == EXP.error)
             return false;
 
         (*elements)[i] = doCopyOrMove(sc, e);
@@ -13090,22 +13176,22 @@ Expression toBoolean(Expression exp, Scope* sc)
 {
     switch(exp.op)
     {
-        case TOK.delete_:
+        case EXP.delete_:
             exp.error("`delete` does not give a boolean result");
             return ErrorExp.get();
 
-        case TOK.comma:
+        case EXP.comma:
             auto ce = exp.isCommaExp();
             auto ex2 = ce.e2.toBoolean(sc);
-            if (ex2.op == TOK.error)
+            if (ex2.op == EXP.error)
                 return ex2;
             ce.e2 = ex2;
             ce.type = ce.e2.type;
             return ce;
 
-        case TOK.assign:
-        case TOK.construct:
-        case TOK.blit:
+        case EXP.assign:
+        case EXP.construct:
+        case EXP.blit:
             // Things like:
             //  if (a = b) ...
             // are usually mistakes.
@@ -13113,22 +13199,22 @@ Expression toBoolean(Expression exp, Scope* sc)
             return ErrorExp.get();
 
         //LogicalExp
-        case TOK.andAnd:
-        case TOK.orOr:
+        case EXP.andAnd:
+        case EXP.orOr:
             auto le = exp.isLogicalExp();
             auto ex2 = le.e2.toBoolean(sc);
-            if (ex2.op == TOK.error)
+            if (ex2.op == EXP.error)
                 return ex2;
             le.e2 = ex2;
             return le;
 
-        case TOK.question:
+        case EXP.question:
             auto ce = exp.isCondExp();
             auto ex1 = ce.e1.toBoolean(sc);
             auto ex2 = ce.e2.toBoolean(sc);
-            if (ex1.op == TOK.error)
+            if (ex1.op == EXP.error)
                 return ex1;
-            if (ex2.op == TOK.error)
+            if (ex2.op == EXP.error)
                 return ex2;
             ce.e1 = ex1;
             ce.e2 = ex2;
diff --git a/gcc/d/dmd/foreachvar.d b/gcc/d/dmd/foreachvar.d
index 9579ac7d6a5..e9a43f92105 100644
--- a/gcc/d/dmd/foreachvar.d
+++ b/gcc/d/dmd/foreachvar.d
@@ -223,7 +223,7 @@ void foreachExpAndVar(Statement s,
         void visitWith(WithStatement s)
         {
             // If it is with(Enum) {...}, just execute the body.
-            if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type)
+            if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type)
             {
             }
             else
diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d
index 2d6a756178e..da33587fc88 100644
--- a/gcc/d/dmd/func.d
+++ b/gcc/d/dmd/func.d
@@ -1306,7 +1306,14 @@ extern (C++) class FuncDeclaration : Declaration
         if (!fbody)
             return false;
 
-        if (isVirtualMethod())
+        if (isVirtualMethod() &&
+            /*
+             * https://issues.dlang.org/show_bug.cgi?id=21719
+             *
+             * If we have an auto virtual function we can infer
+             * the attributes.
+             */
+            !(inferRetType && !isCtorDeclaration()))
             return false;               // since they may be overridden
 
         if (sc.func &&
diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d
index 747a1138905..7409dcc2902 100644
--- a/gcc/d/dmd/globals.d
+++ b/gcc/d/dmd/globals.d
@@ -148,7 +148,7 @@ extern (C++) struct Param
     bool betterC;           // be a "better C" compiler; no dependency on D runtime
     bool addMain;           // add a default main() function
     bool allInst;           // generate code for all template instantiations
-    bool fix16997;          // fix integral promotions for unary + - ~ operators
+    bool fix16997 = true;   // fix integral promotions for unary + - ~ operators
                             // https://issues.dlang.org/show_bug.cgi?id=16997
     bool fixAliasThis;      // if the current scope has an alias this, check it before searching upper scopes
     bool inclusiveInContracts;   // 'in' contracts of overridden methods must be a superset of parent contract
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index 4ff07b5de32..a4542986739 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -164,7 +164,7 @@ public:
 
     override void visit(ExpStatement s)
     {
-        if (s.exp && s.exp.op == TOK.declaration &&
+        if (s.exp && s.exp.op == EXP.declaration &&
             (cast(DeclarationExp)s.exp).declaration)
         {
             // bypass visit(DeclarationExp)
@@ -202,7 +202,7 @@ public:
         foreach (sx; *s.statements)
         {
             auto ds = sx ? sx.isExpStatement() : null;
-            if (ds && ds.exp.op == TOK.declaration)
+            if (ds && ds.exp.op == EXP.declaration)
             {
                 auto d = (cast(DeclarationExp)ds.exp).declaration;
                 assert(d.isDeclaration());
@@ -1260,7 +1260,7 @@ public:
             {
                 buf.writestring(" = ");
                 ExpInitializer ie = vd._init.isExpInitializer();
-                if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
+                if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
                     (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
                 else
                     vd._init.initializerToBuffer(buf, hgs);
@@ -1511,7 +1511,7 @@ public:
         {
             buf.writestring(" = ");
             auto ie = v._init.isExpInitializer();
-            if (ie && (ie.exp.op == TOK.construct || ie.exp.op == TOK.blit))
+            if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
                 (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
             else
                 v._init.initializerToBuffer(buf, hgs);
@@ -1569,7 +1569,7 @@ public:
                 buf.writestring("in");
                 if (auto es = frequire.isExpStatement())
                 {
-                    assert(es.exp && es.exp.op == TOK.assert_);
+                    assert(es.exp && es.exp.op == EXP.assert_);
                     buf.writestring(" (");
                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
                     buf.writeByte(')');
@@ -1592,7 +1592,7 @@ public:
                 buf.writestring("out");
                 if (auto es = fensure.ensure.isExpStatement())
                 {
-                    assert(es.exp && es.exp.op == TOK.assert_);
+                    assert(es.exp && es.exp.op == EXP.assert_);
                     buf.writestring(" (");
                     if (fensure.id)
                     {
@@ -1749,7 +1749,7 @@ public:
         buf.writestring("invariant");
         if(auto es = d.fbody.isExpStatement())
         {
-            assert(es.exp && es.exp.op == TOK.assert_);
+            assert(es.exp && es.exp.op == EXP.assert_);
             buf.writestring(" (");
             (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
             buf.writestring(");");
@@ -1812,7 +1812,7 @@ public:
     ////////////////////////////////////////////////////////////////////////////
     override void visit(Expression e)
     {
-        buf.writestring(Token.toString(e.op));
+        buf.writestring(EXPtoString(e.op));
     }
 
     override void visit(IntegerExp e)
@@ -2278,7 +2278,7 @@ public:
 
     override void visit(UnaExp e)
     {
-        buf.writestring(Token.toString(e.op));
+        buf.writestring(EXPtoString(e.op));
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
@@ -2286,7 +2286,7 @@ public:
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
         buf.writeByte(' ');
-        buf.writestring(Token.toString(e.op));
+        buf.writestring(EXPtoString(e.op));
         buf.writeByte(' ');
         expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
     }
@@ -2415,7 +2415,7 @@ public:
 
     override void visit(CallExp e)
     {
-        if (e.e1.op == TOK.type)
+        if (e.e1.op == EXP.type)
         {
             /* Avoid parens around type to prevent forbidden cast syntax:
              *   (sometype)(arg1)
@@ -2540,12 +2540,12 @@ public:
     override void visit(PostExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
-        buf.writestring(Token.toString(e.op));
+        buf.writestring(EXPtoString(e.op));
     }
 
     override void visit(PreExp e)
     {
-        buf.writestring(Token.toString(e.op));
+        buf.writestring(EXPtoString(e.op));
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
@@ -2568,7 +2568,7 @@ public:
 
     override void visit(DefaultInitExp e)
     {
-        buf.writestring(Token.toString(e.op));
+        buf.writestring(EXPtoString(e.op));
     }
 
     override void visit(ClassReferenceExp e)
@@ -3144,7 +3144,7 @@ private void parameterToBuffer(Parameter p, OutBuffer* buf, HdrGenState* hgs)
     {
         buf.writeByte('@');
 
-        bool isAnonymous = p.userAttribDecl.atts.dim > 0 && (*p.userAttribDecl.atts)[0].op != TOK.call;
+        bool isAnonymous = p.userAttribDecl.atts.dim > 0 && !(*p.userAttribDecl.atts)[0].isCallExp();
         if (isAnonymous)
             buf.writeByte('(');
 
@@ -3253,9 +3253,9 @@ private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
 {
     if (e.type == Type.tsize_t)
     {
-        Expression ex = (e.op == TOK.cast_ ? (cast(CastExp)e).e1 : e);
+        Expression ex = (e.op == EXP.cast_ ? (cast(CastExp)e).e1 : e);
         ex = ex.optimize(WANTvalue);
-        const dinteger_t uval = ex.op == TOK.int64 ? ex.toInteger() : cast(dinteger_t)-1;
+        const dinteger_t uval = ex.op == EXP.int64 ? ex.toInteger() : cast(dinteger_t)-1;
         if (cast(sinteger_t)uval >= 0)
         {
             dinteger_t sizemax = void;
@@ -3292,7 +3292,7 @@ private void expToBuffer(Expression e, PREC pr, OutBuffer* buf, HdrGenState* hgs
     debug
     {
         if (precedence[e.op] == PREC.zero)
-            printf("precedence not defined for token '%s'\n", Token.toChars(e.op));
+            printf("precedence not defined for token '%s'\n", EXPtoString(e.op).ptr);
     }
     if (e.op == 0xFF)
     {
@@ -3422,7 +3422,7 @@ private void tiargsToBuffer(TemplateInstance ti, OutBuffer* buf, HdrGenState* hg
         }
         else if (Expression e = isExpression(oarg))
         {
-            if (e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.this_)
+            if (e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_)
             {
                 buf.writestring(e.toChars());
                 return;
@@ -3460,7 +3460,7 @@ private void objectToBuffer(RootObject oarg, OutBuffer* buf, HdrGenState* hgs)
     }
     else if (auto e = isExpression(oarg))
     {
-        if (e.op == TOK.variable)
+        if (e.op == EXP.variable)
             e = e.optimize(WANTvalue); // added to fix https://issues.dlang.org/show_bug.cgi?id=7375
         expToBuffer(e, PREC.assign, buf, hgs);
     }
@@ -3962,3 +3962,158 @@ private void typeToBufferx(Type t, OutBuffer* buf, HdrGenState* hgs)
         case Ttag:       return visitTag(cast(TypeTag)t);
     }
 }
+
+/****************************************
+ * Convert EXP to char*.
+ */
+
+string EXPtoString(EXP op)
+{
+    static immutable char*[EXP.max + 1] strings =
+    [
+        EXP.type : "type",
+        EXP.error : "error",
+        EXP.objcClassReference : "class",
+
+        EXP.typeof_ : "typeof",
+        EXP.mixin_ : "mixin",
+
+        EXP.import_ : "import",
+        EXP.dotVariable : "dotvar",
+        EXP.scope_ : "scope",
+        EXP.identifier : "identifier",
+        EXP.this_ : "this",
+        EXP.super_ : "super",
+        EXP.int64 : "long",
+        EXP.float64 : "double",
+        EXP.complex80 : "creal",
+        EXP.null_ : "null",
+        EXP.string_ : "string",
+        EXP.arrayLiteral : "arrayliteral",
+        EXP.assocArrayLiteral : "assocarrayliteral",
+        EXP.classReference : "classreference",
+        EXP.file : "__FILE__",
+        EXP.fileFullPath : "__FILE_FULL_PATH__",
+        EXP.line : "__LINE__",
+        EXP.moduleString : "__MODULE__",
+        EXP.functionString : "__FUNCTION__",
+        EXP.prettyFunction : "__PRETTY_FUNCTION__",
+        EXP.typeid_ : "typeid",
+        EXP.is_ : "is",
+        EXP.assert_ : "assert",
+        EXP.halt : "halt",
+        EXP.template_ : "template",
+        EXP.dSymbol : "symbol",
+        EXP.function_ : "function",
+        EXP.variable : "var",
+        EXP.symbolOffset : "symoff",
+        EXP.structLiteral : "structLiteral",
+        EXP.compoundLiteral : "compoundliteral",
+        EXP.arrayLength : "arraylength",
+        EXP.delegatePointer : "delegateptr",
+        EXP.delegateFunctionPointer : "delegatefuncptr",
+        EXP.remove : "remove",
+        EXP.tuple : "tuple",
+        EXP.traits : "__traits",
+        EXP.default_ : "default",
+        EXP.overloadSet : "__overloadset",
+        EXP.void_ : "void",
+        EXP.vectorArray : "vectorarray",
+        EXP._Generic : "_Generic",
+
+        // post
+        EXP.dotTemplateInstance : "dotti",
+        EXP.dotIdentifier : "dotid",
+        EXP.dotTemplateDeclaration : "dottd",
+        EXP.dot : ".",
+        EXP.dotType : "dottype",
+        EXP.plusPlus : "++",
+        EXP.minusMinus : "--",
+        EXP.prePlusPlus : "++",
+        EXP.preMinusMinus : "--",
+        EXP.call : "call",
+        EXP.slice : "..",
+        EXP.array : "[]",
+        EXP.index : "[i]",
+
+        EXP.delegate_ : "delegate",
+        EXP.address : "&",
+        EXP.star : "*",
+        EXP.negate : "-",
+        EXP.uadd : "+",
+        EXP.not : "!",
+        EXP.tilde : "~",
+        EXP.delete_ : "delete",
+        EXP.new_ : "new",
+        EXP.newAnonymousClass : "newanonclass",
+        EXP.cast_ : "cast",
+
+        EXP.vector : "__vector",
+        EXP.pow : "^^",
+
+        EXP.mul : "*",
+        EXP.div : "/",
+        EXP.mod : "%",
+
+        EXP.add : "+",
+        EXP.min : "-",
+        EXP.concatenate : "~",
+
+        EXP.leftShift : "<<",
+        EXP.rightShift : ">>",
+        EXP.unsignedRightShift : ">>>",
+
+        EXP.lessThan : "<",
+        EXP.lessOrEqual : "<=",
+        EXP.greaterThan : ">",
+        EXP.greaterOrEqual : ">=",
+        EXP.in_ : "in",
+
+        EXP.equal : "==",
+        EXP.notEqual : "!=",
+        EXP.identity : "is",
+        EXP.notIdentity : "!is",
+
+        EXP.and : "&",
+        EXP.xor : "^",
+        EXP.or : "|",
+
+        EXP.andAnd : "&&",
+        EXP.orOr : "||",
+
+        EXP.question : "?",
+
+        EXP.assign : "=",
+        EXP.construct : "=",
+        EXP.blit : "=",
+        EXP.addAssign : "+=",
+        EXP.minAssign : "-=",
+        EXP.concatenateAssign : "~=",
+        EXP.concatenateElemAssign : "~=",
+        EXP.concatenateDcharAssign : "~=",
+        EXP.mulAssign : "*=",
+        EXP.divAssign : "/=",
+        EXP.modAssign : "%=",
+        EXP.powAssign : "^^=",
+        EXP.leftShiftAssign : "<<=",
+        EXP.rightShiftAssign : ">>=",
+        EXP.unsignedRightShiftAssign : ">>>=",
+        EXP.andAssign : "&=",
+        EXP.orAssign : "|=",
+        EXP.xorAssign : "^=",
+
+        EXP.comma : ",",
+        EXP.declaration : "declaration",
+
+        EXP.interval : "interval",
+    ];
+    const p = strings[op];
+    if (!p)
+    {
+        printf("error: EXP %d has no string\n", op);
+        return "XXXXX";
+        //assert(0);
+    }
+    assert(p);
+    return p[0 .. strlen(p)];
+}
diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d
index 3ff0e894e2a..cd3011731f5 100644
--- a/gcc/d/dmd/iasmgcc.d
+++ b/gcc/d/dmd/iasmgcc.d
@@ -348,7 +348,7 @@ extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
 
             e = (*s.constraints)[i];
             e = e.expressionSemantic(sc);
-            assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1);
+            assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
             (*s.constraints)[i] = e;
         }
     }
@@ -360,7 +360,7 @@ extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
         {
             Expression e = (*s.clobbers)[i];
             e = e.expressionSemantic(sc);
-            assert(e.op == TOK.string_ && (cast(StringExp) e).sz == 1);
+            assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
             (*s.clobbers)[i] = e;
         }
     }
diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d
index 1ee51533b53..83c89c05fda 100644
--- a/gcc/d/dmd/id.d
+++ b/gcc/d/dmd/id.d
@@ -315,6 +315,8 @@ immutable Msgtable[] msgtable =
     { "dup" },
     { "_aaApply" },
     { "_aaApply2" },
+    { "_d_arrayctor" },
+    { "_d_arraysetctor" },
 
     // For pragma's
     { "Pinline", "inline" },
@@ -463,6 +465,7 @@ immutable Msgtable[] msgtable =
     { "getUnitTests" },
     { "getVirtualIndex" },
     { "getPointerBitmap" },
+    { "initSymbol" },
     { "getCppNamespaces" },
     { "isReturnOnStack" },
     { "isZeroInit" },
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index 5828486c6fe..c5aa0f46dd1 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -225,7 +225,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                 auto tm = vd.type.addMod(t.mod);
                 auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret);
                 auto ex = iz.initializerToExpression();
-                if (ex.op == TOK.error)
+                if (ex.op == EXP.error)
                 {
                     errors = true;
                     elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
@@ -331,7 +331,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                     errors = true;
                 }
                 length = cast(uint)idxvalue;
-                if (idx.op == TOK.error)
+                if (idx.op == EXP.error)
                     errors = true;
             }
             Initializer val = i.value[j];
@@ -344,7 +344,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                 errors = true;
             ei = val.isExpInitializer();
             // found a tuple, expand it
-            if (ei && ei.exp.op == TOK.tuple)
+            if (ei && ei.exp.op == EXP.tuple)
             {
                 TupleExp te = cast(TupleExp)ei.exp;
                 i.index.remove(j);
@@ -384,6 +384,8 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             return err();
 
         const sz = t.nextOf().size();
+        if (sz == SIZE_INVALID)
+            return err();
         bool overflow;
         const max = mulu(i.dim, sz, overflow);
         if (overflow || max >= amax)
@@ -403,7 +405,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         i.exp = resolveProperties(sc, i.exp);
         if (needInterpret)
             sc = sc.endCTFE();
-        if (i.exp.op == TOK.error)
+        if (i.exp.op == EXP.error)
             return err();
         uint olderrors = global.errors;
 
@@ -437,7 +439,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                  */
                 i.exp = i.exp.optimize(WANTvalue);
             i.exp = i.exp.ctfeInterpret();
-            if (i.exp.op == TOK.voidExpression)
+            if (i.exp.op == EXP.voidExpression)
                 error(i.loc, "variables cannot be initialized with an expression of type `void`. Use `void` initialization instead.");
         }
         else
@@ -455,7 +457,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             i.exp = new TupleExp(i.exp.loc, new Expressions());
             i.exp.type = et;
         }
-        if (i.exp.op == TOK.type)
+        if (i.exp.op == EXP.type)
         {
             i.exp.error("initializer must be an expression, not `%s`", i.exp.toChars());
             return err();
@@ -467,7 +469,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             return err();
         }
         Type ti = i.exp.type.toBasetype();
-        if (i.exp.op == TOK.tuple && i.expandTuples && !i.exp.implicitConvTo(t))
+        if (i.exp.op == EXP.tuple && i.expandTuples && !i.exp.implicitConvTo(t))
         {
             return new ExpInitializer(i.loc, i.exp);
         }
@@ -477,7 +479,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
          * Allow this by doing an explicit cast, which will lengthen the string
          * literal.
          */
-        if (i.exp.op == TOK.string_ && tb.ty == Tsarray)
+        if (i.exp.op == EXP.string_ && tb.ty == Tsarray)
         {
             StringExp se = cast(StringExp)i.exp;
             Type typeb = se.type.toBasetype();
@@ -562,12 +564,12 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             {
                 uinteger_t dim1 = (cast(TypeSArray)tb).dim.toInteger();
                 uinteger_t dim2 = dim1;
-                if (i.exp.op == TOK.arrayLiteral)
+                if (i.exp.op == EXP.arrayLiteral)
                 {
                     ArrayLiteralExp ale = cast(ArrayLiteralExp)i.exp;
                     dim2 = ale.elements ? ale.elements.dim : 0;
                 }
-                else if (i.exp.op == TOK.slice)
+                else if (i.exp.op == EXP.slice)
                 {
                     Type tx = toStaticArrayType(cast(SliceExp)i.exp);
                     if (tx)
@@ -582,7 +584,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             i.exp = i.exp.implicitCastTo(sc, t);
         }
     L1:
-        if (i.exp.op == TOK.error)
+        if (i.exp.op == EXP.error)
         {
             return i;
         }
@@ -667,7 +669,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                 auto tm = vd.type.addMod(ts.mod);
                 auto iz = di.initializer.initializerSemantic(sc, tm, needInterpret);
                 auto ex = iz.initializerToExpression();
-                if (ex.op == TOK.error)
+                if (ex.op == EXP.error)
                 {
                     errors = true;
                     continue;
@@ -823,6 +825,8 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         }
 
         const sz = tn.size(); // element size
+        if (sz == SIZE_INVALID)
+            return err();
         bool overflow;
         const max = mulu(edim, sz, overflow);
         if (overflow || max >= amax)
@@ -930,7 +934,7 @@ Initializer inferType(Initializer init, Scope* sc)
                 }
                 assert(iz.isExpInitializer());
                 (*values)[i] = (cast(ExpInitializer)iz).exp;
-                assert((*values)[i].op != TOK.error);
+                assert(!(*values)[i].isErrorExp());
             }
             Expression e = new AssocArrayLiteralExp(init.loc, keys, values);
             auto ei = new ExpInitializer(init.loc, e);
@@ -953,7 +957,7 @@ Initializer inferType(Initializer init, Scope* sc)
                 }
                 assert(iz.isExpInitializer());
                 (*elements)[i] = (cast(ExpInitializer)iz).exp;
-                assert((*elements)[i].op != TOK.error);
+                assert(!(*elements)[i].isErrorExp());
             }
             Expression e = new ArrayLiteralExp(init.loc, null, elements);
             auto ei = new ExpInitializer(init.loc, e);
@@ -977,11 +981,11 @@ Initializer inferType(Initializer init, Scope* sc)
         init.exp = init.exp.expressionSemantic(sc);
 
         // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-        if (init.exp.op == TOK.type)
+        if (init.exp.op == EXP.type)
             init.exp = resolveAliasThis(sc, init.exp);
 
         init.exp = resolveProperties(sc, init.exp);
-        if (init.exp.op == TOK.scope_)
+        if (init.exp.op == EXP.scope_)
         {
             ScopeExp se = cast(ScopeExp)init.exp;
             TemplateInstance ti = se.sds.isTemplateInstance();
@@ -1006,16 +1010,16 @@ Initializer inferType(Initializer init, Scope* sc)
                 return new ErrorInitializer();
             }
         }
-        if (init.exp.op == TOK.address)
+        if (init.exp.op == EXP.address)
         {
             AddrExp ae = cast(AddrExp)init.exp;
-            if (ae.e1.op == TOK.overloadSet)
+            if (ae.e1.op == EXP.overloadSet)
             {
                 init.exp.error("cannot infer type from overloaded function symbol `%s`", init.exp.toChars());
                 return new ErrorInitializer();
             }
         }
-        if (init.exp.op == TOK.error)
+        if (init.exp.op == EXP.error)
         {
             return new ErrorInitializer();
         }
@@ -1125,7 +1129,7 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n
             {
                 if (auto e = init.index[i])
                 {
-                    if (e.op == TOK.int64)
+                    if (e.op == EXP.int64)
                     {
                         const uinteger_t idxval = e.toInteger();
                         if (idxval >= amax)
@@ -1206,7 +1210,7 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n
          */
         foreach (e; (*elements)[0 .. edim])
         {
-            if (e.op == TOK.error)
+            if (e.op == EXP.error)
             {
                 return e;
             }
@@ -1222,7 +1226,7 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n
         {
             //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", itype.toChars(), i.exp.toChars());
             Type tb = itype.toBasetype();
-            Expression e = (i.exp.op == TOK.construct || i.exp.op == TOK.blit) ? (cast(AssignExp)i.exp).e2 : i.exp;
+            Expression e = (i.exp.op == EXP.construct || i.exp.op == EXP.blit) ? (cast(AssignExp)i.exp).e2 : i.exp;
             if (tb.ty == Tsarray && e.implicitConvTo(tb.nextOf()))
             {
                 TypeSArray tsa = cast(TypeSArray)tb;
@@ -1277,7 +1281,7 @@ private bool hasNonConstPointers(Expression e)
 
     if (e.type.ty == Terror)
         return false;
-    if (e.op == TOK.null_)
+    if (e.op == EXP.null_)
         return false;
     if (auto se = e.isStructLiteralExp())
     {
@@ -1318,11 +1322,11 @@ private bool hasNonConstPointers(Expression e)
     }
     if (e.type.ty == Tpointer && !e.type.isPtrToFunction())
     {
-        if (e.op == TOK.symbolOffset) // address of a global is OK
+        if (e.op == EXP.symbolOffset) // address of a global is OK
             return false;
-        if (e.op == TOK.int64) // cast(void *)int is OK
+        if (e.op == EXP.int64) // cast(void *)int is OK
             return false;
-        if (e.op == TOK.string_) // "abc".ptr is OK
+        if (e.op == EXP.string_) // "abc".ptr is OK
             return false;
         return true;
     }
diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d
index 44a6c066507..336f8dd80ec 100644
--- a/gcc/d/dmd/lambdacomp.d
+++ b/gcc/d/dmd/lambdacomp.d
@@ -26,6 +26,7 @@ import dmd.dtemplate;
 import dmd.expression;
 import dmd.func;
 import dmd.dmangle;
+import dmd.hdrgen;
 import dmd.mtype;
 import dmd.common.outbuffer;
 import dmd.root.rmem;
@@ -337,7 +338,7 @@ public:
             return;
 
         buf.writeByte('(');
-        buf.writestring(Token.toString(exp.op));
+        buf.writestring(EXPtoString(exp.op));
         exp.e1.accept(this);
         if (buf.length != 0)
             buf.writestring(")_");
@@ -370,7 +371,7 @@ public:
             return;
 
         buf.writeByte('(');
-        buf.writestring(Token.toChars(exp.op));
+        buf.writestring(EXPtoString(exp.op).ptr);
 
         exp.e1.accept(this);
         if (buf.length == 0)
diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d
index e2b4199b80a..d38cce4ca00 100644
--- a/gcc/d/dmd/lexer.d
+++ b/gcc/d/dmd/lexer.d
@@ -386,6 +386,20 @@ class Lexer
                 // Intentionally not advancing `p`, such that subsequent calls keep returning TOK.endOfFile.
                 return;
             case ' ':
+                // Skip 4 spaces at a time after aligning 'p' to a 4-byte boundary.
+                while ((cast(size_t)p) % uint.sizeof)
+                {
+                    if (*p != ' ')
+                        goto LendSkipFourSpaces;
+                    p++;
+                }
+                while (*(cast(uint*)p) == 0x20202020) // ' ' == 0x20
+                    p += 4;
+                // Skip over any remaining space on the line.
+                while (*p == ' ')
+                    p++;
+            LendSkipFourSpaces:
+                continue; // skip white space
             case '\t':
             case '\v':
             case '\f':
@@ -394,19 +408,11 @@ class Lexer
             case '\r':
                 p++;
                 if (*p != '\n') // if CR stands by itself
-                {
                     endOfLine();
-                    goto skipFourSpaces;
-                }
                 continue; // skip white space
             case '\n':
                 p++;
                 endOfLine();
-                skipFourSpaces:
-                while (*(cast(uint*)p) == 0x20202020) //' ' == 0x20
-                {
-                    p+=4;
-                }
                 continue; // skip white space
             case '0':
                 if (!isZeroSecond(p[1]))        // if numeric literal does not continue
diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d
index a21924b3aac..e338a05a524 100644
--- a/gcc/d/dmd/mtype.d
+++ b/gcc/d/dmd/mtype.d
@@ -515,7 +515,7 @@ extern (C++) abstract class Type : ASTNode
                         }
                         else if (tp1.ty == Tdelegate)
                         {
-                            if (tp1.implicitConvTo(tp2))
+                            if (tp2.implicitConvTo(tp1))
                                 goto Lcov;
                         }
                     }
@@ -4783,7 +4783,7 @@ extern (C++) final class TypeFunction : TypeNext
                             goto Nomatch;
                         }
 
-                        if (arg.op == TOK.string_ && tp.ty == Tsarray)
+                        if (arg.op == EXP.string_ && tp.ty == Tsarray)
                         {
                             if (ta.ty != Tsarray)
                             {
@@ -4792,7 +4792,7 @@ extern (C++) final class TypeFunction : TypeNext
                                 ta = tn.sarrayOf(dim);
                             }
                         }
-                        else if (arg.op == TOK.slice && tp.ty == Tsarray)
+                        else if (arg.op == EXP.slice && tp.ty [...]

[diff truncated at 524288 bytes]


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-12-10  4:27 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-10  4:27 [gcc r12-5887] d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6 Iain Buclaw

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).