[gcc r16-7211] d: Merge upstream dmd, druntime e1f6680f50, phobos f87979028

Iain Buclaw ibuclaw@gcc.gnu.org
Sat Jan 31 08:48:31 GMT 2026


https://gcc.gnu.org/g:8b0ccc95f1a70415d50bb87397cf0b1761be1e40

commit r16-7211-g8b0ccc95f1a70415d50bb87397cf0b1761be1e40
Author: Iain Buclaw <ibuclaw@gdcproject.org>
Date:   Fri Jan 30 17:30:33 2026 +0100

    d: Merge upstream dmd, druntime e1f6680f50, phobos f87979028
    
    D front-end changes:
    
            - Import latest fixes from v2.112.0-beta.1.
            - Associative array operations are now lowered to templates
              defined in `core.internal.newaa'.
    
    D runtime changes:
    
            - Import latest fixes from v2.112.0-beta.1.
    
    Phobos changes:
    
            - Import latest fixes from v2.112.0-beta.1.
    
    gcc/d/ChangeLog:
    
            * dmd/MERGE: Merge upstream dmd e1f6680f50.
            * Make-lang.in (D_FRONTEND_OBJS): Remove d/gluelayer.o, d/iasm.o,
            d/iasmgcc.o.  Add d/iasm-package.o, d/iasm-gcc.o.
            (d/iasm-%.o): New recipe.
            * d-convert.cc (convert_expr): Remove library call to dynamic_cast.
            * d-lang.cc (d_init_options): Set errorSupplementLimit parameter.
            * decl.cc (DeclVisitor::visit): Update for new front-end interface.
            * expr.cc (ExprVisitor::visit (EqualExp *)): Update for front-end
            lowering expression using templates.
            (ExprVisitor::visit (InExp *)): Likewise.
            (ExprVisitor::visit (IndexExp *)): Likewise.
            (ExprVisitor::visit (CastExp *)): Likewise.
            (ExprVisitor::visit (RemoveExp *)): Likewise.
            (ExprVisitor::visit (AddrExp *)): Likewise.
            (ExprVisitor::visit (NewExp *)): Likewise.
            (ExprVisitor::visit (ArrayLiteralExp *)): Likewise.
            (ExprVisitor::visit (AssocArrayLiteralExp *)): Likewise.
            * imports.cc (ImportVisitor::visit (AliasDeclaration *)): Update for
            new front-end interface.
            * runtime.def (DYNAMIC_CAST): Remove.
            (INTERFACE_CAST): Remove.
            (ARRAYLITERALTX): Remove.
            (ADEQ2): Remove.
            (ASSOCARRAYLITERALTX): Remove.
            (AANEW): Remove.
            (AAEQUAL): Remove.
            (AAINX): Remove.
            (AAGETY): Remove.
            (AAGETRVALUEX): Remove.
            (AADELX): Remove.
            * typeinfo.cc (TypeInfoVisitor::visit): Layout xopEquals and xtoHash
            in TypeInfo_AssociativeArray.
            (create_typeinfo): Add new fields to internal typeinfo.
            (check_typeinfo_type): Print instantiation trace of error.
            * types.cc (TypeVisitor::visit (TypeStruct *)): Update for new
            front-end interface.
    
    libphobos/ChangeLog:
    
            * libdruntime/MERGE: Merge upstream druntime e1f6680f50.
            * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add
            core/internal/cast_.d.  Remove rt/aaA.d, rt/adi.d, rt/cast_.d.
            (DRUNTIME_DSOURCES_POSIX): Add core/sys/posix/endian.d.
            * libdruntime/Makefile.in: Regenerate.
            * src/MERGE: Merge upstream phobos f87979028.
            * src/Makefile.am (PHOBOS_DSOURCES): Add std/internal/entropy.d.
            * src/Makefile.in: Regenerate.
            * testsuite/libphobos.aa/test_aa.d: Adjust test.
            * testsuite/libphobos.init_fini/custom_gc.d: Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * gdc.dg/asm1.d: Adjust test.
            * gdc.dg/asm5.d: Likewise.
            * gdc.dg/pr100967.d: Likewise.
            * gdc.dg/rtti1.d: Likewise.
            * gdc.dg/rtti2.d: New test.

Diff:
---
 gcc/d/Make-lang.in                                 |    9 +-
 gcc/d/d-convert.cc                                 |    7 -
 gcc/d/d-lang.cc                                    |    1 +
 gcc/d/decl.cc                                      |    8 +-
 gcc/d/dmd/MERGE                                    |    2 +-
 gcc/d/dmd/README.md                                |   43 +-
 gcc/d/dmd/access.d                                 |    1 +
 gcc/d/dmd/aggregate.d                              |  161 +-
 gcc/d/dmd/aggregate.h                              |    3 -
 gcc/d/dmd/arrayop.d                                |    1 +
 gcc/d/dmd/astenums.d                               |   17 +-
 gcc/d/dmd/attrib.d                                 |    7 +-
 gcc/d/dmd/canthrow.d                               |    2 +-
 gcc/d/dmd/clone.d                                  |   28 +-
 gcc/d/dmd/common/bitfields.d                       |   69 +-
 gcc/d/dmd/common/outbuffer.d                       |   78 +-
 gcc/d/dmd/common/outbuffer.h                       |    2 +-
 gcc/d/dmd/cond.d                                   |    4 +-
 gcc/d/dmd/constfold.d                              |    7 +-
 gcc/d/dmd/cparse.d                                 |   80 +-
 gcc/d/dmd/ctfeexpr.d                               |   25 +-
 gcc/d/dmd/cxxfrontend.d                            |   56 +-
 gcc/d/dmd/dcast.d                                  |    4 +-
 gcc/d/dmd/dclass.d                                 |    3 -
 gcc/d/dmd/declaration.d                            |  184 +-
 gcc/d/dmd/declaration.h                            |   15 +-
 gcc/d/dmd/delegatize.d                             |    5 +
 gcc/d/dmd/denum.d                                  |    3 +-
 gcc/d/dmd/dimport.d                                |    7 -
 gcc/d/dmd/dinterpret.d                             |  525 +-
 gcc/d/dmd/dmodule.d                                |  139 +-
 gcc/d/dmd/dscope.d                                 |   21 +-
 gcc/d/dmd/dstruct.d                                |  172 -
 gcc/d/dmd/dsymbol.d                                |   52 +-
 gcc/d/dmd/dsymbol.h                                |    6 +-
 gcc/d/dmd/dsymbolsem.d                             |  971 +++-
 gcc/d/dmd/dtemplate.d                              | 5619 ++++----------------
 gcc/d/dmd/dtoh.d                                   |    6 +-
 gcc/d/dmd/dversion.d                               |    1 -
 gcc/d/dmd/enum.h                                   |    2 +-
 gcc/d/dmd/enumsem.d                                |    4 +-
 gcc/d/dmd/escape.d                                 |    2 +-
 gcc/d/dmd/expression.d                             |  122 +-
 gcc/d/dmd/expression.h                             |   11 +
 gcc/d/dmd/expressionsem.d                          | 1775 +++++--
 gcc/d/dmd/file_manager.d                           |  168 +-
 gcc/d/dmd/func.d                                   |   43 +-
 gcc/d/dmd/funcsem.d                                |  170 +-
 gcc/d/dmd/globals.d                                |   18 +-
 gcc/d/dmd/globals.h                                |   16 +-
 gcc/d/dmd/gluelayer.d                              |   59 -
 gcc/d/dmd/hdrgen.d                                 | 1439 ++---
 gcc/d/dmd/iasm/gcc.d                               |  780 +++
 gcc/d/dmd/{iasm.d => iasm/package.d}               |   23 +-
 gcc/d/dmd/iasmgcc.d                                |  604 ---
 gcc/d/dmd/id.d                                     |   22 +-
 gcc/d/dmd/identifier.d                             |   10 +-
 gcc/d/dmd/import.h                                 |    1 -
 gcc/d/dmd/importc.d                                |    8 +-
 gcc/d/dmd/lambdacomp.d                             |    1 +
 gcc/d/dmd/lexer.d                                  |    8 +-
 gcc/d/dmd/mangle/cpp.d                             |    2 +-
 gcc/d/dmd/mangle/package.d                         |   15 +-
 gcc/d/dmd/module.h                                 |   24 +-
 gcc/d/dmd/mtype.d                                  |  323 +-
 gcc/d/dmd/mtype.h                                  |    9 +-
 gcc/d/dmd/mustuse.d                                |    1 -
 gcc/d/dmd/nogc.d                                   |   31 +-
 gcc/d/dmd/ob.d                                     |    5 +-
 gcc/d/dmd/objc.d                                   |    5 +-
 gcc/d/dmd/opover.d                                 |   31 +-
 gcc/d/dmd/optimize.d                               |    9 +-
 gcc/d/dmd/parse.d                                  |  115 +-
 gcc/d/dmd/root/array.d                             |   28 +
 gcc/d/dmd/root/filename.d                          |   17 +-
 gcc/d/dmd/root/hash.d                              |    2 +-
 gcc/d/dmd/safe.d                                   |    3 +-
 gcc/d/dmd/scope.h                                  |    7 +-
 gcc/d/dmd/semantic2.d                              |   42 +-
 gcc/d/dmd/semantic3.d                              |  150 +-
 gcc/d/dmd/sideeffect.d                             |    8 +-
 gcc/d/dmd/statement.d                              |   18 +-
 gcc/d/dmd/statement.h                              |   14 +-
 gcc/d/dmd/statementsem.d                           |  187 +-
 gcc/d/dmd/target.d                                 |    4 +-
 gcc/d/dmd/template.h                               |   16 +-
 gcc/d/dmd/templatesem.d                            | 3440 +++++++++++-
 gcc/d/dmd/tokens.d                                 |    2 +-
 gcc/d/dmd/traits.d                                 |   15 +-
 gcc/d/dmd/typesem.d                                |  417 +-
 gcc/d/dmd/typinf.d                                 |   25 +-
 gcc/d/dmd/visitor/foreachvar.d                     |    6 +-
 gcc/d/dmd/visitor/postorder.d                      |   36 +
 gcc/d/expr.cc                                      |  299 +-
 gcc/d/imports.cc                                   |    2 +-
 gcc/d/runtime.def                                  |   40 -
 gcc/d/typeinfo.cc                                  |   24 +-
 gcc/d/types.cc                                     |    2 +-
 gcc/testsuite/gdc.dg/asm1.d                        |    8 +-
 gcc/testsuite/gdc.dg/asm5.d                        |    2 +-
 gcc/testsuite/gdc.dg/pr100967.d                    |    8 +-
 gcc/testsuite/gdc.dg/rtti1.d                       |   23 +-
 gcc/testsuite/gdc.dg/rtti2.d                       |   29 +
 gcc/testsuite/gdc.test/compilable/b1215.d          |    2 +-
 .../gdc.test/compilable/extra-files/header1.d      |    4 +-
 .../gdc.test/compilable/extra-files/test21331.d    |   17 +
 .../gdc.test/compilable/extra-files/test21359a.d   |    3 +
 .../gdc.test/compilable/extra-files/test21359b.d   |   11 +
 gcc/testsuite/gdc.test/compilable/imports/h20184.h |    1 +
 gcc/testsuite/gdc.test/compilable/interpret4.d     |   17 +
 gcc/testsuite/gdc.test/compilable/mixintype.d      |    6 +-
 gcc/testsuite/gdc.test/compilable/parens_inc.d     |    5 +-
 gcc/testsuite/gdc.test/compilable/shared.d         |    4 +-
 gcc/testsuite/gdc.test/compilable/test14838.d      |    2 +-
 gcc/testsuite/gdc.test/compilable/test17146.d      |    2 +-
 gcc/testsuite/gdc.test/compilable/test20100.d      |    4 +-
 gcc/testsuite/gdc.test/compilable/test20184.d      |    5 +
 gcc/testsuite/gdc.test/compilable/test20365.d      |   20 +
 gcc/testsuite/gdc.test/compilable/test21331.sh     |   11 +
 gcc/testsuite/gdc.test/compilable/test21359.sh     |   15 +
 gcc/testsuite/gdc.test/compilable/test21416.d      |   17 +
 gcc/testsuite/gdc.test/compilable/test21476.d      |   13 +
 gcc/testsuite/gdc.test/compilable/test23169.d      |    2 +-
 gcc/testsuite/gdc.test/compilable/test3004.d       |    2 +-
 gcc/testsuite/gdc.test/compilable/testgotoskips.d  |   10 +
 gcc/testsuite/gdc.test/compilable/testsctreturn.d  |    2 +-
 .../compilable/traits_getFunctionAttributes.d      |    4 +-
 gcc/testsuite/gdc.test/compilable/vcg-ast.d        |    2 +
 gcc/testsuite/gdc.test/compilable/warn3882.d       |   38 +
 .../gdc.test/fail_compilation/aa_assign.d          |   13 +
 gcc/testsuite/gdc.test/fail_compilation/aaerrors.d |   43 +
 .../gdc.test/fail_compilation/biterrors.d          |    4 +-
 .../gdc.test/fail_compilation/biterrors2.d         |    6 +-
 .../gdc.test/fail_compilation/biterrors3.d         |    2 +-
 .../gdc.test/fail_compilation/biterrors4.d         |    2 +-
 .../gdc.test/fail_compilation/biterrors5.d         |   15 +-
 .../gdc.test/fail_compilation/diag13320.d          |   12 +-
 .../gdc.test/fail_compilation/diag16976.d          |   64 +-
 gcc/testsuite/gdc.test/fail_compilation/diag7420.d |   15 +-
 .../gdc.test/fail_compilation/discard_value.d      |    2 +-
 .../gdc.test/fail_compilation/fail13756.d          |    9 +-
 .../gdc.test/fail_compilation/fail20365.d          |   29 +
 .../gdc.test/fail_compilation/fail21414.d          |   47 +
 .../gdc.test/fail_compilation/fail21660.d          |   57 +
 .../gdc.test/fail_compilation/fail21693.d          |   17 +
 .../gdc.test/fail_compilation/fail22749.d          |    2 +-
 gcc/testsuite/gdc.test/fail_compilation/fail6497.d |   11 +-
 gcc/testsuite/gdc.test/fail_compilation/fail6795.d |    4 +-
 gcc/testsuite/gdc.test/fail_compilation/fix22108.d |    2 +-
 .../gdc.test/fail_compilation/foreach_seq.d        |   38 +
 gcc/testsuite/gdc.test/fail_compilation/ice10938.d |    2 +-
 gcc/testsuite/gdc.test/fail_compilation/ice13644.d |    2 +-
 .../gdc.test/fail_compilation/issue21630.d         |   18 +
 .../gdc.test/fail_compilation/previewin3.d         |   19 +
 .../gdc.test/fail_compilation/pull12941.d          |    8 +-
 .../gdc.test/fail_compilation/retscope2.d          |    2 +-
 gcc/testsuite/gdc.test/fail_compilation/shared.d   |    8 +-
 .../gdc.test/fail_compilation/test15191.d          |    4 +-
 .../gdc.test/fail_compilation/test15704.d          |    9 +-
 .../gdc.test/fail_compilation/test18282.d          |    2 +-
 .../gdc.test/fail_compilation/test20881.d          |    2 +-
 .../gdc.test/fail_compilation/test21408.d          |   23 +
 .../gdc.test/fail_compilation/test21429.d          |   21 +
 .../gdc.test/fail_compilation/test21477.d          |    2 +-
 .../gdc.test/fail_compilation/test21546.d          |    8 +-
 .../gdc.test/fail_compilation/test23710.d          |    5 +-
 .../gdc.test/fail_compilation/varargsstc.d         |    6 +-
 .../gdc.test/fail_compilation/verifyhookexist.d    |   11 +-
 gcc/testsuite/gdc.test/runnable/bit.d              |   16 +
 gcc/testsuite/gdc.test/runnable/bug19652.d         |    2 +-
 gcc/testsuite/gdc.test/runnable/constfold.d        |    2 +-
 gcc/testsuite/gdc.test/runnable/foreach5.d         |   34 +
 gcc/testsuite/gdc.test/runnable/mars1.d            |   16 +
 gcc/testsuite/gdc.test/runnable/placenew.d         |   26 +
 gcc/testsuite/gdc.test/runnable/sdtor.d            |   17 +
 gcc/testsuite/gdc.test/runnable/staticaa.d         |   13 +
 gcc/testsuite/gdc.test/runnable/test17487.d        |  179 +
 gcc/testsuite/gdc.test/runnable/test20473.d        |  110 +
 gcc/testsuite/gdc.test/runnable/test21429.d        |  131 +
 gcc/testsuite/gdc.test/runnable/test21435.d        |   73 +
 gcc/testsuite/gdc.test/runnable/test21660.d        |   31 +
 gcc/testsuite/gdc.test/runnable/test42.d           |    4 +-
 gcc/testsuite/gdc.test/runnable/testaa2.d          |   52 +-
 gcc/testsuite/gdc.test/runnable/testaa3.d          |   24 +
 gcc/testsuite/gdc.test/runnable/testscope2.d       |    4 +-
 libphobos/libdruntime/MERGE                        |    2 +-
 libphobos/libdruntime/Makefile.am                  |   41 +-
 libphobos/libdruntime/Makefile.in                  |  110 +-
 libphobos/libdruntime/core/atomic.d                |    2 +-
 libphobos/libdruntime/core/checkedint.d            |   50 +-
 libphobos/libdruntime/core/gc/config.d             |    6 +-
 libphobos/libdruntime/core/gc/gcinterface.d        |   30 +
 libphobos/libdruntime/core/gc/registry.d           |   43 +-
 .../libdruntime/core/internal/array/appending.d    |  204 +-
 .../libdruntime/core/internal/array/capacity.d     |   59 +
 .../libdruntime/core/internal/array/construction.d |  132 +-
 .../libdruntime/core/internal/array/duplication.d  |    2 +-
 .../libdruntime/core/internal/array/equality.d     |  165 +-
 libphobos/libdruntime/core/internal/array/utils.d  |   66 +
 libphobos/libdruntime/core/internal/cast_.d        |  311 ++
 libphobos/libdruntime/core/internal/convert.d      |    6 +
 libphobos/libdruntime/core/internal/gc/blkcache.d  |   15 +-
 .../core/internal/gc/impl/conservative/gc.d        |  255 +-
 .../libdruntime/core/internal/gc/impl/manual/gc.d  |   10 +
 .../libdruntime/core/internal/gc/impl/proto/gc.d   |   27 +
 libphobos/libdruntime/core/internal/gc/proxy.d     |    2 +-
 libphobos/libdruntime/core/internal/hash.d         |   46 +-
 libphobos/libdruntime/core/internal/lifetime.d     |   25 +-
 libphobos/libdruntime/core/internal/newaa.d        | 1038 +++-
 libphobos/libdruntime/core/internal/traits.d       |   37 +-
 libphobos/libdruntime/core/stdc/config.d           |   17 +
 libphobos/libdruntime/core/stdc/stdarg.d           |    2 +
 libphobos/libdruntime/core/stdcpp/string.d         |    4 +-
 libphobos/libdruntime/core/sys/linux/sched.d       |    4 +
 libphobos/libdruntime/core/sys/posix/dirent.d      |   12 +-
 libphobos/libdruntime/core/sys/posix/dlfcn.d       |   27 +-
 libphobos/libdruntime/core/sys/posix/endian.d      |   60 +
 libphobos/libdruntime/core/sys/posix/sched.d       |   11 +-
 libphobos/libdruntime/core/sys/posix/stdio.d       |   58 +-
 libphobos/libdruntime/core/sys/posix/stdlib.d      |   13 +-
 libphobos/libdruntime/core/sys/posix/sys/mman.d    |   11 +-
 libphobos/libdruntime/core/sys/posix/sys/stat.d    |   12 +-
 libphobos/libdruntime/core/sys/posix/sys/statvfs.d |    6 +-
 libphobos/libdruntime/core/thread/threadbase.d     |    8 +
 libphobos/libdruntime/object.d                     |  397 +-
 libphobos/libdruntime/rt/aaA.d                     |  867 ---
 libphobos/libdruntime/rt/adi.d                     |   73 -
 libphobos/libdruntime/rt/cast_.d                   |  208 -
 libphobos/libdruntime/rt/lifetime.d                |  264 +-
 libphobos/src/MERGE                                |    2 +-
 libphobos/src/Makefile.am                          |    9 +-
 libphobos/src/Makefile.in                          |   11 +-
 libphobos/src/index.dd                             |    5 +
 libphobos/src/std/algorithm/iteration.d            |    7 +
 libphobos/src/std/algorithm/mutation.d             |    2 +-
 libphobos/src/std/algorithm/searching.d            |   10 +
 libphobos/src/std/ascii.d                          |    1 +
 libphobos/src/std/container/rbtree.d               |   25 +-
 libphobos/src/std/datetime/date.d                  |   35 +-
 libphobos/src/std/datetime/systime.d               |   11 +
 libphobos/src/std/datetime/timezone.d              |   26 +-
 .../allocator/building_blocks/kernighan_ritchie.d  |   20 +-
 libphobos/src/std/getopt.d                         |   30 +-
 libphobos/src/std/internal/entropy.d               |  918 ++++
 libphobos/src/std/internal/math/gammafunction.d    |   11 +-
 libphobos/src/std/mathspecial.d                    |   39 +-
 libphobos/src/std/numeric.d                        |   52 +-
 libphobos/src/std/random.d                         |  100 +-
 libphobos/src/std/range/package.d                  |   23 +-
 libphobos/src/std/string.d                         |    5 +
 libphobos/src/std/traits.d                         |   21 +-
 libphobos/src/std/typecons.d                       |   18 +-
 libphobos/testsuite/libphobos.aa/test_aa.d         |   41 +
 .../testsuite/libphobos.init_fini/custom_gc.d      |   10 +
 254 files changed, 15322 insertions(+), 11421 deletions(-)

diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index 9f048bb4e5d1..9f91346290cc 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -135,10 +135,9 @@ D_FRONTEND_OBJS = \
 	d/func.o \
 	d/funcsem.o \
 	d/globals.o \
-	d/gluelayer.o \
 	d/hdrgen.o \
-	d/iasm.o \
-	d/iasmgcc.o \
+	d/iasm-package.o \
+	d/iasm-gcc.o \
 	d/id.o \
 	d/identifier.o \
 	d/impcnvtab.o \
@@ -420,6 +419,10 @@ d/common-%.o: d/dmd/common/%.d
 	$(DCOMPILE) $(D_INCLUDES) $<
 	$(DPOSTCOMPILE)
 
+d/iasm-%.o: d/dmd/iasm/%.d
+	$(DCOMPILE) $(D_INCLUDES) $<
+	$(DPOSTCOMPILE)
+
 d/mangle-%.o: d/dmd/mangle/%.d
 	$(DCOMPILE) $(D_INCLUDES) $<
 	$(DPOSTCOMPILE)
diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc
index 81a1b3cd3cfb..30874e72c0dc 100644
--- a/gcc/d/d-convert.cc
+++ b/gcc/d/d-convert.cc
@@ -448,13 +448,6 @@ convert_expr (tree exp, Type *etype, Type *totype)
 
 	      break;
 	    }
-
-	  /* The offset can only be determined at run-time, do dynamic cast.  */
-	  libcall_fn libcall = cdfrom->isInterfaceDeclaration ()
-	    ? LIBCALL_INTERFACE_CAST : LIBCALL_DYNAMIC_CAST;
-
-	  return build_libcall (libcall, totype, 2, exp,
-				build_address (get_classinfo_decl (cdto)));
 	}
       /* else default conversion.  */
       break;
diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
index 4dc8eede74ad..eed1ee97fe97 100644
--- a/gcc/d/d-lang.cc
+++ b/gcc/d/d-lang.cc
@@ -304,6 +304,7 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options)
   global.params.useDeprecated = DIAGNOSTICinform;
   global.params.useWarnings = DIAGNOSTICoff;
   global.params.v.errorLimit = flag_max_errors;
+  global.params.v.errorSupplementLimit = flag_max_errors;
   global.params.v.messageStyle = MessageStyle::gnu;
 
   /* Extra GDC-specific options.  */
diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc
index 0b915b5632a4..8e740aedc997 100644
--- a/gcc/d/decl.cc
+++ b/gcc/d/decl.cc
@@ -449,7 +449,7 @@ public:
     if (dmd::isError (d)|| !d->members)
       return;
 
-    if (!d->needsCodegen ())
+    if (!dmd::needsCodegen (d))
       return;
 
     for (size_t i = 0; i < d->members->length; i++)
@@ -766,7 +766,7 @@ public:
 	    Expression *ie = dmd::initializerToExpression (d->_init);
 	    add_stmt (build_expr (ie));
 
-	    Expression *e = d->type->defaultInitLiteral (d->loc);
+	    Expression *e = dmd::defaultInitLiteral (d->type, d->loc);
 	    add_stmt (build_expr (e));
 	  }
 
@@ -775,7 +775,7 @@ public:
 
     if (d->aliasTuple)
       {
-	this->build_dsymbol (d->toAlias ());
+	this->build_dsymbol (dmd::toAlias (d));
 	return;
       }
 
@@ -834,7 +834,7 @@ public:
 	      DECL_INITIAL (decl) = layout_struct_initializer (ts->sym);
 	    else
 	      {
-		Expression *e = d->type->defaultInitLiteral (d->loc);
+		Expression *e = dmd::defaultInitLiteral (d->type, d->loc);
 		DECL_INITIAL (decl) = build_expr (e, true);
 	      }
 	  }
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 62d90e5770b5..a9235246e938 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-1017635a9636ef6a7a4bda35b1e091875d829871
+e1f6680f50d147846316c2fa3363461a2aa7ac1d
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md
index 2e93d26fe853..f10b162d2745 100644
--- a/gcc/d/dmd/README.md
+++ b/gcc/d/dmd/README.md
@@ -35,6 +35,7 @@ Note that these groups have no strict meaning, the category assignments are a bi
 | [mars.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mars.d)           | Argument parsing, path manipulation.                                  |
 | [cli.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cli.d)             | Define the command line interface                                     |
 | [dmdparams.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmdparams.d) | DMD-specific parameters                                               |
+| [dmsc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmsc.d)           | Configures and initializes the back-end                               |
 | [globals.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/globals.d)     | Define a structure storing command line options                       |
 | [dinifile.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinifile.d)   | Parse settings from .ini file (`sc.ini` / `dmd.conf`)                 |
 | [vsoptions.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking              |
@@ -170,9 +171,10 @@ Note that these groups have no strict meaning, the category assignments are a bi
 
 | File                                                                    | Purpose                                   |
 |-------------------------------------------------------------------------|-------------------------------------------|
-| [iasm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm.d)       | Inline assembly depending on the compiler |
-| [iasmdmd.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmdmd.d) | Inline assembly for DMD                   |
-| [iasmgcc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d) | Inline assembly for GDC                   |
+| [iasm/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/package.d)       | Inline assembly depending on the compiler |
+| [iasm/dmdx86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/dmd.d) | Inline assembly for DMD X86_64            |
+| [iasm/dmdaarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/aarch64.d) | Inline assembly for DMD AArch64   |
+| [iasm/gcc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm/gcc.d) | Inline assembly for GDC                   |
 
 **Other**
 
@@ -208,27 +210,28 @@ Note that these groups have no strict meaning, the category assignments are a bi
 | [lib/scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmach.d)     | Extract symbol names from a library in Mach-O format |
 | [lib/scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmscoff.d) | Extract symbol names from a library in COFF format   |
 
+
+### ABI
+| File                                                                          | Purpose                                              |
+| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_x86.d)     | Convert a D type into simple (register) types for the 32-bit x86 ABI |
+| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI |
+| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_aarch64.d)   | 'argtypes' for the AArch64 ABI |
 ### Code generation / back-end interfacing
 
 | File                                                                                        | Purpose                                                                             |
 |---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
-| [dmsc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmsc.d)                           | Configures and initializes the back-end                                             |
-| [toobj.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toobj.d)                         | Convert an AST that went through all semantic phases into an object file            |
-| [toir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toir.d)                           | Convert Dsymbols intermediate representation                                        |
-| [e2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/e2ir.d)                           | Convert Expressions to intermediate representation                                  |
-| [s2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/s2ir.d)                           | Convert Statements to intermediate representation                                   |
-| [stmtstate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d)                 | Used to help transform statement AST into flow graph                                |
-| [toctype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/toctype.d)                     | Convert a D type to a type the back-end understands                                 |
-| [tocsym.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tocsym.d)                       | Convert a D symbol to a symbol the linker understands (with mangled name)           |
-| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_x86.d)           | Convert a D type into simple (register) types for the 32-bit x86 ABI                |
-| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI                                              |
-| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/argtypes_aarch64.d)   | 'argtypes' for the AArch64 ABI                                                      |
-| [glue.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/glue.d)                           | Generate the object file for function declarations                                  |
-| [gluelayer.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d)                 | Declarations for back-end functions that the front-end invokes                      |
-| [todt.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/todt.d)                           | Convert initializers into structures that the back-end will add to the data segment |
-| [tocvdebug.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tovcdebug.d)                 | Generate debug info in the CV4 debug format.                                        |
-| [objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d)                           | Objective-C interfacing                                                             |
-| [objc_glue.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc_glue.d)                 | Glue code for Objective-C interop.                                                  |
+| [stmtstate.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d)        | Used to help transform statement AST into flow graph    |
+| [objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d)                  | Objective-C interfacing                                 |
+| [irgen/toobj.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toobj.d)      | Convert an AST that went through all semantic phases into an object file|
+| [irgen/toir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toir.d)        | Convert Dsymbols intermediate representation                            |
+| [irgen/e2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/e2ir.d)        | Convert Expressions to intermediate representation                      |
+| [irgen/s2ir.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/s2ir.d)        | Convert Statements to intermediate representation                       |
+| [irgen/toctype.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/toctype.d)  | Convert a D type to a type the back-end understands                     |
+| [irgen/tocsym.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/tocsym.d)    | Convert a D symbol to a symbol the linker understands (with mangled name) |
+| [irgen/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/package.d)  | Generate the object file for function declarations                      |
+| [irgen/todt.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/todt.d)        | Convert initializers into structures that the back-end will add to the data segment |
+| [irgen/tocvdebug.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/tovcdebug.d)| Generate debug info in the CV4 debug format.                            |
+| [irgen/objc.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/irgen/objc.d)| irgen code for Objective-C interop.                                      |
 
 **Name mangling**
 
diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d
index eaaa288cb4a2..1871f601b6ef 100644
--- a/gcc/d/dmd/access.d
+++ b/gcc/d/dmd/access.d
@@ -20,6 +20,7 @@ import dmd.dmodule;
 import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
+import dmd.dsymbolsem : toAlias;
 import dmd.errors;
 import dmd.expression;
 import dmd.funcsem : overloadApply;
diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d
index 6359674d4471..d547bb78626c 100644
--- a/gcc/d/dmd/aggregate.d
+++ b/gcc/d/dmd/aggregate.d
@@ -25,19 +25,17 @@ import dmd.declaration;
 import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include;
+import dmd.dsymbolsem : determineSize;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
 import dmd.func;
-import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.tokens;
-import dmd.typesem : defaultInit, addMod, size;
 import dmd.visitor;
 
 /**
@@ -171,7 +169,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         sc2.parent = this;
         sc2.inunion = isUnionDeclaration();
         sc2.visibility = Visibility(Visibility.Kind.public_);
-        sc2.explicitVisibility = 0;
+        sc2.explicitVisibility = false;
         sc2.aligndecl = null;
         sc2.userAttribDecl = null;
         sc2.namespace = null;
@@ -187,7 +185,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         return fields.length - isNested() - (vthis2 !is null);
     }
 
-    override final uinteger_t size(Loc loc)
+    override final ulong size(Loc loc)
     {
         //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
         bool ok = determineSize(this, loc);
@@ -195,86 +193,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         return ok ? structsize : SIZE_INVALID;
     }
 
-    /***************************************
-     * Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
-     * field initializers have unique memory space on instance.
-     * Returns:
-     *      true if any errors happen.
-     */
-    extern (D) final bool checkOverlappedFields()
-    {
-        //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
-        assert(sizeok == Sizeok.done);
-        size_t nfields = fields.length;
-        if (isNested())
-        {
-            auto cd = isClassDeclaration();
-            if (!cd || !cd.baseClass || !cd.baseClass.isNested())
-                nfields--;
-            if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
-                nfields--;
-        }
-        bool errors = false;
-
-        // Fill in missing any elements with default initializers
-        foreach (i; 0 .. nfields)
-        {
-            auto vd = fields[i];
-            if (vd.errors)
-            {
-                errors = true;
-                continue;
-            }
-
-            const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
-
-            // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
-            foreach (j; 0 .. nfields)
-            {
-                if (i == j)
-                    continue;
-                auto v2 = fields[j];
-                if (v2.errors)
-                {
-                    errors = true;
-                    continue;
-                }
-                if (!vd.isOverlappedWith(v2))
-                    continue;
-
-                // vd and v2 are overlapping.
-                vd.overlapped = true;
-                v2.overlapped = true;
-
-                if (!MODimplicitConv(vd.type.mod, v2.type.mod))
-                    v2.overlapUnsafe = true;
-                if (!MODimplicitConv(v2.type.mod, vd.type.mod))
-                    vd.overlapUnsafe = true;
-
-                if (i > j)
-                    continue;
-
-                if (!v2._init)
-                    continue;
-
-                if (v2._init.isVoidInitializer())
-                    continue;
-
-                if (vd._init && !vdIsVoidInit && v2._init)
-                {
-                    .error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
-                    errors = true;
-                }
-                else if (v2._init && i < j)
-                {
-                    .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
-                        v2.toChars(), dmd.hdrgen.toChars(v2._init), vd.toChars());
-                    errors = true;
-                }
-            }
-        }
-        return errors;
-    }
 
     override final Type getType()
     {
@@ -433,46 +351,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         return visibility.kind == Visibility.Kind.export_;
     }
 
-    /*******************************************
-     * Look for constructor declaration.
-     */
-    extern (D) final Dsymbol searchCtor()
-    {
-        auto s = this.search(Loc.initial, Id.ctor);
-        if (s)
-        {
-            if (!(s.isCtorDeclaration() ||
-                  s.isTemplateDeclaration() ||
-                  s.isOverloadSet()))
-            {
-                error(s.loc, "%s name `__ctor` is not allowed", s.kind);
-                errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use");
-                errors = true;
-                s = null;
-            }
-        }
-        if (s && s.toParent() != this)
-            s = null; // search() looks through ancestor classes
-        if (s)
-        {
-            // Finish all constructors semantics to determine this.noDefaultCtor.
-            static int searchCtor(Dsymbol s, void*)
-            {
-                auto f = s.isCtorDeclaration();
-                if (f && f.semanticRun == PASS.initial)
-                    f.dsymbolSemantic(null);
-                return 0;
-            }
-
-            for (size_t i = 0; i < members.length; i++)
-            {
-                auto sm = (*members)[i];
-                sm.apply(&searchCtor, null);
-            }
-        }
-        return s;
-    }
-
     override final Visibility visible() pure nothrow @nogc @safe
     {
         return visibility;
@@ -499,39 +377,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
     }
 }
 
-/*********************************
- * Iterate this dsymbol or members of this scoped dsymbol, then
- * call `fp` with the found symbol and `params`.
- * Params:
- *  symbol = the dsymbol or parent of members to call fp on
- *  fp = function pointer to process the iterated symbol.
- *       If it returns nonzero, the iteration will be aborted.
- *  ctx = context parameter passed to fp.
- * Returns:
- *  nonzero if the iteration is aborted by the return value of fp,
- *  or 0 if it's completed.
- */
-int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx)
-{
-    if (auto nd = symbol.isNspace())
-    {
-        return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
-    }
-    if (auto ad = symbol.isAttribDeclaration())
-    {
-        return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
-    }
-    if (auto tm = symbol.isTemplateMixin())
-    {
-        if (tm._scope) // if fwd reference
-            dsymbolSemantic(tm, null); // try to resolve it
-
-        return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
-    }
-
-    return fp(symbol, ctx);
-}
-
 /****************************
  * Do byte or word alignment as necessary.
  * Align sizes of 0, as we may not know array sizes yet.
diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h
index d89755cea27c..3c4c84a09398 100644
--- a/gcc/d/dmd/aggregate.h
+++ b/gcc/d/dmd/aggregate.h
@@ -171,7 +171,6 @@ public:
     static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
     StructDeclaration *syntaxCopy(Dsymbol *s) override;
     const char *kind() const override;
-    bool isPOD();
     bool zeroInit() const;          // !=0 if initialize with 0 fill
     bool zeroInit(bool v);
     bool hasIdentityAssign() const; // true if has identity opAssign
@@ -194,7 +193,6 @@ public:
 
     unsigned numArgTypes() const;
     Type *argType(unsigned index);
-    bool hasRegularCtor(bool ignoreDisabled = false);
 };
 
 class UnionDeclaration final : public StructDeclaration
@@ -273,7 +271,6 @@ public:
     ThreeState isabstract;              // if abstract class
     Baseok baseok;                      // set the progress of base classes resolving
     ObjcClassDeclaration objc;          // Data for a class declaration that is needed for the Objective-C integration
-    Symbol *cpp_type_info_ptr_sym;      // cached instance of class Id.cpp_type_info_ptr
 
     static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
     const char *toPrettyChars(bool QualifyTypes = false) override;
diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d
index 7d25e185129f..ea6f87f60ec6 100644
--- a/gcc/d/dmd/arrayop.d
+++ b/gcc/d/dmd/arrayop.d
@@ -31,6 +31,7 @@ import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
 import dmd.tokens;
+import dmd.typesem : isAssignable;
 import dmd.visitor;
 
 /**********************************************
diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d
index b71b6c40aeb0..5cde5b334c5e 100644
--- a/gcc/d/dmd/astenums.d
+++ b/gcc/d/dmd/astenums.d
@@ -19,12 +19,12 @@ enum Sizeok : ubyte
 }
 
 /// D Language version
-enum Edition : ubyte
+enum Edition : ushort
 {
-    none,
-    legacy,          /// Before the introduction of editions
-    v2024,           /// Experimental first new edition
-    latest = v2024   /// Newest edition that this compiler knows of
+    v2023 = 2023,    /// Edition.min for default edition
+    v2024,           /// first Edition
+    v2025,           /// next Edition
+                     /// use Edition.max for latest edition
 }
 
 enum Baseok : ubyte
@@ -77,9 +77,10 @@ enum STC : ulong  // transfer changes to declaration.h
     ref_                = 0x4_0000,   /// `ref`
     scope_              = 0x8_0000,   /// `scope`
 
-    scopeinferred       = 0x20_0000,   /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set
-    return_             = 0x40_0000,   /// 'return ref' or 'return scope' for function parameters
-    returnScope         = 0x80_0000,   /// if `ref return scope` then resolve to `ref` and `return scope`
+    scopeinferred       = 0x10_0000,   /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set
+    return_             = 0x20_0000,   /// 'return ref' or 'return scope' for function parameters
+    returnScope         = 0x40_0000,   /// if `ref return scope` then resolve to `ref` and `return scope`
+    returnRef           = 0x80_0000,   /// if `return ref`
 
     returninferred      = 0x100_0000,   /// `return` has been inferred and should not be part of mangling, `return_` must also be set
     immutable_          = 0x200_0000,   /// `immutable`
diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d
index a03e7bbdc0e5..97dfa5ef683f 100644
--- a/gcc/d/dmd/attrib.d
+++ b/gcc/d/dmd/attrib.d
@@ -32,10 +32,8 @@ import dmd.declaration;
 import dmd.dmodule;
 import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem : include;
 import dmd.expression;
 import dmd.func;
-import dmd.globals;
 import dmd.hdrgen : visibilityToBuffer;
 import dmd.id;
 import dmd.identifier;
@@ -80,7 +78,7 @@ extern (C++) abstract class AttribDeclaration : Dsymbol
      * the scope after it used.
      */
     extern (D) static Scope* createNewScope(Scope* sc, STC stc, LINK linkage,
-        CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility,
+        CPPMANGLE cppmangle, Visibility visibility, bool explicitVisibility,
         AlignDeclaration aligndecl, PragmaDeclaration inlining)
     {
         Scope* sc2 = sc;
@@ -414,8 +412,7 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
         this.dsym = DSYM.alignDeclaration;
         if (exp)
         {
-            exps = new Expressions();
-            exps.push(exp);
+            exps = new Expressions(exp);
         }
     }
 
diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
index 96acf0552931..bd4aa197ab93 100644
--- a/gcc/d/dmd/canthrow.d
+++ b/gcc/d/dmd/canthrow.d
@@ -20,7 +20,7 @@ import dmd.astenums;
 import dmd.blockexit : BE, checkThrow;
 import dmd.declaration;
 import dmd.dsymbol;
-import dmd.dsymbolsem : include;
+import dmd.dsymbolsem : include, toAlias;
 import dmd.errorsink;
 import dmd.expression;
 import dmd.expressionsem : errorSupplementalInferredAttr;
diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d
index a21f5a05be78..f3f359ee3129 100644
--- a/gcc/d/dmd/clone.d
+++ b/gcc/d/dmd/clone.d
@@ -300,8 +300,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
             stc = (stc & ~STC.safe) | STC.trusted;
     }
 
-    auto fparams = new Parameters();
-    fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null));
+    auto fparams = new Parameters(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null));
     auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_);
     auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.opAssign, stc, tf);
     fop.storage_class |= STC.inference;
@@ -574,8 +573,8 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
                 scx.eSink = sc.eSink;
                 /* const bool opEquals(ref const S s);
                  */
-                auto parameters = new Parameters();
-                parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null));
+                auto parameters = new Parameters(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type,
+                                                               null, null, null));
                 tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d);
                 tfeqptr.mod = MODFlags.const_;
                 tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction();
@@ -602,8 +601,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
     }
     Loc declLoc; // loc is unnecessary so __xopEquals is never called directly
     Loc loc; // loc is unnecessary so errors are gagged
-    auto parameters = new Parameters();
-    parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+    auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
     auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_);
     tf = tf.addSTC(STC.const_).toTypeFunction();
     Identifier id = Id.xopEquals;
@@ -649,8 +647,8 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
                 scx.eSink = sc.eSink;
                 /* const int opCmp(ref const S s);
                  */
-                auto parameters = new Parameters();
-                parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null));
+                auto parameters = new Parameters(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type,
+                                                               null, null, null));
                 tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d);
                 tfcmpptr.mod = MODFlags.const_;
                 tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction();
@@ -726,8 +724,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
     }
     Loc declLoc; // loc is unnecessary so __xopCmp is never called directly
     Loc loc; // loc is unnecessary so errors are gagged
-    auto parameters = new Parameters();
-    parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+    auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
     auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_);
     tf = tf.addSTC(STC.const_).toTypeFunction();
     Identifier id = Id.xopCmp;
@@ -854,8 +851,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
     //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
     Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
     Loc loc; // internal code should have no loc to prevent coverage
-    auto parameters = new Parameters();
-    parameters.push(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
+    auto parameters = new Parameters(new Parameter(loc, STC.ref_ | STC.const_, sd.type, Id.p, null, null));
     auto tf = new TypeFunction(ParameterList(parameters), Type.thash_t, LINK.d, STC.nothrow_ | STC.trusted);
     Identifier id = Id.xtoHash;
     auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.static_, tf);
@@ -872,6 +868,11 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
         // workaround https://issues.dlang.org/show_bug.cgi?id=17968
         "    static if(is(T* : const(.object.Object)*)) " ~
         "        h = h * 33 + typeid(const(.object.Object)).getHash(cast(const void*)&p.tupleof[i]);" ~
+        // and another workaround for bitfields https://github.com/dlang/dmd/issues/20473
+        "    else static if (!__traits(compiles, &p.tupleof[i])) {" ~
+        "        auto t = p.tupleof[i];" ~
+        "        h = h * 33 + typeid(T).getHash(cast(const void*)&t);" ~
+        "    } " ~
         "    else " ~
         "        h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~
         "return h;";
@@ -1561,10 +1562,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
  */
 private CtorDeclaration generateCtorDeclaration(StructDeclaration sd, const STC paramStc, const STC funcStc, bool move)
 {
-    auto fparams = new Parameters();
     auto structType = sd.type;
     STC stc = move ? STC.none : STC.ref_;     // the only difference between copy or move
-    fparams.push(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null));
+    auto fparams = new Parameters(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null));
     ParameterList pList = ParameterList(fparams);
     auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
     auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf);
diff --git a/gcc/d/dmd/common/bitfields.d b/gcc/d/dmd/common/bitfields.d
index 72cb3e7d91c8..5732f9d385f6 100644
--- a/gcc/d/dmd/common/bitfields.d
+++ b/gcc/d/dmd/common/bitfields.d
@@ -10,18 +10,25 @@
  */
 module dmd.common.bitfields;
 
+//version = Has_Bitfields; // does not work (yet) because hashOf doesn't work on bitfields
+version(Has_Bitfields)
+    version = Debugger_friendly; // without Has_Bitfields, this uses more space by using S
+
 /**
  * Generate code for bit fields inside a struct/class body
  * Params:
  *   S = type of a struct with only boolean fields, which should become bit fields
  *   T = type of bit fields variable, must have enough bits to store all booleans
+ *   field = if provided, assume it is declared and initialized elsewhere
+ *   bitOff = start using bits at the given offset
  * Returns: D code with a bit fields variable and getter / setter functions
  */
-extern (D) string generateBitFields(S, T)()
+extern (D) string generateBitFields(S, T, string field = "", int bitOff = 0, int ID = __LINE__)()
 if (__traits(isUnsigned, T))
 {
     import core.bitop: bsr;
-
+    // if _fieldName provided, assume it declared and initialized elsewhere
+    enum fieldName = field.length == 0 ? "bitFields" : field;
     string result = "extern (C++) pure nothrow @nogc @safe final {";
 
     struct BitInfo
@@ -35,7 +42,7 @@ if (__traits(isUnsigned, T))
     // Iterate over members to compute bit offset and bit size for each of them
     enum BitInfo bitInfo = () {
         BitInfo result;
-        int bitOffset = 0;
+        int bitOffset = bitOff;
         foreach (size_t i, mem; __traits(allMembers, S))
         {
             alias memType = typeof(__traits(getMember, S, mem));
@@ -55,22 +62,60 @@ if (__traits(isUnsigned, T))
     static assert(bitInfo.totalSize <= T.sizeof * 8,
         "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`");
 
+    version(Debugger_friendly)
+    {
+        // unique name needed to allow same name as in base class using `alias`, but without overloading
+        string bitfieldsName = fieldName ~ toString!(ID);
+        string bitfieldsRead = T.stringof~" "~bitfieldsName~"() const pure { return 0";
+        string bitfieldsWrite = "void "~bitfieldsName~"("~T.stringof~" v) {\n";
+    }
+
     foreach (size_t i, mem; __traits(allMembers, S))
     {
         enum typeName = typeof(__traits(getMember, S, mem)).stringof;
         enum shift = toString!(bitInfo.offset[i]);
         enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc.
-        result ~= "
-        "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); }
-        "~typeName~" "~mem~"("~typeName~" v) scope
+        version(Debugger_friendly)
+        {
+            string memacc = mem;
+            bitfieldsRead ~= "\n| (cast("~T.stringof~")("~memacc~" & "~sizeMask~") << "~shift~")";
+            bitfieldsWrite ~= memacc~" = cast("~typeName~")((v >> "~shift~") & "~sizeMask~");\n";
+            result ~= typeName~" "~mem;
+            version(Has_Bitfields)
+                result ~= " : "~toString!(bitInfo.size[i]);
+            enum meminit = __traits(getMember, S.init, mem);
+            result ~= " = "~meminit.stringof~";\n";
+        }
+        else
         {
-            bitFields &= ~("~sizeMask~" << "~shift~");
-            bitFields |= v << "~shift~";
-            return v;
-        }";
+            result ~= "
+                "~typeName~" "~mem~"() const scope { return cast("~typeName~") (("~fieldName~" >>> "~shift~") & "~sizeMask~"); }
+            "~typeName~" "~mem~"("~typeName~" v) scope
+            {
+                "~fieldName~" &= ~("~sizeMask~" << "~shift~");
+                "~fieldName~" |= v << "~shift~";
+                return v;
+            }";
+        }
+    }
+    version(Debugger_friendly)
+    {
+        bitfieldsRead ~= ";\n}\n";
+        bitfieldsWrite ~= "}\n";
+        if (field.length == 0)
+            result ~= "alias "~fieldName~" = "~bitfieldsName~";\n";
+        result ~= bitfieldsRead ~ bitfieldsWrite;
+        result ~= "\n}\n";
+        return result;
+    }
+    else
+    {
+        result ~= "\n}\n";
+        enum TP initVal = bitInfo.initialValue;
+        if (field.length == 0)
+            result ~= " private "~T.stringof~" "~fieldName~" = " ~ toString!(initVal) ~ ";\n";
+        return result;
     }
-    enum TP initVal = bitInfo.initialValue;
-    return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n";
 }
 
 ///
diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d
index 01fc3774002d..a5340ef8058c 100644
--- a/gcc/d/dmd/common/outbuffer.d
+++ b/gcc/d/dmd/common/outbuffer.d
@@ -230,9 +230,11 @@ struct OutBuffer
         offset += len;
     }
 
-    extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow @system
+    alias put = write;  // transition to output range which uses put()
+
+    extern (C++) void write(scope const(void)* data, size_t nbytes) pure nothrow @system
     {
-        write(data[0 .. nbytes]);
+        put(data[0 .. nbytes]);
     }
 
     void write(scope const(void)[] buf) pure nothrow @trusted
@@ -244,6 +246,23 @@ struct OutBuffer
         offset += buf.length;
     }
 
+    void write(scope string buf) pure nothrow @trusted // so write("hello") chooses this overload
+    {
+        if (doindent && !notlinehead)
+            indent();
+        reserve(buf.length);
+        memcpy(this.data.ptr + offset, buf.ptr, buf.length);
+        offset += buf.length;
+    }
+
+    extern (C++) void write(scope const(char)* s) pure nothrow @trusted
+    {
+        if (!s)
+            return;
+        import core.stdc.string : strlen;
+        put(s[0 .. strlen(s)]);
+    }
+
     /**
      * Writes a 16 bit value, no reserve check.
      */
@@ -262,7 +281,7 @@ struct OutBuffer
     void write16(int v) nothrow
     {
         auto u = cast(ushort) v;
-        write(&u, u.sizeof);
+        put(&u, u.sizeof);
     }
 
     /**
@@ -270,7 +289,7 @@ struct OutBuffer
      */
     void write32(int v) nothrow @trusted
     {
-        write(&v, v.sizeof);
+        put(&v, v.sizeof);
     }
 
     /**
@@ -278,34 +297,31 @@ struct OutBuffer
      */
     @trusted void write64(long v) nothrow
     {
-        write(&v, v.sizeof);
+        put(&v, v.sizeof);
     }
 
     /// Buffer will NOT be zero-terminated
     extern (C++) void writestring(const(char)* s) pure nothrow @system
     {
-        if (!s)
-            return;
-        import core.stdc.string : strlen;
-        write(s[0 .. strlen(s)]);
+        put(s);
     }
 
     /// ditto
     void writestring(scope const(char)[] s) pure nothrow @safe
     {
-        write(s);
+        put(s);
     }
 
     /// ditto
     void writestring(scope string s) pure nothrow @safe
     {
-        write(s);
+        put(s);
     }
 
     /// Buffer will NOT be zero-terminated, followed by newline
     void writestringln(const(char)[] s) pure nothrow @safe
     {
-        writestring(s);
+        put(s);
         writenl();
     }
 
@@ -313,13 +329,13 @@ struct OutBuffer
      */
     void writeStringz(const(char)* s) pure nothrow @system
     {
-        write(s[0 .. strlen(s)+1]);
+        put(s[0 .. strlen(s)+1]);
     }
 
     /// ditto
     void writeStringz(const(char)[] s) pure nothrow @safe
     {
-        write(s);
+        put(s);
         writeByte(0);
     }
 
@@ -382,17 +398,26 @@ struct OutBuffer
      * Writes an 8 bit byte, no reserve check.
      */
     extern (C++) nothrow @safe
-    void writeByten(int b)
+    void writeByten(ubyte b)
     {
-        this.data[offset++] = cast(ubyte) b;
+        this.data[offset++] = b;
     }
 
-    extern (C++) void writeByte(uint b) pure nothrow @safe
+    extern (C++) void writeByte(ubyte b) pure nothrow @safe
     {
         if (doindent && !notlinehead && b != '\n')
             indent();
         reserve(1);
-        this.data[offset] = cast(ubyte)b;
+        this.data[offset] = b;
+        offset++;
+    }
+
+    void write(ubyte b) pure nothrow @safe
+    {
+        if (doindent && !notlinehead && b != '\n')
+            indent();
+        reserve(1);
+        this.data[offset] = b;
         offset++;
     }
 
@@ -502,14 +527,17 @@ struct OutBuffer
         offset += 4;
     }
 
-    extern (C++) void write(const OutBuffer* buf) pure nothrow @trusted
+    extern (C++) void write(scope const OutBuffer* buf) pure nothrow @trusted
     {
         if (buf)
-        {
-            reserve(buf.offset);
-            memcpy(data.ptr + offset, buf.data.ptr, buf.offset);
-            offset += buf.offset;
-        }
+            put(*buf);
+    }
+
+    void write(ref scope const OutBuffer buf) pure nothrow @trusted
+    {
+        reserve(buf.offset);
+        memcpy(data.ptr + offset, buf.data.ptr, buf.offset);
+        offset += buf.offset;
     }
 
     extern (C++) void fill0(size_t nbytes) pure nothrow @trusted
@@ -871,7 +899,7 @@ unittest
     buf.prependbyte('x');
     OutBuffer buf2;
     buf2.writestring("mmm");
-    buf.write(&buf2);
+    buf.put(&buf2);
     char[] s = buf.extractSlice();
     assert(s == "xdefabcmmm");
 }
diff --git a/gcc/d/dmd/common/outbuffer.h b/gcc/d/dmd/common/outbuffer.h
index 62aca7a8014a..9630f1220157 100644
--- a/gcc/d/dmd/common/outbuffer.h
+++ b/gcc/d/dmd/common/outbuffer.h
@@ -53,7 +53,7 @@ public:
     void writestring(const char *string);
     void prependstring(const char *string);
     void writenl();                     // write newline
-    void writeByte(unsigned b);
+    void writeByte(uint8_t b);
     void writeUTF8(unsigned b);
     void prependbyte(unsigned b);
     void writewchar(unsigned w);
diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d
index 31c01f1853e6..96c7db9086cd 100644
--- a/gcc/d/dmd/cond.d
+++ b/gcc/d/dmd/cond.d
@@ -29,7 +29,6 @@ import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.optimize;
-import dmd.typesem;
 import dmd.common.outbuffer;
 import dmd.rootobject;
 import dmd.root.string;
@@ -211,10 +210,9 @@ extern (C++) final class StaticForeach : RootObject
         auto sid = Identifier.generateId("Tuple");
         auto sdecl = new StructDeclaration(loc, sid, false);
         sdecl.storage_class |= STC.static_;
-        sdecl.members = new Dsymbols();
         auto fid = Identifier.idPool(tupleFieldName);
         auto ty = new TypeTypeof(loc, new TupleExp(loc, e));
-        sdecl.members.push(new VarDeclaration(loc, ty, fid, null, STC.none));
+        sdecl.members = new Dsymbols(new VarDeclaration(loc, ty, fid, null, STC.none));
         auto r = cast(TypeStruct)sdecl.type;
         if (global.params.useTypeInfo && Type.dtypeinfo)
             r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file
diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d
index fad9c9ac44f6..1e0024e92cef 100644
--- a/gcc/d/dmd/constfold.d
+++ b/gcc/d/dmd/constfold.d
@@ -25,6 +25,7 @@ import dmd.declaration;
 import dmd.dstruct;
 import dmd.errors;
 import dmd.expression;
+import dmd.expressionsem : getField;
 import dmd.globals;
 import dmd.location;
 import dmd.mtype;
@@ -1432,8 +1433,7 @@ UnionExp Cat(Loc loc, Type type, Expression e1, Expression e2)
         else
         {
             // Create an ArrayLiteralExp
-            auto elements = new Expressions();
-            elements.push(e);
+            auto elements = new Expressions(e);
             emplaceExp!(ArrayLiteralExp)(&ue, e.loc, type, elements);
         }
         assert(ue.exp().type);
@@ -1674,8 +1674,7 @@ UnionExp Cat(Loc loc, Type type, Expression e1, Expression e2)
         Type tb = t.toBasetype();
         if (tb.ty == Tarray && tb.nextOf().equivalent(e.type))
         {
-            auto expressions = new Expressions();
-            expressions.push(e);
+            auto expressions = new Expressions(e);
             emplaceExp!(ArrayLiteralExp)(&ue, loc, t, expressions);
             e = ue.exp();
         }
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index 70369ba20c7a..4a6e5195f5fd 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -655,8 +655,10 @@ final class CParser(AST) : Parser!AST
         case TOK.asm_:
             switch (peekNext())
             {
+                case TOK.const_:
                 case TOK.goto_:
                 case TOK.inline:
+                case TOK.restrict:
                 case TOK.volatile:
                 case TOK.leftParenthesis:
                     s = cparseGnuAsm();
@@ -1474,7 +1476,7 @@ final class CParser(AST) : Parser!AST
      *    logical-OR-expression
      *    logical-OR-expression ? expression : conditional-expression
      */
-    private AST.Expression cparseCondExp()
+    AST.Expression cparseCondExp()
     {
         const loc = token.loc;
 
@@ -1637,7 +1639,7 @@ final class CParser(AST) : Parser!AST
      */
     private AST.Expression cparseConstantExp()
     {
-        return cparseAssignExp();
+        return cparseCondExp();
     }
 
     /*****************************
@@ -3437,6 +3439,11 @@ final class CParser(AST) : Parser!AST
                         cparseParens();
                 }
             }
+            else if (token.value == TOK._Noreturn)
+            {
+                specifier.noreturn = true;
+                nextToken();
+            }
             else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword.
                 nextToken();
             else
@@ -3485,19 +3492,51 @@ final class CParser(AST) : Parser!AST
 
         nextToken();
 
-        // Consume all asm-qualifiers. As a future optimization, we could record
-        // the `inline` and `volatile` storage classes against the statement.
-        while (token.value == TOK.goto_ ||
-               token.value == TOK.inline ||
-               token.value == TOK.volatile)
-            nextToken();
+        // Consume all asm-qualifiers.
+        enum AsmQualifiers { none = 0, goto_ = 1, inline = 2, volatile = 4, }
+        AsmQualifiers asmQualifiers;
+        while (1)
+        {
+            AsmQualifiers qual;
+            switch (token.value)
+            {
+                case TOK.goto_:
+                    qual = AsmQualifiers.goto_;
+                    goto Lcontinue;
+
+                case TOK.inline:
+                    qual = AsmQualifiers.inline;
+                    goto Lcontinue;
+
+                case TOK.volatile:
+                    qual = AsmQualifiers.volatile;
+                    goto Lcontinue;
+
+                Lcontinue:
+                    if (asmQualifiers & qual)
+                        error(token.loc, "duplicate `asm` qualifier `%s`", token.toChars());
+                    else
+                        asmQualifiers |= qual;
+                    nextToken();
+                    continue;
+
+                case TOK.const_:
+                case TOK.restrict:
+                    error(token.loc, "`%s` is not a valid `asm` qualifier", token.toChars());
+                    nextToken();
+                    continue;
+
+                default:
+                    break;
+            }
+            break;
+        }
 
         check(TOK.leftParenthesis);
         if (token.value != TOK.string_)
             error("string literal expected for Assembler Template, not `%s`", token.toChars());
         Token* toklist = null;
         Token** ptoklist = &toklist;
-        //Identifier label = null;
         auto statements = new AST.Statements();
 
         int parens;
@@ -3524,21 +3563,6 @@ final class CParser(AST) : Parser!AST
                     error("matching `)` expected, not end of file");
                     break;
 
-                case TOK.colonColon:  // treat as two separate : tokens for iasmgcc
-                    *ptoklist = allocateToken();
-                    **ptoklist = this.token;
-                    (*ptoklist).value = TOK.colon;
-                    ptoklist = &(*ptoklist).next;
-
-                    *ptoklist = allocateToken();
-                    **ptoklist = this.token;
-                    (*ptoklist).value = TOK.colon;
-                    ptoklist = &(*ptoklist).next;
-
-                    *ptoklist = null;
-                    nextToken();
-                    continue;
-
                 default:
                     *ptoklist = allocateToken();
                     **ptoklist = this.token;
@@ -3550,7 +3574,11 @@ final class CParser(AST) : Parser!AST
             if (toklist)
             {
                 // Create AsmStatement from list of tokens we've saved
-                AST.Statement s = new AST.AsmStatement(token.loc, toklist);
+                auto s = new AST.AsmStatement(token.loc, toklist);
+                if (asmQualifiers & AsmQualifiers.volatile)
+                    s.isVolatile = true;
+                if (asmQualifiers & AsmQualifiers.inline)
+                    s.isInline = true;
                 statements.push(s);
             }
             break;
@@ -6069,7 +6097,7 @@ final class CParser(AST) : Parser!AST
         {
             //printf("addSym() %s\n", s.toChars());
             if (auto v = s.isVarDeclaration())
-                v.isCmacro(true);       // mark it as coming from a C #define
+                v.isCmacro = true;       // mark it as coming from a C #define
             if (auto td = s.isTemplateDeclaration())
                 td.isCmacro = true; // mark as coming from a C #define
             /* If it's already defined, replace the earlier
diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d
index 2f577cea92f2..58a00341d4e0 100644
--- a/gcc/d/dmd/ctfeexpr.d
+++ b/gcc/d/dmd/ctfeexpr.d
@@ -26,7 +26,7 @@ import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
 import dmd.func;
-import dmd.globals;
+import dmd.globals : dinteger_t, sinteger_t, uinteger_t;
 import dmd.location;
 import dmd.mtype;
 import dmd.root.bitarray;
@@ -241,6 +241,7 @@ UnionExp copyLiteral(Expression e)
         AssocArrayLiteralExp r = ue.exp().isAssocArrayLiteralExp();
         r.type = aae.type;
         r.lowering = aae.lowering;
+        r.loweringCtfe = aae.loweringCtfe;
         r.ownedByCtfe = OwnedBy.ctfe;
         return ue;
     }
@@ -1459,7 +1460,7 @@ UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2)
 /*  Given an AA literal 'ae', and a key 'e2':
  *  Return ae[e2] if present, or NULL if not found.
  */
-Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2)
+Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2, size_t* pidx = null)
 {
     /* Search the keys backwards, in case there are duplicate keys
      */
@@ -1470,6 +1471,8 @@ Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2)
         const int eq = ctfeEqual(loc, EXP.equal, ekey, e2);
         if (eq)
         {
+            if (pidx)
+                *pidx = i;
             return (*ae.values)[i];
         }
     }
@@ -1583,11 +1586,6 @@ Expression ctfeCast(UnionExp* pue, Loc loc, Type type, Type to, Expression e, bo
  */
 void assignInPlace(Expression dest, Expression src)
 {
-    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 == EXP.structLiteral)
@@ -1622,6 +1620,16 @@ void assignInPlace(Expression dest, Expression src)
         sliceAssignStringFromArrayLiteral(dest.isStringExp(), src.isArrayLiteralExp(), 0);
         return;
     }
+    else if (dest.op == EXP.int64 && src.op == EXP.int64)
+    {
+        dest.isIntegerExp().setInteger(src.isIntegerExp().getInteger());
+        return;
+    }
+    else if (dest.op == EXP.float64 && src.op == EXP.float64)
+    {
+        dest.isRealExp().value = src.isRealExp().value;
+        return;
+    }
     else
     {
         printf("invalid op %d %d\n", src.op, dest.op);
@@ -1829,6 +1837,9 @@ bool isCtfeValueValid(Expression newval)
             (
                 (e1.op == EXP.structLiteral || e1.op == EXP.arrayLiteral) && isCtfeValueValid(e1) ||
                  e1.op == EXP.variable ||
+                 e1.op == EXP.int64 ||
+                 e1.op == EXP.float64 ||
+                 e1.op == EXP.string_ ||
                  e1.op == EXP.dotVariable && isCtfeReferenceValid(e1) ||
                  e1.op == EXP.index && isCtfeReferenceValid(e1) ||
                  e1.op == EXP.slice && e1.type.toBasetype().ty == Tsarray
diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d
index b61137ff20fa..ce474c9bbef1 100644
--- a/gcc/d/dmd/cxxfrontend.d
+++ b/gcc/d/dmd/cxxfrontend.d
@@ -26,12 +26,13 @@ import dmd.dtemplate /*: TemplateInstance, TemplateParameter, Tuple*/;
 import dmd.errorsink : ErrorSink;
 import dmd.expression /*: Expression*/;
 import dmd.func : FuncDeclaration;
-import dmd.globals;
+import dmd.globals : dinteger_t, uinteger_t, JsonFieldFlags;
 import dmd.identifier : Identifier;
 import dmd.init : Initializer, NeedInterpret;
 import dmd.location : Loc;
 import dmd.mtype /*: Covariant, Type, Parameter, ParameterList*/;
 import dmd.rootobject : RootObject;
+import dmd.semantic3;
 import dmd.statement : Statement, AsmStatement, GccAsmStatement;
 
 // NB: At some point in the future, we can switch to shortened function syntax.
@@ -132,7 +133,7 @@ void gendocfile(Module m, const char* ddoctext_ptr, size_t ddoctext_length,
  */
 FuncDeclaration search_toString(StructDeclaration sd)
 {
-    return dmd.dstruct.search_toString(sd);
+    return dmd.semantic3.search_toString(sd);
 }
 
 /***********************************************************
@@ -205,6 +206,24 @@ void getLocalClasses(Module mod, ref ClassDeclarations aclasses)
     return dmd.dsymbolsem.getLocalClasses(mod, aclasses);
 }
 
+Dsymbol toAlias(Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.toAlias(s);
+}
+
+Dsymbol toAlias2(Dsymbol s)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.toAlias2(s);
+}
+
+bool isPOD(StructDeclaration sd)
+{
+    import dmd.dsymbolsem;
+    return dmd.dsymbolsem.isPOD(sd);
+}
+
 /***********************************************************
  * dtemplate.d
  */
@@ -248,6 +267,11 @@ void printTemplateStats(bool listInstances, ErrorSink eSink)
     return dmd.dtemplate.printTemplateStats(listInstances, eSink);
 }
 
+void printInstantiationTrace(TemplateInstance ti)
+{
+    return ti.printInstantiationTrace();
+}
+
 /***********************************************************
  * dtoh.d
  */
@@ -395,14 +419,14 @@ void asmSemantic(CAsmDeclaration d, Scope* sc)
  */
 Statement gccAsmSemantic(GccAsmStatement s, Scope* sc)
 {
-    import dmd.iasmgcc;
-    return dmd.iasmgcc.gccAsmSemantic(s, sc);
+    import dmd.iasm.gcc;
+    return dmd.iasm.gcc.gccAsmSemantic(s, sc);
 }
 
 void gccAsmSemantic(CAsmDeclaration d, Scope* sc)
 {
-    import dmd.iasmgcc;
-    return dmd.iasmgcc.gccAsmSemantic(d, sc);
+    import dmd.iasm.gcc;
+    return dmd.iasm.gcc.gccAsmSemantic(d, sc);
 }
 
 /***********************************************************
@@ -721,6 +745,12 @@ MATCH constConv(Type from, Type to)
     return dmd.typesem.constConv(from, to);
 }
 
+Expression defaultInitLiteral(Type t, Loc loc)
+{
+    import dmd.typesem;
+    return dmd.typesem.defaultInitLiteral(t, loc);
+}
+
 /***********************************************************
  * typinf.d
  */
@@ -754,6 +784,20 @@ TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc)
     return dmd.typinf.getTypeInfoAssocArrayDeclaration(t, sc);
 }
 
+/**
+ * templatesem.d
+ */
+bool needsCodegen(TemplateInstance ti)
+{
+    import dmd.templatesem;
+    return dmd.templatesem.needsCodegen(ti);
+}
+
+bool isDiscardable(TemplateInstance ti)
+{
+    import dmd.templatesem;
+    return dmd.templatesem.isDiscardable(ti);
+}
 version (IN_LLVM)
 {
     /***********************************************************
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
index 9e35dde29697..4ac8b1f44711 100644
--- a/gcc/d/dmd/dcast.d
+++ b/gcc/d/dmd/dcast.d
@@ -2862,7 +2862,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 (*ae.keys)[i] = ex;
             }
             ae.type = t;
-            semanticTypeInfo(sc, ae.type);
+            ae.lowering = null; // we need a different lowering
+            ae.loweringCtfe = null;
+            ae.expressionSemantic(sc);
             return ae;
         }
         return visit(e);
diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d
index 51ac939ade09..2129aa00cf60 100644
--- a/gcc/d/dmd/dclass.d
+++ b/gcc/d/dmd/dclass.d
@@ -19,7 +19,6 @@ import core.stdc.string;
 import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
-import dmd.gluelayer;
 import dmd.declaration;
 import dmd.dscope;
 import dmd.dsymbol;
@@ -194,8 +193,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
      */
     ObjcClassDeclaration objc;
 
-    Symbol* cpp_type_info_ptr_sym;      // cached instance of class Id.cpp_type_info_ptr
-
     final extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
     {
         objc = ObjcClassDeclaration(this);
diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d
index 25e5c31dcc6f..1bda9948cbf4 100644
--- a/gcc/d/dmd/declaration.d
+++ b/gcc/d/dmd/declaration.d
@@ -22,18 +22,16 @@ import dmd.delegatize;
 import dmd.dscope;
 import dmd.dstruct;
 import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic;
+import dmd.dsymbolsem : toAlias;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
 import dmd.func;
 import dmd.globals;
-import dmd.gluelayer;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
-import dmd.initsem : initializerToExpression, initializerSemantic;
 import dmd.intrange;
 import dmd.location;
 import dmd.mtype;
@@ -42,7 +40,7 @@ import dmd.rootobject;
 import dmd.root.filename;
 import dmd.target;
 import dmd.tokens;
-import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers;
+import dmd.typesem : typeSemantic, size;
 import dmd.visitor;
 
 version (IN_GCC) {}
@@ -122,7 +120,7 @@ extern (C++) abstract class Declaration : Dsymbol
         return "declaration";
     }
 
-    override final uinteger_t size(Loc loc)
+    override final ulong size(Loc loc)
     {
         assert(type);
         const sz = type.size();
@@ -358,21 +356,6 @@ extern (C++) final class TupleDeclaration : Declaration
         return tupletype;
     }
 
-    override Dsymbol toAlias2()
-    {
-        //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
-        for (size_t i = 0; i < objects.length; i++)
-        {
-            RootObject o = (*objects)[i];
-            if (Dsymbol s = isDsymbol(o))
-            {
-                s = s.toAlias2();
-                (*objects)[i] = s;
-            }
-        }
-        return this;
-    }
-
     override bool needThis()
     {
         //printf("TupleDeclaration::needThis(%s)\n", toChars());
@@ -601,117 +584,7 @@ extern (C++) final class AliasDeclaration : Declaration
     {
         if (type)
             return type;
-        return toAlias().getType();
-    }
-
-    override Dsymbol toAlias()
-    {
-        static if (0)
-        printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
-            loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse);
-        assert(this != aliassym);
-        //static int count; if (++count == 10) *(char*)0=0;
-
-        Dsymbol err()
-        {
-            // Avoid breaking "recursive alias" state during errors gagged
-            if (global.gag)
-                return this;
-            aliassym = new AliasDeclaration(loc, ident, Type.terror);
-            type = Type.terror;
-            return aliassym;
-        }
-        // Reading the AliasDeclaration
-        if (!this.ignoreRead)
-            this.wasRead = true;                 // can never assign to this AliasDeclaration again
-
-        if (inuse == 1 && type && _scope)
-        {
-            inuse = 2;
-            const olderrors = global.errors;
-            Dsymbol s = type.toDsymbol(_scope);
-            //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
-            if (global.errors != olderrors)
-                return err();
-            if (s)
-            {
-                s = s.toAlias();
-                if (global.errors != olderrors)
-                    return err();
-                aliassym = s;
-                inuse = 0;
-            }
-            else
-            {
-                Type t = type.typeSemantic(loc, _scope);
-                if (t.ty == Terror)
-                    return err();
-                if (global.errors != olderrors)
-                    return err();
-                //printf("t = %s\n", t.toChars());
-                inuse = 0;
-            }
-        }
-        if (inuse)
-        {
-            .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars);
-            return err();
-        }
-
-        if (semanticRun >= PASS.semanticdone)
-        {
-            // semantic is already done.
-
-            // Do not see aliassym !is null, because of lambda aliases.
-
-            // Do not see type.deco !is null, even so "alias T = const int;` needs
-            // semantic analysis to take the storage class `const` as type qualifier.
-        }
-        else
-        {
-            // stop AliasAssign tuple building
-            if (aliassym)
-            {
-                if (auto td = aliassym.isTupleDeclaration())
-                {
-                    if (td.building)
-                    {
-                        td.building = false;
-                        semanticRun = PASS.semanticdone;
-                        return td;
-                    }
-                }
-            }
-            if (_import && _import._scope)
-            {
-                /* If this is an internal alias for selective/renamed import,
-                 * load the module first.
-                 */
-                _import.dsymbolSemantic(null);
-            }
-            if (_scope)
-            {
-                aliasSemantic(this, _scope);
-            }
-        }
-
-        inuse = 1;
-        Dsymbol s = aliassym ? aliassym.toAlias() : this;
-        inuse = 0;
-        return s;
-    }
-
-    override Dsymbol toAlias2()
-    {
-        if (inuse)
-        {
-            .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars);
-            return this;
-        }
-        inuse = 1;
-        Dsymbol s = aliassym ? aliassym.toAlias2() : this;
-        inuse = 0;
-        return s;
+        return toAlias(this).getType();
     }
 
     override bool isOverloadable() const
@@ -958,7 +831,7 @@ extern (C++) class VarDeclaration : Declaration
         {
             isdataseg = 2; // The Variables does not go into the datasegment
 
-            if (!canTakeAddressOf())
+            if (!canTakeAddressOf() || (storage_class & STC.exptemp))
             {
                 return false;
             }
@@ -1011,7 +884,7 @@ extern (C++) class VarDeclaration : Declaration
         auto bitoffset  =   offset * 8;
         auto vbitoffset = v.offset * 8;
 
-        // Bitsize of types are overridden by any bit-field widths.
+        // Bitsize of types are overridden by any bitfield widths.
         ulong tbitsize;
         if (auto bf = isBitFieldDeclaration())
         {
@@ -1051,49 +924,6 @@ extern (C++) class VarDeclaration : Declaration
         return edtor && !(storage_class & STC.nodtor);
     }
 
-    /*******************************************
-     * If variable has a constant expression initializer, get it.
-     * Otherwise, return null.
-     */
-    extern (D) final Expression getConstInitializer(bool needFullType = true)
-    {
-        assert(type && _init);
-
-        // Ungag errors when not speculative
-        const oldgag = global.gag;
-        if (global.gag)
-        {
-            Dsymbol sym = isMember();
-            if (sym && !sym.isSpeculative())
-                global.gag = 0;
-        }
-
-        if (_scope)
-        {
-            inuse++;
-            _init = _init.initializerSemantic(_scope, type, INITinterpret);
-            import dmd.semantic2 : lowerStaticAAs;
-            lowerStaticAAs(this, _scope);
-            _scope = null;
-            inuse--;
-        }
-
-        Expression e = _init.initializerToExpression(needFullType ? type : null);
-        global.gag = oldgag;
-        return e;
-    }
-
-    override final Dsymbol toAlias()
-    {
-        //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
-        if ((!type || !type.deco) && _scope)
-            dsymbolSemantic(this, _scope);
-
-        assert(this != aliasTuple);
-        Dsymbol s = aliasTuple ? aliasTuple.toAlias() : this;
-        return s;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -1396,6 +1226,8 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration
 extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration
 {
     Type entry; // type of TypeInfo_AssociativeArray.Entry!(t.index, t.next)
+    Declaration xopEqual; // implementation of TypeInfo_AssociativeArray.equals
+    Declaration xtoHash;  // implementation of TypeInfo_AssociativeArray.getHash
 
     extern (D) this(Type tinfo)
     {
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index 74ea20cb56f8..7a160770e144 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -71,9 +71,10 @@ namespace dmd
     #define STCref                0x40000ULL    /// `ref`
     #define STCscope              0x80000ULL    /// `scope`
 
-    #define STCscopeinferred      0x200000ULL    /// `scope` has been inferred and should not be part of mangling, `scope` must also be set
-    #define STCreturn             0x400000ULL    /// 'return ref' or 'return scope' for function parameters
-    #define STCreturnScope        0x800000ULL    /// if `ref return scope` then resolve to `ref` and `return scope`
+    #define STCscopeinferred      0x100000ULL    /// `scope` has been inferred and should not be part of mangling, `scope` must also be set
+    #define STCreturn             0x200000ULL    /// 'return ref' or 'return scope' for function parameters
+    #define STCreturnScope        0x400000ULL    /// if `ref return scope` then resolve to `ref` and `return scope`
+    #define STCreturnRef          0x800000ULL,   /// if `return ref`
 
     #define STCreturninferred     0x1000000ULL    /// `return` has been inferred and should not be part of mangling, `return` must also be set
     #define STCimmutable          0x2000000ULL    /// `immutable`
@@ -183,7 +184,6 @@ public:
     TupleDeclaration *syntaxCopy(Dsymbol *) override;
     const char *kind() const override;
     Type *getType() override;
-    Dsymbol *toAlias2() override;
     bool needThis() override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -203,8 +203,6 @@ public:
     bool overloadInsert(Dsymbol *s) override;
     const char *kind() const override;
     Type *getType() override;
-    Dsymbol *toAlias() override;
-    Dsymbol *toAlias2() override;
     bool isOverloadable() const override;
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -222,7 +220,6 @@ public:
     bool equals(const RootObject * const o) const override;
     bool overloadInsert(Dsymbol *s) override;
 
-    Dsymbol *toAlias() override;
     Dsymbol *isUnique();
     bool isOverloadable() const override;
 
@@ -300,7 +297,6 @@ public:
     bool isOverlappedWith(VarDeclaration *v);
     bool canTakeAddressOf();
     bool needsScopeDtor();
-    Dsymbol *toAlias() override final;
     // Eliminate need for dynamic_cast
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -395,6 +391,8 @@ class TypeInfoAssociativeArrayDeclaration final : public TypeInfoDeclaration
 {
 public:
     Type* entry;
+    Declaration* xopEqual;
+    Declaration* xtoHash;
 
     static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo);
 
@@ -727,7 +725,6 @@ public:
     virtual bool addPreInvariant();
     virtual bool addPostInvariant();
     const char *kind() const override;
-    bool isUnique();
     bool needsClosure();
     bool hasNestedFrameRefs();
     ParameterList getParameterList();
diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d
index 1f5dca66b598..f37c83cbca7c 100644
--- a/gcc/d/dmd/delegatize.d
+++ b/gcc/d/dmd/delegatize.d
@@ -180,6 +180,11 @@ private void lambdaSetParent(Expression e, FuncDeclaration fd)
                     iz.accept(this);
             }
         }
+        override void visit(AssocArrayLiteralExp e)
+        {
+            if (e.lowering)
+                walkPostorder(e.lowering, this);
+        }
     }
 
     scope LambdaSetParent lsp = new LambdaSetParent(fd);
diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d
index e1a4ab6d6a3d..8b7d5e501235 100644
--- a/gcc/d/dmd/denum.d
+++ b/gcc/d/dmd/denum.d
@@ -18,7 +18,6 @@ import core.stdc.stdio;
 
 import dmd.astenums;
 import dmd.attrib;
-import dmd.gluelayer;
 import dmd.declaration;
 import dmd.dsymbol;
 import dmd.expression;
@@ -62,7 +61,7 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol
     import dmd.common.bitfields : generateBitFields;
     mixin(generateBitFields!(BitFields, ubyte));
 
-    Symbol* sinit;
+    void* sinit;
 
     extern (D) this(Loc loc, Identifier ident, Type memtype)
     {
diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d
index bbd74a4da270..66810cb35fdb 100644
--- a/gcc/d/dmd/dimport.d
+++ b/gcc/d/dmd/dimport.d
@@ -141,13 +141,6 @@ extern (C++) final class Import : Dsymbol
         scopesym.addAccessiblePackage(mod, visibility); // d
      }
 
-    override Dsymbol toAlias()
-    {
-        if (aliasId)
-            return mod;
-        return this;
-    }
-
     override bool overloadInsert(Dsymbol s)
     {
         /* Allow multiple imports with the same package base, but disallow
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
index 543c0e22e39f..eaf3d993b019 100644
--- a/gcc/d/dmd/dinterpret.d
+++ b/gcc/d/dmd/dinterpret.d
@@ -50,7 +50,7 @@ import dmd.rootobject;
 import dmd.root.utf;
 import dmd.statement;
 import dmd.tokens;
-import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size;
+import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size, merge, defaultInitLiteral;
 import dmd.utils : arrayCastBigEndian;
 import dmd.visitor;
 
@@ -569,7 +569,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
     istatex.caller = istate;
     istatex.fd = fd;
 
-    if (fd.hasDualContext())
+    if (fd.hasDualContext)
     {
         Expression arg0 = thisarg;
         if (arg0 && arg0.type.ty == Tstruct)
@@ -722,7 +722,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
 
     if (tf.isRef && e.op == EXP.variable && e.isVarExp().var == fd.vthis)
         e = thisarg;
-    if (tf.isRef && fd.hasDualContext() && e.op == EXP.index)
+    if (tf.isRef && fd.hasDualContext && e.op == EXP.index)
     {
         auto ie = e.isIndexExp();
         auto pe = ie.e1.isPtrExp();
@@ -1745,7 +1745,7 @@ public:
             if (istate && istate.fd.vthis)
             {
                 result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis);
-                if (istate.fd.hasDualContext())
+                if (istate.fd.hasDualContext)
                 {
                     result = ctfeEmplaceExp!PtrExp(e.loc, result);
                     result.type = Type.tvoidptr.sarrayOf(2);
@@ -1761,7 +1761,7 @@ public:
         result = ctfeGlobals.stack.getThis();
         if (result)
         {
-            if (istate && istate.fd.hasDualContext())
+            if (istate && istate.fd.hasDualContext)
             {
                 assert(result.op == EXP.address);
                 result = result.isAddrExp().e1;
@@ -2348,6 +2348,9 @@ public:
                 if (ExpInitializer ie = v._init.isExpInitializer())
                 {
                     result = interpretRegion(ie.exp, istate, goal);
+                    if (result !is null && v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone)
+                        if (!getValue(v))
+                            setValueWithoutChecking(v, result); // a temporary from extractSideEffects can be a ref
                     return;
                 }
                 else if (v._init.isVoidInitializer())
@@ -2362,7 +2365,12 @@ public:
                 {
                     result = v._init.initializerToExpression(v.type);
                     if (result !is null)
+                    {
+                        if (v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone)
+                            if (!getValue(v))
+                                setValueWithoutChecking(v, result); // a temporary from extractSideEffects can be a ref
                         return;
+                    }
                 }
                 error(e.loc, "declaration `%s` is not yet implemented in CTFE", e.toChars());
                 result = CTFEExp.cantexp;
@@ -2641,6 +2649,8 @@ public:
                    valuesx !is e.values);
             auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
             aae.type = e.type;
+            aae.lowering = e.lowering;
+            aae.loweringCtfe = e.loweringCtfe;
             aae.ownedByCtfe = OwnedBy.ctfe;
             result = aae;
         }
@@ -3406,131 +3416,10 @@ public:
         // ---------------------------------------
         //      Interpret left hand side
         // ---------------------------------------
-        AssocArrayLiteralExp existingAA = null;
-        Expression lastIndex = null;
         Expression oldval = null;
         if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
         {
-            // ---------------------------------------
-            //      Deal with AA index assignment
-            // ---------------------------------------
-            /* This needs special treatment if the AA doesn't exist yet.
-             * There are two special cases:
-             * (1) If the AA is itself an index of another AA, we may need to create
-             *     multiple nested AA literals before we can insert the new value.
-             * (2) If the ultimate AA is null, no insertion happens at all. Instead,
-             *     we create nested AA literals, and change it into a assignment.
-             */
-            IndexExp ie = e1.isIndexExp();
-            int depth = 0; // how many nested AA indices are there?
-            while (ie.e1.op == EXP.index && ie.e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
-            {
-                assert(ie.modifiable);
-                ie = ie.e1.isIndexExp();
-                ++depth;
-            }
-
-            // Get the AA value to be modified.
-            Expression aggregate = interpretRegion(ie.e1, istate);
-            if (exceptionOrCant(aggregate))
-                return;
-            if ((existingAA = aggregate.isAssocArrayLiteralExp()) !is null)
-            {
-                // Normal case, ultimate parent AA already exists
-                // We need to walk from the deepest index up, checking that an AA literal
-                // already exists on each level.
-                lastIndex = interpretRegion(e1.isIndexExp().e2, istate);
-                lastIndex = resolveSlice(lastIndex); // only happens with AA assignment
-                if (exceptionOrCant(lastIndex))
-                    return;
-
-                while (depth > 0)
-                {
-                    // Walk the syntax tree to find the indexExp at this depth
-                    IndexExp xe = e1.isIndexExp();
-                    foreach (d; 0 .. depth)
-                        xe = xe.e1.isIndexExp();
-
-                    Expression ekey = interpretRegion(xe.e2, istate);
-                    if (exceptionOrCant(ekey))
-                        return;
-                    UnionExp ekeyTmp = void;
-                    ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment
-
-                    // Look up this index in it up in the existing AA, to get the next level of AA.
-                    AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey);
-                    if (exceptionOrCant(newAA))
-                        return;
-                    if (!newAA)
-                    {
-                        // Doesn't exist yet, create an empty AA...
-                        auto keysx = new Expressions();
-                        auto valuesx = new Expressions();
-                        newAA = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
-                        newAA.type = xe.type;
-                        newAA.ownedByCtfe = OwnedBy.ctfe;
-                        //... and insert it into the existing AA.
-                        existingAA.keys.push(ekey);
-                        existingAA.values.push(newAA);
-                    }
-                    existingAA = newAA;
-                    --depth;
-                }
-
-                if (fp)
-                {
-                    oldval = findKeyInAA(e.loc, existingAA, lastIndex);
-                    if (!oldval)
-                        oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
-                }
-            }
-            else
-            {
-                /* The AA is currently null. 'aggregate' is actually a reference to
-                 * whatever contains it. It could be anything: var, dotvarexp, ...
-                 * We rewrite the assignment from:
-                 *     aa[i][j] op= newval;
-                 * into:
-                 *     aa = [i:[j:T.init]];
-                 *     aa[j] op= newval;
-                 */
-                oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy();
-
-                Expression newaae = oldval;
-                while (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
-                {
-                    Expression ekey = interpretRegion(e1.isIndexExp().e2, istate);
-                    if (exceptionOrCant(ekey))
-                        return;
-                    ekey = resolveSlice(ekey); // only happens with AA assignment
-
-                    auto keysx = new Expressions();
-                    auto valuesx = new Expressions();
-                    keysx.push(ekey);
-                    valuesx.push(newaae);
-
-                    auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx);
-                    aae.type = e1.isIndexExp().e1.type;
-                    aae.ownedByCtfe = OwnedBy.ctfe;
-                    if (!existingAA)
-                    {
-                        existingAA = aae;
-                        lastIndex = ekey;
-                    }
-                    newaae = aae;
-                    e1 = e1.isIndexExp().e1;
-                }
-
-                // We must set to aggregate with newaae
-                e1 = interpretRegion(e1, istate, CTFEGoal.LValue);
-                if (exceptionOrCant(e1))
-                    return;
-                e1 = assignToLvalue(e, e1, newaae);
-                if (exceptionOrCant(e1))
-                    return;
-            }
-            assert(existingAA && lastIndex);
-            e1 = null; // stomp
+            assert(false, "indexing AA should have been lowered in semantic analysis");
         }
         else if (e1.op == EXP.arrayLength)
         {
@@ -3570,10 +3459,7 @@ public:
 
             if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray)
             {
-                IndexExp ie = e1.isIndexExp();
-                assert(ie.e1.op == EXP.assocArrayLiteral);
-                existingAA = ie.e1.isAssocArrayLiteralExp();
-                lastIndex = ie.e2;
+                assert(false, "indexing AA should have been lowered in semantic analysis");
             }
         }
 
@@ -3664,23 +3550,6 @@ public:
             }
         }
 
-        if (existingAA)
-        {
-            if (existingAA.ownedByCtfe != OwnedBy.ctfe)
-            {
-                error(e.loc, "cannot modify read-only constant `%s`", existingAA.toChars());
-                result = CTFEExp.cantexp;
-                return;
-            }
-
-            //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
-            //    __LINE__, existingAA.toChars(), lastIndex.toChars(), oldval ? oldval.toChars() : NULL, newval.toChars());
-            assignAssocArrayElement(e.loc, existingAA, lastIndex, newval);
-
-            // Determine the return value
-            result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval);
-            return;
-        }
         if (e1.op == EXP.arrayLength)
         {
             /* Change the assignment from:
@@ -3725,7 +3594,7 @@ public:
             if (newval == pue.exp())
                 newval = pue.copy();
 
-            e1 = assignToLvalue(e, e1, newval);
+            e1 = assignToLvalue(e, e1, newval, istate);
             if (exceptionOrCant(e1))
                 return;
 
@@ -3799,7 +3668,7 @@ public:
 
         /* Assignment to a CTFE reference.
          */
-        if (Expression ex = assignToLvalue(e, e1, newval))
+        if (Expression ex = assignToLvalue(e, e1, newval, istate))
             result = ex;
 
         return;
@@ -3807,7 +3676,7 @@ public:
 
     /* Set all sibling fields which overlap with v to VoidExp.
      */
-    private void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v)
+    private static void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v)
     {
         if (!v.overlapped)
             return;
@@ -3821,7 +3690,7 @@ public:
         }
     }
 
-    private Expression assignToLvalue(BinExp e, Expression e1, Expression newval)
+    private static Expression assignToLvalue(BinExp e, Expression e1, Expression newval, InterState* istate)
     {
         //printf("assignToLvalue() e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars());
         VarDeclaration vd = null;
@@ -3915,7 +3784,8 @@ public:
             ArrayLiteralExp existingAE = aggregate.isArrayLiteralExp();
             if (existingAE.ownedByCtfe != OwnedBy.ctfe)
             {
-                error(e.loc, "cannot modify read-only constant `%s`", existingAE.toChars());
+                Expression literal = existingAE.aaLiteral ? existingAE.aaLiteral : existingAE;
+                error(e.loc, "cannot modify read-only constant `%s`", literal.toChars());
                 return CTFEExp.cantexp;
             }
 
@@ -4933,7 +4803,7 @@ public:
             }
         }
 
-        if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors())
+        if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors)
         {
             error(e.loc, "CTFE failed because of previous errors in `%s`", fd.toChars());
             result = CTFEExp.cantexp;
@@ -5239,16 +5109,6 @@ public:
             dinteger_t ofs;
             Expression agg = getAggregateFromPointer(e1, &ofs);
 
-            if (agg.op == EXP.null_)
-            {
-                error(e.loc, "cannot index through null pointer `%s`", e.e1.toChars());
-                return false;
-            }
-            if (agg.op == EXP.int64)
-            {
-                error(e.loc, "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 == EXP.symbolOffset)
             {
@@ -5267,9 +5127,15 @@ public:
             }
             else
             {
+                // agg is the value accessed, it is not dereferenced again, so offset 0 is always ok
                 if (ofs + indx != 0)
                 {
-                    error(e.loc, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx);
+                    if (agg.op == EXP.null_)
+                        error(e.loc, "cannot index through null pointer `%s`", e.e1.toChars());
+                    else if (agg.op == EXP.int64)
+                        error(e.loc, "cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars());
+                    else
+                        error(e.loc, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx);
                     return false;
                 }
             }
@@ -5394,47 +5260,7 @@ public:
 
         if (e.e1.type.toBasetype().ty == Taarray)
         {
-            Expression e1 = interpretRegion(e.e1, istate);
-            if (exceptionOrCant(e1))
-                return;
-            if (e1.op == EXP.null_)
-            {
-                if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable)
-                {
-                    assert(0); // does not reach here?
-                }
-                error(e.loc, "cannot index null array `%s`", e.e1.toChars());
-                result = CTFEExp.cantexp;
-                return;
-            }
-            Expression e2 = interpretRegion(e.e2, istate);
-            if (exceptionOrCant(e2))
-                return;
-
-            if (goal == CTFEGoal.LValue)
-            {
-                // Pointer or reference of a scalar type
-                if (e1 == e.e1 && e2 == e.e2)
-                    result = e;
-                else
-                {
-                    emplaceExp!(IndexExp)(pue, e.loc, e1, e2);
-                    result = pue.exp();
-                    result.type = e.type;
-                }
-                return;
-            }
-
-            assert(e1.op == EXP.assocArrayLiteral);
-            UnionExp e2tmp = void;
-            e2 = resolveSlice(e2, &e2tmp);
-            result = findKeyInAA(e.loc, e1.isAssocArrayLiteralExp(), e2);
-            if (!result)
-            {
-                error(e.loc, "key `%s` not found in associative array `%s`", e2.toChars(), e.e1.toChars());
-                result = CTFEExp.cantexp;
-            }
-            return;
+            assert(false, "indexing AA should have been lowered in semantic analysis");
         }
 
         Expression agg;
@@ -5662,50 +5488,6 @@ public:
         result.type = e.type;
     }
 
-    override void visit(InExp e)
-    {
-        debug (LOG)
-        {
-            printf("%s InExp::interpret() %s\n", e.loc.toChars(), e.toChars());
-        }
-        Expression e1 = interpretRegion(e.e1, istate);
-        if (exceptionOrCant(e1))
-            return;
-        Expression e2 = interpretRegion(e.e2, istate);
-        if (exceptionOrCant(e2))
-            return;
-        if (e2.op == EXP.null_)
-        {
-            emplaceExp!(NullExp)(pue, e.loc, e.type);
-            result = pue.exp();
-            return;
-        }
-        if (e2.op != EXP.assocArrayLiteral)
-        {
-            error(e.loc, "`%s` cannot be interpreted at compile time", e.toChars());
-            result = CTFEExp.cantexp;
-            return;
-        }
-
-        e1 = resolveSlice(e1);
-        result = findKeyInAA(e.loc, e2.isAssocArrayLiteralExp(), e1);
-        if (exceptionOrCant(result))
-            return;
-        if (!result)
-        {
-            emplaceExp!(NullExp)(pue, e.loc, e.type);
-            result = pue.exp();
-        }
-        else
-        {
-            // Create a CTFE pointer &aa[index]
-            result = ctfeEmplaceExp!IndexExp(e.loc, e2, e1);
-            result.type = e.type.nextOf();
-            emplaceExp!(AddrExp)(pue, e.loc, result, e.type);
-            result = pue.exp();
-        }
-    }
-
     override void visit(CatExp e)
     {
         debug (LOG)
@@ -6420,45 +6202,6 @@ public:
         }
     }
 
-    override void visit(RemoveExp e)
-    {
-        debug (LOG)
-        {
-            printf("%s RemoveExp::interpret() %s\n", e.loc.toChars(), e.toChars());
-        }
-        Expression agg = interpret(e.e1, istate);
-        if (exceptionOrCant(agg))
-            return;
-        Expression index = interpret(e.e2, istate);
-        if (exceptionOrCant(index))
-            return;
-        if (agg.op == EXP.null_)
-        {
-            result = CTFEExp.voidexp;
-            return;
-        }
-
-        AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp();
-        Expressions* keysx = aae.keys;
-        Expressions* valuesx = aae.values;
-        size_t removed = 0;
-        foreach (j, evalue; *valuesx)
-        {
-            Expression ekey = (*keysx)[j];
-            int eq = ctfeEqual(e.loc, EXP.equal, ekey, index);
-            if (eq)
-                ++removed;
-            else if (removed != 0)
-            {
-                (*keysx)[j - removed] = ekey;
-                (*valuesx)[j - removed] = evalue;
-            }
-        }
-        valuesx.length = valuesx.length - removed;
-        keysx.length = keysx.length - removed;
-        result = IntegerExp.createBool(removed != 0);
-    }
-
     override void visit(ClassReferenceExp e)
     {
         //printf("ClassReferenceExp::interpret() %s\n", e.value.toChars());
@@ -6703,8 +6446,9 @@ ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp
     }
     // Little sanity check to make sure it's really a Throwable
     ClassReferenceExp boss = oldest.thrown;
-    const next = 5;                         // index of Throwable.next
-    assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
+    const next = 5;                          // index of Throwable._nextInChainPtr
+    with ((*boss.value.elements)[next].type) // Throwable._nextInChainPtr
+        assert(ty == Tpointer || ty == Tclass);
     ClassReferenceExp collateral = newest.thrown;
     if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException())
     {
@@ -7150,6 +6894,53 @@ private Expression interpret_values(UnionExp* pue, InterState* istate, Expressio
     return pue.exp();
 }
 
+// signature is bool _d_aaDel(V[K] aa, K key)
+private Expression interpret_aaDel(UnionExp* pue, InterState* istate, Expression aa, Expression key)
+{
+    Expression agg = interpret(aa, istate);
+    if (exceptionOrCantInterpret(agg))
+        return agg;
+    Expression index = interpret(key, istate);
+    if (exceptionOrCantInterpret(index))
+        return index;
+    if (agg.op == EXP.null_)
+        return CTFEExp.voidexp; //???
+
+    AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp();
+    Expressions* keysx = aae.keys;
+    Expressions* valuesx = aae.values;
+    size_t removed = 0;
+    foreach (j, evalue; *valuesx)
+    {
+        Expression ekey = (*keysx)[j];
+        int eq = ctfeEqual(aa.loc, EXP.equal, ekey, index);
+        if (eq)
+            ++removed;
+        else if (removed != 0)
+        {
+            (*keysx)[j - removed] = ekey;
+            (*valuesx)[j - removed] = evalue;
+        }
+    }
+    valuesx.length = valuesx.length - removed;
+    keysx.length = keysx.length - removed;
+    return IntegerExp.createBool(removed != 0);
+}
+
+// signature is bool _d_aaDel(V[K] aa1, V[K] aa2)
+private Expression interpret_aaEqual(UnionExp* pue, InterState* istate, Expression aa1, Expression aa2)
+{
+    Expression e1 = interpret(aa1, istate);
+    if (exceptionOrCantInterpret(e1))
+        return e1;
+    Expression e2 = interpret(aa2, istate);
+    if (exceptionOrCantInterpret(e2))
+        return e2;
+
+    bool equal = ctfeEqual(aa1.loc, EXP.equal, e1, e2);
+    return IntegerExp.createBool(equal);
+}
+
 private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression earg)
 {
     debug (LOG)
@@ -7174,11 +6965,131 @@ private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression e
         if (Expression e = evaluatePostblit(istate, (*aae.values)[i]))
             return e;
     }
-    aae.type = earg.type.mutableOf(); // repaint type from const(int[int]) to const(int)[int]
+    // repaint type from const(int[int]) to int[int]
+    if (auto taa = earg.type.toBasetype().isTypeAArray())
+    {
+        auto aatype = new TypeAArray(taa.next.mutableOf(), taa.index);
+        aae.type = aatype.merge();
+    }
+    else
+        aae.type = earg.type.mutableOf();
     //printf("result is %s\n", aae.toChars());
     return aae;
 }
 
+// signature is bool V* _d_aaIn(V[K] aa, K key)
+private Expression interpret_aaIn(UnionExp* pue, InterState* istate, Expression aa, Expression key)
+{
+    debug (LOG)
+    {
+        printf("%s _d_aaIn::interpret() %s in %s\n", aa.loc.toChars(), key.toChars(), aa.toChars());
+    }
+    Expression eaa = interpretRegion(aa, istate);
+    if (exceptionOrCantInterpret(eaa))
+        return eaa;
+    Expression ekey = interpretRegion(key, istate);
+    if (exceptionOrCantInterpret(ekey))
+        return ekey;
+
+    if (eaa.op != EXP.null_)
+    {
+        auto aalit = eaa.isAssocArrayLiteralExp();
+        if (!aalit)
+        {
+            error(aa.loc, "`%s` cannot be interpreted at compile time", aa.toChars());
+            return CTFEExp.cantexp;
+        }
+
+        size_t idx;
+        auto result = findKeyInAA(aa.loc, aalit, ekey, &idx);
+        if (exceptionOrCantInterpret(result))
+            return result;
+        if (result)
+            return pointerToAAValue(pue, aa, aalit, idx);
+    }
+    emplaceExp!(NullExp)(pue, aa.loc, aa.type.nextOf().pointerTo());
+    return pue.exp();
+}
+
+// signature is V* _d_aaGetRvalueX(V[K] aa, K key)
+private Expression interpret_aaGetRvalueX(UnionExp* pue, InterState* istate, Expression aa, Expression key)
+{
+    Expression e1 = interpret(aa, istate);
+    if (exceptionOrCantInterpret(e1))
+        return e1;
+    Expression e2 = interpretRegion(key, istate);
+    if (exceptionOrCantInterpret(e2))
+        return e2;
+
+    auto aalit = e1.isAssocArrayLiteralExp();
+    if (!aalit)
+    {
+        error(aa.loc, "cannot index null array `%s`", aa.toChars());
+        return CTFEExp.cantexp;
+    }
+    size_t idx;
+    Expression result = findKeyInAA(aa.loc, aalit, e2, &idx);
+    if (!result)
+    {
+        error(aa.loc, "key `%s` not found in associative array `%s`", key.toChars(), aa.toChars());
+        return  CTFEExp.cantexp;
+    }
+
+    return pointerToAAValue(pue, aa, aalit, idx);
+}
+
+// signature is V* _d_aaGetY(ref V[K] aa, K key, out bool found)
+private Expression interpret_aaGetY(UnionExp* pue, InterState* istate, Expression aa, Expression key, Expression found)
+{
+    Expression eaa = interpretRegion(aa, istate, CTFEGoal.LValue);
+    if (exceptionOrCantInterpret(eaa))
+        return eaa;
+    Expression ekey = interpretRegion(key, istate);
+    if (exceptionOrCantInterpret(ekey))
+        return ekey;
+    Expression efound = interpretRegion(found, istate, CTFEGoal.LValue);
+    if (exceptionOrCantInterpret(efound))
+        return efound;
+
+    auto ie = ctfeEmplaceExp!IndexExp(aa.loc, aa, key); // any BinExp for location in assignToLvalue
+    Expression evalaa = interpretRegion(eaa, istate);
+    auto aalit = evalaa.isAssocArrayLiteralExp();
+    if (!aalit)
+    {
+        auto keysx = new Expressions();
+        auto valuesx = new Expressions();
+        aalit = ctfeEmplaceExp!AssocArrayLiteralExp(aa.loc, keysx, valuesx);
+        aalit.type = aa.type;
+        aalit.ownedByCtfe = OwnedBy.ctfe;
+        Interpreter.assignToLvalue(ie, eaa, aalit, istate);
+    }
+    size_t idx;
+    auto result = findKeyInAA(aa.loc, aalit, ekey, &idx);
+    if (found)
+        Interpreter.assignToLvalue(ie, efound, IntegerExp.createBool(result !is null), istate);
+    if (!result)
+    {
+        aalit.keys.push(ekey);
+        result = copyLiteral(aa.type.nextOf().defaultInitLiteral(aa.loc)).copy();
+        idx = aalit.values.length;
+        aalit.values.push(result);
+    }
+    return pointerToAAValue(pue, aa, aalit, idx);
+}
+
+private Expression pointerToAAValue(UnionExp* pue, Expression aa, AssocArrayLiteralExp aalit, size_t idx)
+{
+    auto arr = ctfeEmplaceExp!(ArrayLiteralExp)(aa.loc, aa.type.nextOf().arrayOf(), aalit.values);
+    arr.ownedByCtfe = aalit.ownedByCtfe;
+    arr.aaLiteral = aalit;
+    auto len = ctfeEmplaceExp!(IntegerExp)(aa.loc, idx, Type.tsize_t);
+    auto idxexp = ctfeEmplaceExp!(IndexExp)(aa.loc, arr, len);
+    idxexp.type = arr.type.nextOf();
+    emplaceExp!(AddrExp)(pue, aa.loc, idxexp);
+    pue.exp().type = idxexp.type.pointerTo();
+    return pue.exp();
+}
+
 // signature is int delegate(ref Value) OR int delegate(ref Key, ref Value)
 private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expression aa, Expression deleg)
 {
@@ -7223,7 +7134,10 @@ private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expressi
         if (wantRefValue)
         {
             Type t = evalue.type;
-            evalue = ctfeEmplaceExp!IndexExp(deleg.loc, ae, ekey);
+            auto arr = ctfeEmplaceExp!(ArrayLiteralExp)(aa.loc, t.arrayOf(), ae.values);
+            arr.ownedByCtfe = ae.ownedByCtfe;
+            auto idx = ctfeEmplaceExp!(IntegerExp)(aa.loc, i, Type.tsize_t);
+            evalue = ctfeEmplaceExp!(IndexExp)(aa.loc, arr, idx);
             evalue.type = t;
         }
         args[numParams - 1] = evalue;
@@ -7481,7 +7395,7 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc,
     }
     if (!pthis)
     {
-        if (nargs == 1 || nargs == 3)
+        if (nargs >= 1 && nargs <= 3)
         {
             Expression firstarg = (*arguments)[0];
             if (auto firstAAtype = firstarg.type.toBasetype().isTypeAArray())
@@ -7489,7 +7403,7 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc,
                 const id = fd.ident;
                 if (nargs == 1)
                 {
-                    if (id == Id.aaLen)
+                    if (id == Id._d_aaLen)
                         return interpret_length(pue, istate, firstarg);
 
                     if (fd.toParent2().ident == Id.object)
@@ -7504,12 +7418,25 @@ private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc,
                             return interpret_dup(pue, istate, firstarg);
                     }
                 }
+                else if (nargs == 2)
+                {
+                    if (id == Id._d_aaGetRvalueX)
+                        return interpret_aaGetRvalueX(pue, istate, firstarg, (*arguments)[1]);
+                    if (id == Id._d_aaIn)
+                        return interpret_aaIn(pue, istate, firstarg, (*arguments)[1]);
+                    if (id == Id._d_aaDel)
+                        return interpret_aaDel(pue, istate, firstarg, (*arguments)[1]);
+                    if (id == Id._d_aaEqual)
+                        return interpret_aaEqual(pue, istate, firstarg, (*arguments)[1]);
+                    if (id == Id._d_aaApply)
+                        return interpret_aaApply(pue, istate, firstarg, (*arguments)[1]);
+                    if (id == Id._d_aaApply2)
+                        return interpret_aaApply(pue, istate, firstarg, (*arguments)[1]);
+                }
                 else // (nargs == 3)
                 {
-                    if (id == Id._aaApply)
-                        return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
-                    if (id == Id._aaApply2)
-                        return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]);
+                    if (id == Id._d_aaGetY)
+                        return interpret_aaGetY(pue, istate, firstarg, (*arguments)[1], (*arguments)[2]);
                 }
             }
         }
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index 33539d4bf0b8..08400433d099 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -30,15 +30,13 @@ import dmd.dmacro;
 import dmd.doc;
 import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, importAll, load;
+import dmd.dsymbolsem : dsymbolSemantic;
 import dmd.errors;
 import dmd.errorsink;
 import dmd.expression;
-import dmd.expressionsem;
 import dmd.file_manager;
 import dmd.func;
 import dmd.globals;
-import dmd.gluelayer;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
@@ -412,6 +410,8 @@ extern (C++) final class Module : Package
     SearchOptFlags searchCacheFlags;       // cached flags
     bool insearch;
 
+    bool isExplicitlyOutOfBinary; // Is this module known to be out of binary, and must be DllImport'd?
+
     /**
      * A root module is one that will be compiled all the way to
      * object code.  This field holds the root module that caused
@@ -456,6 +456,7 @@ extern (C++) final class Module : Package
         else if (!FileName.equalsExt(srcfilename, mars_ext) &&
                  !FileName.equalsExt(srcfilename, hdr_ext) &&
                  !FileName.equalsExt(srcfilename, c_ext) &&
+                 !FileName.equalsExt(srcfilename, h_ext) &&
                  !FileName.equalsExt(srcfilename, i_ext) &&
                  !FileName.equalsExt(srcfilename, dd_ext))
         {
@@ -474,7 +475,7 @@ extern (C++) final class Module : Package
         if (doHdrGen)
             hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext);
 
-        this.edition = Edition.legacy;
+        this.edition = Edition.min;
     }
 
     extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
@@ -492,12 +493,6 @@ extern (C++) final class Module : Package
         return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
     }
 
-    static const(char)* find(const(char)* filename)
-    {
-        ImportPathInfo pathThatFoundThis; // is this needed? In fact is this function needed still???
-        return find(filename.toDString, pathThatFoundThis).ptr;
-    }
-
     extern (D) static const(char)[] find(const(char)[] filename, out ImportPathInfo pathThatFoundThis)
     {
         ptrdiff_t whichPathFoundThis;
@@ -532,6 +527,7 @@ extern (C++) final class Module : Package
         auto m = new Module(loc, filename, ident, 0, 0);
 
         // TODO: apply import path information (pathInfo) on to module
+        m.isExplicitlyOutOfBinary = importPathThatFindUs.isOutOfBinary;
 
         if (!m.read(loc))
             return null;
@@ -700,7 +696,8 @@ extern (C++) final class Module : Package
 
         const(ubyte)[] srctext;
         if (global.preprocess &&
-            FileName.equalsExt(srcfile.toString(), c_ext) &&
+            (FileName.equalsExt(srcfile.toString(), c_ext) ||
+             FileName.equalsExt(srcfile.toString(), h_ext)) &&
             FileName.exists(srcfile.toString()))
         {
             /* Apply C preprocessor to the .c file, returning the contents
@@ -783,10 +780,12 @@ extern (C++) final class Module : Package
         DsymbolTable dst;
         Package ppack = null;
 
-        /* If it has the extension ".c", it is a "C" file.
+        /* If it has the extension ".c" or ".h", it is a "C" file.
          * If it has the extension ".i", it is a preprocessed "C" file.
          */
-        if (FileName.equalsExt(arg, c_ext) || FileName.equalsExt(arg, i_ext))
+        if (FileName.equalsExt(arg, c_ext) ||
+            FileName.equalsExt(arg, h_ext) ||
+            FileName.equalsExt(arg, i_ext))
         {
             filetype = FileType.c;
 
@@ -953,32 +952,6 @@ extern (C++) final class Module : Package
         return needmoduleinfo || global.params.cov;
     }
 
-    /*******************************************
-     * Print deprecation warning if we're deprecated, when
-     * this module is imported from scope sc.
-     *
-     * Params:
-     *  sc = the scope into which we are imported
-     *  loc = the location of the import statement
-     */
-    void checkImportDeprecation(Loc loc, Scope* sc)
-    {
-        if (md && md.isdeprecated && !sc.isDeprecated)
-        {
-            Expression msg = md.msg;
-            if (StringExp se = msg ? msg.toStringExp() : null)
-            {
-                const slice = se.peekString();
-                if (slice.length)
-                {
-                    deprecation(loc, "%s `%s` is deprecated - %.*s", kind, toPrettyChars, cast(int)slice.length, slice.ptr);
-                    return;
-                }
-            }
-            deprecation(loc, "%s `%s` is deprecated", kind, toPrettyChars);
-        }
-    }
-
     override bool isPackageAccessible(Package p, Visibility visibility, SearchOptFlags flags = SearchOpt.all)
     {
         if (insearch) // don't follow import cycles
@@ -1170,16 +1143,10 @@ extern (C++) final class Module : Package
     }
 
     // Back end
-    int doppelganger; // sub-module
-    Symbol* cov; // private uint[] __coverage;
+    void* cov; // private uint[] __coverage;
     uint[] covb; // bit array of valid code line numbers
-    Symbol* sictor; // module order independent constructor
-    Symbol* sctor; // module constructor
-    Symbol* sdtor; // module destructor
-    Symbol* ssharedctor; // module shared constructor
-    Symbol* sshareddtor; // module shared destructor
-    Symbol* stest; // module unit test
-    Symbol* sfilename; // symbol for filename
+    void* sfilename; // symbol for filename
+    bool hasCDtor; // this module has a (shared) module constructor or destructor and we are codegenning it
 
     uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
 
@@ -1213,75 +1180,6 @@ extern (C++) final class Module : Package
             _escapetable = new Escape();
         return _escapetable;
     }
-
-    /****************************
-     * A Singleton that loads core.stdc.config
-     * Returns:
-     *  Module of core.stdc.config, null if couldn't find it
-     */
-    extern (D) static Module loadCoreStdcConfig()
-    {
-        __gshared Module core_stdc_config;
-        auto pkgids = new Identifier[2];
-        pkgids[0] = Id.core;
-        pkgids[1] = Id.stdc;
-        return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config);
-    }
-
-    /****************************
-     * A Singleton that loads core.atomic
-     * Returns:
-     *  Module of core.atomic, null if couldn't find it
-     */
-    extern (D) static Module loadCoreAtomic()
-    {
-        __gshared Module core_atomic;
-        auto pkgids = new Identifier[1];
-        pkgids[0] = Id.core;
-        return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic);
-    }
-
-    /****************************
-     * A Singleton that loads std.math
-     * Returns:
-     *  Module of std.math, null if couldn't find it
-     */
-    extern (D) static Module loadStdMath()
-    {
-        __gshared Module std_math;
-        auto pkgids = new Identifier[1];
-        pkgids[0] = Id.std;
-        return loadModuleFromLibrary(std_math, pkgids, Id.math);
-    }
-
-    /**********************************
-     * Load a Module from the library.
-     * Params:
-     *  mod = cached return value of this call
-     *  pkgids = package identifiers
-     *  modid = module id
-     * Returns:
-     *  Module loaded, null if cannot load it
-     */
-    extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid)
-    {
-        if (mod)
-            return mod;
-
-        auto imp = new Import(Loc.initial, pkgids[], modid, null, true);
-        // Module.load will call fatal() if there's no module available.
-        // Gag the error here, pushing the error handling to the caller.
-        const errors = global.startGagging();
-        imp.load(null);
-        if (imp.mod)
-        {
-            imp.mod.importAll(null);
-            imp.mod.dsymbolSemantic(null);
-        }
-        global.endGagging(errors);
-        mod = imp.mod;
-        return mod;
-    }
 }
 
 /***********************************************************
@@ -1386,7 +1284,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod)
                 dbuf.writeUTF8(u);
             }
             else
-                dbuf.writeByte(u);
+                dbuf.writeByte(cast(ubyte)u);
         }
         dbuf.writeByte(0); //add null terminator
         return dbuf.extractSlice();
@@ -1455,7 +1353,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod)
                 dbuf.writeUTF8(u);
             }
             else
-                dbuf.writeByte(u);
+                dbuf.writeByte(cast(ubyte)u);
         }
         dbuf.writeByte(0); //add a terminating null byte
         return dbuf.extractSlice();
@@ -1533,9 +1431,8 @@ FuncDeclaration findGetMembers(ScopeDsymbol dsym)
         {
             Scope sc;
             sc.eSink = global.errorSink;
-            auto parameters = new Parameters();
             Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
-            parameters.push(p);
+            auto parameters = new Parameters(p);
             Type tret = null;
             TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
             tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d
index c70e7d381b56..879ffc11268e 100644
--- a/gcc/d/dmd/dscope.d
+++ b/gcc/d/dmd/dscope.d
@@ -55,7 +55,8 @@ enum Contract : ubyte
     ensure = 3, // out contract
 }
 
-private extern (D) struct BitFields
+/// Bitfield for settable/copyable flags, see `copyFlagsFrom`, `resetAllFlags`
+private extern (D) struct FlagBitFields
 {
     bool ctor;              /// constructor type
     bool noAccessCheck;     /// don't do access checks
@@ -74,6 +75,14 @@ private extern (D) struct BitFields
     bool ctfeBlock;         /// inside a `if (__ctfe)` block
 }
 
+private extern (D) struct NonFlagBitFields
+{
+    ubyte intypeof;                 /// in typeof(exp)
+    bool nofree;                    /// true if shouldn't free it
+    bool inLoop;                    /// true if inside a loop (where constructor calls aren't allowed)
+    bool inDefaultArg;              /// true if inside a default argument (where __FILE__, etc are evaluated at the call site)
+    bool explicitVisibility;        /// set if in an explicit visibility attribute
+}
 /// State of -preview switches
 ///
 /// By making them part of a Scope, we reduce reliance on dmd.globals,
@@ -140,10 +149,6 @@ extern (C++) struct Scope
     ForeachStatement fes;           /// if nested function for ForeachStatement, this is it
     Scope* callsc;                  /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
     Dsymbol inunion;                /// != null if processing members of a union
-    bool nofree;                    /// true if shouldn't free it
-    bool inLoop;                    /// true if inside a loop (where constructor calls aren't allowed)
-    bool inDefaultArg;              /// true if inside a default argument (where __FILE__, etc are evaluated at the call site)
-    int intypeof;                   /// in typeof(exp)
     VarDeclaration lastVar;         /// Previous symbol used to prevent goto-skips-init
     ErrorSink eSink;                /// sink for error messages
 
@@ -174,15 +179,15 @@ extern (C++) struct Scope
 
     /// visibility for class members
     Visibility visibility = Visibility(Visibility.Kind.public_);
-    int explicitVisibility;         /// set if in an explicit visibility attribute
 
     STC stc;                        /// storage class
 
     DeprecatedDeclaration depdecl;  /// customized deprecation message
 
     import dmd.common.bitfields : generateBitFields;
-    mixin(generateBitFields!(BitFields, ushort));
-
+    mixin(generateBitFields!(FlagBitFields, ushort));
+    private ushort bitFields2;
+    mixin(generateBitFields!(NonFlagBitFields, ushort, "bitFields2"));
     Previews previews;
 
     // user defined attributes
diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d
index 5aef3585cafe..b565f159aec8 100644
--- a/gcc/d/dmd/dstruct.d
+++ b/gcc/d/dmd/dstruct.d
@@ -23,13 +23,9 @@ import dmd.declaration;
 import dmd.dmodule;
 import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem : search, setFieldOffset;
 import dmd.dtemplate;
-import dmd.errors;
 import dmd.expression;
 import dmd.func;
-import dmd.funcsem;
-import dmd.globals;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
@@ -37,35 +33,9 @@ import dmd.mtype;
 import dmd.opover;
 import dmd.target;
 import dmd.tokens;
-import dmd.typesem : isZeroInit, merge, size, hasPointers;
 import dmd.typinf;
 import dmd.visitor;
 
-/***************************************
- * Search sd for a member function of the form:
- *   `extern (D) string toString();`
- * Params:
- *   sd = struct declaration to search
- * Returns:
- *   FuncDeclaration of `toString()` if found, `null` if not
- */
-FuncDeclaration search_toString(StructDeclaration sd)
-{
-    Dsymbol s = search_function(sd, Id.tostring);
-    FuncDeclaration fd = s ? s.isFuncDeclaration() : null;
-    if (fd)
-    {
-        __gshared TypeFunction tftostring;
-        if (!tftostring)
-        {
-            tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
-            tftostring = tftostring.merge().toTypeFunction();
-        }
-        fd = fd.overloadExactMatch(tftostring);
-    }
-    return fd;
-}
-
 enum StructFlags : int
 {
     none        = 0x0,
@@ -151,100 +121,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration
         return "struct";
     }
 
-    /// Compute cached type properties for `TypeStruct`
-    extern(D) final void determineTypeProperties()
-    {
-        if (computedTypeProperties)
-            return;
-        foreach (vd; fields)
-        {
-            import dmd.dsymbolsem : hasPointers;
-            if (vd.storage_class & STC.ref_ || vd.hasPointers())
-            {
-                hasPointerField = true;
-                hasUnsafeBitpatterns = true;
-            }
-
-            if (vd._init && vd._init.isVoidInitializer() && vd.hasPointers())
-                hasVoidInitPointers = true;
-
-            if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns())
-                hasUnsafeBitpatterns = true;
-
-            if (!vd._init && vd.type.hasVoidInitPointers())
-                hasVoidInitPointers = true;
-
-            if (vd.type.hasInvariant())
-                hasFieldWithInvariant = true;
-        }
-        computedTypeProperties = true;
-    }
-
-    /***************************************
-     * Determine if struct is POD (Plain Old Data).
-     *
-     * POD is defined as:
-     *      $(OL
-     *      $(LI not nested)
-     *      $(LI no postblits, destructors, or assignment operators)
-     *      $(LI no `ref` fields or fields that are themselves non-POD)
-     *      )
-     * The idea being these are compatible with C structs.
-     *
-     * Returns:
-     *     true if struct is POD
-     */
-    final bool isPOD()
-    {
-        // If we've already determined whether this struct is POD.
-        if (ispod != ThreeState.none)
-            return (ispod == ThreeState.yes);
-
-        import dmd.clone;
-
-        bool hasCpCtorLocal;
-        bool hasMoveCtorLocal;
-        bool needCopyCtor;
-        bool needMoveCtor;
-        needCopyOrMoveCtor(this, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
-
-        if (enclosing                      || // is nested
-            search(this, loc, Id.postblit) || // has postblit
-            search(this, loc, Id.dtor)     || // has destructor
-            /* This is commented out because otherwise buildkite vibe.d:
-               `canCAS!Task` fails to compile
-             */
-            //hasMoveCtorLocal               || // has move constructor
-            hasCpCtorLocal)                   // has copy constructor
-        {
-            ispod = ThreeState.no;
-            return false;
-        }
-
-        // Recursively check all fields are POD.
-        for (size_t i = 0; i < fields.length; i++)
-        {
-            VarDeclaration v = fields[i];
-            if (v.storage_class & STC.ref_)
-            {
-                ispod = ThreeState.no;
-                return false;
-            }
-
-            if (auto ts = v.type.baseElemOf().isTypeStruct())
-            {
-                if (!ts.sym.isPOD())
-                {
-                    ispod = ThreeState.no;
-                    return false;
-                }
-            }
-        }
-
-        ispod = ThreeState.yes;
-        return true;
-    }
-
     /***************************************
      * Determine if struct has copy construction (copy constructor or postblit)
      * Returns:
@@ -269,54 +145,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration
     {
         return index < numArgTypes() ? (*argTypes.arguments)[index].type : null;
     }
-
-
-    /***************************************
-     * Verifies whether the struct declaration has a
-     * constructor that is not a copy constructor.
-     * Optionally, it can check whether the struct
-     * declaration has a regular constructor, that
-     * is not disabled.
-     *
-     * Params:
-     *      ignoreDisabled = true to ignore disabled constructors
-     * Returns:
-     *      true, if the struct has a regular (optionally,
-     *      not disabled) constructor, false otherwise.
-     */
-    final bool hasRegularCtor(bool ignoreDisabled = false)
-    {
-        if (!ctor)
-            return false;
-
-        bool result;
-        overloadApply(ctor, (Dsymbol s)
-        {
-            if (auto td = s.isTemplateDeclaration())
-            {
-                if (ignoreDisabled && td.onemember)
-                {
-                    if (auto ctorDecl = td.onemember.isCtorDeclaration())
-                    {
-                        if (ctorDecl.storage_class & STC.disable)
-                            return 0;
-                    }
-                }
-                result = true;
-                return 1;
-            }
-            if (auto ctorDecl = s.isCtorDeclaration())
-            {
-                if (!ctorDecl.isCpCtor && (!ignoreDisabled || !(ctorDecl.storage_class & STC.disable)))
-                {
-                    result = true;
-                    return 1;
-                }
-            }
-            return 0;
-        });
-        return result;
-    }
 }
 
 
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index e65068c7622b..2059978ef8a5 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -22,7 +22,6 @@ import dmd.arraytypes;
 import dmd.attrib;
 import dmd.astenums;
 import dmd.ast_node;
-import dmd.gluelayer;
 import dmd.dclass;
 import dmd.declaration;
 import dmd.denum;
@@ -31,11 +30,12 @@ import dmd.dmodule;
 import dmd.dversion;
 import dmd.dscope;
 import dmd.dstruct;
+import dmd.dsymbolsem : toAlias;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
+import dmd.expressionsem : getDsymbol;
 import dmd.func;
-import dmd.globals;
 import dmd.id;
 import dmd.identifier;
 import dmd.init;
@@ -106,22 +106,6 @@ void foreachDsymbol(Dsymbols* symbols, scope void delegate(Dsymbol) dg)
     }
 }
 
-
-struct Ungag
-{
-    uint oldgag;
-
-    extern (D) this(uint old) nothrow @safe
-    {
-        this.oldgag = old;
-    }
-
-    extern (C++) ~this() nothrow
-    {
-        global.gag = oldgag;
-    }
-}
-
 struct Visibility
 {
     ///
@@ -334,7 +318,7 @@ extern (C++) class Dsymbol : ASTNode
 {
     Identifier ident;
     Dsymbol parent;
-    Symbol* csym;           // symbol for code generator
+    void* csym;             // symbol for code generator
     Scope* _scope;          // !=null means context to use for semantic()
     private DsymbolAttributes* atts; /// attached attribute declarations
     const Loc loc;          // where defined
@@ -637,7 +621,7 @@ extern (C++) class Dsymbol : ASTNode
         static bool has2This(Dsymbol s)
         {
             if (auto f = s.isFuncDeclaration())
-                return f.hasDualContext();
+                return f.hasDualContext;
             if (auto ad = s.isAggregateDeclaration())
                 return ad.vthis2 !is null;
             return false;
@@ -688,14 +672,6 @@ extern (C++) class Dsymbol : ASTNode
         return parent.isSpeculative();
     }
 
-    final Ungag ungagSpeculative() const
-    {
-        const oldgag = global.gag;
-        if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration())
-            global.gag = 0;
-        return Ungag(oldgag);
-    }
-
     // kludge for template.isSymbol()
     override final DYNCAST dyncast() const
     {
@@ -757,24 +733,6 @@ extern (C++) class Dsymbol : ASTNode
         return "symbol";
     }
 
-    /*********************************
-     * If this symbol is really an alias for another,
-     * return that other.
-     * If needed, semantic() is invoked due to resolve forward reference.
-     */
-    Dsymbol toAlias()
-    {
-        return this;
-    }
-
-    /*********************************
-     * Resolve recursive tuple expansion in eponymous template.
-     */
-    Dsymbol toAlias2()
-    {
-        return toAlias();
-    }
-
     bool overloadInsert(Dsymbol s)
     {
         //printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
@@ -785,7 +743,7 @@ extern (C++) class Dsymbol : ASTNode
      * Returns:
      *  SIZE_INVALID when the size cannot be determined
      */
-    uinteger_t size(Loc loc)
+    ulong size(Loc loc)
     {
         .error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars());
         return SIZE_INVALID;
diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h
index 77e7dabcb6cf..259817e6038d 100644
--- a/gcc/d/dmd/dsymbol.h
+++ b/gcc/d/dmd/dsymbol.h
@@ -29,6 +29,7 @@ class AliasDeclaration;
 class AggregateDeclaration;
 class EnumDeclaration;
 class ClassDeclaration;
+class StructDeclaration;
 class InterfaceDeclaration;
 class StructDeclaration;
 class UnionDeclaration;
@@ -101,10 +102,13 @@ namespace dmd
     void dsymbolSemantic(Dsymbol *dsym, Scope *sc);
     void semantic2(Dsymbol *dsym, Scope *sc);
     void semantic3(Dsymbol *dsym, Scope* sc);
+    Dsymbol *toAlias(Dsymbol* s);
+    Dsymbol *toAlias2(Dsymbol* s);
     // in iasm.d
     void asmSemantic(CAsmDeclaration *ad, Scope *sc);
     // in iasmgcc.d
     void gccAsmSemantic(CAsmDeclaration *ad, Scope *sc);
+    bool isPOD(StructDeclaration *sd);
 }
 
 struct Visibility
@@ -223,8 +227,6 @@ public:
     virtual Identifier *getIdent();
     virtual const char *toPrettyChars(bool QualifyTypes = false);
     virtual const char *kind() const;
-    virtual Dsymbol *toAlias();                 // resolve real symbol
-    virtual Dsymbol *toAlias2();
     virtual bool overloadInsert(Dsymbol *s);
     virtual uinteger_t size(Loc loc);
     virtual bool isforwardRef();
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 43fb0b72e4ba..2c4996573d92 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -154,6 +154,199 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
     return ad;
 }
 
+
+/*********************************
+ * Resolve recursive tuple expansion in eponymous template.
+ */
+Dsymbol toAlias2(Dsymbol s)
+{
+    if (auto ad = s.isAliasDeclaration())
+    {
+        if (ad.inuse)
+        {
+            .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+            return ad;
+        }
+        ad.inuse = 1;
+        Dsymbol ds = ad.aliassym ? ad.aliassym.toAlias2() : ad;
+        ad.inuse = 0;
+        return ds;
+    }
+    if (auto td = s.isTupleDeclaration())
+    {
+        //printf("TupleDeclaration::toAlias2() '%s' objects = %s\n", toChars(), objects.toChars());
+        for (size_t i = 0; i < td.objects.length; i++)
+        {
+            RootObject o = (*td.objects)[i];
+            if (Dsymbol ds = isDsymbol(o))
+            {
+                ds = ds.toAlias2();
+                (*td.objects)[i] = ds;
+            }
+        }
+        return td;
+    }
+    return toAlias(s);
+}
+
+private Dsymbol toAliasImpl(AliasDeclaration ad)
+{
+    static if (0)
+    printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n",
+        ad.loc.toChars(), ad.toChars(), ad, ad.aliassym ? ad.aliassym.toChars() : "", ad.aliassym ? ad.aliassym.kind() : "", ad.inuse);
+    assert(ad != ad.aliassym);
+    //static int count; if (++count == 10) *(char*)0=0;
+
+    Dsymbol err()
+    {
+        // Avoid breaking "recursive alias" state during errors gagged
+        if (global.gag)
+            return ad;
+        ad.aliassym = new AliasDeclaration(ad.loc, ad.ident, Type.terror);
+        ad.type = Type.terror;
+        return ad.aliassym;
+    }
+    // Reading the AliasDeclaration
+    if (!ad.ignoreRead)
+        ad.wasRead = true;                 // can never assign to this AliasDeclaration again
+
+    if (ad.inuse == 1 && ad.type && ad._scope)
+    {
+        ad.inuse = 2;
+        const olderrors = global.errors;
+        Dsymbol s = ad.type.toDsymbol(ad._scope);
+        //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this);
+        if (global.errors != olderrors)
+            return err();
+        if (s)
+        {
+            s = s.toAlias();
+            if (global.errors != olderrors)
+                return err();
+            ad.aliassym = s;
+            ad.inuse = 0;
+        }
+        else
+        {
+            Type t = ad.type.typeSemantic(ad.loc, ad._scope);
+            if (t.ty == Terror)
+                return err();
+            if (global.errors != olderrors)
+                return err();
+            //printf("t = %s\n", t.toChars());
+            ad.inuse = 0;
+        }
+    }
+    if (ad.inuse)
+    {
+        .error(ad.loc, "%s `%s` recursive alias declaration", ad.kind, ad.toPrettyChars);
+        return err();
+    }
+
+    if (ad.semanticRun >= PASS.semanticdone)
+    {
+        // semantic is already done.
+
+        // Do not see aliassym !is null, because of lambda aliases.
+
+        // Do not see type.deco !is null, even so "alias T = const int;` needs
+        // semantic analysis to take the storage class `const` as type qualifier.
+    }
+    else
+    {
+        // stop AliasAssign tuple building
+        if (ad.aliassym)
+        {
+            if (auto td = ad.aliassym.isTupleDeclaration())
+            {
+                if (td.building)
+                {
+                    td.building = false;
+                    ad.semanticRun = PASS.semanticdone;
+                    return td;
+                }
+            }
+        }
+        if (ad._import && ad._import._scope)
+        {
+            /* If this is an internal alias for selective/renamed import,
+             * load the module first.
+             */
+            ad._import.dsymbolSemantic(null);
+        }
+        if (ad._scope)
+        {
+            aliasSemantic(ad, ad._scope);
+        }
+    }
+
+    ad.inuse = 1;
+    Dsymbol s = ad.aliassym ? ad.aliassym.toAlias() : ad;
+    ad.inuse = 0;
+    return s;
+}
+
+/*********************************
+ * If this symbol is really an alias for another,
+ * return that other.
+ * If needed, semantic() is invoked due to resolve forward reference.
+ */
+Dsymbol toAlias(Dsymbol s)
+{
+    if (auto ad = s.isAliasDeclaration())
+    {
+        return ad.toAliasImpl();
+    }
+    if (auto imp = s.isImport())
+    {
+        if (imp.aliasId)
+            return imp.mod;
+        return imp;
+    }
+    if (auto vd = s.isVarDeclaration())
+    {
+        //printf("VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", toChars(), this, aliassym);
+        if ((!vd.type || !vd.type.deco) && vd._scope)
+            dsymbolSemantic(vd, vd._scope);
+
+        assert(vd != vd.aliasTuple);
+        return vd.aliasTuple ? vd.aliasTuple.toAlias() : vd;
+    }
+    // resolve real symbol
+    if (auto ti = s.isTemplateInstance())
+    {
+        static if (LOG)
+        {
+            printf("TemplateInstance.toAlias()\n");
+        }
+        if (!ti.inst)
+        {
+            // Maybe we can resolve it
+            if (ti._scope)
+            {
+                dsymbolSemantic(ti, ti._scope);
+            }
+            if (!ti.inst)
+            {
+                .error(ti.loc, "%s `%s` cannot resolve forward reference", ti.kind, ti.toPrettyChars);
+                ti.errors = true;
+                return ti;
+            }
+        }
+
+        if (ti.inst != ti)
+            return ti.inst.toAlias();
+
+        if (ti.aliasdecl)
+        {
+            return ti.aliasdecl.toAlias();
+        }
+
+        return ti.inst;
+    }
+    return s;
+}
+
 const(char)* getMessage(DeprecatedDeclaration dd)
 {
     if (auto sc = dd._scope)
@@ -244,6 +437,71 @@ package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
     return true;
 }
 
+/***************************************
+ * Determine if struct is POD (Plain Old Data).
+ *
+ * POD is defined as:
+ *      $(OL
+ *      $(LI not nested)
+ *      $(LI no postblits, destructors, or assignment operators)
+ *      $(LI no `ref` fields or fields that are themselves non-POD)
+ *      )
+ * The idea being these are compatible with C structs.
+ *
+ * Returns:
+ *     true if struct is POD
+ */
+bool isPOD(StructDeclaration sd)
+{
+    // If we've already determined whether this struct is POD.
+    if (sd.ispod != ThreeState.none)
+        return (sd.ispod == ThreeState.yes);
+
+    import dmd.clone;
+
+    bool hasCpCtorLocal;
+    bool hasMoveCtorLocal;
+    bool needCopyCtor;
+    bool needMoveCtor;
+    needCopyOrMoveCtor(sd, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor);
+
+    if (sd.enclosing                    || // is nested
+        search(sd, sd.loc, Id.postblit) || // has postblit
+        search(sd, sd.loc, Id.dtor)     || // has destructor
+        /* This is commented out because otherwise buildkite vibe.d:
+           `canCAS!Task` fails to compile
+         */
+        //hasMoveCtorLocal               || // has move constructor
+        hasCpCtorLocal)                   // has copy constructor
+    {
+        sd.ispod = ThreeState.no;
+        return false;
+    }
+
+    // Recursively check all fields are POD.
+    for (size_t i = 0; i < sd.fields.length; i++)
+    {
+        VarDeclaration v = sd.fields[i];
+        if (v.storage_class & STC.ref_)
+        {
+            sd.ispod = ThreeState.no;
+            return false;
+        }
+
+        if (auto ts = v.type.baseElemOf().isTypeStruct())
+        {
+            if (!ts.sym.isPOD())
+            {
+                sd.ispod = ThreeState.no;
+                return false;
+            }
+        }
+    }
+
+    sd.ispod = ThreeState.yes;
+    return true;
+}
+
 /*
 If sd has a copy constructor and ctor is an rvalue constructor,
 issue an error.
@@ -351,7 +609,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find
                  * so resolve it ahead.
                  */
                 {
-                    int save = sc.intypeof;
+                    ubyte save = sc.intypeof;
                     sc.intypeof = 1; // bypass "need this" error check
                     e = resolveProperties(sc, e);
                     sc.intypeof = save;
@@ -431,6 +689,55 @@ void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx)
     Module.addDeferredSemantic(s);
 }
 
+struct Ungag
+{
+    uint oldgag;
+
+    extern (D) this(uint old) nothrow @safe
+    {
+        this.oldgag = old;
+    }
+
+    extern (C++) ~this() nothrow
+    {
+        global.gag = oldgag;
+    }
+}
+
+Ungag ungagSpeculative(const Dsymbol s)
+{
+    const oldgag = global.gag;
+    if (global.gag && !s.isSpeculative() && !s.toParent2().isFuncDeclaration())
+        global.gag = 0;
+    return Ungag(oldgag);
+}
+
+/*******************************************
+ * Print deprecation warning if we're deprecated, when
+ * this module is imported from scope sc.
+ *
+ * Params:
+ *  m = the module
+ *  sc = the scope into which we are imported
+ *  loc = the location of the import statement
+ */
+private void checkImportDeprecation(Module m, Loc loc, Scope* sc)
+{
+    if (!m.md || !m.md.isdeprecated || sc.isDeprecated)
+        return;
+
+    Expression msg = m.md.msg;
+    if (StringExp se = msg ? msg.toStringExp() : null)
+    {
+        const slice = se.peekString();
+        if (slice.length)
+        {
+            deprecation(m.loc, "%s `%s` is deprecated - %.*s", m.kind, m.toPrettyChars, cast(int)slice.length, slice.ptr);
+            return;
+        }
+    }
+    deprecation(m.loc, "%s `%s` is deprecated", m.kind, m.toPrettyChars);
+}
 
 private extern(C++) final class DsymbolSemanticVisitor : Visitor
 {
@@ -779,8 +1086,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 ie = ie.expressionSemantic(sc);
             if (nelems > 0 && ie)
             {
-                auto iexps = new Expressions();
-                iexps.push(ie);
+                auto iexps = new Expressions(ie);
                 auto exps = new Expressions();
                 for (size_t pos = 0; pos < iexps.length; pos++)
                 {
@@ -1397,6 +1703,19 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
                                 exp = ErrorExp.get();
                             }
                         }
+                        else if (exp.isBitField())
+                        {
+                            if (dsym.storage_class & STC.autoref)
+                            {
+                                dsym.storage_class &= ~STC.ref_;
+                                constructInit(false);
+                            }
+                            else
+                            {
+                                .error(dsym.loc, "bitfield `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars());
+                                exp = ErrorExp.get();
+                            }
+                        }
                         else
                         {
                             constructInit(false);
@@ -1592,7 +1911,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
         if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration())
         {
-            .error(dsym.loc, "%s `%s` - bit-field must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
+            .error(dsym.loc, "%s `%s` - bitfield must be member of struct, union, or class", dsym.kind, dsym.toPrettyChars);
         }
 
         sc = sc.startCTFE();
@@ -1602,18 +1921,18 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         if (!dsym.type.isIntegral())
         {
             // C11 6.7.2.1-5
-            error(width.loc, "bit-field type `%s` is not an integer type", dsym.type.toChars());
+            error(width.loc, "bitfield type `%s` is not an integer type", dsym.type.toChars());
             dsym.errors = true;
         }
         if (!width.isIntegerExp())
         {
-            error(width.loc, "bit-field width `%s` is not an integer constant", dsym.width.toChars());
+            error(width.loc, "bitfield width `%s` is not an integer constant", dsym.width.toChars());
             dsym.errors = true;
         }
         const uwidth = width.toInteger(); // uwidth is unsigned
         if (uwidth == 0 && !dsym.isAnonymous())
         {
-            error(width.loc, "bit-field `%s` has zero width", dsym.toChars());
+            error(width.loc, "bitfield `%s` has zero width", dsym.toChars());
             dsym.errors = true;
         }
         const sz = dsym.type.size();
@@ -1622,7 +1941,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         const max_width = sz * 8;
         if (uwidth > max_width)
         {
-            error(width.loc, "width `%lld` of bit-field `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
+            error(width.loc, "width `%lld` of bitfield `%s` does not fit in type `%s`", cast(long)uwidth, dsym.toChars(), dsym.type.toChars());
             dsym.errors = true;
         }
         dsym.fieldWidth = cast(uint)uwidth;
@@ -2104,7 +2423,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
         /* Run semantic on each argument, place results in tiargs[],
          * then find best match template with tiargs
          */
-        if (!tm.findTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
+        if (!tm.findMixinTempDecl(sc) || !tm.semanticTiargs(sc) || !tm.findBestMatch(sc, ArgumentList()))
         {
             if (tm.semanticRun == PASS.initial) // forward reference had occurred
             {
@@ -2686,9 +3005,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
             v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none);
 
-            auto sa = new Statements();
             Statement s = new ExpStatement(Loc.initial, v);
-            sa.push(s);
+            auto sa = new Statements(s);
 
             Expression e;
             if (isShared)
@@ -3329,7 +3647,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
             }
 
             //
-            // For two structures or unions, corresponding bit-fields shall have the same widths.
+            // For two structures or unions, corresponding bitfields shall have the same widths.
             //
             BitFieldDeclaration bfa = a_field.isBitFieldDeclaration();
             BitFieldDeclaration bfb = b_field.isBitFieldDeclaration();
@@ -4377,6 +4695,23 @@ private void attribAddMember(AttribDeclaration atb, Scope* sc, ScopeDsymbol sds)
     }
 }
 
+/****************************************************
+ * Declare parameters of template instance, initialize them with the
+ * template instance arguments.
+ */
+private void declareParameters(TemplateInstance ti, Scope* sc)
+{
+    TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+    assert(tempdecl);
+
+    //printf("TemplateInstance.declareParameters()\n");
+    foreach (i, o; ti.tdtypes) // initializer for tp
+    {
+        TemplateParameter tp = (*tempdecl.parameters)[i];
+        //printf("\ttdtypes[%d] = %p\n", i, o);
+        declareParameter(tempdecl, sc, tp, o);
+    }
+}
 private extern(C++) class AddMemberVisitor : Visitor
 {
     alias visit = Visitor.visit;
@@ -4764,6 +5099,173 @@ private bool isDRuntimeHook(Identifier id)
         id == Id._d_arrayappendcTX;
 }
 
+/*****************************************
+ * Append `ti` to the specific module `ti.members[]`
+ */
+private Dsymbols* appendToModuleMember(TemplateInstance ti)
+{
+    Module mi = ti.minst; // instantiated . inserted module
+
+    //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
+    //    toPrettyChars(),
+    //    enclosing ? enclosing.toPrettyChars() : null,
+    //    mi ? mi.toPrettyChars() : null);
+    if (global.params.allInst || !mi || mi.isRoot())
+    {
+        /* If the instantiated module is speculative or root, insert to the
+         * member of a root module. Then:
+         *  - semantic3 pass will get called on the instance members.
+         *  - codegen pass will get a selection chance to do/skip it (needsCodegen()).
+         */
+        static Dsymbol getStrictEnclosing(TemplateInstance ti)
+        {
+            do
+            {
+                if (ti.enclosing)
+                    return ti.enclosing;
+                ti = ti.tempdecl.isInstantiated();
+            } while (ti);
+            return null;
+        }
+
+        Dsymbol enc = getStrictEnclosing(ti);
+        // insert target is made stable by using the module
+        // where tempdecl is declared.
+        mi = (enc ? enc : ti.tempdecl).getModule();
+        if (!mi.isRoot())
+        {
+            if (mi.importedFrom)
+            {
+                mi = mi.importedFrom;
+                assert(mi.isRoot());
+            }
+            else
+            {
+                // This can happen when using the frontend as a library.
+                // Append it to the non-root module.
+            }
+        }
+    }
+    else
+    {
+        /* If the instantiated module is non-root, insert to the member of the
+         * non-root module. Then:
+         *  - semantic3 pass won't be called on the instance.
+         *  - codegen pass won't reach to the instance.
+         * Unless it is re-appended to a root module later (with changed minst).
+         */
+    }
+    //printf("\t-. mi = %s\n", mi.toPrettyChars());
+
+    if (ti.memberOf) // already appended to some module
+    {
+        assert(mi.isRoot(), "can only re-append to a root module");
+        if (ti.memberOf.isRoot())
+            return null; // no need to move to another root module
+    }
+
+    Dsymbols* a = mi.members;
+    a.push(ti);
+    ti.memberOf = mi;
+    if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
+        Module.addDeferredSemantic2(ti);
+    if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
+        Module.addDeferredSemantic3(ti);
+    return a;
+}
+
+private void expandMembers(TemplateInstance ti,Scope* sc2)
+{
+    ti.members.foreachDsymbol( (s) { s.setScope (sc2); } );
+
+    ti.members.foreachDsymbol( (s) { s.importAll(sc2); } );
+
+    if (!ti.aliasdecl)
+    {
+        /* static if's are crucial to evaluating aliasdecl correctly. But
+         * evaluating the if/else bodies may require aliasdecl.
+         * So, evaluate the condition for static if's, but not their if/else bodies.
+         * Then try to set aliasdecl.
+         * Later do the if/else bodies.
+         * https://issues.dlang.org/show_bug.cgi?id=23598
+         * It might be better to do this by attaching a lambda to the StaticIfDeclaration
+         * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
+         */
+        bool done;
+        void staticIfDg(Dsymbol s)
+        {
+            if (done || ti.aliasdecl)
+                return;
+            //printf("\t staticIfDg on '%s %s' in '%s'\n",  s.kind(), s.toChars(), this.toChars());
+            if (!s.isStaticIfDeclaration())
+            {
+                //s.dsymbolSemantic(sc2);
+                done = true;
+                return;
+            }
+            auto sid = s.isStaticIfDeclaration();
+            sid.include(sc2);
+            if (ti.members.length)
+            {
+                Dsymbol sa;
+                if (oneMembers(ti.members, sa, ti.tempdecl.ident) && sa)
+                    ti.aliasdecl = sa;
+            }
+            done = true;
+        }
+
+        ti.members.foreachDsymbol(&staticIfDg);
+    }
+
+    void symbolDg(Dsymbol s)
+    {
+        //printf("\t semantic on '%s' %p kind %s in '%s'\n",  s.toChars(), s, s.kind(), this.toChars());
+        //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
+        //if (enclosing)
+        //    s.parent = sc.parent;
+        //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+        s.dsymbolSemantic(sc2);
+        //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+        Module.runDeferredSemantic();
+    }
+
+    ti.members.foreachDsymbol(&symbolDg);
+}
+
+private void tryExpandMembers(TemplateInstance ti, Scope* sc2)
+{
+    __gshared int nest;
+    // extracted to a function to allow windows SEH to work without destructors in the same function
+    //printf("%d\n", nest);
+    if (++nest > global.recursionLimit)
+    {
+        global.gag = 0; // ensure error message gets printed
+        .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+        fatal();
+    }
+
+    ti.expandMembers(sc2);
+
+    nest--;
+}
+
+private void trySemantic3(TemplateInstance ti, Scope* sc2)
+{
+    // extracted to a function to allow windows SEH to work without destructors in the same function
+    __gshared int nest;
+    //printf("%d\n", nest);
+    if (++nest > global.recursionLimit)
+    {
+        global.gag = 0; // ensure error message gets printed
+        .error(ti.loc, "%s `%s` recursive expansion exceeded allowed nesting limit", ti.kind, ti.toPrettyChars);
+        fatal();
+    }
+
+    semantic3(ti, sc2);
+
+    --nest;
+}
+
 void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList)
 {
     //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc);
@@ -5080,8 +5582,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList
         if (STC stc = ModToStc(t.mod))
         {
             //printf("t = %s, stc = x%llx\n", t.toChars(), stc);
-            auto s = new Dsymbols();
-            s.push(new StorageClassDeclaration(stc, tempinst.members));
+            auto s = new Dsymbols(new StorageClassDeclaration(stc, tempinst.members));
             tempinst.members = s;
         }
         break;
@@ -5677,6 +6178,196 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc)
     normalRet();
 }
 
+/**********************************************
+ * Find template declaration corresponding to template instance.
+ *
+ * Returns:
+ *      false if finding fails.
+ * Note:
+ *      This function is reentrant against error occurrence. If returns false,
+ *      any members of this object won't be modified, and repetition call will
+ *      reproduce same error.
+ */
+bool findTempDecl(TemplateInstance ti, Scope* sc, WithScopeSymbol* pwithsym)
+{
+    if (pwithsym)
+        *pwithsym = null;
+
+    if (ti.havetempdecl)
+        return true;
+
+    //printf("TemplateInstance.findTempDecl() %s\n", toChars());
+    if (!ti.tempdecl)
+    {
+        /* Given:
+         *    foo!( ... )
+         * figure out which TemplateDeclaration foo refers to.
+         */
+        Identifier id = ti.name;
+        Dsymbol scopesym;
+        Dsymbol s = sc.search(ti.loc, id, scopesym);
+        if (!s)
+        {
+            s = sc.search_correct(id);
+            if (s)
+                .error(ti.loc, "%s `%s` template `%s` is not defined, did you mean %s?", ti.kind, ti.toPrettyChars(), id.toChars(), s.toChars());
+            else
+                .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars(), id.toChars());
+            return false;
+        }
+        static if (LOG)
+        {
+            printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
+            if (s.parent)
+                printf("s.parent = '%s'\n", s.parent.toChars());
+        }
+        if (pwithsym)
+            *pwithsym = scopesym.isWithScopeSymbol();
+
+        /* We might have found an alias within a template when
+         * we really want the template.
+         */
+        TemplateInstance ti2;
+        if (s.parent && (ti2 = s.parent.isTemplateInstance()) !is null)
+        {
+            if (ti2.tempdecl && ti2.tempdecl.ident == id)
+            {
+                /* This is so that one can refer to the enclosing
+                 * template, even if it has the same name as a member
+                 * of the template, if it has a !(arguments)
+                 */
+                TemplateDeclaration td = ti2.tempdecl.isTemplateDeclaration();
+                assert(td);
+                if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+                    td = td.overroot; // then get the start
+                s = td;
+            }
+        }
+
+        // The template might originate from a selective import which implies that
+        // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
+        // This is the last place where we see the deprecated alias because it is
+        // stripped below, so check if the selective import was deprecated.
+        // See https://issues.dlang.org/show_bug.cgi?id=20840.
+        if (s.isAliasDeclaration())
+            s.checkDeprecated(ti.loc, sc);
+
+        if (!ti.updateTempDecl(sc, s))
+        {
+            return false;
+        }
+    }
+    assert(ti.tempdecl);
+
+    // Look for forward references
+    auto tovers = ti.tempdecl.isOverloadSet();
+    foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+    {
+        Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl;
+        int r = overloadApply(dstart, (Dsymbol s)
+        {
+            auto td = s.isTemplateDeclaration();
+            if (!td)
+                return 0;
+
+            if (td.semanticRun == PASS.initial)
+            {
+                if (td._scope)
+                {
+                    // Try to fix forward reference. Ungag errors while doing so.
+                    auto ungag = td.ungagSpeculative();
+                    td.dsymbolSemantic(td._scope);
+                }
+                if (td.semanticRun == PASS.initial)
+                {
+                    .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`",
+                           ti.kind, ti.toPrettyChars(), ti.toChars(), td.toChars());
+                    return 1;
+                }
+            }
+            return 0;
+        });
+        if (r)
+            return false;
+    }
+    return true;
+}
+
+private bool findMixinTempDecl(TemplateMixin tm, Scope* sc)
+{
+    // Follow qualifications to find the TemplateDeclaration
+    if (!tm.tempdecl)
+    {
+        Expression e;
+        Type t;
+        Dsymbol s;
+        tm.tqual.resolve(tm.loc, sc, e, t, s);
+        if (!s)
+        {
+            .error(tm.loc, "%s `%s` is not defined", tm.kind, tm.toPrettyChars);
+            return false;
+        }
+        s = s.toAlias();
+        tm.tempdecl = s.isTemplateDeclaration();
+        OverloadSet os = s.isOverloadSet();
+
+        /* If an OverloadSet, look for a unique member that is a template declaration
+         */
+        if (os)
+        {
+            Dsymbol ds = null;
+            foreach (i, sym; os.a)
+            {
+                Dsymbol s2 = sym.isTemplateDeclaration();
+                if (s2)
+                {
+                    if (ds)
+                    {
+                        tm.tempdecl = os;
+                        break;
+                    }
+                    ds = s2;
+                }
+            }
+        }
+        if (!tm.tempdecl)
+        {
+            .error(tm.loc, "%s `%s` - `%s` is a %s, not a template", tm.kind,
+                   tm.toPrettyChars, s.toChars(), s.kind());
+            return false;
+        }
+    }
+    assert(tm.tempdecl);
+
+    // Look for forward references
+    auto tovers = tm.tempdecl.isOverloadSet();
+    foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
+    {
+        Dsymbol dstart = tovers ? tovers.a[oi] : tm.tempdecl;
+        int r = overloadApply(dstart, (Dsymbol s)
+        {
+            auto td = s.isTemplateDeclaration();
+            if (!td)
+                return 0;
+
+            if (td.semanticRun == PASS.initial)
+            {
+                if (td._scope)
+                    td.dsymbolSemantic(td._scope);
+                else
+                {
+                    tm.semanticRun = PASS.initial;
+                    return 1;
+                }
+            }
+            return 0;
+        });
+        if (r)
+            return false;
+    }
+    return true;
+}
+
 /********************
  * Perform semantic on AliasAssignment.
  * Has a lot of similarities to aliasSemantic(). Perhaps they should share code.
@@ -5939,7 +6630,7 @@ private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst
     if (td)
         aliassym.aliassym = td;
     aliassym.semanticRun = PASS.semanticdone;
-    if (!TemplateInstance.semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td))
+    if (!TemplateInstance_semanticTiargs(tempinst.loc, sc, tempinst.tiargs, 0, td))
     {
         tempinst.errors = true;
         return null;
@@ -5993,6 +6684,39 @@ private TupleDeclaration aliasAssignInPlace(Scope* sc, TemplateInstance tempinst
     return td;
 }
 
+/*********************************
+ * Iterate this dsymbol or members of this scoped dsymbol, then
+ * call `fp` with the found symbol and `params`.
+ * Params:
+ *  symbol = the dsymbol or parent of members to call fp on
+ *  fp = function pointer to process the iterated symbol.
+ *       If it returns nonzero, the iteration will be aborted.
+ *  ctx = context parameter passed to fp.
+ * Returns:
+ *  nonzero if the iteration is aborted by the return value of fp,
+ *  or 0 if it's completed.
+ */
+int apply(Dsymbol symbol, int function(Dsymbol, void*) fp, void* ctx)
+{
+    if (auto nd = symbol.isNspace())
+    {
+        return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
+    }
+    if (auto ad = symbol.isAttribDeclaration())
+    {
+        return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
+    }
+    if (auto tm = symbol.isTemplateMixin())
+    {
+        if (tm._scope) // if fwd reference
+            dsymbolSemantic(tm, null); // try to resolve it
+
+        return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, ctx); } );
+    }
+
+    return fp(symbol, ctx);
+}
+
 /***************************************
  * Check if a template instance is a trivial AliasSeq but without other overloads.
  * We can only be 100% sure of being AliasSeq after running semanticTiargs()
@@ -6109,12 +6833,81 @@ bool determineFields(AggregateDeclaration ad)
     return true;
 }
 
+/****************************
+ * A Singleton that loads core.stdc.config
+ * Returns:
+ *  Module of core.stdc.config, null if couldn't find it
+ */
+Module loadCoreStdcConfig()
+{
+    __gshared Module core_stdc_config;
+    auto pkgids = new Identifier[2];
+    pkgids[0] = Id.core;
+    pkgids[1] = Id.stdc;
+    return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config);
+}
+
+/****************************
+ * A Singleton that loads core.atomic
+ * Returns:
+ *  Module of core.atomic, null if couldn't find it
+ */
+private Module loadCoreAtomic()
+{
+    __gshared Module core_atomic;
+    auto pkgids = new Identifier[1];
+    pkgids[0] = Id.core;
+    return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic);
+}
+
+/****************************
+ * A Singleton that loads std.math
+ * Returns:
+ *  Module of std.math, null if couldn't find it
+ */
+Module loadStdMath()
+{
+    __gshared Module std_math;
+    auto pkgids = new Identifier[1];
+    pkgids[0] = Id.std;
+    return loadModuleFromLibrary(std_math, pkgids, Id.math);
+}
+
+/**********************************
+ * Load a Module from the library.
+ * Params:
+ *  mod = cached return value of this call
+ *  pkgids = package identifiers
+ *  modid = module id
+ * Returns:
+ *  Module loaded, null if cannot load it
+ */
+extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid)
+{
+    if (mod)
+        return mod;
+
+    auto imp = new Import(Loc.initial, pkgids[], modid, null, true);
+    // Module.load will call fatal() if there's no module available.
+    // Gag the error here, pushing the error handling to the caller.
+    const errors = global.startGagging();
+    imp.load(null);
+    if (imp.mod)
+    {
+        imp.mod.importAll(null);
+        imp.mod.dsymbolSemantic(null);
+    }
+    global.endGagging(errors);
+    mod = imp.mod;
+    return mod;
+}
+
 /// Do an atomic operation (currently tailored to [shared] static ctors|dtors) needs
 private CallExp doAtomicOp (string op, Identifier var, Expression arg)
 {
     assert(op == "-=" || op == "+=");
 
-    Module mod = Module.loadCoreAtomic();
+    Module mod = loadCoreAtomic();
     if (!mod)
         return null;    // core.atomic couldn't be loaded
 
@@ -6488,7 +7281,7 @@ private extern(C++) class SearchVisitor : Visitor
     {
         //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident.toChars(), flags);
         if (ident != Id.dollar)
-            return setResult(null);
+            return visit(cast(ScopeDsymbol)ass);
 
         VarDeclaration* pvar;
         Expression ce;
@@ -6608,10 +7401,9 @@ private extern(C++) class SearchVisitor : Visitor
                     {
                         assert(0);
                     }
-                    auto tiargs = new Objects();
                     Expression edim = new IntegerExp(Loc.initial, dim, Type.tsize_t);
                     edim = edim.expressionSemantic(ass._scope);
-                    tiargs.push(edim);
+                    auto tiargs = new Objects(edim);
                     e = new DotTemplateInstanceExp(loc, ce, td.ident, tiargs);
                 }
                 else
@@ -7406,7 +8198,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor
 
         const style = target.c.bitFieldStyle;
         if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang)
-            assert(0, "unsupported bit-field style");
+            assert(0, "unsupported bitfield style");
 
         const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS;
         const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd);
@@ -7476,7 +8268,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor
         }
         else if (!isMicrosoftStyle)
         {
-            // If the bit-field spans more units of alignment than its type
+            // If the bitfield spans more units of alignment than its type
             // and is at the alignment boundary, start a new field at the
             // next alignment boundary. This affects when offsetof reports
             // a higher number and bitoffsetof starts at zero again.
@@ -7622,14 +8414,56 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor
                 isunion);
 
             // Add to the anon fields the base offset of this anonymous aggregate
-            //printf("anon fields, anonoffset = %d\n", anonoffset);
-            foreach (const i; fieldstart .. ad.fields.length)
+            //printf("anon fields, anonoffset = %d\n", anond.anonoffset);
+            if (anond.anonoffset)
+                anond.decl.foreachDsymbol( (s) => s.adjustBaseOffset(anond.anonoffset) );
+        }
+    }
+}
+
+// Adds `offset` as the new base offset of all field members in `d`.
+private void adjustBaseOffset(Dsymbol d, uint offset)
+{
+    switch (d.dsym)
+    {
+        case DSYM.nspace:
+            auto ns = cast(Nspace)d;
+            ns.members.foreachDsymbol( s => s.adjustBaseOffset(offset) );
+            break;
+
+        case DSYM.templateMixin:
+            auto tm = cast(TemplateMixin)d;
+            tm.members.foreachDsymbol( s => s.adjustBaseOffset(offset) );
+            break;
+
+        case DSYM.anonDeclaration:
+            auto ad = cast(AnonDeclaration)d;
+            if (ad.decl)
+                ad.decl.foreachDsymbol( s => s.adjustBaseOffset(offset) );
+            break;
+
+        case DSYM.bitFieldDeclaration:
+            auto bfd = cast(BitFieldDeclaration)d;
+            bfd.offset += offset;
+            //printf("\t%s %d : %d\n", bfd.toChars(), bfd.offset, bfd.bitOffset);
+            break;
+
+        default:
+            if (auto vd = d.isVarDeclaration())
             {
-                VarDeclaration v = ad.fields[i];
-                //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset);
-                v.offset += anond.anonoffset;
+                if (vd.aliasTuple)
+                    vd.aliasTuple.foreachVar( s => s.adjustBaseOffset(offset) );
+                else if (vd.isField())
+                {
+                    vd.offset += offset;
+                    //printf("\t%s %d\n", vd.toChars(), vd.offset);
+                }
             }
-        }
+            else if (auto atd = d.isAttribDeclaration())
+            {
+                atd.include(null).foreachDsymbol( s => s.adjustBaseOffset(offset) );
+            }
+            break;
     }
 }
 
@@ -7989,8 +8823,6 @@ private Expression callScopeDtor(VarDeclaration vd, Scope* sc)
 
         const sz = vd.type.size();
         assert(sz != SIZE_INVALID);
-        if (!sz)
-            return null;
 
         if (vd.type.toBasetype().ty == Tstruct)
         {
@@ -8644,6 +9476,87 @@ bool _isZeroInit(Expression exp)
     }
 }
 
+/***************************************
+ * Calculate `ad.field[i].overlapped` and `overlapUnsafe`, and check that all of explicit
+ * field initializers have unique memory space on instance.
+ * Returns:
+ *      true if any errors happen.
+ */
+private bool checkOverlappedFields(AggregateDeclaration ad)
+{
+    //printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
+    assert(ad.sizeok == Sizeok.done);
+    size_t nfields = ad.fields.length;
+    if (ad.isNested())
+    {
+        auto cd = ad.isClassDeclaration();
+        if (!cd || !cd.baseClass || !cd.baseClass.isNested())
+            nfields--;
+        if (ad.vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
+            nfields--;
+    }
+    bool errors = false;
+
+    // Fill in missing any elements with default initializers
+    foreach (i; 0 .. nfields)
+    {
+        auto vd = ad.fields[i];
+        if (vd.errors)
+        {
+            errors = true;
+            continue;
+        }
+
+        const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
+
+        // Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
+        foreach (j; 0 .. nfields)
+        {
+            if (i == j)
+                continue;
+            auto v2 = ad.fields[j];
+            if (v2.errors)
+            {
+                errors = true;
+                continue;
+            }
+            if (!vd.isOverlappedWith(v2))
+                continue;
+
+            // vd and v2 are overlapping.
+            vd.overlapped = true;
+            v2.overlapped = true;
+
+            if (!MODimplicitConv(vd.type.mod, v2.type.mod))
+                v2.overlapUnsafe = true;
+            if (!MODimplicitConv(v2.type.mod, vd.type.mod))
+                vd.overlapUnsafe = true;
+
+            if (i > j)
+                continue;
+
+            if (!v2._init)
+                continue;
+
+            if (v2._init.isVoidInitializer())
+                continue;
+
+            if (vd._init && !vdIsVoidInit && v2._init)
+            {
+                .error(ad.loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+                errors = true;
+            }
+            else if (v2._init && i < j)
+            {
+                .error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
+                    v2.toChars(), dmd.hdrgen.toChars(v2._init), vd.toChars());
+                errors = true;
+            }
+        }
+    }
+    return errors;
+}
+
 private extern(C++) class FinalizeSizeVisitor : Visitor
 {
     alias visit = Visitor.visit;
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index 16e192da20ff..b2a61dda441b 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -53,13 +53,11 @@ import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dscope;
 import dmd.dsymbol;
-import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include, hasStaticCtorOrDtor, oneMembers;
+import dmd.dsymbolsem : aliasSemantic, oneMembers, toAlias;
 import dmd.errors;
 import dmd.errorsink;
 import dmd.expression;
-import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties, checkValue;
 import dmd.func;
-import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply;
 import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
@@ -74,16 +72,11 @@ import dmd.optimize;
 import dmd.root.array;
 import dmd.common.outbuffer;
 import dmd.rootobject;
-import dmd.semantic3 : semantic3;
-import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter;
+import dmd.templatesem : getExpression, TemplateInstance_semanticTiargs;
 import dmd.tokens;
-import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol,
-                     addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf,
-                     unqualify, aliasthisOf, castMod, substWildTo, addMod;
+import dmd.typesem : typeSemantic, isBaseOf, resolveNamedArgs;
 import dmd.visitor;
 
-import dmd.templateparamsem;
-
 //debug = FindExistingInstance; // print debug stats of findExistingInstance
 private enum LOG = false;
 
@@ -202,76 +195,6 @@ inout(Type) getType(inout RootObject o)
 
 }
 
-/***********************************
- * If oarg represents a Dsymbol, return that Dsymbol
- * Params:
- *      oarg = argument to check
- * Returns:
- *      Dsymbol if a symbol, null if not
- */
-Dsymbol getDsymbol(RootObject oarg)
-{
-    //printf("getDsymbol()\n");
-    //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg));
-    if (auto ea = isExpression(oarg))
-    {
-        // Try to convert Expression to symbol
-        if (auto ve = ea.isVarExp())
-            return ve.var;
-        if (auto fe = ea.isFuncExp())
-            return fe.td ? fe.td : fe.fd;
-        if (auto te = ea.isTemplateExp())
-            return te.td;
-        if (auto te = ea.isScopeExp())
-            return te.sds;
-        return null;
-    }
-    // Try to convert Type to symbol
-    if (auto ta = isType(oarg))
-        return ta.toDsymbol(null);
-    return isDsymbol(oarg); // if already a symbol
-}
-
-
-private Expression getValue(ref Dsymbol s)
-{
-    if (s)
-    {
-        if (VarDeclaration v = s.isVarDeclaration())
-        {
-            if (v.storage_class & STC.manifest)
-                return v.getConstInitializer();
-        }
-    }
-    return null;
-}
-
-/***********************
- * Try to get value from manifest constant
- */
-private Expression getValue(Expression e)
-{
-    if (!e)
-        return null;
-    if (auto ve = e.isVarExp())
-    {
-        if (auto v = ve.var.isVarDeclaration())
-        {
-            if (v.storage_class & STC.manifest)
-            {
-                e = v.getConstInitializer();
-            }
-        }
-    }
-    return e;
-}
-
-private Expression getExpression(RootObject o)
-{
-    auto s = isDsymbol(o);
-    return s ? .getValue(s) : .getValue(isExpression(o));
-}
-
 /******************************
  * See if two objects match
  * Params:
@@ -279,7 +202,7 @@ private Expression getExpression(RootObject o)
  *      o2 = second object
  * Returns: true if they match
  */
-private bool match(RootObject o1, RootObject o2)
+bool match(RootObject o1, RootObject o2)
 {
     enum log = false;
 
@@ -766,66 +689,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
         return visibility;
     }
 
-    /****************************
-     * Destructively get the error message from the last constraint evaluation
-     * Params:
-     *      tip = tip to show after printing all overloads
-     */
-    const(char)* getConstraintEvalError(ref const(char)* tip)
-    {
-        import dmd.staticcond;
-
-        // there will be a full tree view in verbose mode, and more compact list in the usual
-        const full = global.params.v.verbose;
-        uint count;
-        const msg = visualizeStaticCondition(constraint, lastConstraint, lastConstraintNegs[], full, count);
-        scope (exit)
-        {
-            lastConstraint = null;
-            lastConstraintTiargs = null;
-            lastConstraintNegs.setDim(0);
-        }
-        if (!msg)
-            return null;
-
-        OutBuffer buf;
-
-        assert(parameters && lastConstraintTiargs);
-        if (parameters.length > 0)
-        {
-            formatParamsWithTiargs(*parameters, *lastConstraintTiargs, isVariadic() !is null, buf);
-            buf.writenl();
-        }
-        if (!full)
-        {
-            // choosing singular/plural
-            const s = (count == 1) ?
-                "  must satisfy the following constraint:" :
-                "  must satisfy one of the following constraints:";
-            buf.writestring(s);
-            buf.writenl();
-            // the constraints
-            buf.writeByte('`');
-            buf.writestring(msg);
-            buf.writeByte('`');
-        }
-        else
-        {
-            buf.writestring("  whose parameters have the following constraints:");
-            buf.writenl();
-            const sep = "  `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`";
-            buf.writestring(sep);
-            buf.writenl();
-            // the constraints
-            buf.writeByte('`');
-            buf.writestring(msg);
-            buf.writeByte('`');
-            buf.writestring(sep);
-            tip = "not satisfied constraints are marked with `>`";
-        }
-        return buf.extractChars();
-    }
-
     debug (FindExistingInstance)
     {
         __gshared uint nFound, nNotFound, nAdded, nRemoved;
@@ -947,4068 +810,1165 @@ extern (C++) final class TypeDeduced : Type
         argexps.push(e);
         tparams.push(tparam);
     }
-
-    MATCH matchAll(Type tt)
-    {
-        MATCH match = MATCH.exact;
-        foreach (j, e; argexps)
-        {
-            assert(e);
-            if (e == emptyArrayElement)
-                continue;
-
-            Type t = tt.addMod(tparams[j].mod).substWildTo(MODFlags.const_);
-
-            MATCH m = e.implicitConvTo(t);
-            if (match > m)
-                match = m;
-            if (match == MATCH.nomatch)
-                break;
-        }
-        return match;
-    }
 }
 
-
 /* ======================== Type ============================================ */
 
-/****
- * Given an identifier, figure out which TemplateParameter it is.
- * Return IDX_NOTFOUND if not found.
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ *      t           = Tested type, if null, returns false.
+ *      tparams     = Template parameters.
+ *      iStart      = Start index of tparams to limit the tested parameters. If it's
+ *                    nonzero, tparams[0..iStart] will be excluded from the test target.
  */
-private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters)
+bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
 {
-    for (size_t i = 0; i < parameters.length; i++)
-    {
-        TemplateParameter tp = (*parameters)[i];
-        if (tp.ident.equals(id))
-            return i;
-    }
-    return IDX_NOTFOUND;
+    return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]);
 }
 
-size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ *      t           = Tested type, if null, returns false.
+ *      tparams     = Template parameters.
+ */
+bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
 {
-    if (TypeIdentifier tident = tparam.isTypeIdentifier())
+    bool visitVector(TypeVector t)
     {
-        //printf("\ttident = '%s'\n", tident.toChars());
-        return templateIdentifierLookup(tident.ident, parameters);
+        return t.basetype.reliesOnTemplateParameters(tparams);
     }
-    return IDX_NOTFOUND;
-}
-
-private auto X(T, U)(T m, U n)
-{
-    return (m << 4) | n;
-}
-
-ubyte deduceWildHelper(Type t, Type* at, Type tparam)
-{
-    if ((tparam.mod & MODFlags.wild) == 0)
-        return 0;
 
-    *at = null;
+    bool visitAArray(TypeAArray t)
+    {
+        return t.next.reliesOnTemplateParameters(tparams) ||
+               t.index.reliesOnTemplateParameters(tparams);
+    }
 
-    switch (X(tparam.mod, t.mod))
+    bool visitFunction(TypeFunction t)
     {
-    case X(MODFlags.wild, 0):
-    case X(MODFlags.wild, MODFlags.const_):
-    case X(MODFlags.wild, MODFlags.shared_):
-    case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.wild, MODFlags.immutable_):
-    case X(MODFlags.wildconst, 0):
-    case X(MODFlags.wildconst, MODFlags.const_):
-    case X(MODFlags.wildconst, MODFlags.shared_):
-    case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.wildconst, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
-        {
-            ubyte wm = (t.mod & ~MODFlags.shared_);
-            if (wm == 0)
-                wm = MODFlags.mutable;
-            ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_);
-            *at = t.unqualify(m);
-            return wm;
-        }
-    case X(MODFlags.wild, MODFlags.wild):
-    case X(MODFlags.wild, MODFlags.wildconst):
-    case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.wildconst, MODFlags.wild):
-    case X(MODFlags.wildconst, MODFlags.wildconst):
-    case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+        foreach (i, fparam; t.parameterList)
         {
-            *at = t.unqualify(tparam.mod & t.mod);
-            return MODFlags.wild;
+            if (fparam.type.reliesOnTemplateParameters(tparams))
+                return true;
         }
-    default:
-        return 0;
+        return t.next.reliesOnTemplateParameters(tparams);
     }
-}
-
-/**
- * Returns the common type of the 2 types.
- */
-private Type rawTypeMerge(Type t1, Type t2)
-{
-    if (t1.equals(t2))
-        return t1;
-    if (t1.equivalent(t2))
-        return t1.castMod(MODmerge(t1.mod, t2.mod));
-
-    auto t1b = t1.toBasetype();
-    auto t2b = t2.toBasetype();
-    if (t1b.equals(t2b))
-        return t1b;
-    if (t1b.equivalent(t2b))
-        return t1b.castMod(MODmerge(t1b.mod, t2b.mod));
-
-    auto ty = implicitConvCommonTy(t1b.ty, t2b.ty);
-    if (ty != Terror)
-        return Type.basic[ty];
-
-    return null;
-}
 
-MATCH deduceTypeHelper(Type t, out Type at, Type tparam)
-{
-    // 9*9 == 81 cases
-    switch (X(tparam.mod, t.mod))
+    bool visitIdentifier(TypeIdentifier t)
     {
-    case X(0, 0):
-    case X(0, MODFlags.const_):
-    case X(0, MODFlags.wild):
-    case X(0, MODFlags.wildconst):
-    case X(0, MODFlags.shared_):
-    case X(0, MODFlags.shared_ | MODFlags.const_):
-    case X(0, MODFlags.shared_ | MODFlags.wild):
-    case X(0, MODFlags.shared_ | MODFlags.wildconst):
-    case X(0, MODFlags.immutable_):
-        // foo(U)                       T                       => T
-        // foo(U)                       const(T)                => const(T)
-        // foo(U)                       inout(T)                => inout(T)
-        // foo(U)                       inout(const(T))         => inout(const(T))
-        // foo(U)                       shared(T)               => shared(T)
-        // foo(U)                       shared(const(T))        => shared(const(T))
-        // foo(U)                       shared(inout(T))        => shared(inout(T))
-        // foo(U)                       shared(inout(const(T))) => shared(inout(const(T)))
-        // foo(U)                       immutable(T)            => immutable(T)
-        {
-            at = t;
-            return MATCH.exact;
-        }
-    case X(MODFlags.const_, MODFlags.const_):
-    case X(MODFlags.wild, MODFlags.wild):
-    case X(MODFlags.wildconst, MODFlags.wildconst):
-    case X(MODFlags.shared_, MODFlags.shared_):
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.immutable_, MODFlags.immutable_):
-        // foo(const(U))                const(T)                => T
-        // foo(inout(U))                inout(T)                => T
-        // foo(inout(const(U)))         inout(const(T))         => T
-        // foo(shared(U))               shared(T)               => T
-        // foo(shared(const(U)))        shared(const(T))        => T
-        // foo(shared(inout(U)))        shared(inout(T))        => T
-        // foo(shared(inout(const(U)))) shared(inout(const(T))) => T
-        // foo(immutable(U))            immutable(T)            => T
-        {
-            at = t.mutableOf().unSharedOf();
-            return MATCH.exact;
-        }
-    case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
-        // foo(const(U))                shared(const(T))        => shared(T)
-        // foo(inout(U))                shared(inout(T))        => shared(T)
-        // foo(inout(const(U)))         shared(inout(const(T))) => shared(T)
-        {
-            at = t.mutableOf();
-            return MATCH.exact;
-        }
-    case X(MODFlags.const_, 0):
-    case X(MODFlags.const_, MODFlags.wild):
-    case X(MODFlags.const_, MODFlags.wildconst):
-    case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.const_, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_):
-        // foo(const(U))                T                       => T
-        // foo(const(U))                inout(T)                => T
-        // foo(const(U))                inout(const(T))         => T
-        // foo(const(U))                shared(inout(T))        => shared(T)
-        // foo(const(U))                shared(inout(const(T))) => shared(T)
-        // foo(const(U))                immutable(T)            => T
-        // foo(shared(const(U)))        immutable(T)            => T
-        {
-            at = t.mutableOf();
-            return MATCH.constant;
-        }
-    case X(MODFlags.const_, MODFlags.shared_):
-        // foo(const(U))                shared(T)               => shared(T)
-        {
-            at = t;
-            return MATCH.constant;
-        }
-    case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst):
-        // foo(shared(U))               shared(const(T))        => const(T)
-        // foo(shared(U))               shared(inout(T))        => inout(T)
-        // foo(shared(U))               shared(inout(const(T))) => inout(const(T))
-        {
-            at = t.unSharedOf();
-            return MATCH.exact;
-        }
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_):
-        // foo(shared(const(U)))        shared(T)               => T
-        {
-            at = t.unSharedOf();
-            return MATCH.constant;
-        }
-    case X(MODFlags.wildconst, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
-        // foo(inout(const(U)))         immutable(T)            => T
-        // foo(shared(const(U)))        shared(inout(const(T))) => T
-        // foo(shared(inout(const(U)))) immutable(T)            => T
-        // foo(shared(inout(const(U)))) shared(inout(T))        => T
+        foreach (tp; tparams)
         {
-            at = t.unSharedOf().mutableOf();
-            return MATCH.constant;
+            if (tp.ident.equals(t.ident))
+                return true;
         }
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
-        // foo(shared(const(U)))        shared(inout(T))        => T
+        return false;
+    }
+
+    bool visitInstance(TypeInstance t)
+    {
+        foreach (tp; tparams)
         {
-            at = t.unSharedOf().mutableOf();
-            return MATCH.constant;
+            if (t.tempinst.name == tp.ident)
+                return true;
         }
-    case X(MODFlags.wild, 0):
-    case X(MODFlags.wild, MODFlags.const_):
-    case X(MODFlags.wild, MODFlags.wildconst):
-    case X(MODFlags.wild, MODFlags.immutable_):
-    case X(MODFlags.wild, MODFlags.shared_):
-    case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.wildconst, 0):
-    case X(MODFlags.wildconst, MODFlags.const_):
-    case X(MODFlags.wildconst, MODFlags.wild):
-    case X(MODFlags.wildconst, MODFlags.shared_):
-    case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.shared_, 0):
-    case X(MODFlags.shared_, MODFlags.const_):
-    case X(MODFlags.shared_, MODFlags.wild):
-    case X(MODFlags.shared_, MODFlags.wildconst):
-    case X(MODFlags.shared_, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.const_, 0):
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild):
-    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wild, 0):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wildconst, 0):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
-    case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.immutable_, 0):
-    case X(MODFlags.immutable_, MODFlags.const_):
-    case X(MODFlags.immutable_, MODFlags.wild):
-    case X(MODFlags.immutable_, MODFlags.wildconst):
-    case X(MODFlags.immutable_, MODFlags.shared_):
-    case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_):
-    case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild):
-    case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst):
-        // foo(inout(U))                T                       => nomatch
-        // foo(inout(U))                const(T)                => nomatch
-        // foo(inout(U))                inout(const(T))         => nomatch
-        // foo(inout(U))                immutable(T)            => nomatch
-        // foo(inout(U))                shared(T)               => nomatch
-        // foo(inout(U))                shared(const(T))        => nomatch
-        // foo(inout(U))                shared(inout(const(T))) => nomatch
-        // foo(inout(const(U)))         T                       => nomatch
-        // foo(inout(const(U)))         const(T)                => nomatch
-        // foo(inout(const(U)))         inout(T)                => nomatch
-        // foo(inout(const(U)))         shared(T)               => nomatch
-        // foo(inout(const(U)))         shared(const(T))        => nomatch
-        // foo(inout(const(U)))         shared(inout(T))        => nomatch
-        // foo(shared(U))               T                       => nomatch
-        // foo(shared(U))               const(T)                => nomatch
-        // foo(shared(U))               inout(T)                => nomatch
-        // foo(shared(U))               inout(const(T))         => nomatch
-        // foo(shared(U))               immutable(T)            => nomatch
-        // foo(shared(const(U)))        T                       => nomatch
-        // foo(shared(const(U)))        const(T)                => nomatch
-        // foo(shared(const(U)))        inout(T)                => nomatch
-        // foo(shared(const(U)))        inout(const(T))         => nomatch
-        // foo(shared(inout(U)))        T                       => nomatch
-        // foo(shared(inout(U)))        const(T)                => nomatch
-        // foo(shared(inout(U)))        inout(T)                => nomatch
-        // foo(shared(inout(U)))        inout(const(T))         => nomatch
-        // foo(shared(inout(U)))        immutable(T)            => nomatch
-        // foo(shared(inout(U)))        shared(T)               => nomatch
-        // foo(shared(inout(U)))        shared(const(T))        => nomatch
-        // foo(shared(inout(U)))        shared(inout(const(T))) => nomatch
-        // foo(shared(inout(const(U)))) T                       => nomatch
-        // foo(shared(inout(const(U)))) const(T)                => nomatch
-        // foo(shared(inout(const(U)))) inout(T)                => nomatch
-        // foo(shared(inout(const(U)))) inout(const(T))         => nomatch
-        // foo(shared(inout(const(U)))) shared(T)               => nomatch
-        // foo(shared(inout(const(U)))) shared(const(T))        => nomatch
-        // foo(immutable(U))            T                       => nomatch
-        // foo(immutable(U))            const(T)                => nomatch
-        // foo(immutable(U))            inout(T)                => nomatch
-        // foo(immutable(U))            inout(const(T))         => nomatch
-        // foo(immutable(U))            shared(T)               => nomatch
-        // foo(immutable(U))            shared(const(T))        => nomatch
-        // foo(immutable(U))            shared(inout(T))        => nomatch
-        // foo(immutable(U))            shared(inout(const(T))) => nomatch
-        return MATCH.nomatch;
 
-    default:
-        assert(0);
+        if (t.tempinst.tiargs)
+            foreach (arg; *t.tempinst.tiargs)
+            {
+                if (Type ta = isType(arg))
+                {
+                    if (ta.reliesOnTemplateParameters(tparams))
+                        return true;
+                }
+            }
+
+        return false;
+    }
+
+    bool visitTypeof(TypeTypeof t)
+    {
+        //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
+        return t.exp.reliesOnTemplateParameters(tparams);
+    }
+
+    bool visitTuple(TypeTuple t)
+    {
+        if (t.arguments)
+            foreach (arg; *t.arguments)
+            {
+                if (arg.type.reliesOnTemplateParameters(tparams))
+                    return true;
+            }
+
+        return false;
+    }
+
+    if (!t)
+        return false;
+
+    Type tb = t.toBasetype();
+    switch (tb.ty)
+    {
+        case Tvector:   return visitVector(tb.isTypeVector());
+        case Taarray:   return visitAArray(tb.isTypeAArray());
+        case Tfunction: return visitFunction(tb.isTypeFunction());
+        case Tident:    return visitIdentifier(tb.isTypeIdentifier());
+        case Tinstance: return visitInstance(tb.isTypeInstance());
+        case Ttypeof:   return visitTypeof(tb.isTypeTypeof());
+        case Ttuple:    return visitTuple(tb.isTypeTuple());
+        case Tenum:     return false;
+        default:        return tb.nextOf().reliesOnTemplateParameters(tparams);
     }
 }
 
-__gshared Expression emptyArrayElement = null;
-
-/* These form the heart of template argument deduction.
- * Given 'this' being the type argument to the template instance,
- * it is matched against the template declaration parameter specialization
- * 'tparam' to determine the type to be used for the parameter.
- * Example:
- *      template Foo(T:T*)      // template declaration
- *      Foo!(int*)              // template instantiation
- * Input:
- *      this = int*
- *      tparam = T*
- *      parameters = [ T:T* ]   // Array of TemplateParameter's
- * Output:
- *      dedtypes = [ int ]      // Array of Expression/Type's
+/***********************************************************
+ * Check whether the expression representation relies on one or more the template parameters.
+ * Params:
+ *      e           = expression to test
+ *      tparams     = Template parameters.
+ * Returns:
+ *      true if it does
  */
-MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false)
+private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
 {
-    extern (C++) final class DeduceType : Visitor
+    extern (C++) final class ReliesOnTemplateParameters : Visitor
     {
         alias visit = Visitor.visit;
     public:
-        MATCH result;
+        TemplateParameter[] tparams;
+        bool result;
 
-        extern (D) this() @safe
+        extern (D) this(TemplateParameter[] tparams) @safe
         {
-            result = MATCH.nomatch;
+            this.tparams = tparams;
         }
 
-        override void visit(Type t)
+        override void visit(Expression e)
         {
-            if (!tparam)
-                goto Lnomatch;
-
-            if (t == tparam)
-                goto Lexact;
+            //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
+        }
 
-            if (tparam.ty == Tident)
+        override void visit(IdentifierExp e)
+        {
+            //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            foreach (tp; tparams)
             {
-                // Determine which parameter tparam is
-                size_t i = templateParameterLookup(tparam, &parameters);
-                if (i == IDX_NOTFOUND)
+                if (e.ident == tp.ident)
                 {
-                    if (!sc)
-                        goto Lnomatch;
-
-                    /* Need a loc to go with the semantic routine.
-                     */
-                    Loc loc;
-                    if (parameters.length)
-                    {
-                        TemplateParameter tp = parameters[0];
-                        loc = tp.loc;
-                    }
-
-                    /* BUG: what if tparam is a template instance, that
-                     * has as an argument another Tident?
-                     */
-                    tparam = tparam.typeSemantic(loc, sc);
-                    assert(tparam.ty != Tident);
-                    result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
+                    result = true;
                     return;
                 }
+            }
+        }
 
-                TemplateParameter tp = parameters[i];
-
-                TypeIdentifier tident = tparam.isTypeIdentifier();
-                if (tident.idents.length > 0)
+        override void visit(TupleExp e)
+        {
+            //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            if (e.exps)
+            {
+                foreach (ea; *e.exps)
                 {
-                    //printf("matching %s to %s\n", tparam.toChars(), t.toChars());
-                    Dsymbol s = t.toDsymbol(sc);
-                    for (size_t j = tident.idents.length; j-- > 0;)
-                    {
-                        RootObject id = tident.idents[j];
-                        if (id.dyncast() == DYNCAST.identifier)
-                        {
-                            if (!s || !s.parent)
-                                goto Lnomatch;
-                            Dsymbol s2 = s.parent.search(Loc.initial, cast(Identifier)id);
-                            if (!s2)
-                                goto Lnomatch;
-                            s2 = s2.toAlias();
-                            //printf("[%d] s = %s %s, s2 = %s %s\n", j, s.kind(), s.toChars(), s2.kind(), s2.toChars());
-                            if (s != s2)
-                            {
-                                if (Type tx = s2.getType())
-                                {
-                                    if (s != tx.toDsymbol(sc))
-                                        goto Lnomatch;
-                                }
-                                else
-                                    goto Lnomatch;
-                            }
-                            s = s.parent;
-                        }
-                        else
-                            goto Lnomatch;
-                    }
-                    //printf("[e] s = %s\n", s?s.toChars():"(null)");
-                    if (tp.isTemplateTypeParameter())
-                    {
-                        Type tt = s.getType();
-                        if (!tt)
-                            goto Lnomatch;
-                        Type at = cast(Type)dedtypes[i];
-                        if (at && at.ty == Tnone)
-                            at = (cast(TypeDeduced)at).tded;
-                        if (!at || tt.equals(at))
-                        {
-                            dedtypes[i] = tt;
-                            goto Lexact;
-                        }
-                    }
-                    if (tp.isTemplateAliasParameter())
-                    {
-                        Dsymbol s2 = cast(Dsymbol)dedtypes[i];
-                        if (!s2 || s == s2)
-                        {
-                            dedtypes[i] = s;
-                            goto Lexact;
-                        }
-                    }
-                    goto Lnomatch;
+                    ea.accept(this);
+                    if (result)
+                        return;
                 }
+            }
+        }
 
-                // Found the corresponding parameter tp
-                /+
-                    https://issues.dlang.org/show_bug.cgi?id=23578
-                    To pattern match:
-                    static if (is(S!int == S!av, alias av))
-
-                    We eventually need to deduce `int` (Tint32 [0]) and `av` (Tident).
-                    Previously this would not get pattern matched at all, but now we check if the
-                    template parameter `av` came from.
-
-                    This note has been left to serve as a hint for further explorers into
-                    how IsExp matching works.
-                +/
-                if (auto ta = tp.isTemplateAliasParameter())
-                {
-                    dedtypes[i] = t;
-                    goto Lexact;
-                }
-                // (23578) - ensure previous behaviour for non-alias template params
-                if (!tp.isTemplateTypeParameter())
+        override void visit(ArrayLiteralExp e)
+        {
+            //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            if (e.elements)
+            {
+                foreach (el; *e.elements)
                 {
-                    goto Lnomatch;
+                    el.accept(this);
+                    if (result)
+                        return;
                 }
+            }
+        }
 
-                Type at = cast(Type)dedtypes[i];
-                Type tt;
-                if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
-                {
-                    // type vs (none)
-                    if (!at)
-                    {
-                        dedtypes[i] = tt;
-                        *wm |= wx;
-                        result = MATCH.constant;
-                        return;
-                    }
-
-                    // type vs expressions
-                    if (at.ty == Tnone)
-                    {
-                        auto xt = cast(TypeDeduced)at;
-                        result = xt.matchAll(tt);
-                        if (result > MATCH.nomatch)
-                        {
-                            dedtypes[i] = tt;
-                            if (result > MATCH.constant)
-                                result = MATCH.constant; // limit level for inout matches
-                        }
-                        return;
-                    }
-
-                    // type vs type
-                    if (tt.equals(at))
-                    {
-                        dedtypes[i] = tt; // Prefer current type match
-                        goto Lconst;
-                    }
-                    if (tt.implicitConvTo(at.constOf()))
-                    {
-                        dedtypes[i] = at.constOf().mutableOf();
-                        *wm |= MODFlags.const_;
-                        goto Lconst;
-                    }
-                    if (at.implicitConvTo(tt.constOf()))
-                    {
-                        dedtypes[i] = tt.constOf().mutableOf();
-                        *wm |= MODFlags.const_;
-                        goto Lconst;
-                    }
-                    goto Lnomatch;
-                }
-                else if (MATCH m = deduceTypeHelper(t, tt, tparam))
-                {
-                    // type vs (none)
-                    if (!at)
-                    {
-                        dedtypes[i] = tt;
-                        result = m;
-                        return;
-                    }
-
-                    // type vs expressions
-                    if (at.ty == Tnone)
-                    {
-                        auto xt = cast(TypeDeduced)at;
-                        result = xt.matchAll(tt);
-                        if (result > MATCH.nomatch)
-                        {
-                            dedtypes[i] = tt;
-                        }
-                        return;
-                    }
-
-                    // type vs type
-                    if (tt.equals(at))
-                    {
-                        goto Lexact;
-                    }
-                    if (tt.ty == Tclass && at.ty == Tclass)
-                    {
-                        result = tt.implicitConvTo(at);
-                        return;
-                    }
-                    if (tt.ty == Tsarray && at.ty == Tarray && tt.nextOf().implicitConvTo(at.nextOf()) >= MATCH.constant)
-                    {
-                        goto Lexact;
-                    }
-                }
-                goto Lnomatch;
-            }
-
-            if (tparam.ty == Ttypeof)
-            {
-                /* Need a loc to go with the semantic routine.
-                 */
-                Loc loc;
-                if (parameters.length)
-                {
-                    TemplateParameter tp = parameters[0];
-                    loc = tp.loc;
-                }
-
-                tparam = tparam.typeSemantic(loc, sc);
-            }
-            if (t.ty != tparam.ty)
+        override void visit(AssocArrayLiteralExp e)
+        {
+            //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            foreach (ek; *e.keys)
             {
-                if (Dsymbol sym = t.toDsymbol(sc))
-                {
-                    if (sym.isforwardRef() && !tparam.deco)
-                        goto Lnomatch;
-                }
-
-                MATCH m = t.implicitConvTo(tparam);
-                if (m == MATCH.nomatch && !ignoreAliasThis)
-                {
-                    if (auto tc = t.isTypeClass())
-                    {
-                        if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT))
-                        {
-                            if (auto ato = t.aliasthisOf())
-                            {
-                                tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT);
-                                m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
-                                tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT);
-                            }
-                        }
-                    }
-                    else if (auto ts = t.isTypeStruct())
-                    {
-                        if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT))
-                        {
-                            if (auto ato = t.aliasthisOf())
-                            {
-                                ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT);
-                                m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
-                                ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT);
-                            }
-                        }
-                    }
-                }
-                result = m;
-                return;
+                ek.accept(this);
+                if (result)
+                    return;
             }
-
-            if (t.nextOf())
+            foreach (ev; *e.values)
             {
-                if (tparam.deco && !tparam.hasWild())
-                {
-                    result = t.implicitConvTo(tparam);
+                ev.accept(this);
+                if (result)
                     return;
-                }
-
-                Type tpn = tparam.nextOf();
-                if (wm && t.ty == Taarray && tparam.isWild())
-                {
-                    // https://issues.dlang.org/show_bug.cgi?id=12403
-                    // In IFTI, stop inout matching on transitive part of AA types.
-                    tpn = tpn.substWildTo(MODFlags.mutable);
-                }
-
-                result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm);
-                return;
             }
-
-        Lexact:
-            result = MATCH.exact;
-            return;
-
-        Lnomatch:
-            result = MATCH.nomatch;
-            return;
-
-        Lconst:
-            result = MATCH.constant;
         }
 
-        override void visit(TypeVector t)
+        override void visit(StructLiteralExp e)
         {
-            if (auto tp = tparam.isTypeVector())
+            //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            if (e.elements)
             {
-                result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm);
-                return;
+                foreach (ea; *e.elements)
+                {
+                    ea.accept(this);
+                    if (result)
+                        return;
+                }
             }
-            visit(cast(Type)t);
         }
 
-        override void visit(TypeDArray t)
+        override void visit(TypeExp e)
         {
-            visit(cast(Type)t);
+            //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            result = e.type.reliesOnTemplateParameters(tparams);
         }
 
-        override void visit(TypeSArray t)
+        override void visit(NewExp e)
         {
-            // Extra check that array dimensions must match
-            if (!tparam)
-            {
-                visit(cast(Type)t);
-                return;
-            }
-
-            if (tparam.ty == Tarray)
-            {
-                MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
-                result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch;
-                return;
-            }
-
-            TemplateParameter tp = null;
-            Expression edim = null;
-            size_t i;
-            if (auto tsa = tparam.isTypeSArray())
+            //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            if (e.placement)
+                e.placement.accept(this);
+            if (e.thisexp)
+                e.thisexp.accept(this);
+            result = e.newtype.reliesOnTemplateParameters(tparams);
+            if (!result && e.arguments)
             {
-                if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter)
+                foreach (ea; *e.arguments)
                 {
-                    Identifier id = tsa.dim.isVarExp().var.ident;
-                    i = templateIdentifierLookup(id, &parameters);
-                    assert(i != IDX_NOTFOUND);
-                    tp = parameters[i];
+                    ea.accept(this);
+                    if (result)
+                        return;
                 }
-                else
-                    edim = tsa.dim;
             }
-            else if (auto taa = tparam.isTypeAArray())
-            {
-                i = templateParameterLookup(taa.index, &parameters);
-                if (i != IDX_NOTFOUND)
-                    tp = parameters[i];
-                else
-                {
-                    Loc loc;
-                    // The "type" (it hasn't been resolved yet) of the function parameter
-                    // does not have a location but the parameter it is related to does,
-                    // so we use that for the resolution (better error message).
-                    if (inferStart < parameters.length)
-                    {
-                        TemplateParameter loctp = parameters[inferStart];
-                        loc = loctp.loc;
-                    }
+        }
 
-                    Expression e;
-                    Type tx;
-                    Dsymbol s;
-                    taa.index.resolve(loc, sc, e, tx, s);
-                    edim = s ? getValue(s) : getValue(e);
-                }
-            }
-            if ((tp && tp.matchArg(sc, t.dim, i, &parameters, dedtypes, null)) ||
-                (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger())
-            )
-            {
-                result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
-                return;
-            }
+        override void visit(NewAnonClassExp e)
+        {
+            //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            result = true;
+        }
+
+        override void visit(FuncExp e)
+        {
+            //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            result = true;
+        }
 
-            visit(cast(Type)t);
+        override void visit(TypeidExp e)
+        {
+            //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            if (auto ea = isExpression(e.obj))
+                ea.accept(this);
+            else if (auto ta = isType(e.obj))
+                result = ta.reliesOnTemplateParameters(tparams);
         }
 
-        override void visit(TypeAArray t)
+        override void visit(TraitsExp e)
         {
-            // Extra check that index type must match
-            if (tparam && tparam.ty == Taarray)
+            //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            if (e.args)
             {
-                TypeAArray tp = tparam.isTypeAArray();
-                if (!deduceType(t.index, sc, tp.index, parameters, dedtypes))
+                foreach (oa; *e.args)
                 {
-                    result = MATCH.nomatch;
-                    return;
+                    if (auto ea = isExpression(oa))
+                        ea.accept(this);
+                    else if (auto ta = isType(oa))
+                        result = ta.reliesOnTemplateParameters(tparams);
+                    if (result)
+                        return;
                 }
             }
-            visit(cast(Type)t);
         }
 
-        override void visit(TypeFunction t)
+        override void visit(IsExp e)
         {
-            // Extra check that function characteristics must match
-            if (!tparam)
-                return visit(cast(Type)t);
-
-            auto tp = tparam.isTypeFunction();
-            if (!tp)
-            {
-                visit(cast(Type)t);
-                return;
-            }
+            //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            result = e.targ.reliesOnTemplateParameters(tparams);
+        }
 
-            if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
-            {
-                result = MATCH.nomatch;
-                return;
-            }
+        override void visit(UnaExp e)
+        {
+            //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            e.e1.accept(this);
+        }
 
-            foreach (fparam; *tp.parameterList.parameters)
+        override void visit(DotTemplateInstanceExp e)
+        {
+            //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            visit(e.isUnaExp());
+            if (!result && e.ti.tiargs)
             {
-                // https://issues.dlang.org/show_bug.cgi?id=2579
-                // Apply function parameter storage classes to parameter types
-                fparam.type = fparam.type.addStorageClass(fparam.storageClass);
-                fparam.storageClass &= ~STC.TYPECTOR;
-
-                // https://issues.dlang.org/show_bug.cgi?id=15243
-                // Resolve parameter type if it's not related with template parameters
-                if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length]))
+                foreach (oa; *e.ti.tiargs)
                 {
-                    auto tx = fparam.type.typeSemantic(Loc.initial, sc);
-                    if (tx.ty == Terror)
-                    {
-                        result = MATCH.nomatch;
+                    if (auto ea = isExpression(oa))
+                        ea.accept(this);
+                    else if (auto ta = isType(oa))
+                        result = ta.reliesOnTemplateParameters(tparams);
+                    if (result)
                         return;
-                    }
-                    fparam.type = tx;
                 }
             }
+        }
 
-            const size_t nfargs = t.parameterList.length;
-            size_t nfparams = tp.parameterList.length;
-
-            /* See if tuple match
-             */
-            if (nfparams > 0 && nfargs >= nfparams - 1)
+        override void visit(CallExp e)
+        {
+            //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            visit(e.isUnaExp());
+            if (!result && e.arguments)
             {
-                /* See if 'A' of the template parameter matches 'A'
-                 * of the type of the last function parameter.
-                 */
-                Parameter fparam = tp.parameterList[nfparams - 1];
-                assert(fparam);
-                assert(fparam.type);
-                if (fparam.type.ty != Tident)
-                    goto L1;
-                TypeIdentifier tid = fparam.type.isTypeIdentifier();
-                if (tid.idents.length)
-                    goto L1;
-
-                /* Look through parameters to find tuple matching tid.ident
-                 */
-                size_t tupi = 0;
-                for (; 1; tupi++)
-                {
-                    if (tupi == parameters.length)
-                        goto L1;
-                    TemplateParameter tx = parameters[tupi];
-                    TemplateTupleParameter tup = tx.isTemplateTupleParameter();
-                    if (tup && tup.ident.equals(tid.ident))
-                        break;
-                }
-
-                /* The types of the function arguments [nfparams - 1 .. nfargs]
-                 * now form the tuple argument.
-                 */
-                size_t tuple_dim = nfargs - (nfparams - 1);
-
-                /* See if existing tuple, and whether it matches or not
-                 */
-                RootObject o = dedtypes[tupi];
-                if (o)
+                foreach (ea; *e.arguments)
                 {
-                    // Existing deduced argument must be a tuple, and must match
-                    Tuple tup = isTuple(o);
-                    if (!tup || tup.objects.length != tuple_dim)
-                    {
-                        result = MATCH.nomatch;
+                    ea.accept(this);
+                    if (result)
                         return;
-                    }
-                    for (size_t i = 0; i < tuple_dim; i++)
-                    {
-                        Parameter arg = t.parameterList[nfparams - 1 + i];
-                        if (!arg.type.equals(tup.objects[i]))
-                        {
-                            result = MATCH.nomatch;
-                            return;
-                        }
-                    }
-                }
-                else
-                {
-                    // Create new tuple
-                    auto tup = new Tuple(tuple_dim);
-                    for (size_t i = 0; i < tuple_dim; i++)
-                    {
-                        Parameter arg = t.parameterList[nfparams - 1 + i];
-                        tup.objects[i] = arg.type;
-                    }
-                    dedtypes[tupi] = tup;
                 }
-                nfparams--; // don't consider the last parameter for type deduction
-                goto L2;
-            }
-
-        L1:
-            if (nfargs != nfparams)
-            {
-                result = MATCH.nomatch;
-                return;
             }
-        L2:
-            assert(nfparams <= tp.parameterList.length);
-            foreach (i, ap; tp.parameterList)
-            {
-                if (i == nfparams)
-                    break;
-
-                Parameter a = t.parameterList[i];
+        }
 
-                if (!a.isCovariant(t.isRef, ap) ||
-                    !deduceType(a.type, sc, ap.type, parameters, dedtypes))
-                {
-                    result = MATCH.nomatch;
-                    return;
-                }
-            }
+        override void visit(CastExp e)
+        {
+            //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            visit(e.isUnaExp());
+            // e.to can be null for cast() with no type
+            if (!result && e.to)
+                result = e.to.reliesOnTemplateParameters(tparams);
+        }
 
-            visit(cast(Type)t);
+        override void visit(SliceExp e)
+        {
+            //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            visit(e.isUnaExp());
+            if (!result && e.lwr)
+                e.lwr.accept(this);
+            if (!result && e.upr)
+                e.upr.accept(this);
         }
 
-        override void visit(TypeIdentifier t)
+        override void visit(IntervalExp e)
         {
-            // Extra check
-            if (tparam && tparam.ty == Tident)
-            {
-                TypeIdentifier tp = tparam.isTypeIdentifier();
-                for (size_t i = 0; i < t.idents.length; i++)
-                {
-                    RootObject id1 = t.idents[i];
-                    RootObject id2 = tp.idents[i];
-                    if (!id1.equals(id2))
-                    {
-                        result = MATCH.nomatch;
-                        return;
-                    }
-                }
-            }
-            visit(cast(Type)t);
+            //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            e.lwr.accept(this);
+            if (!result)
+                e.upr.accept(this);
         }
 
-        override void visit(TypeInstance t)
+        override void visit(ArrayExp e)
         {
-            // Extra check
-            if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl)
+            //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            visit(e.isUnaExp());
+            if (!result && e.arguments)
             {
-                visit(cast(Type)t);
-                return;
+                foreach (ea; *e.arguments)
+                    ea.accept(this);
             }
+        }
 
-            TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
-            assert(tempdecl);
-
-            TypeInstance tp = tparam.isTypeInstance();
+        override void visit(BinExp e)
+        {
+            //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            e.e1.accept(this);
+            if (!result)
+                e.e2.accept(this);
+        }
 
-            //printf("tempinst.tempdecl = %p\n", tempdecl);
-            //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl);
-            if (!tp.tempinst.tempdecl)
-            {
-                //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars());
-
-                /* Handle case of:
-                 *  template Foo(T : sa!(T), alias sa)
-                 */
-                size_t i = templateIdentifierLookup(tp.tempinst.name, &parameters);
-                if (i == IDX_NOTFOUND)
-                {
-                    /* Didn't find it as a parameter identifier. Try looking
-                     * it up and seeing if is an alias.
-                     * https://issues.dlang.org/show_bug.cgi?id=1454
-                     */
-                    auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name);
-                    Type tx;
-                    Expression e;
-                    Dsymbol s;
-                    tid.resolve(tp.loc, sc, e, tx, s);
-                    if (tx)
-                    {
-                        s = tx.toDsymbol(sc);
-                        if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null)
-                        {
-                            // https://issues.dlang.org/show_bug.cgi?id=14290
-                            // Try to match with ti.tempecl,
-                            // only when ti is an enclosing instance.
-                            Dsymbol p = sc.parent;
-                            while (p && p != ti)
-                                p = p.parent;
-                            if (p)
-                                s = ti.tempdecl;
-                        }
-                    }
-                    if (s)
-                    {
-                        s = s.toAlias();
-                        TemplateDeclaration td = s.isTemplateDeclaration();
-                        if (td)
-                        {
-                            if (td.overroot)
-                                td = td.overroot;
-                            for (; td; td = td.overnext)
-                            {
-                                if (td == tempdecl)
-                                    goto L2;
-                            }
-                        }
-                    }
-                    goto Lnomatch;
-                }
+        override void visit(CondExp e)
+        {
+            //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+            e.econd.accept(this);
+            if (!result)
+                visit(e.isBinExp());
+        }
+    }
 
-                TemplateParameter tpx = parameters[i];
-                if (!tpx.matchArg(sc, tempdecl, i, &parameters, dedtypes, null))
-                    goto Lnomatch;
-            }
-            else if (tempdecl != tp.tempinst.tempdecl)
-                goto Lnomatch;
+    scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
+    e.accept(v);
+    return v.result;
+}
 
-        L2:
-            if (!resolveTemplateInstantiation(sc, &parameters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes))
-                goto Lnomatch;
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateParameter
+ */
+extern (C++) class TemplateParameter : ASTNode
+{
+    Loc loc;
+    Identifier ident;
 
-            visit(cast(Type)t);
-            return;
+    /* True if this is a part of precedent parameter specialization pattern.
+     *
+     *  template A(T : X!TL, alias X, TL...) {}
+     *  // X and TL are dependent template parameter
+     *
+     * A dependent template parameter should return MATCH.exact in matchArg()
+     * to respect the match level of the corresponding precedent parameter.
+     */
+    bool dependent;
 
-        Lnomatch:
-            //printf("no match\n");
-            result = MATCH.nomatch;
-        }
+    /* ======================== TemplateParameter =============================== */
+    extern (D) this(Loc loc, Identifier ident) @safe
+    {
+        this.loc = loc;
+        this.ident = ident;
+    }
 
-        override void visit(TypeStruct t)
-        {
-            /* If this struct is a template struct, and we're matching
-             * it against a template instance, convert the struct type
-             * to a template instance, too, and try again.
-             */
-            TemplateInstance ti = t.sym.parent.isTemplateInstance();
+    TemplateTypeParameter isTemplateTypeParameter()
+    {
+        return null;
+    }
 
-            if (tparam && tparam.ty == Tinstance)
-            {
-                if (ti && ti.toAlias() == t.sym)
-                {
-                    auto tx = new TypeInstance(Loc.initial, ti);
-                    auto m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
-                    // if we have a no match we still need to check alias this
-                    if (m != MATCH.nomatch)
-                    {
-                        result = m;
-                        return;
-                    }
-                }
+    TemplateValueParameter isTemplateValueParameter()
+    {
+        return null;
+    }
 
-                /* Match things like:
-                 *  S!(T).foo
-                 */
-                TypeInstance tpi = tparam.isTypeInstance();
-                if (tpi.idents.length)
-                {
-                    RootObject id = tpi.idents[tpi.idents.length - 1];
-                    if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
-                    {
-                        Type tparent = t.sym.parent.getType();
-                        if (tparent)
-                        {
-                            /* Slice off the .foo in S!(T).foo
-                             */
-                            tpi.idents.length--;
-                            result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
-                            tpi.idents.length++;
-                            return;
-                        }
-                    }
-                }
-            }
+    TemplateAliasParameter isTemplateAliasParameter()
+    {
+        return null;
+    }
 
-            // Extra check
-            if (tparam && tparam.ty == Tstruct)
-            {
-                TypeStruct tp = tparam.isTypeStruct();
+    TemplateThisParameter isTemplateThisParameter()
+    {
+        return null;
+    }
 
-                //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
-                if (wm && t.deduceWild(tparam, false))
-                {
-                    result = MATCH.constant;
-                    return;
-                }
-                result = t.implicitConvTo(tp);
-                return;
-            }
-            visit(cast(Type)t);
-        }
+    TemplateTupleParameter isTemplateTupleParameter()
+    {
+        return null;
+    }
 
-        override void visit(TypeEnum t)
-        {
-            // Extra check
-            if (tparam && tparam.ty == Tenum)
-            {
-                TypeEnum tp = tparam.isTypeEnum();
-                if (t.sym == tp.sym)
-                    visit(cast(Type)t);
-                else
-                    result = MATCH.nomatch;
-                return;
-            }
-            Type tb = t.toBasetype();
-            if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray)
-            {
-                result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
-                if (result == MATCH.exact)
-                    result = MATCH.convert;
-                return;
-            }
-            visit(cast(Type)t);
-        }
+    abstract TemplateParameter syntaxCopy();
 
-        override void visit(TypeClass t)
-        {
-            //printf("TypeClass.deduceType(this = %s)\n", t.toChars());
+    abstract bool declareParameter(Scope* sc);
 
-            /* If this class is a template class, and we're matching
-             * it against a template instance, convert the class type
-             * to a template instance, too, and try again.
-             */
-            TemplateInstance ti = t.sym.parent.isTemplateInstance();
+    abstract void print(RootObject oarg, RootObject oded);
 
-            if (tparam && tparam.ty == Tinstance)
-            {
-                if (ti && ti.toAlias() == t.sym)
-                {
-                    auto tx = new TypeInstance(Loc.initial, ti);
-                    MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
-                    // Even if the match fails, there is still a chance it could match
-                    // a base class.
-                    if (m != MATCH.nomatch)
-                    {
-                        result = m;
-                        return;
-                    }
-                }
+    abstract RootObject specialization();
 
-                /* Match things like:
-                 *  S!(T).foo
-                 */
-                TypeInstance tpi = tparam.isTypeInstance();
-                if (tpi.idents.length)
-                {
-                    RootObject id = tpi.idents[tpi.idents.length - 1];
-                    if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
-                    {
-                        Type tparent = t.sym.parent.getType();
-                        if (tparent)
-                        {
-                            /* Slice off the .foo in S!(T).foo
-                             */
-                            tpi.idents.length--;
-                            result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
-                            tpi.idents.length++;
-                            return;
-                        }
-                    }
-                }
+    abstract bool hasDefaultArg();
 
-                // If it matches exactly or via implicit conversion, we're done
-                visit(cast(Type)t);
-                if (result != MATCH.nomatch)
-                    return;
+    override const(char)* toChars() const
+    {
+        return this.ident.toChars();
+    }
 
-                /* There is still a chance to match via implicit conversion to
-                 * a base class or interface. Because there could be more than one such
-                 * match, we need to check them all.
-                 */
+    override DYNCAST dyncast() const
+    {
+        return DYNCAST.templateparameter;
+    }
 
-                int numBaseClassMatches = 0; // Have we found an interface match?
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
 
-                // Our best guess at dedtypes
-                auto best = new Objects(dedtypes.length);
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateTypeParameter
+ * Syntax:
+ *  ident : specType = defaultType
+ */
+extern (C++) class TemplateTypeParameter : TemplateParameter
+{
+    Type specType;      // if !=null, this is the type specialization
+    Type defaultType;
 
-                ClassDeclaration s = t.sym;
-                while (s && s.baseclasses.length > 0)
-                {
-                    // Test the base class
-                    deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
+    extern (D) __gshared Type tdummy = null;
 
-                    // Test the interfaces inherited by the base class
-                    foreach (b; s.interfaces)
-                    {
-                        deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
-                    }
-                    s = (*s.baseclasses)[0].sym;
-                }
+    extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
+    {
+        super(loc, ident);
+        this.specType = specType;
+        this.defaultType = defaultType;
+    }
 
-                if (numBaseClassMatches == 0)
-                {
-                    result = MATCH.nomatch;
-                    return;
-                }
+    override final TemplateTypeParameter isTemplateTypeParameter()
+    {
+        return this;
+    }
 
-                // If we got at least one match, copy the known types into dedtypes
-                memcpy(dedtypes.tdata(), best.tdata(), best.length * (void*).sizeof);
-                result = MATCH.convert;
-                return;
-            }
+    override TemplateTypeParameter syntaxCopy()
+    {
+        return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+    }
 
-            // Extra check
-            if (tparam && tparam.ty == Tclass)
-            {
-                TypeClass tp = tparam.isTypeClass();
+    override final bool declareParameter(Scope* sc)
+    {
+        //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
+        auto ti = new TypeIdentifier(loc, ident);
+        Declaration ad = new AliasDeclaration(loc, ident, ti);
+        return sc.insert(ad) !is null;
+    }
 
-                //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
-                if (wm && t.deduceWild(tparam, false))
-                {
-                    result = MATCH.constant;
-                    return;
-                }
-                result = t.implicitConvTo(tp);
-                return;
-            }
-            visit(cast(Type)t);
-        }
+    override final void print(RootObject oarg, RootObject oded)
+    {
+        printf(" %s\n", ident.toChars());
 
-        override void visit(Expression e)
-        {
-            //printf("Expression.deduceType(e = %s)\n", e.toChars());
-            size_t i = templateParameterLookup(tparam, &parameters);
-            if (i == IDX_NOTFOUND || tparam.isTypeIdentifier().idents.length > 0)
-            {
-                if (e == emptyArrayElement && tparam.ty == Tarray)
-                {
-                    Type tn = (cast(TypeNext)tparam).next;
-                    result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
-                    return;
-                }
-                e.type.accept(this);
-                return;
-            }
+        Type t = isType(oarg);
+        Type ta = isType(oded);
+        assert(ta);
 
-            TemplateTypeParameter tp = parameters[i].isTemplateTypeParameter();
-            if (!tp)
-                return; // nomatch
+        if (specType)
+            printf("\tSpecialization: %s\n", specType.toChars());
+        if (defaultType)
+            printf("\tDefault:        %s\n", defaultType.toChars());
+        printf("\tParameter:       %s\n", t ? t.toChars() : "NULL");
+        printf("\tDeduced Type:   %s\n", ta.toChars());
+    }
 
-            if (e == emptyArrayElement)
-            {
-                if (dedtypes[i])
-                {
-                    result = MATCH.exact;
-                    return;
-                }
-                if (tp.defaultType)
-                {
-                    tp.defaultType.accept(this);
-                    return;
-                }
-            }
+    override final RootObject specialization()
+    {
+        return specType;
+    }
 
-            /* Returns `true` if `t` is a reference type, or an array of reference types
-             */
-            bool isTopRef(Type t)
-            {
-                auto tb = t.baseElemOf();
-                return tb.ty == Tclass ||
-                       tb.ty == Taarray ||
-                       tb.ty == Tstruct && tb.hasPointers();
-            }
+    override final bool hasDefaultArg()
+    {
+        return defaultType !is null;
+    }
 
-            Type at = cast(Type)dedtypes[i];
-            Type tt;
-            if (ubyte wx = deduceWildHelper(e.type, &tt, tparam))
-            {
-                *wm |= wx;
-                result = MATCH.constant;
-            }
-            else if (MATCH m = deduceTypeHelper(e.type, tt, tparam))
-            {
-                result = m;
-            }
-            else if (!isTopRef(e.type))
-            {
-                /* https://issues.dlang.org/show_bug.cgi?id=15653
-                 * In IFTI, recognize top-qualifier conversions
-                 * through the value copy, e.g.
-                 *      int --> immutable(int)
-                 *      immutable(string[]) --> immutable(string)[]
-                 */
-                tt = e.type.mutableOf();
-                result = MATCH.convert;
-            }
-            else
-                return; // nomatch
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
 
-            // expression vs (none)
-            if (!at)
-            {
-                dedtypes[i] = new TypeDeduced(tt, e, tparam);
-                return;
-            }
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateThisParameter
+ * Syntax:
+ *  this ident : specType = defaultType
+ */
+extern (C++) final class TemplateThisParameter : TemplateTypeParameter
+{
+    extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
+    {
+        super(loc, ident, specType, defaultType);
+    }
 
-            TypeDeduced xt = null;
-            if (at.ty == Tnone)
-            {
-                xt = cast(TypeDeduced)at;
-                at = xt.tded;
-            }
+    override TemplateThisParameter isTemplateThisParameter()
+    {
+        return this;
+    }
 
-            // From previous matched expressions to current deduced type
-            MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch;
+    override TemplateThisParameter syntaxCopy()
+    {
+        return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+    }
 
-            // From current expressions to previous deduced type
-            Type pt = at.addMod(tparam.mod);
-            if (*wm)
-                pt = pt.substWildTo(*wm);
-            MATCH match2 = e.implicitConvTo(pt);
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
 
-            if (match1 > MATCH.nomatch && match2 > MATCH.nomatch)
-            {
-                if (at.implicitConvTo(tt) == MATCH.nomatch)
-                    match1 = MATCH.nomatch; // Prefer at
-                else if (tt.implicitConvTo(at) == MATCH.nomatch)
-                    match2 = MATCH.nomatch; // Prefer tt
-                else if (tt.isTypeBasic() && tt.ty == at.ty && tt.mod != at.mod)
-                {
-                    if (!tt.isMutable() && !at.isMutable())
-                        tt = tt.mutableOf().addMod(MODmerge(tt.mod, at.mod));
-                    else if (tt.isMutable())
-                    {
-                        if (at.mod == 0) // Prefer unshared
-                            match1 = MATCH.nomatch;
-                        else
-                            match2 = MATCH.nomatch;
-                    }
-                    else if (at.isMutable())
-                    {
-                        if (tt.mod == 0) // Prefer unshared
-                            match2 = MATCH.nomatch;
-                        else
-                            match1 = MATCH.nomatch;
-                    }
-                    //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars());
-                }
-                else
-                {
-                    match1 = MATCH.nomatch;
-                    match2 = MATCH.nomatch;
-                }
-            }
-            if (match1 > MATCH.nomatch)
-            {
-                // Prefer current match: tt
-                if (xt)
-                    xt.update(tt, e, tparam);
-                else
-                    dedtypes[i] = tt;
-                result = match1;
-                return;
-            }
-            if (match2 > MATCH.nomatch)
-            {
-                // Prefer previous match: (*dedtypes)[i]
-                if (xt)
-                    xt.update(e, tparam);
-                result = match2;
-                return;
-            }
-
-            /* Deduce common type
-             */
-            if (Type t = rawTypeMerge(at, tt))
-            {
-                if (xt)
-                    xt.update(t, e, tparam);
-                else
-                    dedtypes[i] = t;
-
-                pt = tt.addMod(tparam.mod);
-                if (*wm)
-                    pt = pt.substWildTo(*wm);
-                result = e.implicitConvTo(pt);
-                return;
-            }
-
-            result = MATCH.nomatch;
-        }
-
-        MATCH deduceEmptyArrayElement()
-        {
-            if (!emptyArrayElement)
-            {
-                emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy
-                emptyArrayElement.type = Type.tvoid;
-            }
-            assert(tparam.ty == Tarray);
-
-            Type tn = (cast(TypeNext)tparam).next;
-            return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
-        }
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateValueParameter
+ * Syntax:
+ *  valType ident : specValue = defaultValue
+ */
+extern (C++) final class TemplateValueParameter : TemplateParameter
+{
+    Type valType;
+    Expression specValue;
+    Expression defaultValue;
 
-        override void visit(NullExp e)
-        {
-            if (tparam.ty == Tarray && e.type.ty == Tnull)
-            {
-                // tparam:T[] <- e:null (void[])
-                result = deduceEmptyArrayElement();
-                return;
-            }
-            visit(cast(Expression)e);
-        }
+    extern (D) __gshared Expression[void*] edummies;
 
-        override void visit(StringExp e)
-        {
-            Type taai;
-            if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
-            {
-                // Consider compile-time known boundaries
-                e.type.nextOf().sarrayOf(e.len).accept(this);
-                return;
-            }
-            visit(cast(Expression)e);
-        }
+    extern (D) this(Loc loc, Identifier ident, Type valType,
+        Expression specValue, Expression defaultValue) @safe
+    {
+        super(loc, ident);
+        this.valType = valType;
+        this.specValue = specValue;
+        this.defaultValue = defaultValue;
+    }
 
-        override void visit(ArrayLiteralExp e)
-        {
-            // https://issues.dlang.org/show_bug.cgi?id=20092
-            if (e.elements && e.elements.length && e.type.toBasetype().nextOf().ty == Tvoid)
-            {
-                result = deduceEmptyArrayElement();
-                return;
-            }
-            if ((!e.elements || !e.elements.length) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray)
-            {
-                // tparam:T[] <- e:[] (void[])
-                result = deduceEmptyArrayElement();
-                return;
-            }
+    override TemplateValueParameter isTemplateValueParameter()
+    {
+        return this;
+    }
 
-            if (tparam.ty == Tarray && e.elements && e.elements.length)
-            {
-                Type tn = (cast(TypeDArray)tparam).next;
-                result = MATCH.exact;
-                if (e.basis)
-                {
-                    MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm);
-                    if (m < result)
-                        result = m;
-                }
-                foreach (el; *e.elements)
-                {
-                    if (result == MATCH.nomatch)
-                        break;
-                    if (!el)
-                        continue;
-                    MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm);
-                    if (m < result)
-                        result = m;
-                }
-                return;
-            }
+    override TemplateValueParameter syntaxCopy()
+    {
+        return new TemplateValueParameter(loc, ident,
+            valType.syntaxCopy(),
+            specValue ? specValue.syntaxCopy() : null,
+            defaultValue ? defaultValue.syntaxCopy() : null);
+    }
 
-            Type taai;
-            if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
-            {
-                // Consider compile-time known boundaries
-                e.type.nextOf().sarrayOf(e.elements.length).accept(this);
-                return;
-            }
-            visit(cast(Expression)e);
-        }
+    override bool declareParameter(Scope* sc)
+    {
+        /*
+            Do type semantic earlier.
 
-        override void visit(AssocArrayLiteralExp e)
-        {
-            if (tparam.ty == Taarray && e.keys && e.keys.length)
-            {
-                TypeAArray taa = cast(TypeAArray)tparam;
-                result = MATCH.exact;
-                foreach (i, key; *e.keys)
-                {
-                    MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm);
-                    if (m1 < result)
-                        result = m1;
-                    if (result == MATCH.nomatch)
-                        break;
-                    MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm);
-                    if (m2 < result)
-                        result = m2;
-                    if (result == MATCH.nomatch)
-                        break;
-                }
-                return;
-            }
-            visit(cast(Expression)e);
-        }
+            This means for certain erroneous value parameters
+            their "type" can be known earlier and thus a better
+            error message given.
 
-        override void visit(FuncExp e)
-        {
-            //printf("e.type = %s, tparam = %s\n", e.type.toChars(), tparam.toChars());
-            if (e.td)
-            {
-                Type to = tparam;
-                if (!to.nextOf())
-                    return;
-                auto tof = to.nextOf().isTypeFunction();
-                if (!tof)
-                    return;
+            For example:
+            `template test(x* x) {}`
+            now yields "undefined identifier" rather than the opaque
+            "variable `x` is used as a type".
+         */
+        if (valType)
+            valType = valType.typeSemantic(loc, sc);
+        auto v = new VarDeclaration(loc, valType, ident, null);
+        v.storage_class = STC.templateparameter;
+        return sc.insert(v) !is null;
+    }
 
-                // Parameter types inference from 'tof'
-                assert(e.td._scope);
-                TypeFunction tf = e.fd.type.isTypeFunction();
-                //printf("\ttof = %s\n", tof.toChars());
-                //printf("\ttf  = %s\n", tf.toChars());
-                const dim = tf.parameterList.length;
+    override void print(RootObject oarg, RootObject oded)
+    {
+        printf(" %s\n", ident.toChars());
+        Expression ea = isExpression(oded);
+        if (specValue)
+            printf("\tSpecialization: %s\n", specValue.toChars());
+        printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL");
+    }
 
-                if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
-                    return;
+    override RootObject specialization()
+    {
+        return specValue;
+    }
 
-                auto tiargs = new Objects();
-                tiargs.reserve(e.td.parameters.length);
+    override bool hasDefaultArg()
+    {
+        return defaultValue !is null;
+    }
 
-                foreach (tp; *e.td.parameters)
-                {
-                    size_t u = 0;
-                    foreach (i, p; tf.parameterList)
-                    {
-                        if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
-                            break;
-                        ++u;
-                    }
-                    assert(u < dim);
-                    Parameter pto = tof.parameterList[u];
-                    if (!pto)
-                        break;
-                    Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774
-                    if (reliesOnTemplateParameters(t, parameters[inferStart .. parameters.length]))
-                        return;
-                    t = t.typeSemantic(e.loc, sc);
-                    if (t.ty == Terror)
-                        return;
-                    tiargs.push(t);
-                }
+    override void accept(Visitor v)
+    {
+        v.visit(this);
+    }
+}
 
-                // Set target of return type inference
-                if (!tf.next && tof.next)
-                    e.fd.treq = tparam;
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateAliasParameter
+ * Syntax:
+ *  specType ident : specAlias = defaultAlias
+ */
+extern (C++) final class TemplateAliasParameter : TemplateParameter
+{
+    Type specType;
+    RootObject specAlias;
+    RootObject defaultAlias;
 
-                auto ti = new TemplateInstance(e.loc, e.td, tiargs);
-                Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope);
+    extern (D) __gshared Dsymbol sdummy = null;
 
-                // Reset inference target for the later re-semantic
-                e.fd.treq = null;
+    extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe
+    {
+        super(loc, ident);
+        this.specType = specType;
+        this.specAlias = specAlias;
+        this.defaultAlias = defaultAlias;
+    }
 
-                if (ex.op == EXP.error)
-                    return;
-                if (ex.op != EXP.function_)
-                    return;
-                visit(ex.type);
-                return;
-            }
+    override TemplateAliasParameter isTemplateAliasParameter()
+    {
+        return this;
+    }
 
-            Type t = e.type;
+    override TemplateAliasParameter syntaxCopy()
+    {
+        return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
+    }
 
-            if (t.ty == Tdelegate && tparam.ty == Tpointer)
-                return;
+    override bool declareParameter(Scope* sc)
+    {
+        auto ti = new TypeIdentifier(loc, ident);
+        Declaration ad = new AliasDeclaration(loc, ident, ti);
+        return sc.insert(ad) !is null;
+    }
 
-            // Allow conversion from implicit function pointer to delegate
-            if (e.tok == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate)
-            {
-                TypeFunction tf = t.nextOf().isTypeFunction();
-                t = (new TypeDelegate(tf)).merge();
-            }
-            //printf("tparam = %s <= e.type = %s, t = %s\n", tparam.toChars(), e.type.toChars(), t.toChars());
-            visit(t);
-        }
+    override void print(RootObject oarg, RootObject oded)
+    {
+        printf(" %s\n", ident.toChars());
+        Dsymbol sa = isDsymbol(oded);
+        assert(sa);
+        printf("\tParameter alias: %s\n", sa.toChars());
+    }
 
-        override void visit(SliceExp e)
-        {
-            Type taai;
-            if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0))
-            {
-                // Consider compile-time known boundaries
-                if (Type tsa = toStaticArrayType(e))
-                {
-                    tsa.accept(this);
-                    if (result > MATCH.convert)
-                        result = MATCH.convert; // match with implicit conversion at most
-                    return;
-                }
-            }
-            visit(cast(Expression)e);
-        }
+    override RootObject specialization()
+    {
+        return specAlias;
+    }
 
-        override void visit(CommaExp e)
-        {
-            e.e2.accept(this);
-        }
+    override bool hasDefaultArg()
+    {
+        return defaultAlias !is null;
     }
 
-    scope DeduceType v = new DeduceType();
-    if (Type t = isType(o))
-        t.accept(v);
-    else if (Expression e = isExpression(o))
+    override void accept(Visitor v)
     {
-        assert(wm);
-        e.accept(v);
+        v.visit(this);
     }
-    else
-        assert(0);
-    return v.result;
 }
 
-
-/* Helper for TypeClass.deduceType().
- * Classes can match with implicit conversion to a base class or interface.
- * This is complicated, because there may be more than one base class which
- * matches. In such cases, one or more parameters remain ambiguous.
- * For example,
- *
- *   interface I(X, Y) {}
- *   class C : I(uint, double), I(char, double) {}
- *   C x;
- *   foo(T, U)( I!(T, U) x)
- *
- *   deduces that U is double, but T remains ambiguous (could be char or uint).
- *
- * Given a baseclass b, and initial deduced types 'dedtypes', this function
- * tries to match tparam with b, and also tries all base interfaces of b.
- * If a match occurs, numBaseClassMatches is incremented, and the new deduced
- * types are ANDed with the current 'best' estimate for dedtypes.
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateSequenceParameter
+ * Syntax:
+ *  ident ...
  */
-private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches)
+extern (C++) final class TemplateTupleParameter : TemplateParameter
 {
-    if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null)
+    extern (D) this(Loc loc, Identifier ident) @safe
     {
-        // Make a temporary copy of dedtypes so we don't destroy it
-        auto tmpdedtypes = new Objects(dedtypes.length);
-        memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.length * (void*).sizeof);
+        super(loc, ident);
+    }
 
-        auto t = new TypeInstance(Loc.initial, parti);
-        MATCH m = deduceType(t, sc, tparam, parameters, *tmpdedtypes);
-        if (m > MATCH.nomatch)
-        {
-            // If this is the first ever match, it becomes our best estimate
-            if (numBaseClassMatches == 0)
-                memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.length * (void*).sizeof);
-            else
-                for (size_t k = 0; k < tmpdedtypes.length; ++k)
-                {
-                    // If we've found more than one possible type for a parameter,
-                    // mark it as unknown.
-                    if ((*tmpdedtypes)[k] != best[k])
-                        best[k] = dedtypes[k];
-                }
-            ++numBaseClassMatches;
-        }
+    override TemplateTupleParameter isTemplateTupleParameter()
+    {
+        return this;
     }
 
-    // Now recursively test the inherited interfaces
-    foreach (ref bi; b.baseInterfaces)
+    override TemplateTupleParameter syntaxCopy()
     {
-        deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+        return new TemplateTupleParameter(loc, ident);
     }
-}
 
-/********************
- * Match template `parameters` to the target template instance.
- * Example:
- *    struct Temp(U, int Z) {}
- *    void foo(T)(Temp!(T, 3));
- *    foo(Temp!(int, 3)());
- * Input:
- *    sc               = context
- *    parameters       = template params of foo -> [T]
- *    tiargs           = <Temp!(int, 3)>.tiargs  -> [int, 3]
- *    tdtypes          = <Temp!(int, 3)>.tdtypes -> [int, 3]
- *    tempdecl         = <struct Temp!(T, int Z)> -> [T, Z]
- *    tp               = <Temp!(T, 3)>
- * Output:
- *    dedtypes         = deduced params of `foo(Temp!(int, 3)())` -> [int]
- */
-private bool resolveTemplateInstantiation(Scope* sc, TemplateParameters* parameters, Objects* tiargs, Objects* tdtypes, TemplateDeclaration tempdecl, TypeInstance tp, Objects* dedtypes)
-{
-    for (size_t i = 0; 1; i++)
+    override bool declareParameter(Scope* sc)
     {
-        //printf("\ttest: tempinst.tiargs[%zu]\n", i);
-        RootObject o1 = null;
-        if (i < tiargs.length)
-            o1 = (*tiargs)[i];
-        else if (i < tdtypes.length && i < tp.tempinst.tiargs.length)
-        {
-            // Pick up default arg
-            o1 = (*tdtypes)[i];
-        }
-        else if (i >= tp.tempinst.tiargs.length)
-            break;
-        //printf("\ttest: o1 = %s\n", o1.toChars());
-        if (i >= tp.tempinst.tiargs.length)
-        {
-            size_t dim = tempdecl.parameters.length - (tempdecl.isVariadic() ? 1 : 0);
-            while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg()))
-            {
-                i++;
-            }
-            if (i >= dim)
-                break; // match if all remained parameters are dependent
-            return false;
-        }
+        auto ti = new TypeIdentifier(loc, ident);
+        Declaration ad = new AliasDeclaration(loc, ident, ti);
+        return sc.insert(ad) !is null;
+    }
 
-        RootObject o2 = (*tp.tempinst.tiargs)[i];
-        Type t2 = isType(o2);
-        //printf("\ttest: o2 = %s\n", o2.toChars());
-        size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.length - 1)
-            ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
-        if (j != IDX_NOTFOUND && j == parameters.length - 1 &&
-            (*parameters)[j].isTemplateTupleParameter())
-        {
-            /* Given:
-                 *  struct A(B...) {}
-                 *  alias A!(int, float) X;
-                 *  static if (is(X Y == A!(Z), Z...)) {}
-                 * deduce that Z is a tuple(int, float)
-                 */
-
-            /* Create tuple from remaining args
-                 */
-            size_t vtdim = (tempdecl.isVariadic() ? tiargs.length : tdtypes.length) - i;
-            auto vt = new Tuple(vtdim);
-            for (size_t k = 0; k < vtdim; k++)
-            {
-                RootObject o;
-                if (k < tiargs.length)
-                    o = (*tiargs)[i + k];
-                else // Pick up default arg
-                    o = (*tdtypes)[i + k];
-                vt.objects[k] = o;
-            }
+    override void print(RootObject oarg, RootObject oded)
+    {
+        printf(" %s... [", ident.toChars());
+        Tuple v = isTuple(oded);
+        assert(v);
 
-            Tuple v = cast(Tuple)(*dedtypes)[j];
-            if (v)
-            {
-                if (!match(v, vt))
-                    return false;
-            }
-            else
-                (*dedtypes)[j] = vt;
-            break;
-        }
-        else if (!o1)
-            break;
-
-        Type t1 = isType(o1);
-        Dsymbol s1 = isDsymbol(o1);
-        Dsymbol s2 = isDsymbol(o2);
-        Expression e1 = s1 ? getValue(s1) : getValue(isExpression(o1));
-        Expression e2 = isExpression(o2);
-        version (none)
+        //printf("|%d| ", v.objects.length);
+        foreach (i, o; v.objects)
         {
-            Tuple v1 = isTuple(o1);
-            Tuple v2 = isTuple(o2);
-            if (t1)
-                printf("t1 = %s\n", t1.toChars());
-            if (t2)
-                printf("t2 = %s\n", t2.toChars());
-            if (e1)
-                printf("e1 = %s\n", e1.toChars());
-            if (e2)
-                printf("e2 = %s\n", e2.toChars());
-            if (s1)
-                printf("s1 = %s\n", s1.toChars());
-            if (s2)
-                printf("s2 = %s\n", s2.toChars());
-            if (v1)
-                printf("v1 = %s\n", v1.toChars());
-            if (v2)
-                printf("v2 = %s\n", v2.toChars());
-        }
+            if (i)
+                printf(", ");
 
-        if (t1 && t2)
-        {
-            if (!deduceType(t1, sc, t2, *parameters, *dedtypes))
-                return false;
-        }
-        else if (e1 && e2)
-        {
-        Le:
-            e1 = e1.ctfeInterpret();
+            Dsymbol sa = isDsymbol(o);
+            if (sa)
+                printf("alias: %s", sa.toChars());
+            Type ta = isType(o);
+            if (ta)
+                printf("type: %s", ta.toChars());
+            Expression ea = isExpression(o);
+            if (ea)
+                printf("exp: %s", ea.toChars());
 
-            /* 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 == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter))
-            {
-                /*
-                     * (T:Number!(e2), int e2)
-                     */
-                j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters);
-                if (j != IDX_NOTFOUND)
-                    goto L1;
-                // The template parameter was not from this template
-                // (it may be from a parent template, for example)
-            }
+            assert(!isTuple(o)); // no nested Tuple arguments
+        }
+        printf("]\n");
+    }
 
-            e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417
-            e2 = e2.ctfeInterpret();
+    override RootObject specialization()
+    {
+        return null;
+    }
 
-            //printf("e1 = %s, type = %s %d\n", e1.toChars(), e1.type.toChars(), e1.type.ty);
-            //printf("e2 = %s, type = %s %d\n", e2.toChars(), e2.type.toChars(), e2.type.ty);
-            if (!e1.equals(e2))
-            {
-                if (!e2.implicitConvTo(e1.type))
-                    return false;
+    override bool hasDefaultArg()
+    {
+        return false;
+    }
 
-                e2 = e2.implicitCastTo(sc, e1.type);
-                e2 = e2.ctfeInterpret();
-                if (!e1.equals(e2))
-                    return false;
-            }
-        }
-        else if (e1 && t2 && t2.ty == Tident)
-        {
-            j = templateParameterLookup(t2, parameters);
-        L1:
-            if (j == IDX_NOTFOUND)
-            {
-                t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
-                if (e2)
-                    goto Le;
-                return false;
-            }
-            if (!(*parameters)[j].matchArg(sc, e1, j, parameters, *dedtypes, null))
-                return false;
-        }
-        else if (s1 && s2)
-        {
-        Ls:
-            if (!s1.equals(s2))
-                return false;
-        }
-        else if (s1 && t2 && t2.ty == Tident)
-        {
-            j = templateParameterLookup(t2, parameters);
-            if (j == IDX_NOTFOUND)
-            {
-                t2.resolve((cast(TypeIdentifier)t2).loc, sc, e2, t2, s2);
-                if (s2)
-                    goto Ls;
-                return false;
-            }
-            if (!(*parameters)[j].matchArg(sc, s1, j, parameters, *dedtypes, null))
-                return false;
-        }
-        else
-            return false;
+    override void accept(Visitor v)
+    {
+        v.visit(this);
     }
-    return true;
 }
 
-
 /***********************************************************
- * Check whether the type t representation relies on one or more the template parameters.
- * Params:
- *      t           = Tested type, if null, returns false.
- *      tparams     = Template parameters.
- *      iStart      = Start index of tparams to limit the tested parameters. If it's
- *                    nonzero, tparams[0..iStart] will be excluded from the test target.
+ * https://dlang.org/spec/template.html#explicit_tmp_instantiation
+ * Given:
+ *  foo!(args) =>
+ *      name = foo
+ *      tiargs = args
  */
-bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
+extern (C++) class TemplateInstance : ScopeDsymbol
 {
-    return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.length]);
-}
+    Identifier name;
 
-/***********************************************************
- * Check whether the type t representation relies on one or more the template parameters.
- * Params:
- *      t           = Tested type, if null, returns false.
- *      tparams     = Template parameters.
- */
-bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
-{
-    bool visitVector(TypeVector t)
+    // Array of Types/Expressions of template
+    // instance arguments [int*, char, 10*10]
+    Objects* tiargs;
+
+    // Array of Types/Expressions corresponding
+    // to TemplateDeclaration.parameters
+    // [int, char, 100]
+    Objects tdtypes;
+
+    // Modules imported by this template instance
+    Modules importedModules;
+
+    Dsymbol tempdecl;           // referenced by foo.bar.abc
+    Dsymbol enclosing;          // if referencing local symbols, this is the context
+    Dsymbol aliasdecl;          // !=null if instance is an alias for its sole member
+
+    /**
+    If this is not null and it has a value that is not the current object,
+     then this field points to an existing template instance
+     and that object has been duplicated into us.
+
+    If this object is a duplicate,
+     the ``memberOf`` field will be set to a root module (passed on CLI).
+
+    This information is useful to deduplicate analysis that may occur
+     after semantic 3 has completed.
+
+    See_Also: memberOf
+    */
+    TemplateInstance inst;
+
+    ScopeDsymbol argsym;        // argument symbol table
+    size_t hash;                // cached result of toHash()
+
+    /// For function template, these are the function fnames(name and loc of it) and arguments
+    /// Relevant because different resolutions of `auto ref` parameters
+    /// create different template instances even with the same template arguments
+    Expressions* fargs;
+    ArgumentLabels* fnames;
+
+    TemplateInstances* deferred;
+
+    /**
+    If this is not null then this template instance appears in a root module's members.
+
+    Note:   This is not useful for determining duplication status of this template instance.
+            Use the field ``inst`` for determining if a template instance has been duplicated into this object.
+
+    See_Also: inst
+    */
+    Module memberOf;
+
+    // Used to determine the instance needs code generation.
+    // Note that these are inaccurate until semantic analysis phase completed.
+    TemplateInstance tinst;     // enclosing template instance
+    TemplateInstance tnext;     // non-first instantiated instances
+    Module minst;               // the top module that instantiated this instance
+
+    private ushort _nest;       // for recursive pretty printing detection, 3 MSBs reserved for flags (below)
+    ubyte inuse;                // for recursive expansion detection
+
+    private enum Flag : uint
     {
-        return t.basetype.reliesOnTemplateParameters(tparams);
+        semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest
+        havetempdecl = semantictiargsdone >> 1,
+        gagged = semantictiargsdone >> 2,
+        available = gagged - 1 // always last flag minus one, 1s for all available bits
     }
 
-    bool visitAArray(TypeAArray t)
+    extern(D) final @safe @property pure nothrow @nogc
     {
-        return t.next.reliesOnTemplateParameters(tparams) ||
-               t.index.reliesOnTemplateParameters(tparams);
+        ushort nest() const { return _nest & Flag.available; }
+        void nestUp() { assert(nest() < Flag.available); ++_nest; }
+        void nestDown() { assert(nest() > 0); --_nest; }
+        /// has semanticTiargs() been done?
+        bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; }
+        void semantictiargsdone(bool x)
+        {
+            if (x) _nest |= Flag.semantictiargsdone;
+            else _nest &= ~Flag.semantictiargsdone;
+        }
+        /// if used second constructor
+        bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; }
+        void havetempdecl(bool x)
+        {
+            if (x) _nest |= Flag.havetempdecl;
+            else _nest &= ~Flag.havetempdecl;
+        }
+        /// if the instantiation is done with error gagging
+        bool gagged() const { return (_nest & Flag.gagged) != 0; }
+        void gagged(bool x)
+        {
+            if (x) _nest |= Flag.gagged;
+            else _nest &= ~Flag.gagged;
+        }
     }
 
-    bool visitFunction(TypeFunction t)
+    extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope
     {
-        foreach (i, fparam; t.parameterList)
+        super(loc, null);
+        static if (LOG)
         {
-            if (fparam.type.reliesOnTemplateParameters(tparams))
-                return true;
+            printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null");
         }
-        return t.next.reliesOnTemplateParameters(tparams);
+        this.dsym = DSYM.templateInstance;
+        this.name = ident;
+        this.tiargs = tiargs;
     }
 
-    bool visitIdentifier(TypeIdentifier t)
+    /*****************
+     * This constructor is only called when we figured out which function
+     * template to instantiate.
+     */
+    extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope
     {
-        foreach (tp; tparams)
+        super(loc, null);
+        static if (LOG)
         {
-            if (tp.ident.equals(t.ident))
-                return true;
+            printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars());
         }
-        return false;
+        this.dsym = DSYM.templateInstance;
+        this.name = td.ident;
+        this.tiargs = tiargs;
+        this.tempdecl = td;
+        this.semantictiargsdone = true;
+        this.havetempdecl = true;
+        assert(tempdecl._scope);
     }
 
-    bool visitInstance(TypeInstance t)
+    extern (D) static Objects* arraySyntaxCopy(Objects* objs)
     {
-        foreach (tp; tparams)
+        Objects* a = null;
+        if (objs)
         {
-            if (t.tempinst.name == tp.ident)
-                return true;
+            a = new Objects(objs.length);
+            foreach (i, o; *objs)
+                (*a)[i] = objectSyntaxCopy(o);
         }
-
-        if (t.tempinst.tiargs)
-            foreach (arg; *t.tempinst.tiargs)
-            {
-                if (Type ta = isType(arg))
-                {
-                    if (ta.reliesOnTemplateParameters(tparams))
-                        return true;
-                }
-            }
-
-        return false;
+        return a;
     }
 
-    bool visitTypeof(TypeTypeof t)
+    override TemplateInstance syntaxCopy(Dsymbol s)
     {
-        //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
-        return t.exp.reliesOnTemplateParameters(tparams);
+        TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null);
+        ti.tiargs = arraySyntaxCopy(tiargs);
+        TemplateDeclaration td;
+        if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null)
+            td.ScopeDsymbol.syntaxCopy(ti);
+        else
+            ScopeDsymbol.syntaxCopy(ti);
+        return ti;
     }
 
-    bool visitTuple(TypeTuple t)
+    override const(char)* kind() const
     {
-        if (t.arguments)
-            foreach (arg; *t.arguments)
-            {
-                if (arg.type.reliesOnTemplateParameters(tparams))
-                    return true;
-            }
+        return "template instance";
+    }
 
-        return false;
-    }
-
-    if (!t)
-        return false;
-
-    Type tb = t.toBasetype();
-    switch (tb.ty)
-    {
-        case Tvector:   return visitVector(tb.isTypeVector());
-        case Taarray:   return visitAArray(tb.isTypeAArray());
-        case Tfunction: return visitFunction(tb.isTypeFunction());
-        case Tident:    return visitIdentifier(tb.isTypeIdentifier());
-        case Tinstance: return visitInstance(tb.isTypeInstance());
-        case Ttypeof:   return visitTypeof(tb.isTypeTypeof());
-        case Ttuple:    return visitTuple(tb.isTypeTuple());
-        case Tenum:     return false;
-        default:        return tb.nextOf().reliesOnTemplateParameters(tparams);
-    }
-}
-
-/***********************************************************
- * Check whether the expression representation relies on one or more the template parameters.
- * Params:
- *      e           = expression to test
- *      tparams     = Template parameters.
- * Returns:
- *      true if it does
- */
-private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
-{
-    extern (C++) final class ReliesOnTemplateParameters : Visitor
-    {
-        alias visit = Visitor.visit;
-    public:
-        TemplateParameter[] tparams;
-        bool result;
-
-        extern (D) this(TemplateParameter[] tparams) @safe
-        {
-            this.tparams = tparams;
-        }
-
-        override void visit(Expression e)
-        {
-            //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
-        }
-
-        override void visit(IdentifierExp e)
-        {
-            //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            foreach (tp; tparams)
-            {
-                if (e.ident == tp.ident)
-                {
-                    result = true;
-                    return;
-                }
-            }
-        }
-
-        override void visit(TupleExp e)
-        {
-            //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            if (e.exps)
-            {
-                foreach (ea; *e.exps)
-                {
-                    ea.accept(this);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(ArrayLiteralExp e)
-        {
-            //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            if (e.elements)
-            {
-                foreach (el; *e.elements)
-                {
-                    el.accept(this);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(AssocArrayLiteralExp e)
-        {
-            //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            foreach (ek; *e.keys)
-            {
-                ek.accept(this);
-                if (result)
-                    return;
-            }
-            foreach (ev; *e.values)
-            {
-                ev.accept(this);
-                if (result)
-                    return;
-            }
-        }
-
-        override void visit(StructLiteralExp e)
-        {
-            //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            if (e.elements)
-            {
-                foreach (ea; *e.elements)
-                {
-                    ea.accept(this);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(TypeExp e)
-        {
-            //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            result = e.type.reliesOnTemplateParameters(tparams);
-        }
-
-        override void visit(NewExp e)
-        {
-            //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            if (e.placement)
-                e.placement.accept(this);
-            if (e.thisexp)
-                e.thisexp.accept(this);
-            result = e.newtype.reliesOnTemplateParameters(tparams);
-            if (!result && e.arguments)
-            {
-                foreach (ea; *e.arguments)
-                {
-                    ea.accept(this);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(NewAnonClassExp e)
-        {
-            //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            result = true;
-        }
-
-        override void visit(FuncExp e)
-        {
-            //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            result = true;
-        }
-
-        override void visit(TypeidExp e)
-        {
-            //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            if (auto ea = isExpression(e.obj))
-                ea.accept(this);
-            else if (auto ta = isType(e.obj))
-                result = ta.reliesOnTemplateParameters(tparams);
-        }
-
-        override void visit(TraitsExp e)
-        {
-            //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            if (e.args)
-            {
-                foreach (oa; *e.args)
-                {
-                    if (auto ea = isExpression(oa))
-                        ea.accept(this);
-                    else if (auto ta = isType(oa))
-                        result = ta.reliesOnTemplateParameters(tparams);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(IsExp e)
-        {
-            //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            result = e.targ.reliesOnTemplateParameters(tparams);
-        }
-
-        override void visit(UnaExp e)
-        {
-            //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            e.e1.accept(this);
-        }
-
-        override void visit(DotTemplateInstanceExp e)
-        {
-            //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(e.isUnaExp());
-            if (!result && e.ti.tiargs)
-            {
-                foreach (oa; *e.ti.tiargs)
-                {
-                    if (auto ea = isExpression(oa))
-                        ea.accept(this);
-                    else if (auto ta = isType(oa))
-                        result = ta.reliesOnTemplateParameters(tparams);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(CallExp e)
-        {
-            //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(e.isUnaExp());
-            if (!result && e.arguments)
-            {
-                foreach (ea; *e.arguments)
-                {
-                    ea.accept(this);
-                    if (result)
-                        return;
-                }
-            }
-        }
-
-        override void visit(CastExp e)
-        {
-            //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(e.isUnaExp());
-            // e.to can be null for cast() with no type
-            if (!result && e.to)
-                result = e.to.reliesOnTemplateParameters(tparams);
-        }
-
-        override void visit(SliceExp e)
-        {
-            //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(e.isUnaExp());
-            if (!result && e.lwr)
-                e.lwr.accept(this);
-            if (!result && e.upr)
-                e.upr.accept(this);
-        }
-
-        override void visit(IntervalExp e)
-        {
-            //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            e.lwr.accept(this);
-            if (!result)
-                e.upr.accept(this);
-        }
-
-        override void visit(ArrayExp e)
-        {
-            //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            visit(e.isUnaExp());
-            if (!result && e.arguments)
-            {
-                foreach (ea; *e.arguments)
-                    ea.accept(this);
-            }
-        }
-
-        override void visit(BinExp e)
-        {
-            //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            e.e1.accept(this);
-            if (!result)
-                e.e2.accept(this);
-        }
-
-        override void visit(CondExp e)
-        {
-            //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
-            e.econd.accept(this);
-            if (!result)
-                visit(e.isBinExp());
-        }
-    }
-
-    scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
-    e.accept(v);
-    return v.result;
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateParameter
- */
-extern (C++) class TemplateParameter : ASTNode
-{
-    Loc loc;
-    Identifier ident;
-
-    /* True if this is a part of precedent parameter specialization pattern.
-     *
-     *  template A(T : X!TL, alias X, TL...) {}
-     *  // X and TL are dependent template parameter
-     *
-     * A dependent template parameter should return MATCH.exact in matchArg()
-     * to respect the match level of the corresponding precedent parameter.
-     */
-    bool dependent;
-
-    /* ======================== TemplateParameter =============================== */
-    extern (D) this(Loc loc, Identifier ident) @safe
-    {
-        this.loc = loc;
-        this.ident = ident;
-    }
-
-    TemplateTypeParameter isTemplateTypeParameter()
-    {
-        return null;
-    }
-
-    TemplateValueParameter isTemplateValueParameter()
-    {
-        return null;
-    }
-
-    TemplateAliasParameter isTemplateAliasParameter()
-    {
-        return null;
-    }
-
-    TemplateThisParameter isTemplateThisParameter()
-    {
-        return null;
-    }
-
-    TemplateTupleParameter isTemplateTupleParameter()
-    {
-        return null;
-    }
-
-    abstract TemplateParameter syntaxCopy();
-
-    abstract bool declareParameter(Scope* sc);
-
-    abstract void print(RootObject oarg, RootObject oded);
-
-    abstract RootObject specialization();
-
-    abstract RootObject defaultArg(Loc instLoc, Scope* sc);
-
-    abstract bool hasDefaultArg();
-
-    override const(char)* toChars() const
-    {
-        return this.ident.toChars();
-    }
-
-    override DYNCAST dyncast() const
-    {
-        return DYNCAST.templateparameter;
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateTypeParameter
- * Syntax:
- *  ident : specType = defaultType
- */
-extern (C++) class TemplateTypeParameter : TemplateParameter
-{
-    Type specType;      // if !=null, this is the type specialization
-    Type defaultType;
-
-    extern (D) __gshared Type tdummy = null;
-
-    extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
-    {
-        super(loc, ident);
-        this.specType = specType;
-        this.defaultType = defaultType;
-    }
-
-    override final TemplateTypeParameter isTemplateTypeParameter()
-    {
-        return this;
-    }
-
-    override TemplateTypeParameter syntaxCopy()
-    {
-        return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
-    }
-
-    override final bool declareParameter(Scope* sc)
-    {
-        //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
-        auto ti = new TypeIdentifier(loc, ident);
-        Declaration ad = new AliasDeclaration(loc, ident, ti);
-        return sc.insert(ad) !is null;
-    }
-
-    override final void print(RootObject oarg, RootObject oded)
-    {
-        printf(" %s\n", ident.toChars());
-
-        Type t = isType(oarg);
-        Type ta = isType(oded);
-        assert(ta);
-
-        if (specType)
-            printf("\tSpecialization: %s\n", specType.toChars());
-        if (defaultType)
-            printf("\tDefault:        %s\n", defaultType.toChars());
-        printf("\tParameter:       %s\n", t ? t.toChars() : "NULL");
-        printf("\tDeduced Type:   %s\n", ta.toChars());
-    }
-
-    override final RootObject specialization()
-    {
-        return specType;
-    }
-
-    override final RootObject defaultArg(Loc instLoc, Scope* sc)
-    {
-        Type t = defaultType;
-        if (t)
-        {
-            t = t.syntaxCopy();
-            t = t.typeSemantic(loc, sc); // use the parameter loc
-        }
-        return t;
-    }
-
-    override final bool hasDefaultArg()
-    {
-        return defaultType !is null;
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateThisParameter
- * Syntax:
- *  this ident : specType = defaultType
- */
-extern (C++) final class TemplateThisParameter : TemplateTypeParameter
-{
-    extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe
-    {
-        super(loc, ident, specType, defaultType);
-    }
-
-    override TemplateThisParameter isTemplateThisParameter()
-    {
-        return this;
-    }
-
-    override TemplateThisParameter syntaxCopy()
-    {
-        return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateValueParameter
- * Syntax:
- *  valType ident : specValue = defaultValue
- */
-extern (C++) final class TemplateValueParameter : TemplateParameter
-{
-    Type valType;
-    Expression specValue;
-    Expression defaultValue;
-
-    extern (D) __gshared Expression[void*] edummies;
-
-    extern (D) this(Loc loc, Identifier ident, Type valType,
-        Expression specValue, Expression defaultValue) @safe
-    {
-        super(loc, ident);
-        this.valType = valType;
-        this.specValue = specValue;
-        this.defaultValue = defaultValue;
-    }
-
-    override TemplateValueParameter isTemplateValueParameter()
-    {
-        return this;
-    }
-
-    override TemplateValueParameter syntaxCopy()
-    {
-        return new TemplateValueParameter(loc, ident,
-            valType.syntaxCopy(),
-            specValue ? specValue.syntaxCopy() : null,
-            defaultValue ? defaultValue.syntaxCopy() : null);
-    }
-
-    override bool declareParameter(Scope* sc)
-    {
-        /*
-            Do type semantic earlier.
-
-            This means for certain erroneous value parameters
-            their "type" can be known earlier and thus a better
-            error message given.
-
-            For example:
-            `template test(x* x) {}`
-            now yields "undefined identifier" rather than the opaque
-            "variable `x` is used as a type".
-         */
-        if (valType)
-            valType = valType.typeSemantic(loc, sc);
-        auto v = new VarDeclaration(loc, valType, ident, null);
-        v.storage_class = STC.templateparameter;
-        return sc.insert(v) !is null;
-    }
-
-    override void print(RootObject oarg, RootObject oded)
-    {
-        printf(" %s\n", ident.toChars());
-        Expression ea = isExpression(oded);
-        if (specValue)
-            printf("\tSpecialization: %s\n", specValue.toChars());
-        printf("\tParameter Value: %s\n", ea ? ea.toChars() : "NULL");
-    }
-
-    override RootObject specialization()
-    {
-        return specValue;
-    }
-
-    override RootObject defaultArg(Loc instLoc, Scope* sc)
-    {
-        Expression e = defaultValue;
-        if (!e)
-            return null;
-
-        e = e.syntaxCopy();
-        Scope* sc2 = sc.push();
-        sc2.inDefaultArg = true;
-        e = e.expressionSemantic(sc2);
-        sc2.pop();
-        if (e is null)
-            return null;
-        if (auto te = e.isTemplateExp())
-        {
-            assert(sc && sc.tinst);
-            if (te.td == sc.tinst.tempdecl)
-            {
-                // defaultValue is a reference to its template declaration
-                // i.e: `template T(int arg = T)`
-                // Raise error now before calling resolveProperties otherwise we'll
-                // start looping on the expansion of the template instance.
-                auto td = sc.tinst.tempdecl;
-                .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars);
-                return ErrorExp.get();
-            }
-        }
-        if ((e = resolveProperties(sc, e)) is null)
-            return null;
-        e = e.resolveLoc(instLoc, sc); // use the instantiated loc
-        e = e.optimize(WANTvalue);
-
-        return e;
-    }
-
-    override bool hasDefaultArg()
-    {
-        return defaultValue !is null;
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateAliasParameter
- * Syntax:
- *  specType ident : specAlias = defaultAlias
- */
-extern (C++) final class TemplateAliasParameter : TemplateParameter
-{
-    Type specType;
-    RootObject specAlias;
-    RootObject defaultAlias;
-
-    extern (D) __gshared Dsymbol sdummy = null;
-
-    extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe
-    {
-        super(loc, ident);
-        this.specType = specType;
-        this.specAlias = specAlias;
-        this.defaultAlias = defaultAlias;
-    }
-
-    override TemplateAliasParameter isTemplateAliasParameter()
-    {
-        return this;
-    }
-
-    override TemplateAliasParameter syntaxCopy()
-    {
-        return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
-    }
-
-    override bool declareParameter(Scope* sc)
-    {
-        auto ti = new TypeIdentifier(loc, ident);
-        Declaration ad = new AliasDeclaration(loc, ident, ti);
-        return sc.insert(ad) !is null;
-    }
-
-    override void print(RootObject oarg, RootObject oded)
-    {
-        printf(" %s\n", ident.toChars());
-        Dsymbol sa = isDsymbol(oded);
-        assert(sa);
-        printf("\tParameter alias: %s\n", sa.toChars());
-    }
-
-    override RootObject specialization()
-    {
-        return specAlias;
-    }
-
-    override RootObject defaultArg(Loc instLoc, Scope* sc)
-    {
-        RootObject da = defaultAlias;
-        if (auto ta = isType(defaultAlias))
-        {
-            switch (ta.ty)
-            {
-            // If the default arg is a template, instantiate for each type
-            case Tinstance :
-            // same if the default arg is a mixin, traits, typeof
-            // since the content might rely on a previous parameter
-            // (https://issues.dlang.org/show_bug.cgi?id=23686)
-            case Tmixin, Ttypeof, Ttraits :
-                da = ta.syntaxCopy();
-                break;
-            default:
-            }
-        }
-
-        RootObject o = aliasParameterSemantic(loc, sc, da, null); // use the parameter loc
-        return o;
-    }
-
-    override bool hasDefaultArg()
-    {
-        return defaultAlias !is null;
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#TemplateSequenceParameter
- * Syntax:
- *  ident ...
- */
-extern (C++) final class TemplateTupleParameter : TemplateParameter
-{
-    extern (D) this(Loc loc, Identifier ident) @safe
-    {
-        super(loc, ident);
-    }
-
-    override TemplateTupleParameter isTemplateTupleParameter()
-    {
-        return this;
-    }
-
-    override TemplateTupleParameter syntaxCopy()
-    {
-        return new TemplateTupleParameter(loc, ident);
-    }
-
-    override bool declareParameter(Scope* sc)
-    {
-        auto ti = new TypeIdentifier(loc, ident);
-        Declaration ad = new AliasDeclaration(loc, ident, ti);
-        return sc.insert(ad) !is null;
-    }
-
-    override void print(RootObject oarg, RootObject oded)
-    {
-        printf(" %s... [", ident.toChars());
-        Tuple v = isTuple(oded);
-        assert(v);
-
-        //printf("|%d| ", v.objects.length);
-        foreach (i, o; v.objects)
-        {
-            if (i)
-                printf(", ");
-
-            Dsymbol sa = isDsymbol(o);
-            if (sa)
-                printf("alias: %s", sa.toChars());
-            Type ta = isType(o);
-            if (ta)
-                printf("type: %s", ta.toChars());
-            Expression ea = isExpression(o);
-            if (ea)
-                printf("exp: %s", ea.toChars());
-
-            assert(!isTuple(o)); // no nested Tuple arguments
-        }
-        printf("]\n");
-    }
-
-    override RootObject specialization()
-    {
-        return null;
-    }
-
-    override RootObject defaultArg(Loc instLoc, Scope* sc)
-    {
-        return null;
-    }
-
-    override bool hasDefaultArg()
-    {
-        return false;
-    }
-
-    override void accept(Visitor v)
-    {
-        v.visit(this);
-    }
-}
-
-/***********************************************************
- * https://dlang.org/spec/template.html#explicit_tmp_instantiation
- * Given:
- *  foo!(args) =>
- *      name = foo
- *      tiargs = args
- */
-extern (C++) class TemplateInstance : ScopeDsymbol
-{
-    Identifier name;
-
-    // Array of Types/Expressions of template
-    // instance arguments [int*, char, 10*10]
-    Objects* tiargs;
-
-    // Array of Types/Expressions corresponding
-    // to TemplateDeclaration.parameters
-    // [int, char, 100]
-    Objects tdtypes;
-
-    // Modules imported by this template instance
-    Modules importedModules;
-
-    Dsymbol tempdecl;           // referenced by foo.bar.abc
-    Dsymbol enclosing;          // if referencing local symbols, this is the context
-    Dsymbol aliasdecl;          // !=null if instance is an alias for its sole member
-
-    /**
-    If this is not null and it has a value that is not the current object,
-     then this field points to an existing template instance
-     and that object has been duplicated into us.
-
-    If this object is a duplicate,
-     the ``memberOf`` field will be set to a root module (passed on CLI).
-
-    This information is useful to deduplicate analysis that may occur
-     after semantic 3 has completed.
-
-    See_Also: memberOf
-    */
-    TemplateInstance inst;
-
-    ScopeDsymbol argsym;        // argument symbol table
-    size_t hash;                // cached result of toHash()
-
-    /// For function template, these are the function fnames(name and loc of it) and arguments
-    /// Relevant because different resolutions of `auto ref` parameters
-    /// create different template instances even with the same template arguments
-    Expressions* fargs;
-    ArgumentLabels* fnames;
-
-    TemplateInstances* deferred;
-
-    /**
-    If this is not null then this template instance appears in a root module's members.
-
-    Note:   This is not useful for determining duplication status of this template instance.
-            Use the field ``inst`` for determining if a template instance has been duplicated into this object.
-
-    See_Also: inst
-    */
-    Module memberOf;
-
-    // Used to determine the instance needs code generation.
-    // Note that these are inaccurate until semantic analysis phase completed.
-    TemplateInstance tinst;     // enclosing template instance
-    TemplateInstance tnext;     // non-first instantiated instances
-    Module minst;               // the top module that instantiated this instance
-
-    private ushort _nest;       // for recursive pretty printing detection, 3 MSBs reserved for flags (below)
-    ubyte inuse;                // for recursive expansion detection
-
-    private enum Flag : uint
-    {
-        semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest
-        havetempdecl = semantictiargsdone >> 1,
-        gagged = semantictiargsdone >> 2,
-        available = gagged - 1 // always last flag minus one, 1s for all available bits
-    }
-
-    extern(D) final @safe @property pure nothrow @nogc
-    {
-        ushort nest() const { return _nest & Flag.available; }
-        void nestUp() { assert(nest() < Flag.available); ++_nest; }
-        void nestDown() { assert(nest() > 0); --_nest; }
-        /// has semanticTiargs() been done?
-        bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; }
-        void semantictiargsdone(bool x)
-        {
-            if (x) _nest |= Flag.semantictiargsdone;
-            else _nest &= ~Flag.semantictiargsdone;
-        }
-        /// if used second constructor
-        bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; }
-        void havetempdecl(bool x)
-        {
-            if (x) _nest |= Flag.havetempdecl;
-            else _nest &= ~Flag.havetempdecl;
-        }
-        /// if the instantiation is done with error gagging
-        bool gagged() const { return (_nest & Flag.gagged) != 0; }
-        void gagged(bool x)
-        {
-            if (x) _nest |= Flag.gagged;
-            else _nest &= ~Flag.gagged;
-        }
-    }
-
-    extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope
-    {
-        super(loc, null);
-        static if (LOG)
-        {
-            printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null");
-        }
-        this.dsym = DSYM.templateInstance;
-        this.name = ident;
-        this.tiargs = tiargs;
-    }
-
-    /*****************
-     * This constructor is only called when we figured out which function
-     * template to instantiate.
-     */
-    extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope
-    {
-        super(loc, null);
-        static if (LOG)
-        {
-            printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars());
-        }
-        this.dsym = DSYM.templateInstance;
-        this.name = td.ident;
-        this.tiargs = tiargs;
-        this.tempdecl = td;
-        this.semantictiargsdone = true;
-        this.havetempdecl = true;
-        assert(tempdecl._scope);
-    }
-
-    extern (D) static Objects* arraySyntaxCopy(Objects* objs)
-    {
-        Objects* a = null;
-        if (objs)
-        {
-            a = new Objects(objs.length);
-            foreach (i, o; *objs)
-                (*a)[i] = objectSyntaxCopy(o);
-        }
-        return a;
-    }
-
-    override TemplateInstance syntaxCopy(Dsymbol s)
-    {
-        TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null);
-        ti.tiargs = arraySyntaxCopy(tiargs);
-        TemplateDeclaration td;
-        if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null)
-            td.ScopeDsymbol.syntaxCopy(ti);
-        else
-            ScopeDsymbol.syntaxCopy(ti);
-        return ti;
-    }
-
-    // resolve real symbol
-    override final Dsymbol toAlias()
-    {
-        static if (LOG)
-        {
-            printf("TemplateInstance.toAlias()\n");
-        }
-        if (!inst)
-        {
-            // Maybe we can resolve it
-            if (_scope)
-            {
-                dsymbolSemantic(this, _scope);
-            }
-            if (!inst)
-            {
-                .error(loc, "%s `%s` cannot resolve forward reference", kind, toPrettyChars);
-                errors = true;
-                return this;
-            }
-        }
-
-        if (inst != this)
-            return inst.toAlias();
-
-        if (aliasdecl)
-        {
-            return aliasdecl.toAlias();
-        }
-
-        return inst;
-    }
-
-    override const(char)* kind() const
-    {
-        return "template instance";
-    }
-
-    override final const(char)* toPrettyCharsHelper()
-    {
-        OutBuffer buf;
-        toCBufferInstance(this, buf, true);
-        return buf.extractChars();
-    }
-
-    /**************************************
-     * Given an error instantiating the TemplateInstance,
-     * give the nested TemplateInstance instantiations that got
-     * us here. Those are a list threaded into the nested scopes.
-     * Params:
-     *  cl = classification of this trace as printing either errors or deprecations
-     *  max_shown = maximum number of trace elements printed (controlled with -v/-verror-limit)
-     */
-    extern(D) final void printInstantiationTrace(Classification cl = Classification.error,
-                                                 const(uint) max_shown = global.params.v.errorSupplementCount())
-    {
-        if (global.gag)
-            return;
-
-        // Print full trace for verbose mode, otherwise only short traces
-        const(char)* format = "instantiated from here: `%s`";
-
-        // This returns a function pointer
-        scope printFn = () {
-            final switch (cl)
-            {
-                case Classification.error:
-                    return &errorSupplemental;
-                case Classification.deprecation:
-                    return &deprecationSupplemental;
-                case Classification.gagged, Classification.tip, Classification.warning:
-                    assert(0);
-            }
-        }();
-
-        // determine instantiation depth and number of recursive instantiations
-        int n_instantiations = 1;
-        int n_totalrecursions = 0;
-        for (TemplateInstance cur = this; cur; cur = cur.tinst)
-        {
-            ++n_instantiations;
-            // Set error here as we don't want it to depend on the number of
-            // entries that are being printed.
-            if (cl == Classification.error ||
-                (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) ||
-                (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
-                cur.errors = true;
-
-            // If two instantiations use the same declaration, they are recursive.
-            // (this works even if they are instantiated from different places in the
-            // same template).
-            // In principle, we could also check for multiple-template recursion, but it's
-            // probably not worthwhile.
-            if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
-                ++n_totalrecursions;
-        }
-
-        if (n_instantiations <= max_shown)
-        {
-            for (TemplateInstance cur = this; cur; cur = cur.tinst)
-                printFn(cur.loc, format, cur.toErrMsg());
-        }
-        else if (n_instantiations - n_totalrecursions <= max_shown)
-        {
-            // By collapsing recursive instantiations into a single line,
-            // we can stay under the limit.
-            int recursionDepth = 0;
-            for (TemplateInstance cur = this; cur; cur = cur.tinst)
-            {
-                if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
-                {
-                    ++recursionDepth;
-                }
-                else
-                {
-                    if (recursionDepth)
-                        printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars());
-                    else
-                        printFn(cur.loc, format, cur.toChars());
-                    recursionDepth = 0;
-                }
-            }
-        }
-        else
-        {
-            // Even after collapsing the recursions, the depth is too deep.
-            // Just display the first few and last few instantiations.
-            uint i = 0;
-            for (TemplateInstance cur = this; cur; cur = cur.tinst)
-            {
-                if (i == max_shown / 2)
-                    printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
-
-                if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2)
-                    printFn(cur.loc, format, cur.toChars());
-                ++i;
-            }
-        }
-    }
-
-    /*************************************
-     * Lazily generate identifier for template instance.
-     * This is because 75% of the ident's are never needed.
-     */
-    override final Identifier getIdent()
-    {
-        if (!ident && inst && !errors)
-            ident = genIdent(tiargs); // need an identifier for name mangling purposes.
-        return ident;
-    }
-
-    /*************************************
-     * Compare proposed template instantiation with existing template instantiation.
-     * Note that this is not commutative because of the auto ref check.
-     * Params:
-     *  ti = existing template instantiation
-     * Returns:
-     *  true for match
-     */
-    final bool equalsx(TemplateInstance ti)
-    {
-        //printf("this = %p, ti = %p\n", this, ti);
-        assert(tdtypes.length == ti.tdtypes.length);
-
-        // Nesting must match
-        if (enclosing != ti.enclosing)
-        {
-            //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
-            return false;
-        }
-        //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
-
-        if (!arrayObjectMatch(tdtypes, ti.tdtypes))
-            return false;
-
-        /* Template functions may have different instantiations based on
-         * "auto ref" parameters.
-         */
-        auto fd = ti.toAlias().isFuncDeclaration();
-        if (!fd)
-            return true;
-        if (fd.errors)
-            return true;
-
-        auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
-            ArgumentList(this.fargs, this.fnames), null);
-
-        // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
-        // In that case, equalsx returns true to prevent endless template instantiations
-        // However, it can also mean the function was explicitly instantiated
-        // without function arguments: fail_compilation/fail14669
-        // Hence the following check:
-        if (this.fargs && !resolvedArgs)
-            return true;
-
-        Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
-
-        auto fparameters = fd.getParameterList();
-        size_t nfparams = fparameters.length;   // Num function parameters
-        for (size_t j = 0; j < nfparams; j++)
-        {
-            Parameter fparam = fparameters[j];
-            if (!(fparam.storageClass & STC.autoref) )      // if "auto ref"
-                continue;
-
-            Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
-            // resolveNamedArgs strips trailing nulls / default params
-            // when it doesn't anymore, the ternary can be replaced with:
-            // assert(j < resolvedArgs.length);
-            if (!farg)
-                farg = fparam.defaultArg;
-            if (!farg)
-                return false;
-            if (farg.isLvalue())
-            {
-                if (!(fparam.storageClass & STC.ref_))
-                    return false; // auto ref's don't match
-            }
-            else
-            {
-                if (fparam.storageClass & STC.ref_)
-                    return false; // auto ref's don't match
-            }
-        }
-        return true;
-    }
-
-    extern (D) final size_t toHash()
-    {
-        if (!hash)
-        {
-            hash = cast(size_t)cast(void*)enclosing;
-            hash += arrayObjectHash(tdtypes);
-            hash += hash == 0;
-        }
-        return hash;
-    }
-
-    /**
-        Returns: true if the instances' innards are discardable.
-
-        The idea of this function is to see if the template instantiation
-        can be 100% replaced with its eponymous member. All other members
-        can be discarded, even in the compiler to free memory (for example,
-        the template could be expanded in a region allocator, deemed trivial,
-        the end result copied back out independently and the entire region freed),
-        and can be elided entirely from the binary.
-
-        The current implementation affects code that generally looks like:
-
-        ---
-        template foo(args...) {
-            some_basic_type_or_string helper() { .... }
-            enum foo = helper();
-        }
-        ---
-
-        since it was the easiest starting point of implementation but it can and
-        should be expanded more later.
-    */
-    final bool isDiscardable()
-    {
-        if (aliasdecl is null)
-            return false;
-
-        auto v = aliasdecl.isVarDeclaration();
-        if (v is null)
-            return false;
-
-        if (!(v.storage_class & STC.manifest))
-            return false;
-
-        // Currently only doing basic types here because it is the easiest proof-of-concept
-        // implementation with minimal risk of side effects, but it could likely be
-        // expanded to any type that already exists outside this particular instance.
-        if (!(v.type.equals(Type.tstring) || (v.type.isTypeBasic() !is null)))
-            return false;
-
-        // Static ctors and dtors, even in an eponymous enum template, are still run,
-        // so if any of them are in here, we'd better not assume it is trivial lest
-        // we break useful code
-        foreach(member; *members)
-        {
-            if(member.hasStaticCtorOrDtor())
-                return false;
-            if(member.isStaticDtorDeclaration())
-                return false;
-            if(member.isStaticCtorDeclaration())
-                return false;
-        }
-
-        // but if it passes through this gauntlet... it should be fine. D code will
-        // see only the eponymous member, outside stuff can never access it, even through
-        // reflection; the outside world ought to be none the wiser. Even dmd should be
-        // able to simply free the memory of everything except the final result.
-
-        return true;
-    }
-
-
-    /***********************************************
-     * Returns true if this is not instantiated in non-root module, and
-     * is a part of non-speculative instantiatiation.
-     *
-     * Note: minst does not stabilize until semantic analysis is completed,
-     * so don't call this function during semantic analysis to return precise result.
-     */
-    final bool needsCodegen()
-    {
-        //printf("needsCodegen() %s\n", toChars());
-
-        // minst is finalized after the 1st invocation.
-        // tnext is only needed for the 1st invocation and
-        // cleared for further invocations.
-        TemplateInstance tnext = this.tnext;
-        TemplateInstance tinst = this.tinst;
-        this.tnext = null;
-
-        // Don't do codegen if the instance has errors,
-        // is a dummy instance (see evaluateConstraint),
-        // or is determined to be discardable.
-        if (errors || inst is null || inst.isDiscardable())
-        {
-            minst = null; // mark as speculative
-            return false;
-        }
-
-        // This should only be called on the primary instantiation.
-        assert(this is inst);
-
-        if (global.params.allInst)
-        {
-            // Do codegen if there is an instantiation from a root module, to maximize link-ability.
-            static ThreeState needsCodegenAllInst(TemplateInstance tithis, TemplateInstance tinst)
-            {
-                // Do codegen if `this` is instantiated from a root module.
-                if (tithis.minst && tithis.minst.isRoot())
-                    return ThreeState.yes;
-
-                // Do codegen if the ancestor needs it.
-                if (tinst && tinst.inst && tinst.inst.needsCodegen())
-                {
-                    tithis.minst = tinst.inst.minst; // cache result
-                    assert(tithis.minst);
-                    assert(tithis.minst.isRoot());
-                    return ThreeState.yes;
-                }
-                return ThreeState.none;
-            }
-
-            if (const needsCodegen = needsCodegenAllInst(this, tinst))
-                return needsCodegen == ThreeState.yes ? true : false;
-
-            // Do codegen if a sibling needs it.
-            for (; tnext; tnext = tnext.tnext)
-            {
-                const needsCodegen = needsCodegenAllInst(tnext, tnext.tinst);
-                if (needsCodegen == ThreeState.yes)
-                {
-                    minst = tnext.minst; // cache result
-                    assert(minst);
-                    assert(minst.isRoot());
-                    return true;
-                }
-                else if (!minst && tnext.minst)
-                {
-                    minst = tnext.minst; // cache result from non-speculative sibling
-                    // continue searching
-                }
-                else if (needsCodegen != ThreeState.none)
-                    break;
-            }
-
-            // Elide codegen because there's no instantiation from any root modules.
-            return false;
-        }
-
-        // Prefer instantiations from non-root modules, to minimize object code size.
-
-        /* If a TemplateInstance is ever instantiated from a non-root module,
-         * we do not have to generate code for it,
-         * because it will be generated when the non-root module is compiled.
-         *
-         * But, if the non-root 'minst' imports any root modules, it might still need codegen.
-         *
-         * The problem is if A imports B, and B imports A, and both A
-         * and B instantiate the same template, does the compilation of A
-         * or the compilation of B do the actual instantiation?
-         *
-         * See https://issues.dlang.org/show_bug.cgi?id=2500.
-         *
-         * => Elide codegen if there is at least one instantiation from a non-root module
-         *    which doesn't import any root modules.
-         */
-        static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst)
-        {
-            // If the ancestor isn't speculative,
-            // 1. do codegen if the ancestor needs it
-            // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree)
-            if (tinst && tinst.inst)
-            {
-                tinst = tinst.inst;
-                const needsCodegen = tinst.needsCodegen(); // sets tinst.minst
-                if (tinst.minst) // not speculative
-                {
-                    tithis.minst = tinst.minst; // cache result
-                    return needsCodegen ? ThreeState.yes : ThreeState.no;
-                }
-            }
-
-            // Elide codegen if `this` doesn't need it.
-            if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports())
-                return ThreeState.no;
-
-            return ThreeState.none;
-        }
-
-        if (const needsCodegen = needsCodegenRootOnly(this, tinst))
-            return needsCodegen == ThreeState.yes ? true : false;
-
-        // Elide codegen if a (non-speculative) sibling doesn't need it.
-        for (; tnext; tnext = tnext.tnext)
-        {
-            const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst
-            if (tnext.minst) // not speculative
-            {
-                if (needsCodegen == ThreeState.no)
-                {
-                    minst = tnext.minst; // cache result
-                    assert(!minst.isRoot() && !minst.rootImports());
-                    return false;
-                }
-                else if (!minst)
-                {
-                    minst = tnext.minst; // cache result from non-speculative sibling
-                    // continue searching
-                }
-                else if (needsCodegen != ThreeState.none)
-                    break;
-            }
-        }
-
-        // Unless `this` is still speculative (=> all further siblings speculative too),
-        // do codegen because we found no guaranteed-codegen'd non-root instantiation.
-        return minst !is null;
-    }
-
-    /**********************************************
-     * Find template declaration corresponding to template instance.
-     *
-     * Returns:
-     *      false if finding fails.
-     * Note:
-     *      This function is reentrant against error occurrence. If returns false,
-     *      any members of this object won't be modified, and repetition call will
-     *      reproduce same error.
-     */
-    extern (D) final bool findTempDecl(Scope* sc, WithScopeSymbol* pwithsym)
+    override final const(char)* toPrettyCharsHelper()
     {
-        if (pwithsym)
-            *pwithsym = null;
-
-        if (havetempdecl)
-            return true;
-
-        //printf("TemplateInstance.findTempDecl() %s\n", toChars());
-        if (!tempdecl)
-        {
-            /* Given:
-             *    foo!( ... )
-             * figure out which TemplateDeclaration foo refers to.
-             */
-            Identifier id = name;
-            Dsymbol scopesym;
-            Dsymbol s = sc.search(loc, id, scopesym);
-            if (!s)
-            {
-                s = sc.search_correct(id);
-                if (s)
-                    .error(loc, "%s `%s` template `%s` is not defined, did you mean %s?", kind, toPrettyChars, id.toChars(), s.toChars());
-                else
-                    .error(loc, "%s `%s` template `%s` is not defined", kind, toPrettyChars, id.toChars());
-                return false;
-            }
-            static if (LOG)
-            {
-                printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
-                if (s.parent)
-                    printf("s.parent = '%s'\n", s.parent.toChars());
-            }
-            if (pwithsym)
-                *pwithsym = scopesym.isWithScopeSymbol();
-
-            /* We might have found an alias within a template when
-             * we really want the template.
-             */
-            TemplateInstance ti;
-            if (s.parent && (ti = s.parent.isTemplateInstance()) !is null)
-            {
-                if (ti.tempdecl && ti.tempdecl.ident == id)
-                {
-                    /* This is so that one can refer to the enclosing
-                     * template, even if it has the same name as a member
-                     * of the template, if it has a !(arguments)
-                     */
-                    TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
-                    assert(td);
-                    if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
-                        td = td.overroot; // then get the start
-                    s = td;
-                }
-            }
-
-            // The template might originate from a selective import which implies that
-            // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
-            // This is the last place where we see the deprecated alias because it is
-            // stripped below, so check if the selective import was deprecated.
-            // See https://issues.dlang.org/show_bug.cgi?id=20840.
-            if (s.isAliasDeclaration())
-                s.checkDeprecated(this.loc, sc);
-
-            if (!updateTempDecl(sc, s))
-            {
-                return false;
-            }
-        }
-        assert(tempdecl);
-
-        // Look for forward references
-        auto tovers = tempdecl.isOverloadSet();
-        foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
-        {
-            Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
-            int r = overloadApply(dstart, (Dsymbol s)
-            {
-                auto td = s.isTemplateDeclaration();
-                if (!td)
-                    return 0;
-
-                if (td.semanticRun == PASS.initial)
-                {
-                    if (td._scope)
-                    {
-                        // Try to fix forward reference. Ungag errors while doing so.
-                        Ungag ungag = td.ungagSpeculative();
-                        td.dsymbolSemantic(td._scope);
-                    }
-                    if (td.semanticRun == PASS.initial)
-                    {
-                        .error(loc, "%s `%s` `%s` forward references template declaration `%s`", kind, toPrettyChars,
-                            toChars(), td.toChars());
-                        return 1;
-                    }
-                }
-                return 0;
-            });
-            if (r)
-                return false;
-        }
-        return true;
+        OutBuffer buf;
+        toCBufferInstance(this, buf, true);
+        return buf.extractChars();
     }
 
-    /**********************************************
-     * Confirm s is a valid template, then store it.
-     * Input:
-     *      sc
-     *      s   candidate symbol of template. It may be:
-     *          TemplateDeclaration
-     *          FuncDeclaration with findTemplateDeclRoot() != NULL
-     *          OverloadSet which contains candidates
-     * Returns:
-     *      true if updating succeeds.
+    /**************************************
+     * Given an error instantiating the TemplateInstance,
+     * give the nested TemplateInstance instantiations that got
+     * us here. Those are a list threaded into the nested scopes.
+     * Params:
+     *  cl = classification of this trace as printing either errors or deprecations
+     *  max_shown = maximum number of trace elements printed (controlled with -v/-verror-limit)
      */
-    extern (D) final bool updateTempDecl(Scope* sc, Dsymbol s)
+    extern(D) final void printInstantiationTrace(Classification cl = Classification.error,
+                                                 const(uint) max_shown = global.params.v.errorSupplementCount())
     {
-        if (!s)
-            return tempdecl !is null;
-
-        Identifier id = name;
-        s = s.toAlias();
-
-        /* If an OverloadSet, look for a unique member that is a template declaration
-         */
-        if (OverloadSet os = s.isOverloadSet())
-        {
-            s = null;
-            foreach (s2; os.a)
-            {
-                if (FuncDeclaration f = s2.isFuncDeclaration())
-                    s2 = f.findTemplateDeclRoot();
-                else
-                    s2 = s2.isTemplateDeclaration();
-                if (s2)
-                {
-                    if (s)
-                    {
-                        tempdecl = os;
-                        return true;
-                    }
-                    s = s2;
-                }
-            }
-            if (!s)
-            {
-                .error(loc, "%s `%s` template `%s` is not defined", kind, toPrettyChars, id.toChars());
-                return false;
-            }
-        }
-
-        if (OverDeclaration od = s.isOverDeclaration())
-        {
-            tempdecl = od; // TODO: more strict check
-            return true;
-        }
-
-        /* It should be a TemplateDeclaration, not some other symbol
-         */
-        if (FuncDeclaration f = s.isFuncDeclaration())
-            tempdecl = f.findTemplateDeclRoot();
-        else
-            tempdecl = s.isTemplateDeclaration();
-
-        // We're done
-        if (tempdecl)
-            return true;
+        if (global.gag)
+            return;
 
-        // Error already issued, just return `false`
-        if (!s.parent && global.errors)
-            return false;
+        // Print full trace for verbose mode, otherwise only short traces
+        const(char)* format = "instantiated from here: `%s`";
 
-        if (!s.parent && s.getType())
-        {
-            Dsymbol s2 = s.getType().toDsymbol(sc);
-            if (!s2)
-            {
-                .error(loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", toChars(), id.toChars(), id.toChars(), s.getType.kind());
-                return false;
-            }
-            // because s can be the alias created for a TemplateParameter
-            const AliasDeclaration ad = s.isAliasDeclaration();
-            version (none)
+        // This returns a function pointer
+        scope printFn = () {
+            final switch (cl)
             {
-                if (ad && ad.isAliasedTemplateParameter())
-                    printf("`%s` is an alias created from a template parameter\n", s.toChars());
+                case Classification.error:
+                    return &errorSupplemental;
+                case Classification.deprecation:
+                    return &deprecationSupplemental;
+                case Classification.gagged, Classification.tip, Classification.warning:
+                    assert(0);
             }
-            if (!ad || !ad.isAliasedTemplateParameter())
-                s = s2;
-        }
-
-        TemplateInstance ti = s.parent ? s.parent.isTemplateInstance() : null;
-
-        /* This avoids the VarDeclaration.toAlias() which runs semantic() too soon
-         */
-        static bool matchId(TemplateInstance ti, Identifier id)
-        {
-            if (ti.aliasdecl && ti.aliasdecl.isVarDeclaration())
-                return ti.aliasdecl.isVarDeclaration().ident == id;
-            return ti.toAlias().ident == id;
-        }
-
-        if (ti && (ti.name == s.ident || matchId(ti, s.ident)) && ti.tempdecl)
-        {
-            /* This is so that one can refer to the enclosing
-             * template, even if it has the same name as a member
-             * of the template, if it has a !(arguments)
-             */
-            TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration();
-            assert(td);
-            if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
-                td = td.overroot; // then get the start
-            tempdecl = td;
-            return true;
-        }
-        else
-        {
-            .error(loc, "%s `%s` `%s` is not a template declaration, it is a %s", kind, toPrettyChars, id.toChars(), s.kind());
-            return false;
-        }
-    }
-
-    /**********************************
-     * Run semantic of tiargs as arguments of template.
-     * Input:
-     *      loc
-     *      sc
-     *      tiargs  array of template arguments
-     *      flags   1: replace const variables with their initializers
-     *              2: don't devolve Parameter to Type
-     *      atd     tuple being optimized. If found, it's not expanded here
-     *              but in AliasAssign semantic.
-     * Returns:
-     *      false if one or more arguments have errors.
-     */
-    extern (D) static bool semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null)
-    {
-        // Run semantic on each argument, place results in tiargs[]
-        //printf("+TemplateInstance.semanticTiargs()\n");
-        if (!tiargs)
-            return true;
-        bool err = false;
-
-        // The arguments are not treated as part of a default argument,
-        // because they are evaluated at compile time.
-        const inCondition = sc.condition;
-        sc = sc.push();
-        sc.inDefaultArg = false;
-
-        // https://issues.dlang.org/show_bug.cgi?id=24699
-        sc.condition = inCondition;
+        }();
 
-        for (size_t j = 0; j < tiargs.length; j++)
+        // determine instantiation depth and number of recursive instantiations
+        int n_instantiations = 1;
+        int n_totalrecursions = 0;
+        for (TemplateInstance cur = this; cur; cur = cur.tinst)
         {
-            RootObject o = (*tiargs)[j];
-            Type ta = isType(o);
-            Expression ea = isExpression(o);
-            Dsymbol sa = isDsymbol(o);
-
-            //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
-            if (ta)
-            {
-                //printf("type %s\n", ta.toChars());
-
-                // It might really be an Expression or an Alias
-                ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0);
-                if (ea)
-                    goto Lexpr;
-                if (sa)
-                    goto Ldsym;
-                if (ta is null)
-                {
-                    assert(global.errors);
-                    ta = Type.terror;
-                }
-
-            Ltype:
-                if (TypeTuple tt = ta.isTypeTuple())
-                {
-                    // Expand tuple
-                    size_t dim = tt.arguments.length;
-                    tiargs.remove(j);
-                    if (dim)
-                    {
-                        tiargs.reserve(dim);
-                        foreach (i, arg; *tt.arguments)
-                        {
-                            if (flags & 2 && (arg.storageClass & STC.parameter))
-                                tiargs.insert(j + i, arg);
-                            else
-                                tiargs.insert(j + i, arg.type);
-                        }
-                    }
-                    j--;
-                    continue;
-                }
-                if (ta.ty == Terror)
-                {
-                    err = true;
-                    continue;
-                }
-                (*tiargs)[j] = ta.merge2();
-            }
-            else if (ea)
-            {
-            Lexpr:
-                //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.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter))
-                    {
-                        ea = ea.optimize(WANTvalue);
-                    }
-                }
-                else
-                {
-                    sc = sc.startCTFE();
-                    ea = ea.expressionSemantic(sc);
-                    sc = sc.endCTFE();
-
-                    if (auto varExp = ea.isVarExp())
-                    {
-                        /* If the parameter is a function that is not called
-                         * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
-                         * then it is a dsymbol, not the return value of `func()`
-                         */
-                        Declaration vd = varExp.var;
-                        if (auto fd = vd.isFuncDeclaration())
-                        {
-                            sa = fd;
-                            goto Ldsym;
-                        }
-                        /* Otherwise skip substituting a const var with
-                         * its initializer. The problem is the initializer won't
-                         * match with an 'alias' parameter. Instead, do the
-                         * const substitution in TemplateValueParameter.matchArg().
-                         */
-                    }
-                    else if (definitelyValueParameter(ea))
-                    {
-                        if (ea.checkValue()) // check void expression
-                            ea = ErrorExp.get();
-                        const olderrs = global.errors;
-                        ea = ea.ctfeInterpret();
-                        if (global.errors != olderrs)
-                            ea = ErrorExp.get();
-                    }
-                }
-                //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars());
-                if (TupleExp te = ea.isTupleExp())
-                {
-                    // Expand tuple
-                    size_t dim = te.exps.length;
-                    tiargs.remove(j);
-                    if (dim)
-                    {
-                        tiargs.reserve(dim);
-                        foreach (i, exp; *te.exps)
-                            tiargs.insert(j + i, exp);
-                    }
-                    j--;
-                    continue;
-                }
-                if (ea.op == EXP.error)
-                {
-                    err = true;
-                    continue;
-                }
-                (*tiargs)[j] = ea;
+            ++n_instantiations;
+            // Set error here as we don't want it to depend on the number of
+            // entries that are being printed.
+            if (cl == Classification.error ||
+                (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) ||
+                (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
+                cur.errors = true;
 
-                if (ea.op == EXP.type)
-                {
-                    ta = ea.type;
-                    goto Ltype;
-                }
-                if (ea.op == EXP.scope_)
-                {
-                    sa = ea.isScopeExp().sds;
-                    goto Ldsym;
-                }
-                if (FuncExp fe = ea.isFuncExp())
-                {
-                    /* A function literal, that is passed to template and
-                     * already semanticed as function pointer, never requires
-                     * outer frame. So convert it to global function is valid.
-                     */
-                    if (fe.fd.tok == TOK.reserved && fe.type.ty == Tpointer)
-                    {
-                        // change to non-nested
-                        fe.fd.tok = TOK.function_;
-                        fe.fd.vthis = null;
-                    }
-                    else if (fe.td)
-                    {
-                        /* If template argument is a template lambda,
-                         * get template declaration itself. */
-                        //sa = fe.td;
-                        //goto Ldsym;
-                    }
-                }
-                if (ea.op == EXP.dotVariable && !(flags & 1))
-                {
-                    // translate expression to dsymbol.
-                    sa = ea.isDotVarExp().var;
-                    goto Ldsym;
-                }
-                if (auto te = ea.isTemplateExp())
-                {
-                    sa = te.td;
-                    goto Ldsym;
-                }
-                if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1))
-                {
-                    // translate expression to dsymbol.
-                    sa = ea.isDotTemplateExp().td;
-                    goto Ldsym;
-                }
-                if (auto de = ea.isDotExp())
-                {
-                    if (auto se = de.e2.isScopeExp())
-                    {
-                        sa = se.sds;
-                        goto Ldsym;
-                    }
-                }
-            }
-            else if (sa)
-            {
-            Ldsym:
-                //printf("dsym %s %s\n", sa.kind(), sa.toChars());
-                if (sa.errors)
-                {
-                    err = true;
-                    continue;
-                }
+            // If two instantiations use the same declaration, they are recursive.
+            // (this works even if they are instantiated from different places in the
+            // same template).
+            // In principle, we could also check for multiple-template recursion, but it's
+            // probably not worthwhile.
+            if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
+                ++n_totalrecursions;
+        }
 
-                TupleDeclaration d = sa.toAlias().isTupleDeclaration();
-                if (d)
-                {
-                    if (d is atd)
-                    {
-                        (*tiargs)[j] = d;
-                        continue;
-                    }
-                    // Expand tuple
-                    tiargs.remove(j);
-                    tiargs.insert(j, d.objects);
-                    j--;
-                    continue;
-                }
-                if (FuncAliasDeclaration fa = sa.isFuncAliasDeclaration())
+        if (n_instantiations <= max_shown)
+        {
+            for (TemplateInstance cur = this; cur; cur = cur.tinst)
+                printFn(cur.loc, format, cur.toErrMsg());
+        }
+        else if (n_instantiations - n_totalrecursions <= max_shown)
+        {
+            // By collapsing recursive instantiations into a single line,
+            // we can stay under the limit.
+            int recursionDepth = 0;
+            for (TemplateInstance cur = this; cur; cur = cur.tinst)
+            {
+                if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
                 {
-                    FuncDeclaration f = fa.toAliasFunc();
-                    if (!fa.hasOverloads && f.isUnique())
-                    {
-                        // Strip FuncAlias only when the aliased function
-                        // does not have any overloads.
-                        sa = f;
-                    }
+                    ++recursionDepth;
                 }
-                (*tiargs)[j] = sa;
-
-                TemplateDeclaration td = sa.isTemplateDeclaration();
-                if (td && td.semanticRun == PASS.initial && td.literal)
+                else
                 {
-                    td.dsymbolSemantic(sc);
+                    if (recursionDepth)
+                        printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars());
+                    else
+                        printFn(cur.loc, format, cur.toChars());
+                    recursionDepth = 0;
                 }
-                FuncDeclaration fd = sa.isFuncDeclaration();
-                if (fd)
-                    functionSemantic(fd);
-            }
-            else if (isParameter(o))
-            {
-            }
-            else
-            {
-                assert(0);
             }
-            //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
         }
-        sc.pop();
-        version (none)
+        else
         {
-            printf("-TemplateInstance.semanticTiargs()\n");
-            for (size_t j = 0; j < tiargs.length; j++)
+            // Even after collapsing the recursions, the depth is too deep.
+            // Just display the first few and last few instantiations.
+            uint i = 0;
+            for (TemplateInstance cur = this; cur; cur = cur.tinst)
             {
-                RootObject o = (*tiargs)[j];
-                Type ta = isType(o);
-                Expression ea = isExpression(o);
-                Dsymbol sa = isDsymbol(o);
-                Tuple va = isTuple(o);
-                printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va);
+                if (i == max_shown / 2)
+                    printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
+
+                if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2)
+                    printFn(cur.loc, format, cur.toChars());
+                ++i;
             }
         }
-        return !err;
     }
 
-    /**********************************
-     * Run semantic on the elements of tiargs.
-     * Input:
-     *      sc
-     * Returns:
-     *      false if one or more arguments have errors.
-     * Note:
-     *      This function is reentrant against error occurrence. If returns false,
-     *      all elements of tiargs won't be modified.
+    /*************************************
+     * Lazily generate identifier for template instance.
+     * This is because 75% of the ident's are never needed.
      */
-    extern (D) final bool semanticTiargs(Scope* sc)
+    override final Identifier getIdent()
     {
-        //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
-        if (semantictiargsdone)
-            return true;
-        if (semanticTiargs(loc, sc, tiargs, 0))
-        {
-            // cache the result iff semantic analysis succeeded entirely
-            semantictiargsdone = 1;
-            return true;
-        }
-        return false;
+        if (!ident && inst && !errors)
+            ident = genIdent(tiargs); // need an identifier for name mangling purposes.
+        return ident;
     }
 
-    /**********************************
-     * Find the TemplateDeclaration that matches this TemplateInstance best.
-     *
+    /*************************************
+     * Compare proposed template instantiation with existing template instantiation.
+     * Note that this is not commutative because of the auto ref check.
      * Params:
-     *   sc    = the scope this TemplateInstance resides in
-     *   argumentList = function arguments in case of a template function
-     *
+     *  ti = existing template instantiation
      * Returns:
-     *   `true` if a match was found, `false` otherwise
+     *  true for match
      */
-    extern (D) final bool findBestMatch(Scope* sc, ArgumentList argumentList)
+    final bool equalsx(TemplateInstance ti)
     {
-        if (havetempdecl)
-        {
-            TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
-            assert(tempdecl);
-            assert(tempdecl._scope);
-            // Deduce tdtypes
-            tdtypes.setDim(tempdecl.parameters.length);
-            if (!matchWithInstance(sc, tempdecl, this, tdtypes, argumentList, 2))
-            {
-                .error(loc, "%s `%s` incompatible arguments for template instantiation", kind, toPrettyChars);
-                return false;
-            }
-            // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary?
-            return true;
-        }
+        //printf("this = %p, ti = %p\n", this, ti);
+        assert(tdtypes.length == ti.tdtypes.length);
 
-        static if (LOG)
+        // Nesting must match
+        if (enclosing != ti.enclosing)
         {
-            printf("TemplateInstance.findBestMatch()\n");
+            //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
+            return false;
         }
+        //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
 
-        const errs = global.errors;
-        TemplateDeclaration td_last = null;
-        Objects dedtypes;
+        if (!arrayObjectMatch(tdtypes, ti.tdtypes))
+            return false;
 
-        /* Since there can be multiple TemplateDeclaration's with the same
-         * name, look for the best match.
+        /* Template functions may have different instantiations based on
+         * "auto ref" parameters.
          */
-        auto tovers = tempdecl.isOverloadSet();
-        foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
-        {
-            TemplateDeclaration td_best;
-            TemplateDeclaration td_ambig;
-            MATCH m_best = MATCH.nomatch;
-
-            Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
-            overloadApply(dstart, (Dsymbol s)
-            {
-                auto td = s.isTemplateDeclaration();
-                if (!td)
-                    return 0;
-                if (td == td_best)   // skip duplicates
-                    return 0;
-
-                //printf("td = %s\n", td.toPrettyChars());
-                // If more arguments than parameters,
-                // then this is no match.
-                if (td.parameters.length < tiargs.length)
-                {
-                    if (!td.isVariadic())
-                        return 0;
-                }
-
-                dedtypes.setDim(td.parameters.length);
-                dedtypes.zero();
-                assert(td.semanticRun != PASS.initial);
-
-                MATCH m = matchWithInstance(sc, td, this, dedtypes, argumentList, 0);
-                //printf("matchWithInstance = %d\n", m);
-                if (m == MATCH.nomatch) // no match at all
-                    return 0;
-                if (m < m_best) goto Ltd_best;
-                if (m > m_best) goto Ltd;
-
-                // Disambiguate by picking the most specialized TemplateDeclaration
-                {
-                MATCH c1 = leastAsSpecialized(sc, td, td_best, argumentList);
-                MATCH c2 = leastAsSpecialized(sc, td_best, td, argumentList);
-                //printf("c1 = %d, c2 = %d\n", c1, c2);
-                if (c1 > c2) goto Ltd;
-                if (c1 < c2) goto Ltd_best;
-                }
+        auto fd = ti.toAlias().isFuncDeclaration();
+        if (!fd)
+            return true;
+        if (fd.errors)
+            return true;
 
-                td_ambig = td;
-                return 0;
-
-            Ltd_best:
-                // td_best is the best match so far
-                td_ambig = null;
-                return 0;
-
-            Ltd:
-                // td is the new best match
-                td_ambig = null;
-                td_best = td;
-                m_best = m;
-                tdtypes.setDim(dedtypes.length);
-                memcpy(tdtypes.tdata(), dedtypes.tdata(), tdtypes.length * (void*).sizeof);
-                return 0;
-            });
-
-            if (td_ambig)
-            {
-                .error(loc, "%s `%s.%s` matches more than one template declaration:",
-                    td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars());
-                .errorSupplemental(td_best.loc, "`%s`\nand:", td_best.toChars());
-                .errorSupplemental(td_ambig.loc, "`%s`", td_ambig.toChars());
-                return false;
-            }
-            if (td_best)
-            {
-                if (!td_last)
-                    td_last = td_best;
-                else if (td_last != td_best)
-                {
-                    ScopeDsymbol.multiplyDefined(loc, td_last, td_best);
-                    return false;
-                }
-            }
-        }
+        auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs(
+            ArgumentList(this.fargs, this.fnames), null);
 
-        if (td_last)
-        {
-            /* https://issues.dlang.org/show_bug.cgi?id=7469
-             * Normalize tiargs by using corresponding deduced
-             * template value parameters and tuples for the correct mangling.
-             *
-             * By doing this before hasNestedArgs, CTFEable local variable will be
-             * accepted as a value parameter. For example:
-             *
-             *  void foo() {
-             *    struct S(int n) {}   // non-global template
-             *    const int num = 1;   // CTFEable local variable
-             *    S!num s;             // S!1 is instantiated, not S!num
-             *  }
-             */
-            size_t dim = td_last.parameters.length - (td_last.isVariadic() ? 1 : 0);
-            for (size_t i = 0; i < dim; i++)
-            {
-                if (tiargs.length <= i)
-                    tiargs.push(tdtypes[i]);
-                assert(i < tiargs.length);
+        // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d
+        // In that case, equalsx returns true to prevent endless template instantiations
+        // However, it can also mean the function was explicitly instantiated
+        // without function arguments: fail_compilation/fail14669
+        // Hence the following check:
+        if (this.fargs && !resolvedArgs)
+            return true;
 
-                auto tvp = (*td_last.parameters)[i].isTemplateValueParameter();
-                if (!tvp)
-                    continue;
-                assert(tdtypes[i]);
-                // tdtypes[i] is already normalized to the required type in matchArg
+        Expression[] args = resolvedArgs ? (*resolvedArgs)[] : [];
 
-                (*tiargs)[i] = tdtypes[i];
-            }
-            if (td_last.isVariadic() && tiargs.length == dim && tdtypes[dim])
-            {
-                Tuple va = isTuple(tdtypes[dim]);
-                assert(va);
-                tiargs.pushSlice(va.objects[]);
-            }
-        }
-        else if (errors && inst)
-        {
-            // instantiation was failed with error reporting
-            assert(global.errors);
-            return false;
-        }
-        else
+        auto fparameters = fd.getParameterList();
+        size_t nfparams = fparameters.length;   // Num function parameters
+        for (size_t j = 0; j < nfparams; j++)
         {
-            auto tdecl = tempdecl.isTemplateDeclaration();
+            Parameter fparam = fparameters[j];
+            if (!(fparam.storageClass & STC.autoref) )      // if "auto ref"
+                continue;
 
-            if (errs != global.errors)
-                errorSupplemental(loc, "while looking for match for `%s`", toChars());
-            else if (tdecl && !tdecl.overnext)
+            Expression farg = (j < args.length) ? args[j] : fparam.defaultArg;
+            // resolveNamedArgs strips trailing nulls / default params
+            // when it doesn't anymore, the ternary can be replaced with:
+            // assert(j < resolvedArgs.length);
+            if (!farg)
+                farg = fparam.defaultArg;
+            if (!farg)
+                return false;
+            if (farg.isLvalue())
             {
-                // Only one template, so we can give better error message
-                const(char)* msg = "does not match template declaration";
-                const(char)* tip;
-                OutBuffer buf;
-                HdrGenState hgs;
-                hgs.skipConstraints = true;
-                toCharsMaybeConstraints(tdecl, buf, hgs);
-                const tmsg = buf.peekChars();
-                const cmsg = tdecl.getConstraintEvalError(tip);
-                if (cmsg)
-                {
-                    .error(loc, "%s `%s` %s `%s`\n%s", kind, toPrettyChars, msg, tmsg, cmsg);
-                    if (tip)
-                        .tip(tip);
-                }
-                else
-                {
-                    .error(loc, "%s `%s` %s `%s`", kind, toPrettyChars, msg, tmsg);
-
-                    if (tdecl.parameters.length == tiargs.length)
-                    {
-                        // https://issues.dlang.org/show_bug.cgi?id=7352
-                        // print additional information, e.g. `foo` is not a type
-                        foreach (i, param; *tdecl.parameters)
-                        {
-                            MATCH match = param.matchArg(loc, sc, tiargs, i, tdecl.parameters, dedtypes, null);
-                            auto arg = (*tiargs)[i];
-                            auto sym = arg.isDsymbol;
-                            auto exp = arg.isExpression;
-
-                            if (exp)
-                                exp = exp.optimize(WANTvalue);
-
-                            if (match == MATCH.nomatch &&
-                                ((sym && sym.isFuncDeclaration) ||
-                                 (exp && exp.isVarExp)))
-                            {
-                                if (param.isTemplateTypeParameter)
-                                    errorSupplemental(loc, "`%s` is not a type", arg.toChars);
-                                else if (auto tvp = param.isTemplateValueParameter)
-                                    errorSupplemental(loc, "`%s` is not of a value of type `%s`",
-                                                      arg.toChars, tvp.valType.toChars);
-
-                            }
-                        }
-                    }
-                }
+                if (!(fparam.storageClass & STC.ref_))
+                    return false; // auto ref's don't match
             }
             else
             {
-                .error(loc, "%s `%s` does not match any template declaration", kind(), toPrettyChars());
-                bool found;
-                overloadApply(tempdecl, (s){
-                    if (!found)
-                        errorSupplemental(loc, "Candidates are:");
-                    found = true;
-                    errorSupplemental(s.loc, "%s", s.toChars());
-                    return 0;
-                });
+                if (fparam.storageClass & STC.ref_)
+                    return false; // auto ref's don't match
             }
-            return false;
         }
+        return true;
+    }
 
-        /* The best match is td_last
-         */
-        tempdecl = td_last;
+    extern (D) final size_t toHash()
+    {
+        if (!hash)
+        {
+            hash = cast(size_t)cast(void*)enclosing;
+            hash += arrayObjectHash(tdtypes);
+            hash += hash == 0;
+        }
+        return hash;
+    }
 
-        static if (LOG)
+    /**********************************
+     * Run semantic on the elements of tiargs.
+     * Input:
+     *      sc
+     * Returns:
+     *      false if one or more arguments have errors.
+     * Note:
+     *      This function is reentrant against error occurrence. If returns false,
+     *      all elements of tiargs won't be modified.
+     */
+    extern (D) final bool semanticTiargs(Scope* sc)
+    {
+        //printf("+TemplateInstance.semanticTiargs() %s\n", toChars());
+        if (semantictiargsdone)
+            return true;
+        if (TemplateInstance_semanticTiargs(loc, sc, tiargs, 0))
         {
-            printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars());
+            // cache the result iff semantic analysis succeeded entirely
+            semantictiargsdone = 1;
+            return true;
         }
-        return (errs == global.errors);
+        return false;
     }
 
     /*****************************************
@@ -5121,108 +2081,15 @@ extern (C++) class TemplateInstance : ScopeDsymbol
                 L1:
                     //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars());
                     nested |= 1;
-                }
-            }
-            else if (va)
-            {
-                nested |= cast(int)hasNestedArgs(&va.objects, isstatic);
-            }
-        }
-        //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
-        return nested != 0;
-    }
-
-    /*****************************************
-     * Append 'this' to the specific module members[]
-     */
-    extern (D) final Dsymbols* appendToModuleMember()
-    {
-        Module mi = minst; // instantiated . inserted module
-
-        //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n",
-        //    toPrettyChars(),
-        //    enclosing ? enclosing.toPrettyChars() : null,
-        //    mi ? mi.toPrettyChars() : null);
-        if (global.params.allInst || !mi || mi.isRoot())
-        {
-            /* If the instantiated module is speculative or root, insert to the
-             * member of a root module. Then:
-             *  - semantic3 pass will get called on the instance members.
-             *  - codegen pass will get a selection chance to do/skip it (needsCodegen()).
-             */
-            static Dsymbol getStrictEnclosing(TemplateInstance ti)
-            {
-                do
-                {
-                    if (ti.enclosing)
-                        return ti.enclosing;
-                    ti = ti.tempdecl.isInstantiated();
-                } while (ti);
-                return null;
-            }
-
-            Dsymbol enc = getStrictEnclosing(this);
-            // insert target is made stable by using the module
-            // where tempdecl is declared.
-            mi = (enc ? enc : tempdecl).getModule();
-            if (!mi.isRoot())
-            {
-                if (mi.importedFrom)
-                {
-                    mi = mi.importedFrom;
-                    assert(mi.isRoot());
-                }
-                else
-                {
-                    // This can happen when using the frontend as a library.
-                    // Append it to the non-root module.
-                }
-            }
-        }
-        else
-        {
-            /* If the instantiated module is non-root, insert to the member of the
-             * non-root module. Then:
-             *  - semantic3 pass won't be called on the instance.
-             *  - codegen pass won't reach to the instance.
-             * Unless it is re-appended to a root module later (with changed minst).
-             */
-        }
-        //printf("\t-. mi = %s\n", mi.toPrettyChars());
-
-        if (memberOf) // already appended to some module
-        {
-            assert(mi.isRoot(), "can only re-append to a root module");
-            if (memberOf.isRoot())
-                return null; // no need to move to another root module
-        }
-
-        Dsymbols* a = mi.members;
-        a.push(this);
-        memberOf = mi;
-        if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
-            Module.addDeferredSemantic2(this);
-        if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
-            Module.addDeferredSemantic3(this);
-        return a;
-    }
-
-    /****************************************************
-     * Declare parameters of template instance, initialize them with the
-     * template instance arguments.
-     */
-    extern (D) final void declareParameters(Scope* sc)
-    {
-        TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
-        assert(tempdecl);
-
-        //printf("TemplateInstance.declareParameters()\n");
-        foreach (i, o; tdtypes) // initializer for tp
-        {
-            TemplateParameter tp = (*tempdecl.parameters)[i];
-            //printf("\ttdtypes[%d] = %p\n", i, o);
-            declareParameter(tempdecl, sc, tp, o);
+                }
+            }
+            else if (va)
+            {
+                nested |= cast(int)hasNestedArgs(&va.objects, isstatic);
+            }
         }
+        //printf("-TemplateInstance.hasNestedArgs('%s') = %d\n", tempdecl.ident.toChars(), nested);
+        return nested != 0;
     }
 
     /****************************************
@@ -5240,166 +2107,12 @@ extern (C++) class TemplateInstance : ScopeDsymbol
         return Identifier.idPool(buf[]);
     }
 
-    extern (D) final void expandMembers(Scope* sc2)
-    {
-        members.foreachDsymbol( (s) { s.setScope (sc2); } );
-
-        members.foreachDsymbol( (s) { s.importAll(sc2); } );
-
-        if (!aliasdecl)
-        {
-            /* static if's are crucial to evaluating aliasdecl correctly. But
-             * evaluating the if/else bodies may require aliasdecl.
-             * So, evaluate the condition for static if's, but not their if/else bodies.
-             * Then try to set aliasdecl.
-             * Later do the if/else bodies.
-             * https://issues.dlang.org/show_bug.cgi?id=23598
-             * It might be better to do this by attaching a lambda to the StaticIfDeclaration
-             * to do the oneMembers call after the sid.include(sc2) is run as part of dsymbolSemantic().
-             */
-            bool done;
-            void staticIfDg(Dsymbol s)
-            {
-                if (done || aliasdecl)
-                    return;
-                //printf("\t staticIfDg on '%s %s' in '%s'\n",  s.kind(), s.toChars(), this.toChars());
-                if (!s.isStaticIfDeclaration())
-                {
-                    //s.dsymbolSemantic(sc2);
-                    done = true;
-                    return;
-                }
-                auto sid = s.isStaticIfDeclaration();
-                sid.include(sc2);
-                if (members.length)
-                {
-                    Dsymbol sa;
-                    if (oneMembers(members, sa, tempdecl.ident) && sa)
-                        aliasdecl = sa;
-                }
-                done = true;
-            }
-
-            members.foreachDsymbol(&staticIfDg);
-        }
-
-        void symbolDg(Dsymbol s)
-        {
-            //printf("\t semantic on '%s' %p kind %s in '%s'\n",  s.toChars(), s, s.kind(), this.toChars());
-            //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
-            //if (enclosing)
-            //    s.parent = sc.parent;
-            //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
-            s.dsymbolSemantic(sc2);
-            //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
-            Module.runDeferredSemantic();
-        }
-
-        members.foreachDsymbol(&symbolDg);
-    }
-
-    extern (D) final void tryExpandMembers(Scope* sc2)
-    {
-        __gshared int nest;
-        // extracted to a function to allow windows SEH to work without destructors in the same function
-        //printf("%d\n", nest);
-        if (++nest > global.recursionLimit)
-        {
-            global.gag = 0; // ensure error message gets printed
-            .error(loc, "%s `%s` recursive expansion exceeded allowed nesting limit", kind, toPrettyChars);
-            fatal();
-        }
-
-        expandMembers(sc2);
-
-        nest--;
-    }
-
-    extern (D) final void trySemantic3(Scope* sc2)
-    {
-        // extracted to a function to allow windows SEH to work without destructors in the same function
-        __gshared int nest;
-        //printf("%d\n", nest);
-        if (++nest > global.recursionLimit)
-        {
-            global.gag = 0; // ensure error message gets printed
-            .error(loc, "%s `%s` recursive expansion exceeded allowed nesting limit", kind, toPrettyChars);
-            fatal();
-        }
-
-        semantic3(this, sc2);
-
-        --nest;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
     }
 }
 
-/**************************************
- * IsExpression can evaluate the specified type speculatively, and even if
- * it instantiates any symbols, they are normally unnecessary for the
- * final executable.
- * However, if those symbols leak to the actual code, compiler should remark
- * them as non-speculative to generate their code and link to the final executable.
- */
-void unSpeculative(Scope* sc, RootObject o)
-{
-    if (!o)
-        return;
-
-    if (Tuple tup = isTuple(o))
-    {
-        foreach (obj; tup.objects)
-        {
-            unSpeculative(sc, obj);
-        }
-        return;
-    }
-
-    Dsymbol s = getDsymbol(o);
-    if (!s)
-        return;
-
-    if (Declaration d = s.isDeclaration())
-    {
-        if (VarDeclaration vd = d.isVarDeclaration())
-            o = vd.type;
-        else if (AliasDeclaration ad = d.isAliasDeclaration())
-        {
-            o = ad.getType();
-            if (!o)
-                o = ad.toAlias();
-        }
-        else
-            o = d.toAlias();
-
-        s = getDsymbol(o);
-        if (!s)
-            return;
-    }
-
-    if (TemplateInstance ti = s.isTemplateInstance())
-    {
-        // If the instance is already non-speculative,
-        // or it is leaked to the speculative scope.
-        if (ti.minst !is null || sc.minst is null)
-            return;
-
-        // Remark as non-speculative instance.
-        ti.minst = sc.minst;
-        if (!ti.tinst)
-            ti.tinst = sc.tinst;
-
-        unSpeculative(sc, ti.tempdecl);
-    }
-
-    if (TemplateInstance ti = s.isInstantiated())
-        unSpeculative(sc, ti);
-}
-
 /**********************************
  * Return true if e could be valid only as a template value parameter.
  * Return false if it might be an alias or tuple.
@@ -5491,80 +2204,6 @@ extern (C++) final class TemplateMixin : TemplateInstance
         return "mixin";
     }
 
-    extern (D) bool findTempDecl(Scope* sc)
-    {
-        // Follow qualifications to find the TemplateDeclaration
-        if (!tempdecl)
-        {
-            Expression e;
-            Type t;
-            Dsymbol s;
-            tqual.resolve(loc, sc, e, t, s);
-            if (!s)
-            {
-                .error(loc, "%s `%s` is not defined", kind, toPrettyChars);
-                return false;
-            }
-            s = s.toAlias();
-            tempdecl = s.isTemplateDeclaration();
-            OverloadSet os = s.isOverloadSet();
-
-            /* If an OverloadSet, look for a unique member that is a template declaration
-             */
-            if (os)
-            {
-                Dsymbol ds = null;
-                foreach (i, sym; os.a)
-                {
-                    Dsymbol s2 = sym.isTemplateDeclaration();
-                    if (s2)
-                    {
-                        if (ds)
-                        {
-                            tempdecl = os;
-                            break;
-                        }
-                        ds = s2;
-                    }
-                }
-            }
-            if (!tempdecl)
-            {
-                .error(loc, "%s `%s` - `%s` is a %s, not a template", kind, toPrettyChars, s.toChars(), s.kind());
-                return false;
-            }
-        }
-        assert(tempdecl);
-
-        // Look for forward references
-        auto tovers = tempdecl.isOverloadSet();
-        foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1)
-        {
-            Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
-            int r = overloadApply(dstart, (Dsymbol s)
-            {
-                auto td = s.isTemplateDeclaration();
-                if (!td)
-                    return 0;
-
-                if (td.semanticRun == PASS.initial)
-                {
-                    if (td._scope)
-                        td.dsymbolSemantic(td._scope);
-                    else
-                    {
-                        semanticRun = PASS.initial;
-                        return 1;
-                    }
-                }
-                return 0;
-            });
-            if (r)
-                return false;
-        }
-        return true;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -5629,466 +2268,6 @@ struct TemplateInstanceBox
     }
 }
 
-/*******************************************
- * Match to a particular TemplateParameter.
- * Input:
- *      instLoc         location that the template is instantiated.
- *      tiargs[]        actual arguments to template instance
- *      i               i'th argument
- *      parameters[]    template parameters
- *      dedtypes[]      deduced arguments to template instance
- *      *psparam        set to symbol declared and initialized to dedtypes[i]
- */
-MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
-{
-    MATCH matchArgNoMatch()
-    {
-        if (psparam)
-            *psparam = null;
-        return MATCH.nomatch;
-    }
-
-    MATCH matchArgParameter()
-    {
-        RootObject oarg;
-
-        if (i < tiargs.length)
-            oarg = (*tiargs)[i];
-        else
-        {
-            // Get default argument instead
-            oarg = tp.defaultArg(instLoc, sc);
-            if (!oarg)
-            {
-                assert(i < dedtypes.length);
-                // It might have already been deduced
-                oarg = dedtypes[i];
-                if (!oarg)
-                    return matchArgNoMatch();
-            }
-        }
-        return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam);
-    }
-
-    MATCH matchArgTuple(TemplateTupleParameter ttp)
-    {
-        /* The rest of the actual arguments (tiargs[]) form the match
-         * for the variadic parameter.
-         */
-        assert(i + 1 == dedtypes.length); // must be the last one
-        Tuple ovar;
-
-        if (Tuple u = isTuple(dedtypes[i]))
-        {
-            // It has already been deduced
-            ovar = u;
-        }
-        else if (i + 1 == tiargs.length && isTuple((*tiargs)[i]))
-            ovar = isTuple((*tiargs)[i]);
-        else
-        {
-            ovar = new Tuple();
-            //printf("ovar = %p\n", ovar);
-            if (i < tiargs.length)
-            {
-                //printf("i = %d, tiargs.length = %d\n", i, tiargs.length);
-                ovar.objects.setDim(tiargs.length - i);
-                foreach (j, ref obj; ovar.objects)
-                    obj = (*tiargs)[i + j];
-            }
-        }
-        return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam);
-    }
-
-    if (auto ttp = tp.isTemplateTupleParameter())
-        return matchArgTuple(ttp);
-
-    return matchArgParameter();
-}
-
-MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam)
-{
-    MATCH matchArgNoMatch()
-    {
-        //printf("\tm = %d\n", MATCH.nomatch);
-        if (psparam)
-            *psparam = null;
-        return MATCH.nomatch;
-    }
-
-    MATCH matchArgType(TemplateTypeParameter ttp)
-    {
-        //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars());
-        MATCH m = MATCH.exact;
-        Type ta = isType(oarg);
-        if (!ta)
-        {
-            //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
-            return matchArgNoMatch();
-        }
-        //printf("ta is %s\n", ta.toChars());
-
-        if (ttp.specType)
-        {
-            if (!ta || ta == TemplateTypeParameter.tdummy)
-                return matchArgNoMatch();
-
-            //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars());
-            MATCH m2 = deduceType(ta, sc, ttp.specType, *parameters, dedtypes);
-            if (m2 == MATCH.nomatch)
-            {
-                //printf("\tfailed deduceType\n");
-                return matchArgNoMatch();
-            }
-
-            if (m2 < m)
-                m = m2;
-            if (dedtypes[i])
-            {
-                Type t = cast(Type)dedtypes[i];
-
-                if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357
-                    return matchArgNoMatch();
-
-                /* This is a self-dependent parameter. For example:
-                 *  template X(T : T*) {}
-                 *  template X(T : S!T, alias S) {}
-                 */
-                //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
-                ta = t;
-            }
-        }
-        else
-        {
-            if (dedtypes[i])
-            {
-                // Must match already deduced type
-                Type t = cast(Type)dedtypes[i];
-
-                if (!t.equals(ta))
-                {
-                    //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
-                    return matchArgNoMatch();
-                }
-            }
-            else
-            {
-                // So that matches with specializations are better
-                m = MATCH.convert;
-            }
-        }
-        dedtypes[i] = ta;
-
-        if (psparam)
-            *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta);
-        //printf("\tm = %d\n", m);
-        return ttp.dependent ? MATCH.exact : m;
-    }
-
-    MATCH matchArgValue(TemplateValueParameter tvp)
-    {
-        //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars());
-        MATCH m = MATCH.exact;
-
-        Expression ei = isExpression(oarg);
-        Type vt;
-
-        if (!ei && oarg)
-        {
-            Dsymbol si = isDsymbol(oarg);
-            FuncDeclaration f = si ? si.isFuncDeclaration() : null;
-            if (!f || !f.fbody || f.needThis())
-                return matchArgNoMatch();
-
-            ei = new VarExp(tvp.loc, f);
-            ei = ei.expressionSemantic(sc);
-
-            /* If a function is really property-like, and then
-             * it's CTFEable, ei will be a literal expression.
-             */
-            const olderrors = global.startGagging();
-            ei = resolveProperties(sc, ei);
-            ei = ei.ctfeInterpret();
-            if (global.endGagging(olderrors) || ei.op == EXP.error)
-                return matchArgNoMatch();
-
-            /* https://issues.dlang.org/show_bug.cgi?id=14520
-             * A property-like function can match to both
-             * TemplateAlias and ValueParameter. But for template overloads,
-             * it should always prefer alias parameter to be consistent
-             * template match result.
-             *
-             *   template X(alias f) { enum X = 1; }
-             *   template X(int val) { enum X = 2; }
-             *   int f1() { return 0; }  // CTFEable
-             *   int f2();               // body-less function is not CTFEable
-             *   enum x1 = X!f1;    // should be 1
-             *   enum x2 = X!f2;    // should be 1
-             *
-             * e.g. The x1 value must be same even if the f1 definition will be moved
-             *      into di while stripping body code.
-             */
-            m = MATCH.convert;
-        }
-
-        if (ei && ei.op == EXP.variable)
-        {
-            // Resolve const variables that we had skipped earlier
-            ei = ei.ctfeInterpret();
-        }
-
-        //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty);
-        vt = tvp.valType.typeSemantic(tvp.loc, sc);
-        //printf("ei: %s, ei.type: %s\n", ei.toChars(), ei.type.toChars());
-        //printf("vt = %s\n", vt.toChars());
-
-        if (ei.type)
-        {
-            MATCH m2 = ei.implicitConvTo(vt);
-            //printf("m: %d\n", m);
-            if (m2 < m)
-                m = m2;
-            if (m == MATCH.nomatch)
-                return matchArgNoMatch();
-            ei = ei.implicitCastTo(sc, vt);
-            ei = ei.ctfeInterpret();
-        }
-
-        if (tvp.specValue)
-        {
-            if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies &&
-                               TemplateValueParameter.edummies[cast(void*)ei.type] == ei))
-                return matchArgNoMatch();
-
-            Expression e = tvp.specValue;
-
-            sc = sc.startCTFE();
-            e = e.expressionSemantic(sc);
-            e = resolveProperties(sc, e);
-            sc = sc.endCTFE();
-            e = e.implicitCastTo(sc, vt);
-            e = e.ctfeInterpret();
-
-            ei = ei.syntaxCopy();
-            sc = sc.startCTFE();
-            ei = ei.expressionSemantic(sc);
-            sc = sc.endCTFE();
-            ei = ei.implicitCastTo(sc, vt);
-            ei = ei.ctfeInterpret();
-            //printf("\tei: %s, %s\n", ei.toChars(), ei.type.toChars());
-            //printf("\te : %s, %s\n", e.toChars(), e.type.toChars());
-            if (!ei.equals(e))
-                return matchArgNoMatch();
-        }
-        else
-        {
-            if (dedtypes[i])
-            {
-                // Must match already deduced value
-                Expression e = cast(Expression)dedtypes[i];
-                if (!ei || !ei.equals(e))
-                    return matchArgNoMatch();
-            }
-        }
-        dedtypes[i] = ei;
-
-        if (psparam)
-        {
-            Initializer _init = new ExpInitializer(tvp.loc, ei);
-            Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init);
-            sparam.storage_class = STC.manifest;
-            *psparam = sparam;
-        }
-        return tvp.dependent ? MATCH.exact : m;
-    }
-
-    MATCH matchArgAlias(TemplateAliasParameter tap)
-    {
-        //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars());
-        MATCH m = MATCH.exact;
-        Type ta = isType(oarg);
-        RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
-        Expression ea = isExpression(oarg);
-        if (ea)
-        {
-            if (auto te = ea.isThisExp())
-                sa = te.var;
-            else if (auto se = ea.isSuperExp())
-                sa = se.var;
-            else if (auto se = ea.isScopeExp())
-                sa = se.sds;
-        }
-        if (sa)
-        {
-            if ((cast(Dsymbol)sa).isAggregateDeclaration())
-                m = MATCH.convert;
-
-            /* specType means the alias must be a declaration with a type
-             * that matches specType.
-             */
-            if (tap.specType)
-            {
-                tap.specType = typeSemantic(tap.specType, tap.loc, sc);
-                Declaration d = (cast(Dsymbol)sa).isDeclaration();
-                if (!d)
-                    return matchArgNoMatch();
-                if (!d.type.equals(tap.specType))
-                    return matchArgNoMatch();
-            }
-        }
-        else
-        {
-            sa = oarg;
-            if (ea)
-            {
-                if (tap.specType)
-                {
-                    if (!ea.type.equals(tap.specType))
-                        return matchArgNoMatch();
-                }
-            }
-            else if (ta && ta.ty == Tinstance && !tap.specAlias)
-            {
-                /* Specialized parameter should be preferred
-                 * match to the template type parameter.
-                 *  template X(alias a) {}                      // a == this
-                 *  template X(alias a : B!A, alias B, A...) {} // B!A => ta
-                 */
-            }
-            else if (sa && sa == TemplateTypeParameter.tdummy)
-            {
-                /* https://issues.dlang.org/show_bug.cgi?id=2025
-                 * Aggregate Types should preferentially
-                 * match to the template type parameter.
-                 *  template X(alias a) {}  // a == this
-                 *  template X(T) {}        // T => sa
-                 */
-            }
-            else if (ta && ta.ty != Tident)
-            {
-                /* Match any type that's not a TypeIdentifier to alias parameters,
-                 * but prefer type parameter.
-                 * template X(alias a) { }  // a == ta
-                 *
-                 * TypeIdentifiers are excluded because they might be not yet resolved aliases.
-                 */
-                m = MATCH.convert;
-            }
-            else
-                return matchArgNoMatch();
-        }
-
-        if (tap.specAlias)
-        {
-            if (sa == TemplateAliasParameter.sdummy)
-                return matchArgNoMatch();
-            // check specialization if template arg is a symbol
-            Dsymbol sx = isDsymbol(sa);
-            if (sa != tap.specAlias && sx)
-            {
-                Type talias = isType(tap.specAlias);
-                if (!talias)
-                    return matchArgNoMatch();
-
-                TemplateInstance ti = sx.isTemplateInstance();
-                if (!ti && sx.parent)
-                {
-                    ti = sx.parent.isTemplateInstance();
-                    if (ti && ti.name != sx.ident)
-                        return matchArgNoMatch();
-                }
-                if (!ti)
-                    return matchArgNoMatch();
-
-                Type t = new TypeInstance(Loc.initial, ti);
-                MATCH m2 = deduceType(t, sc, talias, *parameters, dedtypes);
-                if (m2 == MATCH.nomatch)
-                    return matchArgNoMatch();
-            }
-            // check specialization if template arg is a type
-            else if (ta)
-            {
-                if (Type tspec = isType(tap.specAlias))
-                {
-                    MATCH m2 = ta.implicitConvTo(tspec);
-                    if (m2 == MATCH.nomatch)
-                        return matchArgNoMatch();
-                }
-                else
-                {
-                    error(tap.loc, "template parameter specialization for a type must be a type and not `%s`",
-                        tap.specAlias.toChars());
-                    return matchArgNoMatch();
-                }
-            }
-        }
-        else if (dedtypes[i])
-        {
-            // Must match already deduced symbol
-            RootObject si = dedtypes[i];
-            if (!sa || si != sa)
-                return matchArgNoMatch();
-        }
-        dedtypes[i] = sa;
-
-        if (psparam)
-        {
-            if (Dsymbol s = isDsymbol(sa))
-            {
-                *psparam = new AliasDeclaration(tap.loc, tap.ident, s);
-            }
-            else if (Type t = isType(sa))
-            {
-                *psparam = new AliasDeclaration(tap.loc, tap.ident, t);
-            }
-            else
-            {
-                assert(ea);
-
-                // Declare manifest constant
-                Initializer _init = new ExpInitializer(tap.loc, ea);
-                auto v = new VarDeclaration(tap.loc, null, tap.ident, _init);
-                v.storage_class = STC.manifest;
-                v.dsymbolSemantic(sc);
-                *psparam = v;
-            }
-        }
-        return tap.dependent ? MATCH.exact : m;
-    }
-
-    MATCH matchArgTuple(TemplateTupleParameter ttp)
-    {
-        //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars());
-        Tuple ovar = isTuple(oarg);
-        if (!ovar)
-            return MATCH.nomatch;
-        if (dedtypes[i])
-        {
-            Tuple tup = isTuple(dedtypes[i]);
-            if (!tup)
-                return MATCH.nomatch;
-            if (!match(tup, ovar))
-                return MATCH.nomatch;
-        }
-        dedtypes[i] = ovar;
-
-        if (psparam)
-            *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects);
-        return ttp.dependent ? MATCH.exact : MATCH.convert;
-    }
-
-    if (auto ttp = tp.isTemplateTypeParameter())
-        return matchArgType(ttp);
-    if (auto tvp = tp.isTemplateValueParameter())
-        return matchArgValue(tvp);
-    if (auto tap = tp.isTemplateAliasParameter())
-        return matchArgAlias(tap);
-    if (auto ttp = tp.isTemplateTupleParameter())
-        return matchArgTuple(ttp);
-    assert(0);
-}
-
-
 /***********************************************
  * Collect and print statistics on template instantiations.
  */
diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d
index 3946c25c9f03..4cb7d1ac5dda 100644
--- a/gcc/d/dmd/dtoh.d
+++ b/gcc/d/dmd/dtoh.d
@@ -895,7 +895,7 @@ public:
     {
         // Omit redundant declarations - the slot was already
         // reserved in the base class
-        if (fd.isVirtual() && fd.isIntroducing())
+        if (fd.isVirtual() && fd.isIntroducing)
         {
             // Hide placeholders because they are not ABI compatible
             writeProtection(AST.Visibility.Kind.private_);
@@ -1164,7 +1164,7 @@ public:
 
         auto fd = ad.aliassym.isFuncDeclaration();
 
-        if (fd && (fd.isGenerated() || fd.isDtorDeclaration()))
+        if (fd && (fd.isGenerated || fd.isDtorDeclaration()))
         {
             // Ignore. It's taken care of while visiting FuncDeclaration
             return;
@@ -1197,7 +1197,7 @@ public:
                 // Print prefix of the base class if this function originates from a superclass
                 // because alias might be resolved through multiple classes, e.g.
                 // e.g. for alias visit = typeof(super).visit in the visitors
-                if (!fd.isIntroducing())
+                if (!fd.isIntroducing)
                     printPrefix(ad.toParent().isClassDeclaration().baseClass);
                 else
                     printPrefix(pd);
diff --git a/gcc/d/dmd/dversion.d b/gcc/d/dmd/dversion.d
index 26528e9f5178..1561ebb0b7c4 100644
--- a/gcc/d/dmd/dversion.d
+++ b/gcc/d/dmd/dversion.d
@@ -20,7 +20,6 @@ import dmd.dmodule;
 import dmd.dscope;
 import dmd.dsymbol;
 import dmd.dsymbolsem;
-import dmd.globals;
 import dmd.identifier;
 import dmd.location;
 import dmd.common.outbuffer;
diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h
index 6d5d3022a7fc..2b4d3b52d89e 100644
--- a/gcc/d/dmd/enum.h
+++ b/gcc/d/dmd/enum.h
@@ -59,7 +59,7 @@ public:
     bool isSpecial() const;
 
 
-    Symbol *sinit;
+    void *sinit;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d
index 6d325e44c964..cdf8a61bd88a 100644
--- a/gcc/d/dmd/enumsem.d
+++ b/gcc/d/dmd/enumsem.d
@@ -240,7 +240,9 @@ void enumSemantic(Scope* sc, EnumDeclaration ed)
             em.dsymbolSemantic(em._scope);
     });
 
-    if (global.params.useTypeInfo && Type.dtypeinfo && !ed.inNonRoot())
+    if (ed.errors)
+        ed.memtype = Type.terror; // avoid infinite recursion in toBaseType
+    else if (global.params.useTypeInfo && Type.dtypeinfo && !ed.inNonRoot())
         semanticTypeInfo(sc, ed.memtype);
     //printf("ed.defaultval = %lld\n", ed.defaultval);
 
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index a0c5472375e5..d3473f691d55 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -1557,7 +1557,7 @@ void escapeExp(Expression e, ref scope EscapeByResults er, int deref)
     void visitThis(ThisExp e)
     {
         // Special case because `__this2` isn't `ref` internally
-        if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
+        if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext)
         {
             escapeByValue(e, er);
             return;
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index c29484d9e7bf..4d873ccecdc0 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -21,6 +21,7 @@ import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.ast_node;
+import dmd.ctfeexpr : isCtfeReferenceValid;
 import dmd.dcast : implicitConvTo;
 import dmd.dclass;
 import dmd.declaration;
@@ -31,6 +32,7 @@ import dmd.dsymbol;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.errorsink;
+import dmd.expressionsem : getDsymbol;
 import dmd.func;
 import dmd.globals;
 import dmd.hdrgen;
@@ -49,7 +51,7 @@ import dmd.root.string;
 import dmd.root.utf;
 import dmd.target;
 import dmd.tokens;
-import dmd.typesem;
+import dmd.typesem : toHeadMutable, size, mutableOf, unSharedOf;
 import dmd.visitor;
 
 enum LOGSEMANTIC = false;
@@ -527,20 +529,6 @@ extern (C++) abstract class Expression : ASTNode
         return false;
     }
 
-    /******************************
-     * Take address of expression.
-     */
-    final Expression addressOf()
-    {
-        //printf("Expression::addressOf()\n");
-        debug
-        {
-            assert(op == EXP.error || isLvalue());
-        }
-        Expression e = new AddrExp(loc, this, type.pointerTo());
-        return e;
-    }
-
     /******************************
      * If this is a reference, dereference it.
      */
@@ -1906,6 +1894,12 @@ extern (C++) final class ArrayLiteralExp : Expression
 
     Expressions* elements;
 
+    Expression lowering;
+
+    // aaLiteral is set if this is an array of values of an AA literal
+    // only used during CTFE to show the original AA in error messages instead
+    AssocArrayLiteralExp aaLiteral;
+
     extern (D) this(Loc loc, Type type, Expressions* elements) @safe
     {
         super(loc, EXP.arrayLiteral);
@@ -1917,8 +1911,7 @@ extern (C++) final class ArrayLiteralExp : Expression
     {
         super(loc, EXP.arrayLiteral);
         this.type = type;
-        elements = new Expressions();
-        elements.push(e);
+        elements = new Expressions(e);
     }
 
     extern (D) this(Loc loc, Type type, Expression basis, Expressions* elements) @safe
@@ -2009,7 +2002,7 @@ extern (C++) final class ArrayLiteralExp : Expression
                     if (ch.op != EXP.int64)
                         return null;
                     if (sz == 1)
-                        buf.writeByte(cast(uint)ch.toInteger());
+                        buf.writeByte(cast(ubyte)ch.toInteger());
                     else if (sz == 2)
                         buf.writeword(cast(uint)ch.toInteger());
                     else
@@ -2059,8 +2052,9 @@ extern (C++) final class AssocArrayLiteralExp : Expression
 
     Expressions* keys;
     Expressions* values;
-    /// Lower to core.internal.newaa for static initializaton
-    Expression lowering;
+
+    Expression lowering;     // call to _d_assocarrayliteralTX()
+    Expression loweringCtfe; // result of interpreting lowering for static initializaton
 
     extern (D) this(Loc loc, Expressions* keys, Expressions* values) @safe
     {
@@ -2217,56 +2211,6 @@ extern (C++) final class StructLiteralExp : Expression
         return exp;
     }
 
-    /**************************************
-     * Gets expression at offset of type.
-     * Returns NULL if not found.
-     */
-    extern (D) Expression getField(Type type, uint offset)
-    {
-        //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
-        //  /*toChars()*/"", type.toChars(), offset);
-        Expression e = null;
-        int i = getFieldIndex(type, offset);
-
-        if (i != -1)
-        {
-            //printf("\ti = %d\n", i);
-            if (i >= sd.nonHiddenFields())
-                return null;
-
-            assert(i < elements.length);
-            e = (*elements)[i];
-            if (e)
-            {
-                //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
-
-                /* If type is a static array, and e is an initializer for that array,
-                 * then the field initializer should be an array literal of e.
-                 */
-                auto tsa = type.isTypeSArray();
-                if (tsa && e.type.castMod(0) != type.castMod(0))
-                {
-                    const length = cast(size_t)tsa.dim.toInteger();
-                    auto z = new Expressions(length);
-                    foreach (ref q; *z)
-                        q = e.copy();
-                    e = new ArrayLiteralExp(loc, type, z);
-                }
-                else
-                {
-                    e = e.copy();
-                    e.type = type;
-                }
-                if (useStaticInit && e.type.needsNested())
-                    if (auto se = e.isStructLiteralExp())
-                    {
-                        se.useStaticInit = true;
-                    }
-            }
-        }
-        return e;
-    }
-
     /************************************
      * Get index of field.
      * Returns -1 if not found.
@@ -3312,6 +3256,7 @@ extern (C++) final class CallExp : UnaExp
     bool ignoreAttributes;  /// don't enforce attributes (e.g. call @gc function in @nogc code)
     bool isUfcsRewrite;     /// the first argument was pushed in here by a UFCS rewrite
     VarDeclaration vthis2;  // container for multi-context
+    Expression loweredFrom; // set if this is the result of a lowering
 
     /// Puts the `arguments` and `names` into an `ArgumentList` for easily passing them around.
     /// The fields are still separate for backwards compatibility
@@ -3486,6 +3431,13 @@ extern (C++) final class AddrExp : UnaExp
         type = t;
     }
 
+    override Optional!bool toBool()
+    {
+        if (isCtfeReferenceValid(e1))
+            return typeof(return)(true);
+        return UnaExp.toBool();
+    }
+
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -3574,6 +3526,8 @@ extern (C++) final class ComExp : UnaExp
  */
 extern (C++) final class NotExp : UnaExp
 {
+    Expression loweredFrom; // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)`
+
     extern (D) this(Loc loc, Expression e) @safe
     {
         super(loc, EXP.not, e);
@@ -3618,6 +3572,7 @@ extern (C++) final class CastExp : UnaExp
     Type to;                    // type to cast to
     ubyte mod = cast(ubyte)~0;  // MODxxxxx
     bool trusted; // assume cast is safe
+    Expression lowering;
 
     extern (D) this(Loc loc, Expression e, Type t) @safe
     {
@@ -3797,6 +3752,7 @@ extern (C++) final class ArrayExp : UnaExp
 
     size_t currentDimension;    // for opDollar
     VarDeclaration lengthVar;
+    bool modifiable = false;    // is this expected to be an lvalue in an AssignExp? propagate to IndexExp
 
     extern (D) this(Loc loc, Expression e1, Expression index = null)
     {
@@ -3994,6 +3950,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp
 extern (C++) final class IndexExp : BinExp
 {
     VarDeclaration lengthVar;
+    Expression loweredFrom;     // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX
     bool modifiable = false;    // assume it is an rvalue
     bool indexIsInBounds;       // true if 0 <= e2 && e2 <= e1.length - 1
 
@@ -4030,29 +3987,6 @@ extern (C++) final class IndexExp : BinExp
         return true;
     }
 
-    extern (D) Expression markSettingAAElem()
-    {
-        if (e1.type.toBasetype().ty == Taarray)
-        {
-            Type t2b = e2.type.toBasetype();
-            if (t2b.ty == Tarray && t2b.nextOf().isMutable())
-            {
-                error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", e2.type.toChars());
-                return ErrorExp.get();
-            }
-            modifiable = true;
-
-            if (auto ie = e1.isIndexExp())
-            {
-                Expression ex = ie.markSettingAAElem();
-                if (ex.op == EXP.error)
-                    return ex;
-                assert(ex == e1);
-            }
-        }
-        return this;
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -4783,7 +4717,6 @@ extern (C++) final class RemoveExp : BinExp
     extern (D) this(Loc loc, Expression e1, Expression e2)
     {
         super(loc, EXP.remove, e1, e2);
-        type = Type.tbool;
     }
 
     override void accept(Visitor v)
@@ -4801,6 +4734,7 @@ extern (C++) final class RemoveExp : BinExp
  */
 extern (C++) final class EqualExp : BinExp
 {
+    Expression lowering;
     extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe
     {
         super(loc, op, e1, e2);
diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h
index 6b4298b3308b..7c92ebe66c44 100644
--- a/gcc/d/dmd/expression.h
+++ b/gcc/d/dmd/expression.h
@@ -407,6 +407,8 @@ public:
     d_bool onstack;
     Expression *basis;
     Expressions *elements;
+    Expression *lowering;
+    AssocArrayLiteralExp* aaLiteral; // set if this is an array of keys/values of an AA literal
 
     static ArrayLiteralExp *create(Loc loc, Expressions *elements);
     ArrayLiteralExp *syntaxCopy() override;
@@ -425,6 +427,7 @@ public:
     Expressions *keys;
     Expressions *values;
     Expression* lowering;
+    Expression* loweringCtfe;
 
     bool equals(const RootObject * const o) const override;
     AssocArrayLiteralExp *syntaxCopy() override;
@@ -833,6 +836,7 @@ public:
     d_bool ignoreAttributes;      // don't enforce attributes (e.g. call @gc function in @nogc code)
     d_bool isUfcsRewrite;       // the first argument was pushed in here by a UFCS rewrite
     VarDeclaration *vthis2;     // container for multi-context
+    Expression* loweredFrom;    // set if this is the result of a lowering
 
     static CallExp *create(Loc loc, Expression *e, Expressions *exps);
     static CallExp *create(Loc loc, Expression *e);
@@ -848,6 +852,7 @@ public:
 class AddrExp final : public UnaExp
 {
 public:
+    Optional<bool> toBool() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -880,6 +885,7 @@ public:
 class NotExp final : public UnaExp
 {
 public:
+    Expression* loweredFrom;    // for lowering of `aa1 != aa2` to `!_d_aaEqual(aa1, aa2)`
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -897,6 +903,7 @@ public:
     Type *to;                   // type to cast to
     unsigned char mod;          // MODxxxxx
     d_bool trusted; // assume cast is safe
+    Expression* lowering;
 
     CastExp *syntaxCopy() override;
     bool isLvalue() override;
@@ -986,6 +993,7 @@ public:
     Expressions *arguments;             // Array of Expression's
     size_t currentDimension;            // for opDollar
     VarDeclaration *lengthVar;
+    d_bool modifiable;
 
     ArrayExp *syntaxCopy() override;
     bool isLvalue() override;
@@ -1016,6 +1024,7 @@ class IndexExp final : public BinExp
 {
 public:
     VarDeclaration *lengthVar;
+    Expression* loweredFrom;      // for associative array lowering to _d_aaGetY or _d_aaGetRvalueX
     d_bool modifiable;
     d_bool indexIsInBounds;       // true if 0 <= e2 && e2 <= e1.length - 1
 
@@ -1279,6 +1288,8 @@ public:
 class EqualExp final : public BinExp
 {
 public:
+    Expression* lowering;
+
     void accept(Visitor *v) override { v->visit(this); }
 };
 
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 2576d7c0d056..68fe3dca41c0 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -1,4 +1,4 @@
-/**
+/***
  * Semantic analysis of expressions.
  *
  * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
@@ -29,6 +29,7 @@ import dmd.cond;
 import dmd.ctorflow;
 import dmd.dscope;
 import dmd.dsymbol;
+import dmd.dsymbolsem;
 import dmd.declaration;
 import dmd.dclass;
 import dmd.dcast;
@@ -39,7 +40,7 @@ import dmd.dimport;
 import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dstruct;
-import dmd.dsymbolsem;
+import dmd.dsymbolsem : findTempDecl;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.errorsink;
@@ -82,7 +83,7 @@ import dmd.semantic3;
 import dmd.sideeffect;
 import dmd.safe;
 import dmd.target;
-import dmd.templatesem : matchWithInstance;
+import dmd.templatesem : matchWithInstance, deduceType, matchArg, updateTempDecl;
 import dmd.tokens;
 import dmd.traits;
 import dmd.typesem;
@@ -202,6 +203,54 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps,
     return false;
 }
 
+/***************************************
+ * Verifies whether the struct declaration has a
+ * constructor that is not a copy constructor.
+ * Optionally, it can check whether the struct
+ * declaration has a regular constructor, that
+ * is not disabled.
+ *
+ * Params:
+ *      sd = struct declaration
+ *      ignoreDisabled = true to ignore disabled constructors
+ * Returns:
+ *      true, if the struct has a regular (optionally,
+ *      not disabled) constructor, false otherwise.
+ */
+bool hasRegularCtor(StructDeclaration sd, bool ignoreDisabled)
+{
+    if (!sd.ctor)
+        return false;
+
+    bool result;
+    overloadApply(sd.ctor, (Dsymbol s)
+    {
+        if (auto td = s.isTemplateDeclaration())
+        {
+            if (ignoreDisabled && td.onemember)
+            {
+                if (auto ctorDecl = td.onemember.isCtorDeclaration())
+                {
+                    if (ctorDecl.storage_class & STC.disable)
+                        return 0;
+                }
+            }
+            result = true;
+            return 1;
+        }
+        if (auto ctorDecl = s.isCtorDeclaration())
+        {
+            if (!ctorDecl.isCpCtor && !ctorDecl.isMoveCtor && (!ignoreDisabled || !(ctorDecl.storage_class & STC.disable)))
+            {
+                result = true;
+                return 1;
+            }
+        }
+        return 0;
+    });
+    return result;
+}
+
 /*****************************************
  * Determine if `this` is available by walking up the enclosing
  * scopes until a function is found.
@@ -228,7 +277,7 @@ FuncDeclaration hasThis(Scope* sc)
         {
             return null;
         }
-        if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2()))
+        if (!fd.isNested() || fd.isThis() || (fd.hasDualContext && fd.isMember2()))
             break;
 
         Dsymbol parent = fd.parent;
@@ -245,7 +294,7 @@ FuncDeclaration hasThis(Scope* sc)
         fd = parent.isFuncDeclaration();
     }
 
-    if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2()))
+    if (!fd.isThis() && !(fd.hasDualContext && fd.isMember2()))
     {
         return null;
     }
@@ -410,54 +459,6 @@ extern (D) Expression incompatibleTypes(BinExp e, Scope* sc = null)
     return ErrorExp.get();
 }
 
-private Expression reorderSettingAAElem(BinExp exp, Scope* sc)
-{
-    BinExp be = exp;
-
-    auto ie = be.e1.isIndexExp();
-    if (!ie)
-        return be;
-    if (ie.e1.type.toBasetype().ty != Taarray)
-        return be;
-
-    /* Fix evaluation order of setting AA element
-     * https://issues.dlang.org/show_bug.cgi?id=3825
-     * Rewrite:
-     *     aa[k1][k2][k3] op= val;
-     * as:
-     *     auto ref __aatmp = aa;
-     *     auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
-     *     auto ref __aaval = val;
-     *     __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval;  // assignment
-     */
-
-    Expression e0;
-    while (1)
-    {
-        Expression de;
-        ie.e2 = extractSideEffect(sc, "__aakey", de, ie.e2);
-        e0 = Expression.combine(de, e0);
-
-        auto ie1 = ie.e1.isIndexExp();
-        if (!ie1 ||
-            ie1.e1.type.toBasetype().ty != Taarray)
-        {
-            break;
-        }
-        ie = ie1;
-    }
-    assert(ie.e1.type.toBasetype().ty == Taarray);
-
-    Expression de;
-    ie.e1 = extractSideEffect(sc, "__aatmp", de, ie.e1);
-    e0 = Expression.combine(de, e0);
-
-    be.e2 = extractSideEffect(sc, "__aaval", e0, be.e2, true);
-
-    //printf("-e0 = %s, be = %s\n", e0.toChars(), be.toChars());
-    return Expression.combine(e0, be);
-}
-
 private Expression checkOpAssignTypes(BinExp binExp, Scope* sc)
 {
     auto e1 = binExp.e1;
@@ -612,20 +613,15 @@ private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue)
     // https://issues.dlang.org/show_bug.cgi?id=12585
     // Extract the side effect part if ue.e1 is comma.
 
-    if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect()
-    {
-        /* Even if opDollar is needed, 'e1' should be evaluate only once. So
-         * Rewrite:
-         *      e1.opIndex( ... use of $ ... )
-         *      e1.opSlice( ... use of $ ... )
-         * as:
-         *      (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
-         *      (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
-         */
-        e1 = extractSideEffect(sc, "__dop", e0, e1, false);
-        assert(e1.isVarExp());
-        e1.isVarExp().var.storage_class |= STC.exptemp;     // lifetime limited to expression
-    }
+    /* Even if opDollar is needed, 'e1' should be evaluate only once. So
+        * Rewrite:
+        *      e1.opIndex( ... use of $ ... )
+        *      e1.opSlice( ... use of $ ... )
+        * as:
+        *      (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
+        *      (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
+        */
+    e1 = extractSideEffect(sc, "__dop", e0, e1, false);
     ue.e1 = e1;
     return e0;
 }
@@ -663,6 +659,72 @@ TupleDeclaration isAliasThisTuple(Expression e)
     }
 }
 
+/******************************
+ * Take address of expression.
+ */
+Expression addressOf(Expression e)
+{
+    //printf("Expression::addressOf() %s\n", e.toChars());
+    debug
+    {
+        assert(e.op == EXP.error || e.isLvalue());
+    }
+    return new AddrExp(e.loc, e, e.type.pointerTo());
+}
+
+/**************************************
+ * Gets expression at offset of type.
+ * Returns NULL if not found.
+ */
+Expression getField(StructLiteralExp sle, Type type, uint offset)
+{
+    //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
+    //  /*toChars()*/"", type.toChars(), offset);
+    Expression e2 = null;
+    const int i = sle.getFieldIndex(type, offset);
+
+    if (i == -1)
+        return null;
+
+    //printf("\ti = %d\n", i);
+    if (i >= sle.sd.nonHiddenFields())
+        return null;
+
+    assert(i < sle.elements.length);
+    e2 = (*sle.elements)[i];
+    if (!e2)
+        return null;
+
+    //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
+
+    /* If type is a static array, and e is an initializer for that array,
+     * then the field initializer should be an array literal of e.
+     */
+    auto tsa = type.isTypeSArray();
+    if (tsa && e2.type.castMod(0) != type.castMod(0))
+    {
+        const length = cast(size_t)tsa.dim.toInteger();
+        auto z = new Expressions(length);
+        foreach (ref q; *z)
+            q = e2.copy();
+        e2 = new ArrayLiteralExp(sle.loc, type, z);
+    }
+    else
+    {
+        e2 = e2.copy();
+        e2.type = type;
+    }
+    if (sle.useStaticInit && e2.type.needsNested())
+        if (auto se = e2.isStructLiteralExp())
+        {
+            se.useStaticInit = true;
+        }
+
+    return e2;
+}
+
+
+
 /**************************************
  * Runs semantic on ae.arguments. Declares temporary variables
  * if '$' was used.
@@ -712,14 +774,11 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, out Expression pe0)
 
         if (auto ie = e.isIntervalExp())
         {
-            auto tiargs = new Objects();
             Expression edim = new IntegerExp(ae.loc, i, Type.tsize_t);
             edim = edim.expressionSemantic(sc);
-            tiargs.push(edim);
+            auto tiargs = new Objects(edim);
 
-            auto fargs = new Expressions(2);
-            (*fargs)[0] = ie.lwr;
-            (*fargs)[1] = ie.upr;
+            auto fargs = new Expressions(ie.lwr, ie.upr);
 
             const xerrors = global.startGagging();
             sc = sc.push();
@@ -1246,7 +1305,8 @@ private Expression resolveUFCS(Scope* sc, CallExp ce)
 
                 semanticTypeInfo(sc, taa.index);
 
-                return new RemoveExp(loc, eleft, key);
+                e = new RemoveExp(loc, eleft, key);
+                return e.expressionSemantic(sc);
             }
         }
         else
@@ -1817,7 +1877,7 @@ L1:
     if (e1.op == EXP.this_)
     {
         FuncDeclaration f = hasThis(sc);
-        if (f && f.hasDualContext())
+        if (f && f.hasDualContext)
         {
             if (f.followInstantiationContext(ad))
             {
@@ -2000,7 +2060,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc,
             scope bool function(DtorDeclaration) check, const string checkName)
 {
     auto dd = f.isDtorDeclaration();
-    if (!dd || !dd.isGenerated())
+    if (!dd || !dd.isGenerated)
         return;
 
     // DtorDeclaration without parents should fail at an earlier stage
@@ -2017,7 +2077,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc,
     }
 
     dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
-                        dd.isGenerated() ? "generated " : "".ptr,
+                        dd.isGenerated ? "generated " : "".ptr,
                         ad.toChars,
                         cast(int) checkName.length, checkName.ptr);
 
@@ -2048,7 +2108,7 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc,
         {
             field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
 
-            if (fieldSd.dtor.isGenerated())
+            if (fieldSd.dtor.isGenerated)
                 fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName);
             else
                 fieldSd.dtor.loc.errorSupplemental("   %.*s `%s.~this` is declared here",
@@ -2351,7 +2411,9 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
     // so don't print anything to avoid double error messages.
     if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
         || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
-        || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+        || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT
+        || f.ident == Id._d_assocarrayliteralTX || f.ident == Id._d_arrayliteralTX
+        || f.ident == Id._d_aaGetY))
     {
         error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
             sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
@@ -2373,7 +2435,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
  *   t = struct type, or static array of struct type to check
  *   loc = error message location
  *   sc = scope in which attributes are checked
- * Returns: true if there's an error
+ * Returns: true if there is an error
  */
 private bool checkPostblit(Type t, ref Loc loc, Scope* sc)
 {
@@ -2441,8 +2503,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
                 return ErrorExp.get();
             e2 = resolveProperties(sc, e2);
 
-            Expressions* a = new Expressions();
-            a.push(e2);
+            Expressions* a = new Expressions(e2);
 
             for (size_t i = 0; i < os.a.length; i++)
             {
@@ -2561,8 +2622,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
                 return ErrorExp.get();
             e2 = resolveProperties(sc, e2);
 
-            Expressions* a = new Expressions();
-            a.push(e2);
+            Expressions* a = new Expressions(e2);
 
             FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(a), FuncResolveFlag.quiet);
             if (fd && fd.type)
@@ -3032,7 +3092,9 @@ private bool functionParameters(Loc loc, Scope* sc,
      */
     if (fd && fd.inlining == PINLINE.always)
     {
-        if (sc._module)
+        if (sc.minst)
+            sc.minst.hasAlwaysInlines = true;
+        else if (sc._module)
             sc._module.hasAlwaysInlines = true;
         if (sc.func)
             sc.func.hasAlwaysInlines = true;
@@ -3754,8 +3816,8 @@ private bool functionParameters(Loc loc, Scope* sc,
             int offset;
             if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0))
             {
-                const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars();
-                const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars();
+                const(char)* s1 = tret.isNaked ? " mutable" : tret.modToChars();
+                const(char)* s2 = tthis.isNaked ? " mutable" : tthis.modToChars();
                 .error(loc, "`inout` constructor `%s` creates%s object, not%s", fd.toPrettyChars(), s1, s2);
                 err = true;
             }
@@ -3807,6 +3869,326 @@ Package resolveIsPackage(Dsymbol sym)
     return pkg;
 }
 
+/**
+ * Performs the lowering of a CastExp to a call to `.object._d_cast`,
+ * populating the `lowering` field of the CastExp.
+ * This is only done for casts between classes/interfaces.
+ *
+ * Params:
+ *  cex = the CastExp to lower
+ *  sc = the current scope
+ */
+private void lowerCastExp(CastExp cex, Scope* sc)
+{
+    Type t1b = cex.e1.type.toBasetype();
+    Type tob = cex.to.toBasetype();
+
+    if (t1b.ty != Tclass || tob.ty != Tclass)
+        return;
+
+    ClassDeclaration cdfrom = t1b.isClassHandle();
+    ClassDeclaration cdto   = tob.isClassHandle();
+
+    int offset;
+    if ((cdto.isBaseOf(cdfrom, &offset) && offset != ClassDeclaration.OFFSET_RUNTIME)
+        || cdfrom.classKind == ClassKind.cpp || cdto.classKind == ClassKind.cpp)
+        return;
+
+    Identifier hook = Id._d_cast;
+    if (!verifyHookExist(cex.loc, *sc, hook, "d_cast", Id.object))
+        return;
+
+    // Lower to .object._d_cast!(To)(exp.e1)
+    Expression lowering = new IdentifierExp(cex.loc, Id.empty);
+    lowering = new DotIdExp(cex.loc, lowering, Id.object);
+
+    // Unqualify the type being casted to, avoiding multiple instantiations
+    auto unqual_tob = tob.unqualify(MODFlags.wild | MODFlags.const_ |
+        MODFlags.immutable_ | MODFlags.shared_);
+    auto tiargs = new Objects(unqual_tob);
+    lowering = new DotTemplateInstanceExp(cex.loc, lowering, hook, tiargs);
+
+    // Unqualify the type being casted from to avoid multiple instantiations
+    auto unqual_t1b = t1b.unqualify(MODFlags.wild | MODFlags.const_ |
+        MODFlags.immutable_ | MODFlags.shared_);
+    Expression e1c = cex.e1.copy();
+    e1c.type = unqual_t1b;
+    auto arguments = new Expressions(e1c);
+
+    lowering = new CallExp(cex.loc, lowering, arguments);
+
+    cex.lowering = lowering.expressionSemantic(sc);
+}
+
+/**
+ * Visitor to check if a type is suitable for comparison using `memcmp`.
+ * Currently used when lowering `EqualExp`.
+ */
+private extern(C++) final class IsMemcmpableVisitor : Visitor
+{
+    alias visit = Visitor.visit;
+    public:
+    bool result = false;
+
+    override void visit(Type t)
+    {
+        result = t.ty == Tvoid || (t.isScalar() && !t.isFloating());
+    }
+
+    override void visit(TypeStruct ts)
+    {
+        result = false;
+
+        if (ts.sym.hasIdentityEquals)
+            return; // has custom opEquals
+
+        if (!ts.sym.members)
+        {
+            result = true;
+            return;
+        }
+
+        /* We recursively check all variable declaration within the struct.
+         * The recursiveness is needed to handle cases like this:
+         * struct Test {
+         *     nothrow:
+         *     int[] contents;
+         * }
+         * Here a `StorageClassDeclaration` symbol will be created, which wraps the variable declaration.
+         */
+        static bool visitAllMembers(Dsymbols* members, TypeStruct root, IsMemcmpableVisitor v)
+        {
+            if (members is null)
+                return true;
+
+            foreach (m; *members)
+            {
+                if (auto vd = m.isVarDeclaration())
+                {
+                    if (vd.type is null)
+                        continue;
+
+                    auto tbvd = vd.type.toBasetype();
+                    if (tbvd !is root)
+                        tbvd.accept(v);
+
+                    if (!v.result)
+                        return false;
+                }
+                else if (auto ad = m.isAttribDeclaration())
+                {
+                    if(!visitAllMembers(ad.decl, root, v))
+                        return false;
+                }
+            }
+            return true;
+        }
+        result = visitAllMembers(ts.sym.members, ts, this);
+    }
+
+    override void visit(TypeSArray tsa)
+    {
+        tsa.nextOf().toBasetype().accept(this);
+    }
+}
+
+/*********************************************
+ * In the current function 'sc.func', we are calling 'fd'.
+ * 1. Check to see if the current function can call 'fd' , issue error if not.
+ * 2. If the current function is not the parent of 'fd' , then add
+ *    the current function to the list of siblings of 'fd' .
+ * 3. If the current function is a literal, and it's accessing an uplevel scope,
+ *    then mark it as a delegate.
+ * Returns true if error occurs.
+ */
+private bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc)
+{
+    //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars());
+    if (auto fld = fd.isFuncLiteralDeclaration())
+    {
+        if (fld.tok == TOK.reserved)
+        {
+            fld.tok = TOK.function_;
+            fld.vthis = null;
+        }
+    }
+    if (!fd.parent || fd.parent == sc.parent)
+        return false;
+    if (fd.ident == Id.require || fd.ident == Id.ensure)
+        return false;
+    if (!fd.isThis() && !fd.isNested())
+        return false;
+    // The current function
+    FuncDeclaration fdthis = sc.parent.isFuncDeclaration();
+    if (!fdthis)
+        return false; // out of function scope
+    Dsymbol p = fd.toParentLocal();
+    Dsymbol p2 = fd.toParent2();
+    // Function literals from fdthis to p must be delegates
+    ensureStaticLinkTo(fdthis, p);
+    if (p != p2)
+        ensureStaticLinkTo(fdthis, p2);
+    if (!fd.isNested())
+        return false;
+
+    // The function that this function is in
+    bool checkEnclosing(FuncDeclaration fdv)
+    {
+        if (!fdv)
+            return false;
+        if (fdv == fdthis)
+            return false;
+        //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars());
+        //printf("fdv  = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars());
+        //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars());
+        // Add this function to the list of those which called us
+        if (fdthis != fd)
+        {
+            bool found = false;
+            for (size_t i = 0; i < fd.siblingCallers.length; ++i)
+            {
+                if (fd.siblingCallers[i] == fdthis)
+                    found = true;
+            }
+            if (!found)
+            {
+                //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars());
+                if (!sc.intypeof && !sc.traitsCompiles)
+                {
+                    fd.siblingCallers.push(fdthis);
+                    fd.computedEscapingSiblings = false;
+                }
+            }
+        }
+        const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd);
+        if (lv == fd.LevelError)
+            return true; // error
+        if (lv == -1)
+            return false; // downlevel call
+        if (lv == 0)
+            return false; // same level call
+        return false; // Uplevel call
+    }
+    if (checkEnclosing(p.isFuncDeclaration()))
+        return true;
+    if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration()))
+        return true;
+    return false;
+}
+
+Expression lowerArrayLiteral(ArrayLiteralExp ale, Scope* sc)
+{
+    const dim = ale.elements ? ale.elements.length : 0;
+
+    Identifier hook = global.params.tracegc ? Id._d_arrayliteralTXTrace : Id._d_arrayliteralTX;
+    if (!verifyHookExist(ale.loc, *sc, hook, "creating array literals"))
+        return null;
+
+    Expression lowering = new IdentifierExp(ale.loc, Id.empty);
+    lowering = new DotIdExp(ale.loc, lowering, Id.object);
+    // Remove `inout`, `const`, `immutable` and `shared` to reduce template instances
+    auto t = ale.type.nextOf().unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_);
+    auto tiargs = new Objects(t);
+    lowering = new DotTemplateInstanceExp(ale.loc, lowering, hook, tiargs);
+
+    auto arguments = new Expressions(new IntegerExp(dim));
+    lowering = new CallExp(ale.loc, lowering, arguments);
+    ale.lowering = lowering.expressionSemantic(sc);
+
+    return ale.lowering;
+}
+
+/***********************************
+ * If oarg represents a Dsymbol, return that Dsymbol
+ * Params:
+ *      oarg = argument to check
+ * Returns:
+ *      Dsymbol if a symbol, null if not
+ */
+Dsymbol getDsymbol(RootObject oarg)
+{
+    //printf("getDsymbol()\n");
+    //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg));
+    if (auto ea = isExpression(oarg))
+    {
+        // Try to convert Expression to symbol
+        if (auto ve = ea.isVarExp())
+            return ve.var;
+        if (auto fe = ea.isFuncExp())
+            return fe.td ? fe.td : fe.fd;
+        if (auto te = ea.isTemplateExp())
+            return te.td;
+        if (auto te = ea.isScopeExp())
+            return te.sds;
+        return null;
+    }
+    // Try to convert Type to symbol
+    if (auto ta = isType(oarg))
+        return ta.toDsymbol(null);
+    return isDsymbol(oarg); // if already a symbol
+}
+
+/**************************************
+ * IsExpression can evaluate the specified type speculatively, and even if
+ * it instantiates any symbols, they are normally unnecessary for the
+ * final executable.
+ * However, if those symbols leak to the actual code, compiler should remark
+ * them as non-speculative to generate their code and link to the final executable.
+ */
+private void unSpeculative(Scope* sc, RootObject o)
+{
+    if (!o)
+        return;
+
+    if (Tuple tup = isTuple(o))
+    {
+        foreach (obj; tup.objects)
+        {
+            unSpeculative(sc, obj);
+        }
+        return;
+    }
+
+    Dsymbol s = getDsymbol(o);
+    if (!s)
+        return;
+
+    if (Declaration d = s.isDeclaration())
+    {
+        if (VarDeclaration vd = d.isVarDeclaration())
+            o = vd.type;
+        else if (AliasDeclaration ad = d.isAliasDeclaration())
+        {
+            o = ad.getType();
+            if (!o)
+                o = ad.toAlias();
+        }
+        else
+            o = d.toAlias();
+
+        s = getDsymbol(o);
+        if (!s)
+            return;
+    }
+
+    if (TemplateInstance ti = s.isTemplateInstance())
+    {
+        // If the instance is already non-speculative,
+        // or it is leaked to the speculative scope.
+        if (ti.minst !is null || sc.minst is null)
+            return;
+
+        // Remark as non-speculative instance.
+        ti.minst = sc.minst;
+        if (!ti.tinst)
+            ti.tinst = sc.tinst;
+
+        unSpeculative(sc, ti.tempdecl);
+    }
+
+    if (TemplateInstance ti = s.isInstantiated())
+        unSpeculative(sc, ti);
+}
 
 private extern (C++) final class ExpressionSemanticVisitor : Visitor
 {
@@ -3921,6 +4303,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars());
         }
 
+        if (exp.rvalue)
+        {
+            if (sc.setUnsafe(false, exp.loc, "moving variable `%s` with `__rvalue`", exp))
+            {
+                setError();
+            }
+        }
         scope (exit) result.rvalue = exp.rvalue;
 
         Dsymbol scopesym;
@@ -4318,8 +4707,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Expression makeNonTemplateItem(Identifier which) {
             Expression id = new IdentifierExp(e.loc, Id.empty);
             id = new DotIdExp(e.loc, id, Id.object);
-            auto moduleNameArgs = new Objects();
-            moduleNameArgs.push(new StringExp(e.loc, "core.interpolation"));
+            auto moduleNameArgs = new Objects(new StringExp(e.loc, "core.interpolation"));
             id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs);
             id = new DotIdExp(e.loc, id, which);
             id = new CallExp(e.loc, id, new Expressions());
@@ -4329,21 +4717,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Expression makeTemplateItem(Identifier which, string arg) {
             Expression id = new IdentifierExp(e.loc, Id.empty);
             id = new DotIdExp(e.loc, id, Id.object);
-            auto moduleNameArgs = new Objects();
-            moduleNameArgs.push(new StringExp(e.loc, "core.interpolation"));
+            auto moduleNameArgs = new Objects(new StringExp(e.loc, "core.interpolation"));
             id = new DotTemplateInstanceExp(e.loc, id, Id.imported, moduleNameArgs);
-            auto tiargs = new Objects();
+
             auto templateStringArg = new StringExp(e.loc, arg);
             // banning those instead of forwarding them
             // templateStringArg.postfix = e.postfix; // forward the postfix to these literals
-            tiargs.push(templateStringArg);
+            auto tiargs = new Objects(templateStringArg);
             id = new DotTemplateInstanceExp(e.loc, id, which, tiargs);
             id = new CallExp(e.loc, id, new Expressions());
             return id;
         }
 
-        auto arguments = new Expressions();
-        arguments.push(makeNonTemplateItem(Id.InterpolationHeader));
+        auto arguments = new Expressions(makeNonTemplateItem(Id.InterpolationHeader));
 
         foreach (idx, str; e.interpolatedSet.parts)
         {
@@ -4355,8 +4741,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             else
             {
                 arguments.push(makeTemplateItem(Id.InterpolatedExpression, str));
-                Expressions* mix = new Expressions();
-                mix.push(new StringExp(e.loc, str));
+                Expressions* mix = new Expressions(new StringExp(e.loc, str));
                 // FIXME: i'd rather not use MixinExp but idk how to do it lol
                 arguments.push(new MixinExp(e.loc, mix));
             }
@@ -4557,6 +4942,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         result = e;
     }
 
+    void tryLowerAALiteral(AssocArrayLiteralExp aaExp)
+    {
+        auto hookId = Id._d_assocarrayliteralTX;
+        if (!verifyHookExist(aaExp.loc, *sc, hookId, "initializing associative arrays", Id.object))
+            return;
+
+        auto aaType = aaExp.type.toBasetype().isTypeAArray();
+        assert(aaType);
+        Expression hookFunc = new IdentifierExp(aaExp.loc, Id.empty);
+        hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id.object);
+        auto keytype = aaType.index.substWildTo(MODFlags.const_);
+        auto valtype = aaType.nextOf().substWildTo(MODFlags.const_);
+        auto tiargs = new Objects(keytype, valtype);
+        hookFunc = new DotTemplateInstanceExp(aaExp.loc, hookFunc, hookId, tiargs);
+        auto ale1 = new ArrayLiteralExp(aaExp.loc, keytype.arrayOf(), aaExp.keys);
+        auto ale2 = new ArrayLiteralExp(aaExp.loc, valtype.arrayOf(), aaExp.values);
+        lowerArrayLiteral(ale1, sc);
+        lowerArrayLiteral(ale2, sc);
+        auto arguments = new Expressions(ale1, ale2);
+
+        Expression loweredExp = new CallExp(aaExp.loc, hookFunc, arguments);
+        loweredExp = loweredExp.expressionSemantic(sc);
+        loweredExp = resolveProperties(sc, loweredExp);
+        aaExp.lowering = loweredExp;
+
+        semanticTypeInfo(sc, loweredExp.type);
+    }
+
     override void visit(AssocArrayLiteralExp e)
     {
         static if (LOGSEMANTIC)
@@ -4567,6 +4980,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             // already done, but we might have missed generating type info
             semanticTypeInfo(sc, e.type);
+            if (!e.lowering)
+                tryLowerAALiteral(e);
             result = e;
             return;
         }
@@ -4593,6 +5008,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         e.type = new TypeAArray(tvalue, tkey);
         e.type = e.type.typeSemantic(e.loc, sc);
 
+        tryLowerAALiteral(e);
         semanticTypeInfo(sc, e.type);
 
         if (checkAssocArrayLiteralEscape(*sc, e, false))
@@ -4866,14 +5282,45 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
          */
         Expression id = new IdentifierExp(ne.loc, Id.empty);
         id = new DotIdExp(ne.loc, id, Id.object);
-        auto tiargs = new Objects();
         /*
          * Remove `inout`, `const`, `immutable` and `shared` to reduce the
          * number of generated `_d_newitemT` instances.
          */
         auto t = ne.type.nextOf.unqualify(MODFlags.wild | MODFlags.const_ |
             MODFlags.immutable_ | MODFlags.shared_);
-        tiargs.push(t);
+        auto tiargs = new Objects(t);
+        id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs);
+
+        auto arguments = new Expressions();
+        id = new CallExp(ne.loc, id, arguments);
+
+        ne.lowering = id.expressionSemantic(sc);
+    }
+
+    /**
+    * Sets the `lowering` field of a `NewExp` to a call to `_d_newAA` unless
+    * compiling with `-betterC` or within `__traits(compiles)`.
+    *
+    * Params:
+    *  ne = the `NewExp` to lower
+    */
+    private void tryLowerToNewAA(NewExp ne)
+    {
+        if (!global.params.useGC || !sc.needsCodegen())
+            return;
+
+        Identifier hook = Id._d_aaNew;
+        if (!verifyHookExist(ne.loc, *sc, hook, "new AA"))
+            return;
+
+        /* Lower the memory allocation and initialization of `new V[K]` to
+        * `_d_newAA!(V[K])()`.
+        */
+        Expression id = new IdentifierExp(ne.loc, Id.empty);
+        id = new DotIdExp(ne.loc, id, Id.object);
+        auto taa = ne.type.isTypeAArray();
+        assert(taa);
+        auto tiargs = new Objects(taa.index, taa.next);
         id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs);
 
         auto arguments = new Expressions();
@@ -4907,7 +5354,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             {
                 return setError();
             }
-            if (!exp.placement.type.isNaked())
+            if (!exp.placement.type.isNaked)
             {
                 error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type));
                 return setError();
@@ -4977,8 +5424,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             else
             {
                 // --> new T[](edim)
-                exp.arguments = new Expressions();
-                exp.arguments.push(edim);
+                exp.arguments = new Expressions(edim);
                 exp.type = exp.type.arrayOf();
             }
         }
@@ -5226,8 +5672,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 Expression id = new IdentifierExp(exp.loc, Id.empty);
                 id = new DotIdExp(exp.loc, id, Id.object);
 
-                auto tiargs = new Objects();
-                tiargs.push(exp.newtype);
+                auto tiargs = new Objects(exp.newtype);
 
                 id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs);
 
@@ -5252,9 +5697,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 Expression id = new IdentifierExp(exp.loc, Id.empty);
                 id = new DotIdExp(exp.loc, id, Id.object);
 
-                auto tiargs = new Objects();
                 auto t = exp.newtype.unqualify(MODFlags.wild);  // remove `inout`
-                tiargs.push(t);
+                auto tiargs = new Objects(t);
                 id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs);
                 auto arguments = new Expressions();
                 id = new CallExp(exp.loc, id, arguments);
@@ -5288,7 +5732,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // If the new expression has arguments, we either should call a
             // regular constructor of a copy constructor if the first argument
             // is the same type as the struct
-            if (nargs && (sd.hasRegularCtor() || (sd.ctor && (*exp.arguments)[0].type.mutableOf() == sd.type.mutableOf())))
+            if (nargs && (sd.hasRegularCtor(false) || (sd.ctor && (*exp.arguments)[0].type.mutableOf() == sd.type.mutableOf())))
             {
                 FuncDeclaration f = resolveFuncCall(exp.loc, sc, sd.ctor, null, tb, exp.argumentList, FuncResolveFlag.standard);
                 if (!f || f.errors)
@@ -5440,19 +5884,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  */
                 Expression lowering = new IdentifierExp(exp.loc, Id.empty);
                 lowering = new DotIdExp(exp.loc, lowering, Id.object);
-                auto tiargs = new Objects();
+
                 /* Remove `inout`, `const`, `immutable` and `shared` to reduce
                  * the number of generated `_d_newarrayT` instances.
                  */
                 const isShared = exp.type.nextOf.isShared();
                 auto t = exp.type.nextOf.unqualify(MODFlags.wild | MODFlags.const_ |
                     MODFlags.immutable_ | MODFlags.shared_);
-                tiargs.push(t);
+                auto tiargs = new Objects(t);
                 lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs);
 
-                auto arguments = new Expressions();
-                arguments.push((*exp.arguments)[0]);
-                arguments.push(new IntegerExp(exp.loc, isShared, Type.tbool));
+                auto arguments = new Expressions((*exp.arguments)[0], new IntegerExp(exp.loc, isShared, Type.tbool));
 
                 lowering = new CallExp(exp.loc, lowering, arguments);
                 exp.lowering = lowering.expressionSemantic(sc);
@@ -5476,15 +5918,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 auto unqualTbn = tbn.unqualify(MODFlags.wild | MODFlags.const_ |
                     MODFlags.immutable_ | MODFlags.shared_);
 
-                auto tiargs = new Objects();
-                tiargs.push(exp.type);
-                tiargs.push(unqualTbn);
+                auto tiargs = new Objects(exp.type, unqualTbn);
                 lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs);
 
-                auto arguments = new Expressions();
-
-                arguments.push(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments));
-                arguments.push(new IntegerExp(exp.loc, tbn.isShared(), Type.tbool));
+                auto arguments = new Expressions(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments),
+                                                 new IntegerExp(exp.loc, tbn.isShared(), Type.tbool));
 
                 lowering = new CallExp(exp.loc, lowering, arguments);
                 exp.lowering = lowering.expressionSemantic(sc);
@@ -5529,6 +5967,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 error(exp.loc, "`new` cannot take arguments for an associative array");
                 return setError();
             }
+            tryLowerToNewAA(exp);
         }
         else
         {
@@ -5702,7 +6141,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             symtab = sds.symtab;
         }
         assert(symtab);
-        Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars()));
+        Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(const void*) sc.parent);
         exp.fd.ident = id;
         if (exp.td)
             exp.td.ident = id;
@@ -5916,11 +6355,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             if (TypeFunction tf = exp.f ? cast(TypeFunction)exp.f.type : null)
             {
-                result.rvalue = tf.isRvalue;
-                if (tf.isRvalue && !tf.isRef)
+                if (tf.isRvalue)
                 {
-                    error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`");
-                    setError();
+                    if(!tf.isRef)
+                    {
+                        error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`");
+                        setError();
+                    }
+                    else if (sc.setUnsafe(false, exp.loc, "calling `__rvalue`-annotated function `%s`", exp.f))
+                    {
+                        setError();
+                    }
+                    else
+                    {
+                        result.rvalue = true;
+                    }
+                }
+                else if (exp.rvalue && tf.isRef)
+                {
+                    if (sc.setUnsafe(false, exp.loc, "moving result of `ref` function `%s` with `__rvalue`", exp.f))
+                    {
+                        setError();
+                    }
                 }
             }
         }
@@ -6213,7 +6669,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (sd.ctor)
                 {
                     auto ctor = sd.ctor.isCtorDeclaration();
-                    if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated())
+                    if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated)
                         sd.ctor = null;
                 }
 
@@ -6227,7 +6683,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                        If all constructors are copy constructors, then
                        try default construction.
                      */
-                    if (!sd.hasRegularCtor &&
+                    if (!sd.hasRegularCtor(false) &&
                         // https://issues.dlang.org/show_bug.cgi?id=22639
                         // we might still have a copy constructor that could be called
                         (*exp.arguments)[0].type.mutableOf != sd.type.mutableOf())
@@ -6898,8 +7354,21 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (!exp.type)
         {
             exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922
-                        // avoid recursive expression printing
-            error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg());
+                            // avoid recursive expression printing
+            FuncDeclaration fd = null;
+            if (auto dve = exp.e1.isDotVarExp())
+                fd = cast(FuncDeclaration)dve.var;
+            else if (auto ve = exp.e1.isVarExp())
+                fd = cast(FuncDeclaration)ve.var;
+
+            if (fd && fd.inferRetType && sc && sc.func == fd)
+            {
+                error(exp.loc, "can't infer return type in function `%s`", fd.toChars());
+            }
+            else
+            {
+                error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg());
+            }
             return setError();
         }
 
@@ -6911,7 +7380,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (tf.next.isBaseOf(t, &offset) && offset)
             {
                 exp.type = tf.next;
-                result = Expression.combine(argprefix, exp.castTo(sc, t));
+                auto casted_exp = exp.castTo(sc, t);
+                if (auto cex = casted_exp.isCastExp())
+                {
+                    lowerCastExp(cex, sc);
+                }
+                result = Expression.combine(argprefix, casted_exp);
                 return;
             }
         }
@@ -6960,7 +7434,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // declare dual-context container
-        if (exp.f && exp.f.hasDualContext() && !sc.intypeof && sc.func)
+        if (exp.f && exp.f.hasDualContext && !sc.intypeof && sc.func)
         {
             // check access to second `this`
             if (AggregateDeclaration ad2 = exp.f.isMember2())
@@ -7700,7 +8174,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         assert(e.op == EXP.assign || e == exp);
-        result = (cast(BinExp)e).reorderSettingAAElem(sc);
+        result = e;
     }
 
     private Expression compileIt(MixinExp exp, Scope* sc)
@@ -7917,7 +8391,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 // Defensively assume that function calls may have side effects even
                 // though it's not detected by hasSideEffect (e.g. `debug puts("Hello")` )
                 // Rewriting CallExp's also avoids some issues with the inliner/debug generation
-                if (op.hasSideEffect(true))
+                if (op.hasSideEffect(true) || op.isAssocArrayLiteralExp())
                 {
                     // Don't create an invalid temporary for void-expressions
                     // Further semantic will issue an appropriate error
@@ -8003,7 +8477,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 isEqualsCallExpression)
             {
                 es = new Expressions(3);
-                tiargs = new Objects(1);
 
                 if (isEqualsCallExpression)
                 {
@@ -8042,7 +8515,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 Expression comp = new StringExp(loc, isEqualsCallExpression ? "==" : EXPtoString(exp.e1.op));
                 comp = comp.expressionSemantic(sc);
                 (*es)[0] = comp;
-                (*tiargs)[0] = (*es)[1].type;
+                tiargs = new Objects((*es)[1].type);
             }
 
             // Format exp.e1 before any additional boolean conversion
@@ -8050,7 +8523,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             else if (op != EXP.andAnd && op != EXP.orOr)
             {
                 es = new Expressions(2);
-                tiargs = new Objects(1);
 
                 if (auto ne = exp.e1.isNotExp())
                 {
@@ -8074,7 +8546,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     (*es)[1] = maybePromoteToTmp(exp.e1);
                 }
 
-                (*tiargs)[0] = (*es)[1].type;
+                tiargs = new Objects((*es)[1].type);
 
                 // Passing __ctfe to auto ref infers ref and aborts compilation:
                 // "cannot modify compiler-generated variable __ctfe"
@@ -8185,6 +8657,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
             printAST(exp);
         }
+        scope (exit)
+        {
+            if (result)
+                result.rvalue = exp.rvalue;
+            else
+                setError();
+        }
 
         if (sc.inCfile)
         {
@@ -8467,7 +8946,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         result = e;
         // declare dual-context container
-        if (f.hasDualContext() && !sc.intypeof && sc.func)
+        if (f.hasDualContext && !sc.intypeof && sc.func)
         {
             // check access to second `this`
             if (AggregateDeclaration ad2 = f.isMember2())
@@ -8641,7 +9120,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             error(exp.loc, "cannot take address of `%s`", exp.e1.toErrMsg());
             return setError();
         }
-        if (!checkAddressable(exp, sc))
+        if (!checkAddressable(exp, sc, "take address of"))
             return setError();
 
         bool hasOverloads;
@@ -9299,13 +9778,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                             Expression id = new IdentifierExp(exp.loc, Id.empty);
                             auto dotid = new DotIdExp(exp.loc, id, Id.object);
 
-                            auto tiargs = new Objects();
-                            tiargs.push(tFrom);
-                            tiargs.push(tTo);
+                            auto tiargs = new Objects(tFrom, tTo);
                             auto dt = new DotTemplateInstanceExp(exp.loc, dotid, Id.__ArrayCast, tiargs);
 
-                            auto arguments = new Expressions();
-                            arguments.push(exp.e1);
+                            auto arguments = new Expressions(exp.e1);
                             Expression ce = new CallExp(exp.loc, dt, arguments);
 
                             result = expressionSemantic(ce, sc);
@@ -9328,6 +9804,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 ex.type = exp.to;
             }
         }
+
+        if (auto cex = ex.isCastExp())
+        {
+            lowerCastExp(cex, sc);
+        }
+
         result = ex;
     }
 
@@ -9966,7 +10448,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         if (t1b.isStaticOrDynamicArray())
         {
-            if (!checkAddressable(exp, sc))
+            if (!checkAddressable(exp, sc, "index through"))
                 return setError();
         }
 
@@ -10053,9 +10535,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 }
 
                 semanticTypeInfo(sc, taa);
-                checkNewEscape(*sc, exp.e2, false);
+                bool escape = checkNewEscape(*sc, exp.e2, false);
+                if (escape)
+                    return setError();
 
                 exp.type = taa.next;
+                if (!exp.modifiable)
+                {
+                    result = lowerAAIndexRead(exp, sc);
+                    return;
+                }
                 break;
             }
         case Ttuple:
@@ -10161,6 +10650,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
+        if (auto ae = exp.e1.isArrayExp())
+            markArrayExpModifiable(ae);
+
         if (Expression ex = binSemantic(exp, sc))
         {
             result = ex;
@@ -10174,6 +10666,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
         exp.e1 = e1x;
 
+        Type[2] aliasThisStop;
+        if (auto res = rewriteIndexAssign(exp, sc, aliasThisStop))
+        {
+            result = res;
+            return;
+        }
         if (exp.e1.checkReadModifyWrite(exp.op))
             return setError();
 
@@ -10243,6 +10741,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     override void visit(PreExp exp)
     {
+        if (auto ae = exp.e1.isArrayExp())
+            markArrayExpModifiable(ae);
+
         // printf("PreExp::semantic('%s')\n", toChars());
         if (Expression e = exp.opOverloadUnary(sc))
         {
@@ -10333,6 +10834,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if (auto ae = exp.e1.isArrayExp())
         {
             Expression res;
+            markArrayExpModifiable(ae);
 
             ae.e1 = ae.e1.expressionSemantic(sc);
             ae.e1 = resolveProperties(sc, ae.e1);
@@ -10408,8 +10910,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     /* Rewrite (a[i..j] = e2) as:
                      *      a.opSliceAssign(e2, i, j)
                      */
-                    auto a = new Expressions();
-                    a.push(exp.e2);
+                    auto a = new Expressions(exp.e2);
                     if (ie)
                     {
                         a.push(ie.lwr);
@@ -10600,8 +11101,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 Expression e0;
                 Expression ev = extractSideEffect(sc, "__tup", e0, e2x);
 
-                auto iexps = new Expressions();
-                iexps.push(ev);
+                auto iexps = new Expressions(ev);
                 for (size_t u = 0; u < iexps.length; u++)
                 {
                 Lexpand:
@@ -10675,18 +11175,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             auto t = exp.type;
             exp = new ConstructExp(exp.loc, exp.e1, exp.e2);
             exp.type = t;
-
-            // https://issues.dlang.org/show_bug.cgi?id=13515
-            // set Index::modifiable flag for complex AA element initialization
-            if (auto ie1 = exp.e1.isIndexExp())
-            {
-                Expression e1x = ie1.markSettingAAElem();
-                if (e1x.op == EXP.error)
-                {
-                    result = e1x;
-                    return;
-                }
-            }
         }
         else if (exp.op == EXP.construct && exp.e1.op == EXP.variable &&
                  (cast(VarExp)exp.e1).var.storage_class & (STC.out_ | STC.ref_))
@@ -10702,6 +11190,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         checkUnsafeAccess(sc, exp.e2, true, true); // Initializer must always be checked
 
+        if (auto res = rewriteIndexAssign(exp, sc, aliasThisStop))
+        {
+            result = res;
+            return;
+        }
+
         /* If it is an assignment from a 'foreign' type,
          * check for operator overloading.
          */
@@ -10991,91 +11485,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
             else if (exp.op == EXP.assign)
             {
-                if (e1x.op == EXP.index && (cast(IndexExp)e1x).e1.type.toBasetype().ty == Taarray)
-                {
-                    /*
-                     * Rewrite:
-                     *      aa[key] = e2;
-                     * as:
-                     *      ref __aatmp = aa;
-                     *      ref __aakey = key;
-                     *      ref __aaval = e2;
-                     *      (__aakey in __aatmp
-                     *          ? __aatmp[__aakey].opAssign(__aaval)
-                     *          : ConstructExp(__aatmp[__aakey], __aaval));
-                     */
-                    // ensure we keep the expr modifiable
-                    Expression esetting = (cast(IndexExp)e1x).markSettingAAElem();
-                    if (esetting.op == EXP.error)
-                    {
-                        result = esetting;
-                        return;
-                    }
-                    assert(esetting.op == EXP.index);
-                    IndexExp ie = cast(IndexExp) esetting;
-                    Type t2 = e2x.type.toBasetype();
-
-                    Expression e0 = null;
-                    Expression ea = extractSideEffect(sc, "__aatmp", e0, ie.e1);
-                    Expression ek = extractSideEffect(sc, "__aakey", e0, ie.e2);
-                    Expression ev = extractSideEffect(sc, "__aaval", e0, e2x);
-
-                    AssignExp ae = cast(AssignExp)exp.copy();
-                    ae.e1 = new IndexExp(exp.loc, ea, ek);
-                    ae.e1 = ae.e1.expressionSemantic(sc);
-                    ae.e1 = ae.e1.optimize(WANTvalue);
-                    ae.e2 = ev;
-                    if (Expression e = ae.opOverloadAssign(sc, aliasThisStop))
-                    {
-                        Expression ey = null;
-                        if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
-                        {
-                            ey = ev;
-                        }
-                        else if (!ev.implicitConvTo(ie.type) && sd.ctor)
-                        {
-                            // Look for implicit constructor call
-                            // Rewrite as S().ctor(e2)
-                            ey = new StructLiteralExp(exp.loc, sd, null);
-                            ey = new DotIdExp(exp.loc, ey, Id.ctor);
-                            ey = new CallExp(exp.loc, ey, ev);
-                            ey = ey.trySemantic(sc);
-                        }
-                        if (ey)
-                        {
-                            Expression ex;
-                            ex = new IndexExp(exp.loc, ea, ek);
-                            ex = ex.expressionSemantic(sc);
-                            ex = ex.modifiableLvalue(sc); // allocate new slot
-                            ex = ex.optimize(WANTvalue);
-
-                            ey = new ConstructExp(exp.loc, ex, ey);
-                            ey = ey.expressionSemantic(sc);
-                            if (ey.op == EXP.error)
-                            {
-                                result = ey;
-                                return;
-                            }
-                            ex = e;
-
-                            // https://issues.dlang.org/show_bug.cgi?id=14144
-                            // 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, EXP.question, ex, ey))
-                            {
-                                ex = new CastExp(ex.loc, ex, Type.tvoid);
-                                ey = new CastExp(ey.loc, ey, Type.tvoid);
-                            }
-                            e = new CondExp(exp.loc, new InExp(exp.loc, ek, ea), ex, ey);
-                        }
-                        e = Expression.combine(e0, e);
-                        e = e.expressionSemantic(sc);
-                        result = e;
-                        return;
-                    }
-                }
-                else if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop))
+                if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop))
                 {
                     result = e;
                     return;
@@ -11239,7 +11649,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
 
             // Ensure e1 is a modifiable lvalue
-            auto ale1x = ale.e1.modifiableLvalueImpl(sc, exp.e1);
+            auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1);
             if (ale1x.op == EXP.error)
                 return setResult(ale1x);
             ale.e1 = ale1x;
@@ -11283,9 +11693,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             id = id.expressionSemantic(sc);
 
             // Generate call: _d_arraysetlengthT(e1, e2)
-            auto arguments = new Expressions();
-            arguments.push(ale.e1);  // array
-            arguments.push(exp.e2);  // new length
+            auto arguments = new Expressions(ale.e1,  // array
+                                             exp.e2);  // new length
 
             Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc);
             auto res = new LoweredAssignExp(exp, ce);
@@ -11319,7 +11728,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 se = cast(SliceExp)se.e1;
             if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray)
             {
-                se.e1 = se.e1.modifiableLvalueImpl(sc, exp.e1);
+                se.e1 = se.e1.modifiableLvalue(sc, exp.e1);
                 if (se.e1.op == EXP.error)
                     return setResult(se.e1);
             }
@@ -11343,7 +11752,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             // Try to do a decent error message with the expression
             // before it gets constant folded
             if (exp.op == EXP.assign)
-                e1x = e1x.modifiableLvalueImpl(sc, e1old);
+                e1x = e1x.modifiableLvalue(sc, e1old);
 
             e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true);
 
@@ -11490,7 +11899,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             t1.isStaticOrDynamicArray() &&
             t1.nextOf().toBasetype().ty == Tvoid)
         {
-            if (t2.nextOf().implicitConvTo(t1.nextOf()))
+            auto t2n = t2.nextOf();
+            if (!t2n)
+            {
+                // filling not allowed
+                error(exp.loc, "cannot copy `%s` to `%s`",
+                    t2.toChars(), t1.toChars());
+            }
+            else if (t2n.implicitConvTo(t1.nextOf()))
             {
                 if (sc.setUnsafe(false, exp.loc, "copying `%s` to `%s`", t2, t1))
                     return setError();
@@ -11568,16 +11984,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         exp.type = exp.e1.type;
         assert(exp.type);
         auto assignElem = exp.e2;
-        auto res = exp.op == EXP.assign ? exp.reorderSettingAAElem(sc) : exp;
-        /* https://issues.dlang.org/show_bug.cgi?id=22366
-         *
-         * `reorderSettingAAElem` creates a tree of comma expressions, however,
-         * `checkAssignExp` expects only AssignExps.
-         */
-        if (res == exp) // no `AA[k] = v` rewrite was performed
-            checkAssignEscape(*sc, res, false, false);
-        else
-            checkNewEscape(*sc, assignElem, false); // assigning to AA puts it on heap
+        Expression res = exp;
+        checkAssignEscape(*sc, res, false, false);
 
         if (auto ae = res.isConstructExp())
         {
@@ -11639,8 +12047,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 id = new DotIdExp(exp.loc, id, Id.object);
                 id = new DotIdExp(exp.loc, id, func);
 
-                auto arguments = new Expressions();
-                arguments.push(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc));
+                auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, t1e.arrayOf).expressionSemantic(sc));
                 if (lowerToArrayCtor)
                 {
                     arguments.push(new CastExp(ae.loc, rhs, t2b.nextOf.arrayOf).expressionSemantic(sc));
@@ -11721,9 +12128,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         id = new DotIdExp(ae.loc, id, Id.object);
         id = new DotIdExp(ae.loc, id, func);
 
-        auto arguments = new Expressions();
-        arguments.push(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf)
-            .expressionSemantic(sc));
+        auto arguments = new Expressions(new CastExp(ae.loc, ae.e1, ae.e1.type.nextOf.arrayOf)
+                                         .expressionSemantic(sc));
 
         Expression eValue2, value2 = ae.e2;
         if (isArrayAssign && value2.isLvalue())
@@ -11752,14 +12158,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Expression ce = new CallExp(ae.loc, id, arguments);
         res = Expression.combine(eValue2, ce).expressionSemantic(sc);
         if (isArrayAssign)
-            res = Expression.combine(res, ae.e1).expressionSemantic(sc);
+            res = Expression.combine(res, ae.e1).expressionSemantic(sc).checkGC(sc);
 
         if (global.params.v.verbose)
             message("lowered   %s =>\n          %s", ae.toChars(), res.toChars());
 
         res = new LoweredAssignExp(ae, res);
         res.type = ae.type;
-        res = res.checkGC(sc);
 
         return res;
     }
@@ -11814,7 +12219,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         if ((exp.e1.type.isIntegral() || exp.e1.type.isFloating()) && (exp.e2.type.isIntegral() || exp.e2.type.isFloating()))
         {
             Expression e0 = null;
-            Expression e = exp.reorderSettingAAElem(sc);
+            Expression e = exp;
             e = Expression.extractLast(e, e0);
             assert(e == exp);
 
@@ -11961,10 +12366,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         exp.type = exp.e1.type;
         auto assignElem = exp.e2;
-        auto res = exp.reorderSettingAAElem(sc);
-        if (res != exp) // `AA[k] = v` rewrite was performed
-            checkNewEscape(*sc, assignElem, false);
-        else if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign)
+        auto res = exp;
+        if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign)
             checkAssignEscape(*sc, res, false, false);
 
         result = res;
@@ -12000,9 +12403,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 id = new DotIdExp(exp.loc, id, Id.object);
                 id = new DotIdExp(exp.loc, id, hook);
 
-                auto arguments = new Expressions();
-                arguments.push(exp.e1);
-                arguments.push(exp.e2);
+                auto arguments = new Expressions(exp.e1, exp.e2);
                 Expression ce = new CallExp(exp.loc, id, arguments);
 
                 exp.lowering = ce.expressionSemantic(sc);
@@ -12034,12 +12435,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 id = new DotIdExp(exp.loc, id, Id.object);
                 id = new DotIdExp(exp.loc, id, hook);
 
-                auto arguments = new Expressions();
                 Expression eValue1;
                 Expression value1 = extractSideEffect(sc, "__appendtmp", eValue1, exp.e1);
 
-                arguments.push(value1);
-                arguments.push(new IntegerExp(exp.loc, 1, Type.tsize_t));
+                auto arguments = new Expressions(value1, new IntegerExp(exp.loc, 1, Type.tsize_t));
 
                 Expression ce = new CallExp(exp.loc, id, arguments);
 
@@ -12051,14 +12450,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                      * for expressions like `a ~= a[$-1]`. Here, $ will be modified
                      * by calling `_d_arrayappendcTX`, so we need to save `a[$-1]` in
                      * a temporary variable.
+                     * `__appendtmp*` will be destroyed together with the array `exp.e1`.
                      */
-                    value2 = extractSideEffect(sc, "__appendtmp", eValue2, value2, true);
-
-                    // `__appendtmp*` will be destroyed together with the array `exp.e1`.
-                    auto vd = eValue2.isDeclarationExp().declaration.isVarDeclaration();
-                    vd.storage_class |= STC.nodtor;
-                    // Be more explicit that this "declaration" is local to the expression
-                    vd.storage_class |= STC.exptemp;
+                    value2 = extractSideEffect(sc, "__appendtmp", eValue2, value2, true, STC.nodtor | STC.exptemp);
                 }
 
                 auto ale = new ArrayLengthExp(exp.loc, value1);
@@ -12392,11 +12786,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Expression id = new IdentifierExp(exp.loc, Id.empty);
         id = new DotIdExp(exp.loc, id, Id.object);
 
-        auto tiargs = new Objects();
-        tiargs.push(exp.type);
+        auto tiargs = new Objects(exp.type);
         id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs);
         id = new CallExp(exp.loc, id, arguments);
-        return id.expressionSemantic(sc);
+        return id.expressionSemantic(sc).checkGC(sc);
     }
 
     void trySetCatExpLowering(Expression exp)
@@ -12845,7 +13238,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             }
         }
 
-        Module mmath = Module.loadStdMath();
+        Module mmath = loadStdMath();
         if (!mmath)
         {
             error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toErrMsg());
@@ -13172,6 +13565,59 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         return;
     }
 
+    /**
+    * lowers an `InExp` to a call to `_d_aaIn!(V[K])`.
+    *
+    * Params:
+    *  ie = the `InExp` to lower
+    * Returns:
+    *  the lowered expression or an ErrorExp
+    */
+    private Expression lowerToAAIn(InExp ie)
+    {
+        Identifier hook = Id._d_aaIn;
+        if (!verifyHookExist(ie.loc, *sc, hook, "key in AA"))
+            return ErrorExp.get();
+
+        Expression id = new IdentifierExp(ie.loc, Id.empty);
+        id = new DotIdExp(ie.loc, id, Id.object);
+        auto tiargs = new Objects();
+        id = new DotIdExp(ie.loc, id, hook);
+
+        Expression e1;
+        Expression ekey = extractSideEffect(sc, "__aakey", e1, ie.e1);
+        auto arguments = new Expressions(ie.e2, ekey);
+        auto ce = new CallExp(ie.loc, id, arguments);
+        ce.loweredFrom = ie;
+        e1 = Expression.combine(e1, ce);
+        return e1.expressionSemantic(sc);
+    }
+
+    /**
+    * lowers a `RemoveExp` to a call to `_d_aaDel!(V[K])`.
+    *
+    * Params:
+    *  re = the `RemoveExp` to lower
+    * Returns:
+    *  the lowered expression or an ErrorExp
+    */
+    private Expression lowerToAADel(RemoveExp re)
+    {
+        Identifier hook = Id._d_aaDel;
+        if (!verifyHookExist(re.loc, *sc, hook, "remove key in AA"))
+            return ErrorExp.get();
+
+        Expression id = new IdentifierExp(re.loc, Id.empty);
+        id = new DotIdExp(re.loc, id, Id.object);
+        auto tiargs = new Objects();
+        id = new DotIdExp(re.loc, id, hook);
+
+        auto arguments = new Expressions(re.e1, re.e2);
+        auto ce = new CallExp(re.loc, id, arguments);
+        ce.loweredFrom = re;
+        return ce.expressionSemantic(sc);
+    }
+
     override void visit(InExp exp)
     {
         if (Expression e = exp.opOverloadBinary(sc, aliasThisStop))
@@ -13194,14 +13640,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     exp.e1 = exp.e1.implicitCastTo(sc, ta.index);
                 }
 
+                // for backward compatibility, type accepted during semantic, but errors in glue layer
+                if (exp.e2.isTypeExp())
+                {
+                    exp.type = ta.nextOf().pointerTo();
+                    break;
+                }
+                result = lowerToAAIn(exp);
+
                 // even though the glue layer only needs the type info of the index,
                 // this might be the first time an AA literal is accessed, so check
                 // the full type info
                 semanticTypeInfo(sc, ta);
-
-                // Return type is pointer to value
-                exp.type = ta.nextOf().pointerTo();
-                break;
+                return;
             }
 
         case Terror:
@@ -13229,7 +13680,39 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             result = ex;
             return;
         }
-        result = e;
+
+        result = lowerToAADel(e);
+    }
+
+    /**
+    * lower a `EqualExp` to a call to `_d_aaEqual!(aa1, aa2)`.
+    *
+    * Params:
+    *  ee = the `EqualExp` to lower
+    * Returns:
+    *  the lowered expression or an ErrorExp
+    */
+    private Expression lowerToAAEqual(EqualExp ee)
+    {
+        Identifier hook = Id._d_aaEqual;
+        if (!verifyHookExist(ee.loc, *sc, hook, "compare AAs"))
+            return ErrorExp.get();
+
+        Expression id = new IdentifierExp(ee.loc, Id.empty);
+        id = new DotIdExp(ee.loc, id, Id.object);
+        auto tiargs = new Objects();
+        id = new DotIdExp(ee.loc, id, hook);
+
+        auto arguments = new Expressions(ee.e1, ee.e2);
+        auto ce = new CallExp(ee.loc, id, arguments);
+        if (ee.op == EXP.notEqual)
+        {
+            auto ne = new NotExp(ee.loc, ce);
+            ne.loweredFrom = ee;
+            return ne.expressionSemantic(sc);
+        }
+        ce.loweredFrom = ee;
+        return ce.expressionSemantic(sc);
     }
 
     override void visit(EqualExp exp)
@@ -13312,19 +13795,53 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         Type t1 = exp.e1.type.toBasetype();
         Type t2 = exp.e2.type.toBasetype();
 
-        // Indicates whether the comparison of the 2 specified array types
-        // requires an object.__equals() lowering.
-        static bool needsDirectEq(Type t1, Type t2, Scope* sc)
+        static bool unifyArrayTypes(Type t1, Type t2, Scope* sc)
         {
             Type t1n = t1.nextOf().toBasetype();
             Type t2n = t2.nextOf().toBasetype();
+
             if ((t1n.ty.isSomeChar && t2n.ty.isSomeChar) ||
                 (t1n.ty == Tvoid || t2n.ty == Tvoid))
             {
-                return false;
+                return true;
             }
             if (t1n.constOf() != t2n.constOf())
+            {
+                return false;
+            }
+
+            Type t = t1n;
+            while (t.toBasetype().nextOf())
+                t = t.nextOf().toBasetype();
+            if (auto ts = t.isTypeStruct())
+            {
+                // semanticTypeInfo() makes sure hasIdentityEquals has been computed
+                if (global.params.useTypeInfo && Type.dtypeinfo)
+                    semanticTypeInfo(sc, ts);
+
+                return !ts.sym.hasIdentityEquals; // has custom opEquals
+            }
+
+            return true;
+        }
+
+        static bool shouldUseMemcmp(Type t1, Type t2, Scope *sc)
+        {
+            Type t1n = t1.nextOf().toBasetype();
+            Type t2n = t2.nextOf().toBasetype();
+            const t1nsz = t1n.size();
+            const t2nsz = t2n.size();
+
+            if ((t1n.ty == Tvoid || (t1n.isScalar() && !t1n.isFloating())) &&
+                (t2n.ty == Tvoid || (t2n.isScalar() && !t1n.isFloating())) &&
+                t1nsz == t2nsz && t1n.isUnsigned() == t2n.isUnsigned())
+            {
                 return true;
+            }
+            if (t1n.constOf() != t2n.constOf())
+            {
+                return false;
+            }
 
             Type t = t1n;
             while (t.toBasetype().nextOf())
@@ -13335,7 +13852,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 if (global.params.useTypeInfo && Type.dtypeinfo)
                     semanticTypeInfo(sc, ts);
 
-                return ts.sym.hasIdentityEquals; // has custom opEquals
+                auto v = new IsMemcmpableVisitor();
+                ts.accept(v);
+                return v.result;
             }
 
             return false;
@@ -13348,9 +13867,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         const isArrayComparison = t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray();
-        const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc);
 
-        if (!needsArrayLowering)
+        if (!isArrayComparison || unifyArrayTypes(t1, t2, sc))
         {
             // https://issues.dlang.org/show_bug.cgi?id=23783
             if (exp.e1.checkSharedAccess(sc) || exp.e2.checkSharedAccess(sc))
@@ -13380,7 +13898,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
         }
 
         // lower some array comparisons to object.__equals(e1, e2)
-        if (needsArrayLowering || (t1.ty == Tarray && t2.ty == Tarray))
+        if (isArrayComparison && !shouldUseMemcmp(t1, t2, sc))
         {
             //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars());
 
@@ -13395,13 +13913,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 return;
             }
 
-            if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays"))
+            Identifier hook = Id.__equals;
+            if (!verifyHookExist(exp.loc, *sc, hook, "equal checks on arrays"))
                 return setError();
 
-            Expression __equals = new IdentifierExp(exp.loc, Id.empty);
-            Identifier id = Identifier.idPool("__equals");
-            __equals = new DotIdExp(exp.loc, __equals, Id.object);
-            __equals = new DotIdExp(exp.loc, __equals, id);
+            Expression lowering = new IdentifierExp(exp.loc, Id.empty);
+            lowering = new DotIdExp(exp.loc, lowering, Id.object);
+            lowering = new DotIdExp(exp.loc, lowering, hook);
 
             /* https://issues.dlang.org/show_bug.cgi?id=23674
              *
@@ -13412,30 +13930,76 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
             exp.e1 = exp.e1.optimize(WANTvalue);
             exp.e2 = exp.e2.optimize(WANTvalue);
 
+            auto e1c = exp.e1.copy();
+            auto e2c = exp.e2.copy();
+
+            /* Remove qualifiers from the types of the arguments to reduce the number
+             * of generated `__equals` instances.
+             */
+            static void unqualifyExp(Expression e)
+            {
+                e.type = e.type.unqualify(MODFlags.wild | MODFlags.immutable_ | MODFlags.shared_);
+                auto eNext = e.type.nextOf();
+                if (eNext && !eNext.toBasetype().isTypeStruct())
+                    e.type = e.type.unqualify(MODFlags.const_);
+
+                if (!e.isArrayLiteralExp())
+                    return;
+
+                if (auto elems = e.isArrayLiteralExp().elements)
+                    foreach(elem; *elems)
+                        if (elem)
+                            unqualifyExp(elem);
+            }
+            unqualifyExp(e1c);
+            unqualifyExp(e2c);
+
             auto arguments = new Expressions(2);
-            (*arguments)[0] = exp.e1;
-            (*arguments)[1] = exp.e2;
+            (*arguments)[0] = e1c;
+            (*arguments)[1] = e2c;
 
-            __equals = new CallExp(exp.loc, __equals, arguments);
+            lowering = new CallExp(exp.loc, lowering, arguments);
             if (exp.op == EXP.notEqual)
             {
-                __equals = new NotExp(exp.loc, __equals);
+                lowering = new NotExp(exp.loc, lowering);
             }
-            __equals = __equals.trySemantic(sc); // for better error message
-            if (!__equals)
+            lowering = lowering.trySemantic(sc); // for better error message
+            if (!lowering)
             {
-                error(exp.loc, "incompatible types for array comparison: `%s` and `%s`",
-                          exp.e1.type.toChars(), exp.e2.type.toChars());
-                __equals = ErrorExp.get();
+                if (sc.func)
+                    error(exp.loc, "can't infer return type in function `%s`", sc.func.toChars());
+                else
+                    error(exp.loc, "incompatible types for array comparison: `%s` and `%s`",
+                  exp.e1.type.toChars(), exp.e2.type.toChars());
+                lowering = ErrorExp.get();
             }
-
-            result = __equals;
+            exp.lowering = lowering;
+            result = exp;
             return;
         }
 
+        // When array comparison is not lowered to `__equals`, `memcmp` is used, but
+        // GC checks occur before the expression is lowered to `memcmp` in e2ir.d.
+        // Thus, we will consider the literal arrays as on-stack arrays to avoid issues
+        // during GC checks.
+        if (isArrayComparison)
+        {
+            if (auto ale1 = exp.e1.isArrayLiteralExp())
+            {
+                ale1.onstack = true;
+            }
+
+            if (auto ale2 = exp.e2.isArrayLiteralExp())
+            {
+                ale2.onstack = true;
+            }
+        }
+
         if (exp.e1.type.toBasetype().ty == Taarray)
         {
+            result = lowerToAAEqual(exp);
             semanticTypeInfo(sc, exp.e1.type.toBasetype());
+            return;
         }
 
         if (!target.isVectorOpSupported(t1, exp.op, t2))
@@ -13924,7 +14488,8 @@ private bool expressionSemanticDone(Expression e)
         || e.isTypeExp() // stores its type in the Expression.type field
         || e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal
         || e.isVarExp() // type sometimes gets set already before semantic
-        || (e.isAssocArrayLiteralExp() && !e.type.vtinfo) // semanticTypeInfo not run during initialization
+        || (e.isAssocArrayLiteralExp() && // semanticTypeInfo not run during initialization
+            (!e.type.vtinfo || !e.isAssocArrayLiteralExp().lowering))
     );
 }
 
@@ -14410,20 +14975,10 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
         Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
         return e;
     }
-    else if ((exp.ident == Id.max || exp.ident == Id.min) &&
-             exp.e1.isVarExp() &&
-             exp.e1.isVarExp().var.isBitFieldDeclaration())
-    {
-        // For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type
-        auto bf = exp.e1.isVarExp().var.isBitFieldDeclaration();
-        return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type);
-    }
-    else if ((exp.ident == Id.max || exp.ident == Id.min) &&
-             exp.e1.isDotVarExp() &&
-             exp.e1.isDotVarExp().var.isBitFieldDeclaration())
+    else if ((exp.ident == Id.max || exp.ident == Id.min) && exp.e1.isBitField())
     {
         // For `x.max` and `x.min` get the max/min of the bitfield, not the max/min of its type
-        auto bf = exp.e1.isDotVarExp().var.isBitFieldDeclaration();
+        auto bf = exp.e1.isBitField();
         return new IntegerExp(exp.loc, bf.getMinMax(exp.ident), bf.type);
     }
     else
@@ -15346,6 +15901,9 @@ Expression resolveLoc(Expression exp, Loc loc, Scope* sc)
                 element = element.resolveLoc(loc, sc);
         }
 
+        if (exp.lowering)
+            exp.lowering.resolveLoc(loc, sc);
+
         return exp;
     }
 
@@ -15540,25 +16098,23 @@ Expression addDtorHook(Expression e, Scope* sc)
 /*******************************
  * Try to convert an expression to be an lvalue.
  *
- * Give error if we're not an lvalue.
+ * Give error if we are not an lvalue.
  * Params:
  *     _this = expression to convert
  *     sc = scope
  *     action = for error messages, what the lvalue is needed for (e.g. take address of for `&x`, modify for `x++`)
+ *     eorig = original un-lowered expression for error messages, in case of recursive calls; null means use `_this`
  * Returns: converted expression, or `ErrorExp` on error
 */
-Expression toLvalue(Expression _this, Scope* sc, const(char)* action)
-{
-    return toLvalueImpl(_this, sc, action, _this);
-}
-
-// e = original un-lowered expression for error messages, in case of recursive calls
-private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action, Expression e)
+Expression toLvalue(Expression _this, Scope* sc, const(char)* action, Expression eorig = null)
 {
+    //printf("toLvalue() %s\n", _this.toChars());
+    if (!eorig)
+        eorig = _this;
     if (!action)
         action = "create lvalue of";
 
-    assert(e);
+    assert(eorig);
     Expression visit(Expression _this)
     {
         // BinaryAssignExp does not have an EXP associated
@@ -15567,14 +16123,14 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
         if (_this.isBinAssignExp())
             return _this;
         if (!_this.loc.isValid())
-            _this.loc = e.loc;
+            _this.loc = eorig.loc;
 
-        if (e.op == EXP.type)
-            error(_this.loc, "cannot %s type `%s`", action, e.type.toChars());
-        else if (e.op == EXP.template_)
-            error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toErrMsg());
+        if (eorig.op == EXP.type)
+            error(_this.loc, "cannot %s type `%s`", action, eorig.type.toChars());
+        else if (eorig.op == EXP.template_)
+            error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, eorig.toErrMsg());
         else
-            error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toErrMsg());
+            error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, eorig.toErrMsg());
 
         return ErrorExp.get();
     }
@@ -15582,8 +16138,8 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
     Expression visitInteger(IntegerExp _this)
     {
         if (!_this.loc.isValid())
-            _this.loc = e.loc;
-        error(e.loc, "cannot %s constant `%s`", action, e.toErrMsg());
+            _this.loc = eorig.loc;
+        error(eorig.loc, "cannot %s constant `%s`", action, eorig.toErrMsg());
         return ErrorExp.get();
     }
 
@@ -15652,14 +16208,15 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
     {
         auto e1 = _this.e1;
         auto var = _this.var;
-        //printf("DotVarExp::toLvalue(%s)\n", toChars());
+        //printf("DotVarExp::toLvalue(%s)\n", _this.toChars());
         if (sc && sc.inCfile)
         {
             /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
              * is an lvalue if the first expression is an lvalue.
              */
-            if (!e1.isLvalue())
-                return visit(_this);
+            e1 = e1.toLvalue(sc, action, eorig);
+            if (e1.isErrorExp())
+                return e1;
         }
         if (!_this.isLvalue())
             return visit(_this);
@@ -15720,7 +16277,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
 
     Expression visitVectorArray(VectorArrayExp _this)
     {
-        _this.e1 = _this.e1.toLvalueImpl(sc, action, e);
+        _this.e1 = _this.e1.toLvalue(sc, action, eorig);
         return _this;
     }
 
@@ -15745,13 +16302,13 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
 
     Expression visitDelegatePointer(DelegatePtrExp _this)
     {
-        _this.e1 = _this.e1.toLvalueImpl(sc, action, e);
+        _this.e1 = _this.e1.toLvalue(sc, action, eorig);
         return _this;
     }
 
     Expression visitDelegateFuncptr(DelegateFuncptrExp _this)
     {
-        _this.e1 = _this.e1.toLvalueImpl(sc, action, e);
+        _this.e1 = _this.e1.toLvalue(sc, action, eorig);
         return _this;
     }
 
@@ -15778,12 +16335,19 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action
 
     Expression visitCond(CondExp _this)
     {
+        if (sc && sc.inCfile)
+        {
+            // C11 6.5.15: A conditional expression does not yield an lvalue.
+            return visit(_this);
+        }
         // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
-        CondExp e = cast(CondExp)(_this.copy());
-        e.e1 = _this.e1.toLvalue(sc, action).addressOf();
-        e.e2 = _this.e2.toLvalue(sc, action).addressOf();
-        e.type = _this.type.pointerTo();
-        return new PtrExp(_this.loc, e, _this.type);
+        CondExp ecopy = cast(CondExp)(_this.copy());
+        ecopy.e1 = _this.e1.toLvalue(sc, action).addressOf();
+        checkAddressable(ecopy.e1, sc, "take address of");
+        ecopy.e2 = _this.e2.toLvalue(sc, action).addressOf();
+        checkAddressable(ecopy.e2, sc, "take address of");
+        ecopy.type = _this.type.pointerTo();
+        return new PtrExp(_this.loc, ecopy, _this.type);
 
     }
 
@@ -15975,17 +16539,14 @@ Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyF
  * Params:
  *     _this = Expression to convert
  *     sc = scope
+ *     eorig = original / un-lowered expression to print in error messages, if null default to `_this`
  * Returns: `_this` converted to an lvalue, or an `ErrorExp`
  */
-Expression modifiableLvalue(Expression _this, Scope* sc)
+Expression modifiableLvalue(Expression _this, Scope* sc, Expression eorig = null)
 {
-    return modifiableLvalueImpl(_this, sc, _this);
-}
+    if (!eorig)
+        eorig = _this;
 
-// e = original / un-lowered expression to print in error messages
-private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression e)
-{
-    assert(e);
     Expression visit(Expression exp)
     {
         //printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars());
@@ -16024,7 +16585,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression
                 return ErrorExp.get();
             }
         }
-        return exp.toLvalueImpl(sc, "modify", e);
+        return exp.toLvalue(sc, "modify", eorig);
     }
 
     Expression visitString(StringExp exp)
@@ -16073,7 +16634,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression
 
     Expression visitComma(CommaExp exp)
     {
-        exp.e2 = exp.e2.modifiableLvalueImpl(sc, e);
+        exp.e2 = exp.e2.modifiableLvalue(sc, eorig);
         return exp;
     }
 
@@ -16095,16 +16656,6 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression
         return visit(exp);
     }
 
-    Expression visitIndex(IndexExp exp)
-    {
-        //printf("IndexExp::modifiableLvalue(%s)\n", exp.toChars());
-        Expression ex = exp.markSettingAAElem();
-        if (ex.op == EXP.error)
-            return ex;
-
-        return visit(exp);
-    }
-
     Expression visitCond(CondExp exp)
     {
         if (!exp.e1.isLvalue() && !exp.e2.isLvalue())
@@ -16127,7 +16678,6 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression
         case EXP.comma:                   return visitComma(_this.isCommaExp());
         case EXP.delegatePointer:         return visitDelegatePtr(_this.isDelegatePtrExp());
         case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp());
-        case EXP.index:                   return visitIndex(_this.isIndexExp());
         case EXP.question:                return visitCond(_this.isCondExp());
     }
 }
@@ -16155,9 +16705,12 @@ private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
     }
     if (sc.func && !sc.intypeof && !v.isDataseg())
     {
+        auto msg = (v.storage_class & STC.ref_) ?
+            "taking the address of local variable `%s`" :
+            "taking the address of stack-allocated local variable `%s`";
         if (sc.useDIP1000 != FeatureState.enabled &&
             !(v.storage_class & STC.temp) &&
-            sc.setUnsafe(false, exp.loc, "taking the address of stack-allocated local variable `%s`", v))
+            sc.setUnsafe(false, exp.loc, msg.ptr, v))
         {
             return false;
         }
@@ -16171,11 +16724,13 @@ private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v)
  * Params:
  *      e = expression to check
  *      sc = context
+ *      action = for error messages, what the address is needed for (e.g. ref return, indexing an array)
  * Returns:
  *      true if the expression is addressable
  */
-bool checkAddressable(Expression e, Scope* sc)
+bool checkAddressable(Expression e, Scope* sc, const(char)* action)
 {
+    //printf("checkAddressable() %s\n", e.toChars());
     Expression ex = e;
     while (true)
     {
@@ -16183,11 +16738,11 @@ bool checkAddressable(Expression e, Scope* sc)
         {
             case EXP.dotVariable:
                 // https://issues.dlang.org/show_bug.cgi?id=22749
-                // Error about taking address of any bit-field, regardless of
+                // Error about taking address of any bitfield, regardless of
                 // whether SCOPE.Cfile is set.
                 if (auto bf = ex.isDotVarExp().var.isBitFieldDeclaration())
                 {
-                    error(e.loc, "cannot take address of bit-field `%s`", bf.toChars());
+                    sc.eSink.error(e.loc, "cannot %s bitfield `%s`", action, ex.toErrMsg());
                     return false;
                 }
                 goto case EXP.cast_;
@@ -16203,7 +16758,7 @@ bool checkAddressable(Expression e, Scope* sc)
                 continue;
 
             case EXP.variable:
-                if (sc.inCfile)
+                if (sc && sc.inCfile)
                 {
                     // C11 6.5.3.2: A variable that has its address taken cannot be
                     // stored in a register.
@@ -16211,10 +16766,7 @@ bool checkAddressable(Expression e, Scope* sc)
                     // or cast to an lvalue pointer cannot be stored in a register.
                     if (ex.isVarExp().var.storage_class & STC.register)
                     {
-                        if (e.isIndexExp())
-                            error(e.loc, "cannot index through register variable `%s`", ex.toErrMsg());
-                        else
-                            error(e.loc, "cannot take address of register variable `%s`", ex.toErrMsg());
+                        sc.eSink.error(e.loc, "cannot %s register variable `%s`", action, ex.toErrMsg());
                         return false;
                     }
                 }
@@ -16275,7 +16827,7 @@ Expression getThisSkipNestedFuncs(Loc loc, Scope* sc, Dsymbol s, AggregateDeclar
         {
             n++;
             e1 = new VarExp(loc, f.vthis);
-            if (f.hasDualContext())
+            if (f.hasDualContext)
             {
                 // (*__this)[i]
                 if (n > 1)
@@ -16382,7 +16934,7 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements
         return true;
 
     const nfields = sd.nonHiddenFields();
-    size_t offset = 0;
+    ulong bitoffset = 0;
     for (size_t i = 0; i < elements.length; i++)
     {
         Expression e = (*elements)[i];
@@ -16401,7 +16953,13 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements
             return false;
         }
         VarDeclaration v = sd.fields[i];
-        if (v.offset < offset)
+        // Overlap is checked by comparing bit offsets
+        ulong vbitoffset = v.offset * 8;
+        auto vbf = v.isBitFieldDeclaration();
+        if (vbf)
+            vbitoffset += vbf.bitOffset;
+
+        if (vbitoffset < bitoffset)
         {
             .error(loc, "overlapping initialization for `%s`", v.toChars());
             if (!sd.isUnionDeclaration())
@@ -16416,7 +16974,11 @@ private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements
         const vsize = v.type.size();
         if (vsize == SIZE_INVALID)
             return false;
-        offset = cast(uint)(v.offset + vsize);
+        // Bitsize of types are overridden by any bitfield widths.
+        if (vbf)
+            bitoffset = vbitoffset + vbf.fieldWidth;
+        else
+            bitoffset = vbitoffset + vsize * 8;
 
         Type t = v.type;
         if (stype)
@@ -16615,8 +17177,12 @@ Expression toBoolean(Expression exp, Scope* sc)
             if (!t.isBoolean())
             {
                 if (tb != Type.terror)
+                {
                     error(exp.loc, "expression `%s` of type `%s` does not have a boolean value",
                               exp.toChars(), t.toChars());
+                    if (auto ts = tb.isTypeStruct())
+                        errorSupplemental(ts.sym.loc, "perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`");
+                }
                 return ErrorExp.get();
             }
             return e;
@@ -17133,7 +17699,7 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration
              * postblit. Print the first field that has
              * a disabled postblit.
              */
-            if (postblit.isGenerated())
+            if (postblit.isGenerated)
             {
                 auto sd = p.isStructDeclaration();
                 assert(sd);
@@ -17175,7 +17741,7 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration
     if (auto ctor = d.isCtorDeclaration())
     {
         //printf("checkDisabled() %s %s\n", ctor.toPrettyChars(), toChars(ctor.type));
-        if (ctor.isCpCtor && ctor.isGenerated())
+        if (ctor.isCpCtor && ctor.isGenerated)
         {
             .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars());
             return true;
@@ -17185,6 +17751,39 @@ bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration
     return true;
 }
 
+
+/*******************************************
+ * If variable has a constant expression initializer, get it.
+ * Otherwise, return null.
+ */
+Expression getConstInitializer(VarDeclaration vd, bool needFullType = true)
+{
+    assert(vd.type && vd._init);
+
+    // Ungag errors when not speculative
+    const oldgag = global.gag;
+    if (global.gag)
+    {
+        Dsymbol sym = vd.isMember();
+        if (sym && !sym.isSpeculative())
+            global.gag = 0;
+    }
+
+    if (vd._scope)
+    {
+        vd.inuse++;
+        vd._init = vd._init.initializerSemantic(vd._scope, vd.type, INITinterpret);
+        import dmd.semantic2 : lowerStaticAAs;
+        lowerStaticAAs(vd, vd._scope);
+        vd._scope = null;
+        vd.inuse--;
+    }
+
+    Expression e = vd._init.initializerToExpression(needFullType ? vd.type : null);
+    global.gag = oldgag;
+    return e;
+}
+
 /*******************************************
  * Helper function for the expansion of manifest constant.
  */
@@ -17200,6 +17799,8 @@ private Expression expandInitializer(VarDeclaration vd, Loc loc)
     }
 
     e = e.copy();
+    if (auto aae = e.isAssocArrayLiteralExp())
+        aae.lowering = null; // need to redo lowering as it contains temporary variables that must be renamed
     e.loc = loc;    // for better error message
     return e;
 }
@@ -17526,10 +18127,11 @@ void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc)
     Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()];
     foreach (i; 0 .. nvars)
     {
-        foreach (params; pparams)
+        foreach (j, params; pparams)
         {
             auto p = sfe.aggrfe ? (*sfe.aggrfe.parameters)[i] : sfe.rangefe.param;
-            params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null));
+            auto storageClass = j == 2 ? p.storageClass : p.storageClass & ~(STC.manifest | STC.alias_);
+            params.push(new Parameter(aloc, storageClass, p.type, p.ident, null, null));
         }
     }
     Expression[2] res;
@@ -17574,12 +18176,12 @@ void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc)
         sfe.rangefe.upr = sfe.rangefe.upr.optimize(WANTvalue);
         sfe.rangefe.upr = sfe.rangefe.upr.ctfeInterpret();
     }
-    auto s1 = new Statements();
+
     auto stmts = new Statements();
     if (tplty) stmts.push(new ExpStatement(sfe.loc, tplty.sym));
     stmts.push(new ReturnStatement(aloc, res[0]));
-    s1.push(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts)));
-    s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
+    auto s1 = new Statements(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts)),
+                             new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
     Type ety = new TypeTypeof(aloc, sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
     auto aty = ety.arrayOf();
     auto idres = Identifier.generateId("__res");
@@ -17737,6 +18339,46 @@ int include(Condition c, Scope* sc)
     return v.result;
 }
 
+/*******************************************
+ * Look for constructor declaration.
+ */
+Dsymbol searchCtor(AggregateDeclaration ad)
+{
+    auto s = ad.search(Loc.initial, Id.ctor);
+    if (s)
+    {
+        if (!(s.isCtorDeclaration() ||
+              s.isTemplateDeclaration() ||
+              s.isOverloadSet()))
+        {
+            error(s.loc, "%s name `__ctor` is not allowed", s.kind);
+            errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use");
+            ad.errors = true;
+            s = null;
+        }
+    }
+    if (s && s.toParent() != ad)
+        s = null; // search() looks through ancestor classes
+    if (s)
+    {
+        // Finish all constructors semantics to determine this.noDefaultCtor.
+        static int searchCtor(Dsymbol s, void*)
+        {
+            auto f = s.isCtorDeclaration();
+            if (f && f.semanticRun == PASS.initial)
+                f.dsymbolSemantic(null);
+            return 0;
+        }
+
+        for (size_t i = 0; i < ad.members.length; i++)
+        {
+            auto sm = (*ad.members)[i];
+            sm.apply(&searchCtor, null);
+        }
+    }
+    return s;
+}
+
 private extern(C++) class IncludeVisitor : Visitor {
     alias visit = Visitor.visit;
 
@@ -17869,3 +18511,334 @@ private extern(C++) class IncludeVisitor : Visitor {
         result = (sic.inc == Include.yes);
     }
 }
+
+/***************************************
+* mark ArrayExp on the left side of an assignment recursively as modifiable before
+* semantic analysis to defer potential lowerings into the assignment when
+* the right side of the expression is analyzed, too, i.e.
+*
+*       a[i][j] = 1
+*
+* is parsed to
+*
+*       AssignExp(ArrayExp(ArrayExp(Id('a'), Id('i')), Id('j')), 1)
+*
+* ArrayExp is converted to IndexExp during semantic analysis, but the lowering
+* for IndexExp on associative arrays is different for reading or writing. For
+* struct types, it can even depend on the right hand side of an assignment.
+*
+* Params:
+*       ae = the ArrayExp to mark
+*/
+void markArrayExpModifiable(ArrayExp ae)
+{
+    ae.modifiable = true;
+    for (auto ae1 = ae.e1.isArrayExp(); ae1; ae1 = ae1.e1.isArrayExp())
+        ae1.modifiable = true;
+}
+
+/***************************************
+* convert an IndexExp on an associative array `aa[key]`
+* to `_d_aaGetRvalueX!(K,V)(aa, key)[0]`
+*
+* Params:
+*       ie = the IndexExp to lower
+*       sc = context
+* Returns:
+*       on success, the lowered expression that is still an IndexExp
+*         with its `loweredFrom` field set to the original expression
+*       on failure, null is returned
+*/
+Expression lowerAAIndexRead(IndexExp ie, Scope* sc)
+{
+    Expression ce = buildAAIndexRValueX(ie.e1.type, ie.e1, ie.e2, sc);
+    if (!ce)
+        return ie;
+    auto rv = new IndexExp(ie.loc, ce, IntegerExp.literal!0);
+    rv.loweredFrom = ie;
+
+    return rv.expressionSemantic(sc);
+}
+
+// helper for lowerAAIndexRead and revertIndexAssignToRvalues
+// to rewrite `aa[key]` to `_d_aaGetRvalueX!(K,V)(aa, key)[0]`
+private Expression buildAAIndexRValueX(Type t, Expression eaa, Expression ekey, Scope* sc)
+{
+    auto taa = t.toBasetype().isTypeAArray();
+    if (!taa)
+        return null;
+
+    auto loc = eaa.loc;
+    Identifier hook = Id._d_aaGetRvalueX;
+    if (!verifyHookExist(loc, *sc, hook, "indexing AA"))
+        return null;
+
+    Expression func = new IdentifierExp(loc, Id.empty);
+    func = new DotIdExp(loc, func, Id.object);
+    auto tiargs = new Objects(taa.index, taa.next);
+    func = new DotTemplateInstanceExp(loc, func, hook, tiargs);
+
+    Expression e0;
+    auto arguments = new Expressions(eaa, ekey);
+    auto call = new CallExp(loc, func, arguments);
+    e0 = Expression.combine(e0, call);
+
+    if (arrayBoundsCheck(sc.func))
+    {
+        // __aaget = _d_aaGetRvalueX(aa, key), __aaget ? __aaget : onRangeError(__FILE__, __LINE__)
+        auto ei = new ExpInitializer(loc, e0);
+        auto vartmp = Identifier.generateId("__aaget");
+        auto vardecl = new VarDeclaration(loc, null, vartmp, ei, STC.exptemp);
+        auto declexp = new DeclarationExp(loc, vardecl);
+
+        //Expression idrange = new IdentifierExp(loc, Identifier.idPool("_d_arraybounds"));
+        Expression idrange = new IdentifierExp(loc, Id.empty);
+        idrange = new DotIdExp(loc, idrange, Id.object);
+        idrange = new DotIdExp(loc, idrange, Identifier.idPool("_d_arraybounds"));
+        auto locargs = new Expressions(new FileInitExp(loc, EXP.file), new LineInitExp(loc));
+        auto ex = new CallExp(loc, idrange, locargs);
+
+        auto idvar1 = new IdentifierExp(loc, vartmp);
+        auto idvar2 = new IdentifierExp(loc, vartmp);
+        auto cond = new CondExp(loc, idvar1, idvar2, ex);
+        auto comma = new CommaExp(loc, declexp, cond);
+        return comma;
+    }
+    return e0;
+}
+
+/***************************************
+* in a multi-dimensional array assignment without the out-most access being to an AA,
+* convert AA accesses back to rvalues
+*
+* Params:
+*       ie = the IndexExp to lower
+*       sc = context
+* Returns:
+*       the lowered expression if the assignment was actually on an associative array,
+*       the original expression otherwise,
+*       an ErorrExp on failure
+*/
+Expression revertIndexAssignToRvalues(IndexExp ie, Scope* sc)
+{
+    if (auto ie1 = ie.e1.isIndexExp())
+    {
+        // convert inner access first, otherwise we'd have to crawl into the lowered AST
+        ie.e1 = revertIndexAssignToRvalues(ie1, sc);
+    }
+    if (!ie.e1.type.isTypeAArray())
+        return ie;
+
+    assert(ie.modifiable);
+    return lowerAAIndexRead(ie, sc);
+}
+
+// helper for rewriteAAIndexAssign
+private Expression implicitConvertToStruct(Expression ev, StructDeclaration sd, Scope* sc)
+{
+    Type t2 = ev.type.toBasetype();
+    Expression ey = null;
+    if (t2.ty == Tstruct && sd == t2.toDsymbol(sc))
+    {
+        ey = ev;
+    }
+    else if (!ev.implicitConvTo(sd.type) && sd.ctor)
+    {
+        // Look for implicit constructor call
+        // Rewrite as S().ctor(e2)
+        ey = new StructLiteralExp(ev.loc, sd, null);
+        ey = new DotIdExp(ev.loc, ey, Id.ctor);
+        ey = new CallExp(ev.loc, ey, ev);
+        ey = ey.trySemantic(sc);
+    }
+    return ey;
+}
+
+// helper for rewriteIndexAssign
+private Expression rewriteAAIndexAssign(BinExp exp, Scope* sc, ref Type[2] aliasThisStop)
+{
+    auto ie = exp.e1.isIndexExp();
+    assert(ie);
+    auto loc = ie.e1.loc;
+
+    Identifier hook = Id._d_aaGetY;
+    if (!verifyHookExist(loc, *sc, hook, "modifying AA"))
+        return ErrorExp.get();
+
+    bool escape = checkNewEscape(*sc, exp.e2, false);
+    if (escape)
+        return ErrorExp.get();
+    auto gcexp = exp.checkGC(sc);
+    if (gcexp.op == EXP.error)
+        return gcexp;
+
+    // build `bool __aafound, auto __aaget = _d_aaGetY(aa, key, found);`
+    auto idfound = Identifier.generateId("__aafound");
+    auto varfound = new VarDeclaration(loc, Type.tbool, idfound, null, STC.temp | STC.ctfe);
+    auto declfound = new DeclarationExp(loc, varfound);
+
+    // handle side effects before the actual AA insert
+    Expression e0 = declfound.expressionSemantic(sc);
+    Expression eaa = ie.e1;
+    // find the AA of multi dimensional access
+    for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp())
+        eaa = ieaa.e1;
+    eaa = extractSideEffect(sc, "__aatmp", e0, eaa);
+    // collect all keys of multi dimensional access
+    Expressions ekeys;
+    ekeys.push(ie.e2);
+    for (auto ieaa = ie.e1.isIndexExp(); ieaa && ieaa.e1.type.isTypeAArray(); ieaa = ieaa.e1.isIndexExp())
+        ekeys.push(ieaa.e2);
+    foreach (ekey; ekeys)
+    {
+        Type tidx = ekey.type.toBasetype();
+        if (tidx.ty == Tarray && tidx.nextOf().isMutable())
+        {
+            error(loc, "associative arrays can only be assigned values with immutable keys, not `%s`", tidx.toChars());
+            return ErrorExp.get();
+        }
+    }
+    // extract side effects in lexical order
+    for (size_t i = ekeys.length; i > 0; --i)
+        ekeys[i-1] = extractSideEffect(sc, "__aakey", e0, ekeys[i-1]);
+    Expression ev = extractSideEffect(sc, "__aaval", e0, exp.e2);
+
+    // generate series of calls to _d_aaGetY
+    for (size_t i = ekeys.length; i > 0; --i)
+    {
+        auto taa = eaa.type.isTypeAArray();
+        assert (taa); // type must not have changed during rewrite
+        Expression func = new IdentifierExp(loc, Id.empty);
+        func = new DotIdExp(loc, func, Id.object);
+        auto tiargs = new Objects(taa.index, taa.next);
+        func = new DotTemplateInstanceExp(loc, func, hook, tiargs);
+
+        auto arguments = new Expressions(eaa, ekeys[i-1], new IdentifierExp(loc, idfound));
+        eaa = new CallExp(loc, func, arguments);
+        if (i > 1)
+        {
+            // extractSideEffect() cannot be used to ref the IndexExp,
+            // because ConstructExp below will not insert a postblit
+            auto ie1 = new IndexExp(loc, eaa, IntegerExp.literal!0);
+            ie1.modifiable = true;
+            ie1.loweredFrom = eaa;
+            eaa = ie1;
+        }
+        eaa = eaa.expressionSemantic(sc);
+        if (eaa.op == EXP.error)
+            return eaa;
+    }
+    Expression eg = extractSideEffect(sc, "__aaget", e0, eaa);
+    auto ie1 = new IndexExp(loc, eg, IntegerExp.literal!0);
+    ie1.modifiable = true;
+    ie1.loweredFrom = ie;
+    auto ex = ie1.expressionSemantic(sc);
+    ex = ex.optimize(WANTvalue);
+    if (ex.op == EXP.error)
+        return ex;
+    if (!exp.isAssignExp())
+    {
+        // modifying assignments work with zero-initialized inserted value
+        BinExp bex = cast(BinExp)exp.copy();
+        bex.e1 = ex;
+        bex.e2 = ev;
+        ex = Expression.combine(e0, bex);
+        ex.isCommaExp().originalExp = exp;
+        return ex.expressionSemantic(sc);
+    }
+    AssignExp ae = new AssignExp(loc, ex, ev);
+    auto ts = ie.type.isTypeStruct();
+    if (Expression overexp = ts ? ae.opOverloadAssign(sc, aliasThisStop) : null)
+    {
+        if (overexp.op == EXP.error)
+            return overexp;
+        if (auto ey = implicitConvertToStruct(ev, ts.sym, sc))
+        {
+            // __aafound ? __aaget.opAssign(__aaval) : __aaget.ctor(__aaval)
+            ey = new ConstructExp(loc, ex, ey);
+            ey = ey.expressionSemantic(sc);
+            if (ey.op == EXP.error)
+                return ey;
+            ex = overexp;
+
+            // https://issues.dlang.org/show_bug.cgi?id=14144
+            // 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, EXP.question, ex, ey))
+            {
+                ex = new CastExp(ex.loc, ex, Type.tvoid);
+                ey = new CastExp(ey.loc, ey, Type.tvoid);
+            }
+            Expression condfound = new IdentifierExp(loc, idfound);
+            ex = new CondExp(loc, condfound, ex, ey);
+            ex = Expression.combine(e0, ex);
+            ex.isCommaExp().originalExp = exp;
+        }
+        else
+        {
+            // write back to _aaGetRValueX(aa, key)[0].opAssign(__aaval)
+            auto call = buildAAIndexRValueX(ie.e1.type, ie.e1, ie.e2, sc);
+            ie1 = new IndexExp(loc, call, IntegerExp.literal!0);
+            ie1.loweredFrom = ie;
+            ex = ie1.expressionSemantic(sc);
+            ex = ex.optimize(WANTvalue);
+            ae = new AssignExp(loc, ex, ev);
+            ex = ae.opOverloadAssign(sc, aliasThisStop);
+            assert(ex);
+        }
+    }
+    else
+    {
+        ex = Expression.combine(e0, ae);
+        ex.isCommaExp().originalExp = exp;
+    }
+    ex = ex.expressionSemantic(sc);
+    return ex;
+}
+
+/***************************************
+* rewrite multi-dimensional array modifying assignments on associative arrays
+*
+* Params:
+*       exp = the assignment expression, AssignExp, ConstructExp, BinAssignExp or PostExp
+*       sc = context
+*       aliasThisStop = for recursion check on `alias this`
+* Returns:
+*       the lowered expression if the assignment was actually on an associative array,
+*       null if no modifying index expression was found on the lhs of the assignemnt
+*/
+Expression rewriteIndexAssign(BinExp exp, Scope* sc, Type[2] aliasThisStop)
+{
+    if (auto ie1 = exp.e1.isIndexExp())
+    {
+        if (ie1.e1.type.isTypeAArray())
+        {
+            if (exp.op == EXP.construct)
+                exp.e1 = exp.e1.toLvalue(sc, "initialize");
+            else
+                exp.e1 = exp.e1.modifiableLvalue(sc);
+            if (exp.e1.op == EXP.error)
+                return exp.e1;
+            assert(exp.e1 == ie1);
+            assert(ie1.modifiable);
+            return rewriteAAIndexAssign(exp, sc, aliasThisStop);
+        }
+        exp.e1 = revertIndexAssignToRvalues(ie1, sc);
+    }
+    return null;
+}
+
+/****************************************
+ * Get bitfield from expression.
+ */
+BitFieldDeclaration isBitField(Expression e)
+{
+    if (auto ve = e.isVarExp())
+        return ve.var.isBitFieldDeclaration();
+
+    if (auto dve = e.isDotVarExp())
+        return dve.var.isBitFieldDeclaration();
+
+    return null;
+}
diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d
index 2ccb1d25497d..8116dda79d74 100644
--- a/gcc/d/dmd/file_manager.d
+++ b/gcc/d/dmd/file_manager.d
@@ -175,121 +175,105 @@ nothrow:
 
         whichPathFoundThis = -1;
 
-        // see if we should check for the module locally.
-        bool checkLocal = pathCache.pathExists(filename);
-        const sdi = FileName.forceExt(filename, hdr_ext);
-        if (checkLocal && FileName.exists(sdi) == 1)
-            return sdi;
-        scope(exit) FileName.free(sdi.ptr);
-
-        const sd = FileName.forceExt(filename, mars_e[...]

[diff truncated at 524288 bytes]


More information about the Gcc-cvs mailing list