]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
d: Merge upstream dmd, druntime e1f6680f50, phobos f87979028
authorIain Buclaw <ibuclaw@gdcproject.org>
Fri, 30 Jan 2026 16:30:33 +0000 (17:30 +0100)
committerIain Buclaw <ibuclaw@gdcproject.org>
Sat, 31 Jan 2026 08:37:45 +0000 (09:37 +0100)
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.

254 files changed:
gcc/d/Make-lang.in
gcc/d/d-convert.cc
gcc/d/d-lang.cc
gcc/d/decl.cc
gcc/d/dmd/MERGE
gcc/d/dmd/README.md
gcc/d/dmd/access.d
gcc/d/dmd/aggregate.d
gcc/d/dmd/aggregate.h
gcc/d/dmd/arrayop.d
gcc/d/dmd/astenums.d
gcc/d/dmd/attrib.d
gcc/d/dmd/canthrow.d
gcc/d/dmd/clone.d
gcc/d/dmd/common/bitfields.d
gcc/d/dmd/common/outbuffer.d
gcc/d/dmd/common/outbuffer.h
gcc/d/dmd/cond.d
gcc/d/dmd/constfold.d
gcc/d/dmd/cparse.d
gcc/d/dmd/ctfeexpr.d
gcc/d/dmd/cxxfrontend.d
gcc/d/dmd/dcast.d
gcc/d/dmd/dclass.d
gcc/d/dmd/declaration.d
gcc/d/dmd/declaration.h
gcc/d/dmd/delegatize.d
gcc/d/dmd/denum.d
gcc/d/dmd/dimport.d
gcc/d/dmd/dinterpret.d
gcc/d/dmd/dmodule.d
gcc/d/dmd/dscope.d
gcc/d/dmd/dstruct.d
gcc/d/dmd/dsymbol.d
gcc/d/dmd/dsymbol.h
gcc/d/dmd/dsymbolsem.d
gcc/d/dmd/dtemplate.d
gcc/d/dmd/dtoh.d
gcc/d/dmd/dversion.d
gcc/d/dmd/enum.h
gcc/d/dmd/enumsem.d
gcc/d/dmd/escape.d
gcc/d/dmd/expression.d
gcc/d/dmd/expression.h
gcc/d/dmd/expressionsem.d
gcc/d/dmd/file_manager.d
gcc/d/dmd/func.d
gcc/d/dmd/funcsem.d
gcc/d/dmd/globals.d
gcc/d/dmd/globals.h
gcc/d/dmd/gluelayer.d [deleted file]
gcc/d/dmd/hdrgen.d
gcc/d/dmd/iasm/gcc.d [new file with mode: 0644]
gcc/d/dmd/iasm/package.d [moved from gcc/d/dmd/iasm.d with 87% similarity]
gcc/d/dmd/iasmgcc.d [deleted file]
gcc/d/dmd/id.d
gcc/d/dmd/identifier.d
gcc/d/dmd/import.h
gcc/d/dmd/importc.d
gcc/d/dmd/lambdacomp.d
gcc/d/dmd/lexer.d
gcc/d/dmd/mangle/cpp.d
gcc/d/dmd/mangle/package.d
gcc/d/dmd/module.h
gcc/d/dmd/mtype.d
gcc/d/dmd/mtype.h
gcc/d/dmd/mustuse.d
gcc/d/dmd/nogc.d
gcc/d/dmd/ob.d
gcc/d/dmd/objc.d
gcc/d/dmd/opover.d
gcc/d/dmd/optimize.d
gcc/d/dmd/parse.d
gcc/d/dmd/root/array.d
gcc/d/dmd/root/filename.d
gcc/d/dmd/root/hash.d
gcc/d/dmd/safe.d
gcc/d/dmd/scope.h
gcc/d/dmd/semantic2.d
gcc/d/dmd/semantic3.d
gcc/d/dmd/sideeffect.d
gcc/d/dmd/statement.d
gcc/d/dmd/statement.h
gcc/d/dmd/statementsem.d
gcc/d/dmd/target.d
gcc/d/dmd/template.h
gcc/d/dmd/templatesem.d
gcc/d/dmd/tokens.d
gcc/d/dmd/traits.d
gcc/d/dmd/typesem.d
gcc/d/dmd/typinf.d
gcc/d/dmd/visitor/foreachvar.d
gcc/d/dmd/visitor/postorder.d
gcc/d/expr.cc
gcc/d/imports.cc
gcc/d/runtime.def
gcc/d/typeinfo.cc
gcc/d/types.cc
gcc/testsuite/gdc.dg/asm1.d
gcc/testsuite/gdc.dg/asm5.d
gcc/testsuite/gdc.dg/pr100967.d
gcc/testsuite/gdc.dg/rtti1.d
gcc/testsuite/gdc.dg/rtti2.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/b1215.d
gcc/testsuite/gdc.test/compilable/extra-files/header1.d
gcc/testsuite/gdc.test/compilable/extra-files/test21331.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/extra-files/test21359a.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/extra-files/test21359b.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/imports/h20184.h [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/interpret4.d
gcc/testsuite/gdc.test/compilable/mixintype.d
gcc/testsuite/gdc.test/compilable/parens_inc.d
gcc/testsuite/gdc.test/compilable/shared.d
gcc/testsuite/gdc.test/compilable/test14838.d
gcc/testsuite/gdc.test/compilable/test17146.d
gcc/testsuite/gdc.test/compilable/test20100.d
gcc/testsuite/gdc.test/compilable/test20184.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test20365.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21331.sh [new file with mode: 0755]
gcc/testsuite/gdc.test/compilable/test21359.sh [new file with mode: 0755]
gcc/testsuite/gdc.test/compilable/test21416.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test21476.d [new file with mode: 0644]
gcc/testsuite/gdc.test/compilable/test23169.d
gcc/testsuite/gdc.test/compilable/test3004.d
gcc/testsuite/gdc.test/compilable/testgotoskips.d
gcc/testsuite/gdc.test/compilable/testsctreturn.d
gcc/testsuite/gdc.test/compilable/traits_getFunctionAttributes.d
gcc/testsuite/gdc.test/compilable/vcg-ast.d
gcc/testsuite/gdc.test/compilable/warn3882.d
gcc/testsuite/gdc.test/fail_compilation/aa_assign.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/aaerrors.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/biterrors.d
gcc/testsuite/gdc.test/fail_compilation/biterrors2.d
gcc/testsuite/gdc.test/fail_compilation/biterrors3.d
gcc/testsuite/gdc.test/fail_compilation/biterrors4.d
gcc/testsuite/gdc.test/fail_compilation/biterrors5.d
gcc/testsuite/gdc.test/fail_compilation/diag13320.d
gcc/testsuite/gdc.test/fail_compilation/diag16976.d
gcc/testsuite/gdc.test/fail_compilation/diag7420.d
gcc/testsuite/gdc.test/fail_compilation/discard_value.d
gcc/testsuite/gdc.test/fail_compilation/fail13756.d
gcc/testsuite/gdc.test/fail_compilation/fail20365.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail21414.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail21660.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail21693.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/fail22749.d
gcc/testsuite/gdc.test/fail_compilation/fail6497.d
gcc/testsuite/gdc.test/fail_compilation/fail6795.d
gcc/testsuite/gdc.test/fail_compilation/fix22108.d
gcc/testsuite/gdc.test/fail_compilation/foreach_seq.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/ice10938.d
gcc/testsuite/gdc.test/fail_compilation/ice13644.d
gcc/testsuite/gdc.test/fail_compilation/issue21630.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/previewin3.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/pull12941.d
gcc/testsuite/gdc.test/fail_compilation/retscope2.d
gcc/testsuite/gdc.test/fail_compilation/shared.d
gcc/testsuite/gdc.test/fail_compilation/test15191.d
gcc/testsuite/gdc.test/fail_compilation/test15704.d
gcc/testsuite/gdc.test/fail_compilation/test18282.d
gcc/testsuite/gdc.test/fail_compilation/test20881.d
gcc/testsuite/gdc.test/fail_compilation/test21408.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test21429.d [new file with mode: 0644]
gcc/testsuite/gdc.test/fail_compilation/test21477.d
gcc/testsuite/gdc.test/fail_compilation/test21546.d
gcc/testsuite/gdc.test/fail_compilation/test23710.d
gcc/testsuite/gdc.test/fail_compilation/varargsstc.d
gcc/testsuite/gdc.test/fail_compilation/verifyhookexist.d
gcc/testsuite/gdc.test/runnable/bit.d
gcc/testsuite/gdc.test/runnable/bug19652.d
gcc/testsuite/gdc.test/runnable/constfold.d
gcc/testsuite/gdc.test/runnable/foreach5.d
gcc/testsuite/gdc.test/runnable/mars1.d
gcc/testsuite/gdc.test/runnable/placenew.d
gcc/testsuite/gdc.test/runnable/sdtor.d
gcc/testsuite/gdc.test/runnable/staticaa.d
gcc/testsuite/gdc.test/runnable/test17487.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test20473.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test21429.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test21435.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test21660.d [new file with mode: 0644]
gcc/testsuite/gdc.test/runnable/test42.d
gcc/testsuite/gdc.test/runnable/testaa2.d
gcc/testsuite/gdc.test/runnable/testaa3.d
gcc/testsuite/gdc.test/runnable/testscope2.d
libphobos/libdruntime/MERGE
libphobos/libdruntime/Makefile.am
libphobos/libdruntime/Makefile.in
libphobos/libdruntime/core/atomic.d
libphobos/libdruntime/core/checkedint.d
libphobos/libdruntime/core/gc/config.d
libphobos/libdruntime/core/gc/gcinterface.d
libphobos/libdruntime/core/gc/registry.d
libphobos/libdruntime/core/internal/array/appending.d
libphobos/libdruntime/core/internal/array/capacity.d
libphobos/libdruntime/core/internal/array/construction.d
libphobos/libdruntime/core/internal/array/duplication.d
libphobos/libdruntime/core/internal/array/equality.d
libphobos/libdruntime/core/internal/array/utils.d
libphobos/libdruntime/core/internal/cast_.d [new file with mode: 0644]
libphobos/libdruntime/core/internal/convert.d
libphobos/libdruntime/core/internal/gc/blkcache.d
libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
libphobos/libdruntime/core/internal/gc/impl/manual/gc.d
libphobos/libdruntime/core/internal/gc/impl/proto/gc.d
libphobos/libdruntime/core/internal/gc/proxy.d
libphobos/libdruntime/core/internal/hash.d
libphobos/libdruntime/core/internal/lifetime.d
libphobos/libdruntime/core/internal/newaa.d
libphobos/libdruntime/core/internal/traits.d
libphobos/libdruntime/core/stdc/config.d
libphobos/libdruntime/core/stdc/stdarg.d
libphobos/libdruntime/core/stdcpp/string.d
libphobos/libdruntime/core/sys/linux/sched.d
libphobos/libdruntime/core/sys/posix/dirent.d
libphobos/libdruntime/core/sys/posix/dlfcn.d
libphobos/libdruntime/core/sys/posix/endian.d [new file with mode: 0644]
libphobos/libdruntime/core/sys/posix/sched.d
libphobos/libdruntime/core/sys/posix/stdio.d
libphobos/libdruntime/core/sys/posix/stdlib.d
libphobos/libdruntime/core/sys/posix/sys/mman.d
libphobos/libdruntime/core/sys/posix/sys/stat.d
libphobos/libdruntime/core/sys/posix/sys/statvfs.d
libphobos/libdruntime/core/thread/threadbase.d
libphobos/libdruntime/object.d
libphobos/libdruntime/rt/aaA.d [deleted file]
libphobos/libdruntime/rt/adi.d [deleted file]
libphobos/libdruntime/rt/cast_.d [deleted file]
libphobos/libdruntime/rt/lifetime.d
libphobos/src/MERGE
libphobos/src/Makefile.am
libphobos/src/Makefile.in
libphobos/src/index.dd
libphobos/src/std/algorithm/iteration.d
libphobos/src/std/algorithm/mutation.d
libphobos/src/std/algorithm/searching.d
libphobos/src/std/ascii.d
libphobos/src/std/container/rbtree.d
libphobos/src/std/datetime/date.d
libphobos/src/std/datetime/systime.d
libphobos/src/std/datetime/timezone.d
libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d
libphobos/src/std/getopt.d
libphobos/src/std/internal/entropy.d [new file with mode: 0644]
libphobos/src/std/internal/math/gammafunction.d
libphobos/src/std/mathspecial.d
libphobos/src/std/numeric.d
libphobos/src/std/random.d
libphobos/src/std/range/package.d
libphobos/src/std/string.d
libphobos/src/std/traits.d
libphobos/src/std/typecons.d
libphobos/testsuite/libphobos.aa/test_aa.d
libphobos/testsuite/libphobos.init_fini/custom_gc.d

index 9f048bb4e5d1cfdc9e74252da16d485198f688ee..9f91346290cc9a0921934ed3428395034f499aa3 100644 (file)
@@ -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)
index 81a1b3cd3cfb2d690b8dc70f04ee16f7a35c3749..30874e72c0dcf4606112883c29be71ad15aa2967 100644 (file)
@@ -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;
index 4dc8eede74ad0ba2e52704df692613c8a6cd54ea..eed1ee97fe9714475dbb7839709faad502b3f5f3 100644 (file)
@@ -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.  */
index 0b915b5632a46eb398fe07a37ac1e68d8e6bb779..8e740aedc997f03267e5c876477f732e44f4dc13 100644 (file)
@@ -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);
              }
          }
index 62d90e5770b5d58c127b0a718df0f874f6231a15..a9235246e938eb70da44bd52b8e67e746c5e2913 100644 (file)
@@ -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.
index 2e93d26fe853b56658023178aead5f5a4baae64b..f10b162d2745e6a45f1ced0d7fa0abfdd44cf5ca 100644 (file)
@@ -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**
 
index eaaa288cb4a2d8ab899a8919ffe05b0c65d8e21a..1871f601b6ef4d38e892234c624379169081d6d3 100644 (file)
@@ -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;
index 6359674d4471adbb5398bf6e2cdae84d326d3b07..d547bb78626c7e0cca71df46459f34165f9099a1 100644 (file)
@@ -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.
index d89755cea27ced17609bbf772b2acc7491971a9f..3c4c84a09398170c7fee34250ffc0fb5d61e5d58 100644 (file)
@@ -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;
index 7d25e185129fe3960c7480fe19965bc7c92031f0..ea6f87f60ec6dd36c15dd95f15af497c0d00674f 100644 (file)
@@ -31,6 +31,7 @@ import dmd.location;
 import dmd.mtype;
 import dmd.common.outbuffer;
 import dmd.tokens;
+import dmd.typesem : isAssignable;
 import dmd.visitor;
 
 /**********************************************
index b71b6c40aeb0af8de7c61b7ec514379717a0c57b..5cde5b334c5ed23d3cb47a3ed44f8eea39b11281 100644 (file)
@@ -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`
index a03e7bbdc0e5ce2c911c46655161ae96c7fa2722..97dfa5ef683f78a0ac80db471cd5af8017934fbc 100644 (file)
@@ -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);
         }
     }
 
index 96acf0552931c780d1967ef0f284cdc3db676da0..bd4aa197ab93e34f26eb4a148f15de1fd682128f 100644 (file)
@@ -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;
index a21f5a05be78f86d50f4e69378151f926e9d086e..f3f359ee3129250ce9a52c31542fa5620497b3a7 100644 (file)
@@ -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);
index 72cb3e7d91c8faf127e3683458d4930fdec392ff..5732f9d385f6bcfbe27c6b82ebc2a56399e8d2c1 100644 (file)
  */
 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";
 }
 
 ///
index 01fc3774002d1c01526c5beb6fd54eacdc64cdcd..a5340ef8058c1130e3b85404a176ca53e91dcbc5 100644 (file)
@@ -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");
 }
index 62aca7a8014a3eb4b0514c0652499d1c638df620..9630f122015768f549b7c058a029fbc77e9ce86d 100644 (file)
@@ -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);
index 31c01f1853e6e0029d324069cbc6196e6312425e..96c7db9086cd8ffb3e49a146eb24b8a472df1ad7 100644 (file)
@@ -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
index fad9c9ac44f69b050cf01b5a894f6523460d869a..1e0024e92cef3ee2cbdb4c7cda0ac353b20969dd 100644 (file)
@@ -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();
         }
index 70369ba20c7afb9f8df3fd5d4f199dfe6b5ebb6a..4a6e5195f5fda4f28879291c50911607a74e00bc 100644 (file)
@@ -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
index 2f577cea92f27678e95f5b241ea9e3c010e16632..58a00341d4e0b1b97c4f0df2dddacdadf15c2e42 100644 (file)
@@ -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
index b61137ff20fa5925e087371ef6754dcfdc6eb811..ce474c9bbef16d7938e95b0bdd83ff30548abfe7 100644 (file)
@@ -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)
 {
     /***********************************************************
index 9e35dde296973baa6038bf26be4b981f4ca83662..4ac8b1f44711a6b9bfef2f8202e19b56c4898dce 100644 (file)
@@ -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);
index 51ac939ade09f77cb96fb8c8b999b85c7572a555..2129aa00cf600cf612f7127e9c928c819cd795ba 100644 (file)
@@ -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);
index 25e5c31dcc6fd32938b2d66d8529703b59cb8483..1bda9948cbf4d0f32996ee5eba8d031166f52284 100644 (file)
@@ -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)
     {
index 74ea20cb56f8dfc63fe42988b728cb4896c1503e..7a160770e144e348eb22ef7c7aad453372a25d93 100644 (file)
@@ -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();
index 1f5dca66b598345ba8af8448d013891c62ea113a..f37c83cbca7c15224ee7e063dd58ec8b2c9e8044 100644 (file)
@@ -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);
index e1a4ab6d6a3da2a081f43a3bdc65bf81b838bdcc..8b7d5e501235fa3ff8101ef7454d4bb15bb9a385 100644 (file)
@@ -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)
     {
index bbd74a4da2701aa45bd1a6b9477e18dc303c2fb7..66810cb35fdb5a83077b1b0965824489749d1836 100644 (file)
@@ -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
index 543c0e22e39f9212d03d0cd7c36e8b42a73d9263..eaf3d993b019242098e293b688c6793161f52e79 100644 (file)
@@ -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]);
                 }
             }
         }
index 33539d4bf0b803b758f551779dae299de25643a7..08400433d099333475f28ace2786d5295bfed72e 100644 (file)
@@ -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();
index c70e7d381b56623fd1b530c749181b82df0d40f7..879ffc11268e97109aa98f09545e3e122c2d946b 100644 (file)
@@ -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
index 5aef3585cafe271c7ecbdfb5639e0c89a4f2a1f6..b565f159aec82183740413773eab78262e0c4bfb 100644 (file)
@@ -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;
-    }
 }
 
 
index e65068c7622b4eb056e1de5dc529534e1574bbde..2059978ef8a5ce8bd834f666506cb177d334d499 100644 (file)
@@ -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;
index 77e7dabcb6cfac4e770148ed4a67858fec915787..259817e6038d5a0387dbbd1191c7d634808b4d94 100644 (file)
@@ -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();
index 43fb0b72e4ba6547432c86ca5cdb817a4557ef18..2c4996573d92693820932e4f0c7d8cee135da913 100644 (file)
@@ -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;
index 16e192da20ff75d46fa2521623e248d4bf79bcf4..b2a61dda441bd4d83245074f119004a0e4a39820 100644 (file)
@@ -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.
  */
index 3946c25c9f0375ad125dae4ba6a2e710e7c382b0..4cb7d1ac5dda71bebd96ae0d088ca56e89fad427 100644 (file)
@@ -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);
index 26528e9f517836e481cf9ee2423d31772ca328d8..1561ebb0b7c46d92cad39307f4a10ca57c175815 100644 (file)
@@ -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;
index 6d5d3022a7fcf57fb5ab0aae50bd08e77a38bc69..2b4d3b52d89e6578e15cf91ce2421abd606ef20a 100644 (file)
@@ -59,7 +59,7 @@ public:
     bool isSpecial() const;
 
 
-    Symbol *sinit;
+    void *sinit;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
index 6d325e44c9641342900fcc3bb82122b085c8328d..cdf8a61bd88ad0418d39db567cb0e870d71de7d3 100644 (file)
@@ -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);
 
index a0c5472375e5b9e826fe2ea41ea5d0c7945ce2a4..d3473f691d55ebbabe9c18da7b3d57f529a96ecb 100644 (file)
@@ -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;
index c29484d9e7bfe9841059b2bdbd26d64864ab19f6..4d873ccecdc0b0daa621f873623094bebcf65fb0 100644 (file)
@@ -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);
index 6b4298b3308b232aef0a515c14e9cd2fbc081550..7c92ebe66c44827c9d0d271740bb4da27695f6bb 100644 (file)
@@ -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); }
 };
 
index 2576d7c0d056978c3de6062cfab8c86dfda88921..68fe3dca41c0621837797944e34c689910d2a2dc 100644 (file)
@@ -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;
+}
index 2ccb1d25497d0418bb33d43f7098feefa4672d11..8116dda79d74f504ea1d6badb234372ac834940d 100644 (file)
@@ -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_ext);
-        if (checkLocal && FileName.exists(sd) == 1)
-            return sd;
-        scope(exit) FileName.free(sd.ptr);
-
-        if (checkLocal)
+        // List of extensions to match, in order of precedence.
+        const(char)[][2] extensions = [
+            FileName.forceExt(filename, hdr_ext),
+            FileName.forceExt(filename, mars_ext),
+        ];
+        const(char)[][3] importCextensions = [
+            FileName.forceExt(filename, i_ext),
+            FileName.forceExt(filename, h_ext),
+            FileName.forceExt(filename, c_ext),
+        ];
+        scope(exit)
         {
-            if (pathCache.isExistingPath(filename))
-            {
-                /* The filename exists but it's a directory.
-                 * Therefore, the result should be: filename/package.d
-                 * iff filename/package.d is a file
-                 */
-                const ni = FileName.combine(filename, package_di);
-                if (FileName.exists(ni) == 1)
-                    return ni;
-                FileName.free(ni.ptr);
-
-                const n = FileName.combine(filename, package_d);
-                if (FileName.exists(n) == 1)
-                    return n;
-                FileName.free(n.ptr);
-            }
+            foreach (ext; extensions)
+                FileName.free(ext.ptr);
+            foreach (ext; importCextensions)
+                FileName.free(ext.ptr);
         }
 
-        if (FileName.absolute(filename))
-            return null;
-        if (!pathsInfo.length)
-            return null;
-        foreach (pathIndex, entry; pathsInfo)
+        /* Search for all combinations of files (mod.di, mod.d, mod/package.d, ...)
+         * within in directory `path`.
+         */
+        const(char)[] lookForSourceFileInPath(const char[] path)
         {
-            const p = entry.path.toDString();
-
-            const(char)[] n = FileName.combine(p, sdi);
-
-            if (!pathCache.pathExists(n))
+            // When checking for modules locally, combine won't allocate a new string.
+            bool checkLocal = path is null;
+            void freePath(const(char)[] p)
             {
-                FileName.free(n.ptr);
-                continue; // no need to check for anything else.
+                if (checkLocal)
+                    return;
+                FileName.free(p.ptr);
             }
-            if (FileName.exists(n) == 1)
-                return n;
 
-            FileName.free(n.ptr);
+            const p = FileName.combine(path, filename);
+            scope(exit) freePath(p);
+            if (!pathCache.pathExists(p))
+                return null; // no need to check for anything else.
 
-            n = FileName.combine(p, sd);
-            if (FileName.exists(n) == 1)
+            // Search for any file matching {path}/{file}.{ext}
+            foreach (ext; extensions)
             {
-                whichPathFoundThis = pathIndex;
-                return n;
+                const file = FileName.combine(path, ext);
+                if (FileName.exists(file) == 1)
+                {
+                    import dmd.root.rmem : xarraydup;
+                    return checkLocal ? file.xarraydup : file;
+                }
+                freePath(file);
             }
-            FileName.free(n.ptr);
-
-            n = FileName.combine(p, FileName.sansExt(filename));
-            scope(exit) FileName.free(n.ptr);
 
-            // also cache this if we are looking for package.d[i]
+            const n = FileName.combine(path, FileName.sansExt(filename));
+            scope(exit) freePath(n);
             if (pathCache.isExistingPath(n))
             {
-                const n2i = FileName.combine(n, package_di);
-                if (FileName.exists(n2i) == 1)
-                {
-                    whichPathFoundThis = pathIndex;
-                    return n2i;
-                }
+                /* The filename exists but it's a directory.
+                 * Therefore, the result should be: filename/package.d
+                 * iff filename/package.d is a file
+                 */
+                const ni = FileName.combine(n, package_di);
+                if (FileName.exists(ni) == 1)
+                    return ni;
+                FileName.free(ni.ptr);
+
+                const nd = FileName.combine(n, package_d);
+                if (FileName.exists(nd) == 1)
+                    return nd;
+                FileName.free(nd.ptr);
+            }
 
-                FileName.free(n2i.ptr);
-                const n2 = FileName.combine(n, package_d);
-                if (FileName.exists(n2) == 1)
+            /* Search for any file with importC extensions after all attempts
+               to find a D module/package in the path are exhausted.  */
+            foreach (ext; importCextensions)
+            {
+                const file = FileName.combine(path, ext);
+                if (FileName.exists(file) == 1)
                 {
-                    whichPathFoundThis = pathIndex;
-                    return n2;
+                    import dmd.root.rmem : xarraydup;
+                    return checkLocal ? file.xarraydup : file;
                 }
-                FileName.free(n2.ptr);
+                freePath(file);
             }
+            return null;
         }
 
-        /* ImportC: No D modules found, now search along paths[] for .i file, then .c file.
-         */
-        const si = FileName.forceExt(filename, i_ext);
-        if (FileName.exists(si) == 1)
-            return si;
-        scope(exit) FileName.free(si.ptr);
-
-        const sc = FileName.forceExt(filename, c_ext);
-        if (FileName.exists(sc) == 1)
-            return sc;
-        scope(exit) FileName.free(sc.ptr);
-        foreach (pathIndex, entry; pathsInfo)
+        // First see if module is found in any search paths.
+        if (!FileName.absolute(filename))
         {
-            const p = entry.path.toDString();
-
-            const(char)[] n = FileName.combine(p, si);
-            if (FileName.exists(n) == 1)
-            {
-                whichPathFoundThis = pathIndex;
-                return n;
-            }
-            FileName.free(n.ptr);
-
-            n = FileName.combine(p, sc);
-            if (FileName.exists(n) == 1)
+            foreach (pathIndex, entry; pathsInfo)
             {
-                whichPathFoundThis = pathIndex;
-                return n;
+                if (auto s = lookForSourceFileInPath(entry.path.toDString()))
+                {
+                    whichPathFoundThis = pathIndex;
+                    return s;
+                }
             }
-            FileName.free(n.ptr);
         }
+        // No modules found, check for the module locally.
+        if (auto s = lookForSourceFileInPath(null))
+            return s;
+
         return null;
     }
 
index 6a573810a4b97f3f83dd5672ce2def0ec90d7b00..17044233ca8576bf9f68a5be7483b0f2dba38c3d 100644 (file)
@@ -24,7 +24,6 @@ import dmd.aggregate;
 import dmd.arraytypes;
 import dmd.astenums;
 import dmd.blockexit;
-import dmd.gluelayer;
 import dmd.dcast;
 import dmd.dclass;
 import dmd.declaration;
@@ -36,7 +35,7 @@ import dmd.dsymbol;
 import dmd.dtemplate;
 import dmd.escape;
 import dmd.expression;
-import dmd.funcsem : overloadApply;
+import dmd.funcsem : isUnique;
 import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
@@ -240,7 +239,7 @@ extern (C++) class FuncDeclaration : Declaration
     // Things that should really go into Scope
 
     VarDeclaration nrvo_var;            /// variable to replace with shidden
-    Symbol* shidden;                    /// hidden pointer passed to function
+    void* shidden;                    /// hidden pointer passed to function
 
     ReturnStatements* returns;
 
@@ -249,7 +248,7 @@ extern (C++) class FuncDeclaration : Declaration
     version (MARS)
     {
         VarDeclarations* alignSectionVars;  /// local variables with alignment needs larger than stackAlign
-        Symbol* salignSection;              /// pointer to aligned section, if any
+        void* salignSection;              /// pointer to aligned section, if any
     }
 
     /// set if this is a known, builtin function we can evaluate at compile time
@@ -820,45 +819,18 @@ extern (C++) class FuncDeclaration : Declaration
     bool addPreInvariant()
     {
         auto ad = isThis();
-        return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked());
+        return (ad && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked);
     }
 
     bool addPostInvariant()
     {
         auto ad = isThis();
-        return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked());
+        return (ad && ad.inv && global.params.useInvariants == CHECKENABLE.on && (visibility.kind == Visibility.Kind.protected_ || visibility.kind == Visibility.Kind.public_ || visibility.kind == Visibility.Kind.export_) && !this.isNaked);
     }
 
     override const(char)* kind() const
     {
-        return this.isGenerated() ? "generated function" : "function";
-    }
-
-    /********************************************
-     * Returns:
-     *  true if there are no overloads of this function
-     */
-    final bool isUnique() const
-    {
-        bool result = false;
-        overloadApply(cast() this, (Dsymbol s)
-        {
-            auto f = s.isFuncDeclaration();
-            auto td = s.isTemplateDeclaration();
-            if (!f && !td)
-                return 0;
-            if (result)
-            {
-                result = false;
-                return 1; // ambiguous, done
-            }
-            else
-            {
-                result = true;
-                return 0;
-            }
-        });
-        return result;
+        return this.isGenerated ? "generated function" : "function";
     }
 
     /*******************************
@@ -1373,7 +1345,8 @@ extern (C++) final class CtorDeclaration : FuncDeclaration
 
     override const(char)* kind() const
     {
-        return isCpCtor ? "copy constructor" : "constructor";
+        return isCpCtor ? "copy constructor" :
+             isMoveCtor ? "move constructor" : "constructor";
     }
 
     override bool isVirtual() const
index 10f6f154b2ff97f9d85543c0e809d3912539cec3..0d147dd0ddcde29e9fc58421a34d4bb98e581578 100644 (file)
@@ -20,7 +20,6 @@ import dmd.arraytypes;
 import dmd.astenums;
 import dmd.attrib;
 import dmd.blockexit;
-import dmd.gluelayer;
 import dmd.dcast;
 import dmd.dclass;
 import dmd.declaration;
@@ -103,7 +102,7 @@ public:
     override void visit(TryFinallyStatement s)
     {
         DtorExpStatement des;
-        if (fd.isNRVO() && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
+        if (fd.isNRVO && s.finalbody && (des = s.finalbody.isDtorExpStatement()) !is null &&
             fd.nrvo_var == des.var)
         {
             if (!(global.params.useExceptions && ClassDeclaration.throwable))
@@ -138,11 +137,10 @@ public:
                 handler = new CompoundStatement(Loc.initial, handler, ts);
             }
 
-            auto catches = new Catches();
             auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
             ctch.internalCatch = true;
             ctch.catchSemantic(sc); // Run semantic to resolve identifier '__o'
-            catches.push(ctch);
+            auto catches = new Catches(ctch);
 
             Statement s2 = new TryCatchStatement(Loc.initial, s._body, catches);
             fd.hasNoEH = false;
@@ -176,6 +174,33 @@ extern (C++) bool onlyOneMain(FuncDeclaration fd)
     return true;
 }
 
+/********************************************
+ * Returns:
+ *  true if there are no overloads of this function
+ */
+bool isUnique(const FuncDeclaration fd)
+{
+    bool result = false;
+    overloadApply(cast() fd, (Dsymbol s)
+    {
+        auto f = s.isFuncDeclaration();
+        auto td = s.isTemplateDeclaration();
+        if (!f && !td)
+            return 0;
+        if (result)
+        {
+            result = false;
+            return 1; // ambiguous, done
+        }
+        else
+        {
+            result = true;
+            return 0;
+        }
+    });
+    return result;
+}
+
 /**********************************
  * Main semantic routine for functions.
  */
@@ -224,7 +249,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
     AggregateDeclaration ad = funcdecl.isThis();
     // Don't nest structs b/c of generated methods which should not access the outer scopes.
     // https://issues.dlang.org/show_bug.cgi?id=16627
-    if (ad && !funcdecl.isGenerated())
+    if (ad && !funcdecl.isGenerated)
     {
         funcdecl.storage_class |= ad.storage_class & (STC.TYPECTOR | STC.synchronized_);
         ad.makeNested();
@@ -409,7 +434,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl)
 
         sc.linkage = funcdecl._linkage;
 
-        if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested()))
+        if (!tf.isNaked && !(funcdecl.isThis() || funcdecl.isNested()))
         {
             import core.bitop : popcnt;
             auto mods = MODtoChars(tf.mod);
@@ -1366,7 +1391,7 @@ bool functionSemantic3(FuncDeclaration fd)
             return false;
     }
 
-    return !fd.errors && !fd.hasSemantic3Errors();
+    return !fd.errors && !fd.hasSemantic3Errors;
 }
 
 // called from semantic3
@@ -1557,7 +1582,7 @@ int findVtblIndex(FuncDeclaration fd, Dsymbol[] vtbl)
                              fdv.toPrettyChars(), fdv.type.toTypeFunction().parameterList.parametersTypeToChars(),
                               fd.toPrettyChars(),  fd.type.toTypeFunction().parameterList.parametersTypeToChars(), fd.type.modToChars());
 
-                const char* where = fd.type.isNaked() ? "parameters" : "type";
+                const char* where = fd.type.isNaked ? "parameters" : "type";
                 deprecationSupplemental(fd.loc, "Either remove `override`, or adjust the `const` qualifiers of the "
                                         ~ "overriding function %s", where);
             }
@@ -2456,7 +2481,7 @@ int getLevelAndCheck(FuncDeclaration fd, Loc loc, Scope* sc, FuncDeclaration tar
  * Returns:
  *  true if can
  */
-bool canInferAttributes(FuncDeclaration fd, Scope* sc)
+private bool canInferAttributes(FuncDeclaration fd, Scope* sc)
 {
     if (!fd.fbody)
         return false;
@@ -2487,90 +2512,6 @@ bool canInferAttributes(FuncDeclaration fd, Scope* sc)
     return false;
 }
 
-/*********************************************
- * 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.
- */
-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;
-}
-
 /****************************************************
  * Check whether result variable can be built.
  * Returns:
@@ -2664,8 +2605,7 @@ Statement mergeFrequire(FuncDeclaration fd, Statement sf, Expressions* params)
         Statement s2 = new ExpStatement(fd.loc, e);
         auto c = new Catch(fd.loc, getThrowable(), null, sf);
         c.internalCatch = true;
-        auto catches = new Catches();
-        catches.push(c);
+        auto catches = new Catches(c);
         sf = new TryCatchStatement(fd.loc, s2, catches);
     }
     return sf;
@@ -2728,8 +2668,7 @@ Statement mergeFrequireInclusivePreview(FuncDeclaration fd, Statement sf, Expres
         Statement s3 = new CompoundStatement(loc, s2, fail);
         auto c = new Catch(loc, getThrowable(), id, s3);
         c.internalCatch = true;
-        auto catches = new Catches();
-        catches.push(c);
+        auto catches = new Catches(c);
         sf = new TryCatchStatement(loc, sf, catches);
     }
     return sf;
@@ -3164,7 +3103,7 @@ extern (D) void checkMain(FuncDeclaration fd)
 extern (D) bool checkNRVO(FuncDeclaration fd)
 {
     //printf("checkNRVO*() %s\n", fd.ident.toChars());
-    if (!fd.isNRVO() || fd.returns is null)
+    if (!fd.isNRVO || fd.returns is null)
         return false;
 
     auto tf = fd.type.toTypeFunction();
@@ -3310,7 +3249,7 @@ extern (D) PURE isPureBypassingInference(FuncDeclaration fd)
  * Returns:
  *      true if reference to `tb` is isolated from reference to `ta`
  */
-bool traverseIndirections(Type ta, Type tb)
+private bool traverseIndirections(Type ta, Type tb)
 {
     //printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars());
 
@@ -3401,7 +3340,7 @@ bool traverseIndirections(Type ta, Type tb)
  *    which could have come from the function's parameters, mutable
  *    globals, or uplevel functions.
  */
-bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t)
+private bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t)
 {
     //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars());
     assert(t);
@@ -3842,3 +3781,34 @@ extern (D) bool checkNestedReference(VarDeclaration vd, Scope* sc, Loc loc)
 
     return false;
 }
+
+/**********************
+ * Check to see if array bounds checking code has to be generated
+ *
+ * Params:
+ *  fd = function for which code is to be generated
+ * Returns:
+ *  true if do array bounds checking for the given function
+ */
+bool arrayBoundsCheck(FuncDeclaration fd)
+{
+    final switch (global.params.useArrayBounds)
+    {
+    case CHECKENABLE.off:
+        return false;
+    case CHECKENABLE.on:
+        return true;
+    case CHECKENABLE.safeonly:
+        {
+            if (fd)
+            {
+                Type t = fd.type;
+                if (t.ty == Tfunction && (cast(TypeFunction)t).trust == TRUST.safe)
+                    return true;
+            }
+            return false;
+        }
+    case CHECKENABLE._default:
+        assert(0);
+    }
+}
index 624738e4e462e7d4670bc83e36e5952cd33ab211..eb8d05533193ad61e216e5ff11d84127e38b6583 100644 (file)
@@ -154,6 +154,7 @@ extern(C++) struct Verbose
 
 extern (C++) struct ImportPathInfo {
     const(char)* path; // char*'s of where to look for import modules
+    bool isOutOfBinary; // Will any module found from this path be out of binary?
 }
 
 /// Put command line switches in here
@@ -189,6 +190,11 @@ extern (C++) struct Param
     Help help;
     Verbose v;
 
+    // Editions
+    Edition edition;             // edition year
+    Edition[const(char)*] editionFiles; // Edition corresponding to a filespec
+
+
     // Options for `-preview=/-revert=`
     FeatureState useDIP25 = FeatureState.enabled; // implement https://wiki.dlang.org/DIP25
     FeatureState useDIP1000;     // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
@@ -284,6 +290,7 @@ enum hdr_ext  = "di";       // for D 'header' import files
 enum json_ext = "json";     // for JSON files
 enum map_ext  = "map";      // for .map files
 enum c_ext    = "c";        // for C source files
+enum h_ext    = "h";        // for C header source files
 enum i_ext    = "i";        // for preprocessed C source file
 
 /**
@@ -386,13 +393,10 @@ extern (C++) struct Global
         errorSinkNull = new ErrorSinkNull;
 
         this.fileManager = new FileManager();
+
         version (MARS)
         {
             compileEnv.vendor = "Digital Mars D";
-
-            // -color=auto is the default value
-            import dmd.console : detectTerminal, detectColorPreference;
-            params.v.color = detectTerminal() && detectColorPreference();
         }
         else version (IN_GCC)
         {
@@ -401,12 +405,10 @@ extern (C++) struct Global
         else version (IN_LLVM)
         {
             compileEnv.vendor = "LDC";
-
-            import dmd.console : detectTerminal;
-            params.v.color = detectTerminal();
         }
+        else
+            static assert(0, "unknown vendor");
 
-        params.v.errorPrintMode = ErrorPrintMode.printErrorContext; // Enable error context globally by default
         compileEnv.versionNumber = parseVersionNumber(versionString());
 
         /* Initialize date, time, and timestamp
index 62a575e322e3e55f791fe63984a001994a911f62..0427347e487b7fbb2db51176168ce4303d64cdc3 100644 (file)
@@ -1,4 +1,3 @@
-
 /* Compiler implementation of the D programming language
  * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
  * written by Walter Bright
@@ -159,9 +158,17 @@ struct Verbose
 struct ImportPathInfo
 {
     const char* path;
+    d_bool isOutOfBinary;
 
-    ImportPathInfo() : path(NULL) { }
-    ImportPathInfo(const char* p) : path(p) { }
+    ImportPathInfo() :
+        path(),
+        isOutOfBinary()
+    {
+    }
+    ImportPathInfo(const char* path, d_bool isOutOfBinary = false) :
+        path(path),
+        isOutOfBinary(isOutOfBinary)
+        {}
 };
 
 // Put command line switches in here
@@ -196,6 +203,9 @@ struct Param
     Help help;
     Verbose v;
 
+    unsigned short edition;      // edition year
+    void* editionFiles;          // Edition corresponding to a filespec
+
     // Options for `-preview=/-revert=`
     FeatureState useDIP25;       // implement https://wiki.dlang.org/DIP25
     FeatureState useDIP1000;     // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
diff --git a/gcc/d/dmd/gluelayer.d b/gcc/d/dmd/gluelayer.d
deleted file mode 100644 (file)
index ba7c1e9..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Declarations for back-end functions that the front-end invokes.
- *
- * This 'glues' either the DMC or GCC back-end to the front-end.
- *
- * Copyright:   Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
- * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
- * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d, _gluelayer.d)
- * Documentation:  https://dlang.org/phobos/dmd_gluelayer.html
- * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/gluelayer.d
- */
-
-module dmd.gluelayer;
-
-import dmd.dmodule;
-import dmd.dscope;
-import dmd.dsymbol;
-import dmd.mtype;
-import dmd.statement;
-import dmd.root.file;
-
-version (NoBackend)
-{
-    struct Symbol;
-    struct code;
-    struct block;
-    struct BlockState;
-    struct elem;
-    struct TYPE;
-    alias type = TYPE;
-
-    extern(C++) abstract class ObjcGlue
-    {
-        static void initialize() {}
-    }
-}
-else version (IN_GCC)
-{
-    extern (C++) union tree_node;
-
-    alias Symbol = tree_node;
-    alias code = tree_node;
-    alias type = tree_node;
-
-    // stubs
-    extern(C++) abstract class ObjcGlue
-    {
-        static void initialize() {}
-    }
-}
-else
-{
-    public import dmd.backend.cc : block, BlockState, Symbol;
-    public import dmd.backend.type : type;
-    public import dmd.backend.el : elem;
-    public import dmd.backend.x86.code_x86 : code;
-    public import dmd.objc_glue : ObjcGlue;
-}
index 0f8c8c67b927c32472945ac8796c7eade2793a68..73f430e3db8795b16d729fba64fb3e34197d6389 100644 (file)
@@ -175,7 +175,7 @@ private void truncateForError(ref OutBuffer buf, size_t maxLength)
     if (buf.length > maxLength)
     {
         buf.setsize(maxLength - 3);
-        buf.writestring("...");
+        buf.put("...");
     }
 }
 
@@ -192,7 +192,7 @@ public const(char)* toChars(const Statement s)
     HdrGenState hgs;
     OutBuffer buf;
     toCBuffer(s, buf, hgs);
-    buf.writeByte(0);
+    buf.put(cast(ubyte)0);
     return buf.extractSlice().ptr;
 }
 
@@ -250,9 +250,9 @@ public const(char)* toChars(const Dsymbol d)
     if (auto tid = d.isTypeInfoDeclaration())
     {
         OutBuffer buf;
-        buf.writestring("typeid(");
-        buf.writestring(tid.tinfo.toChars());
-        buf.writeByte(')');
+        buf.put("typeid(");
+        buf.put(tid.tinfo.toChars());
+        buf.put(')');
         return buf.extractChars();
     }
 
@@ -288,25 +288,25 @@ void moduleToBuffer2(Module m, ref OutBuffer buf, ref HdrGenState hgs)
     {
         if (m.userAttribDecl)
         {
-            buf.writestring("@(");
+            buf.put("@(");
             argsToBuffer(m.userAttribDecl.atts, buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
             buf.writenl();
         }
         if (m.md.isdeprecated)
         {
             if (m.md.msg)
             {
-                buf.writestring("deprecated(");
+                buf.put("deprecated(");
                 m.md.msg.expressionToBuffer(buf, hgs);
-                buf.writestring(") ");
+                buf.put(") ");
             }
             else
-                buf.writestring("deprecated ");
+                buf.put("deprecated ");
         }
-        buf.writestring("module ");
-        buf.writestring(m.md.toChars());
-        buf.writeByte(';');
+        buf.put("module ");
+        buf.put(m.md.toChars());
+        buf.put(';');
         buf.writenl();
     }
 
@@ -326,7 +326,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitError(ErrorStatement s)
     {
-        buf.writestring("__error__");
+        buf.put("__error__");
         buf.writenl();
     }
 
@@ -341,7 +341,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
         }
         if (s.exp)
             s.exp.expressionToBuffer(buf, hgs);
-        buf.writeByte(';');
+        buf.put(';');
         if (!hgs.forStmtInit)
             buf.writenl();
     }
@@ -353,9 +353,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitMixin(MixinStatement s)
     {
-        buf.writestring("mixin(");
+        buf.put("mixin(");
         argsToBuffer(s.exps, buf, hgs, null);
-        buf.writestring(");");
+        buf.put(");");
         if (!hgs.forStmtInit)
             buf.writenl();
     }
@@ -392,14 +392,14 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
                 anywritten = true;
             }
         }
-        buf.writeByte(';');
+        buf.put(';');
         if (!hgs.forStmtInit)
             buf.writenl();
     }
 
     void visitUnrolledLoop(UnrolledLoopStatement s)
     {
-        buf.writestring("/*unrolled*/ {");
+        buf.put("/*unrolled*/ {");
         buf.writenl();
         buf.level++;
         foreach (sx; *s.statements)
@@ -408,19 +408,19 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
                 sx.statementToBuffer(buf, hgs);
         }
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
     void visitScope(ScopeStatement s)
     {
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         if (s.statement)
             s.statement.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
@@ -433,21 +433,21 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
             if (!p.type && !stc)
                 stc = STC.auto_;
             if (stcToBuffer(buf, stc))
-                buf.writeByte(' ');
+                buf.put(' ');
             if (p.type)
                 typeToBuffer(p.type, p.ident, buf, hgs);
             else
-                buf.writestring(p.ident.toString());
-            buf.writestring(" = ");
+                buf.put(p.ident.toString());
+            buf.put(" = ");
         }
         condition.expressionToBuffer(buf, hgs);
     }
 
     void visitWhile(WhileStatement s)
     {
-        buf.writestring("while (");
+        buf.put("while (");
         printConditionAssignment(s.param, s.condition);
-        buf.writeByte(')');
+        buf.put(')');
         buf.writenl();
         if (s._body)
             s._body.statementToBuffer(buf, hgs);
@@ -455,19 +455,19 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitDo(DoStatement s)
     {
-        buf.writestring("do");
+        buf.put("do");
         buf.writenl();
         if (s._body)
             s._body.statementToBuffer(buf, hgs);
-        buf.writestring("while (");
+        buf.put("while (");
         s.condition.expressionToBuffer(buf, hgs);
-        buf.writestring(");");
+        buf.put(");");
         buf.writenl();
     }
 
     void visitFor(ForStatement s)
     {
-        buf.writestring("for (");
+        buf.put("for (");
         if (s._init)
         {
             hgs.forStmtInit++;
@@ -475,96 +475,96 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
             hgs.forStmtInit--;
         }
         else
-            buf.writeByte(';');
+            buf.put(';');
         if (s.condition)
         {
-            buf.writeByte(' ');
+            buf.put(' ');
             s.condition.expressionToBuffer(buf, hgs);
         }
-        buf.writeByte(';');
+        buf.put(';');
         if (s.increment)
         {
-            buf.writeByte(' ');
+            buf.put(' ');
             s.increment.expressionToBuffer(buf, hgs);
         }
-        buf.writeByte(')');
+        buf.put(')');
         buf.writenl();
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         if (s._body)
             s._body.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
     void foreachWithoutBody(ForeachStatement s)
     {
-        buf.writestring(Token.toString(s.op));
-        buf.writestring(" (");
+        buf.put(Token.toString(s.op));
+        buf.put(" (");
         foreach (i, p; *s.parameters)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             if (stcToBuffer(buf, p.storageClass))
-                buf.writeByte(' ');
+                buf.put(' ');
             if (p.type)
                 typeToBuffer(p.type, p.ident, buf, hgs);
             else
-                buf.writestring(p.ident.toString());
+                buf.put(p.ident.toString());
         }
-        buf.writestring("; ");
+        buf.put("; ");
         s.aggr.expressionToBuffer(buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         buf.writenl();
     }
 
     void visitForeach(ForeachStatement s)
     {
         foreachWithoutBody(s);
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         if (s._body)
             s._body.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
     void foreachRangeWithoutBody(ForeachRangeStatement s)
     {
-        buf.writestring(Token.toString(s.op));
-        buf.writestring(" (");
+        buf.put(Token.toString(s.op));
+        buf.put(" (");
         if (s.param.type)
             typeToBuffer(s.param.type, s.param.ident, buf, hgs);
         else
-            buf.writestring(s.param.ident.toString());
-        buf.writestring("; ");
+            buf.put(s.param.ident.toString());
+        buf.put("; ");
         s.lwr.expressionToBuffer(buf, hgs);
-        buf.writestring(" .. ");
+        buf.put(" .. ");
         s.upr.expressionToBuffer(buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         buf.writenl();
     }
 
     void visitForeachRange(ForeachRangeStatement s)
     {
         foreachRangeWithoutBody(s);
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         if (s._body)
             s._body.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
     void visitStaticForeach(StaticForeachStatement s)
     {
-        buf.writestring("static ");
+        buf.put("static ");
         if (s.sfe.aggrfe)
         {
             visitForeach(s.sfe.aggrfe);
@@ -583,9 +583,9 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitIf(IfStatement s)
     {
-        buf.writestring("if (");
+        buf.put("if (");
         printConditionAssignment(s.param, s.condition);
-        buf.writeByte(')');
+        buf.put(')');
         buf.writenl();
         if (s.ifbody.isScopeStatement())
         {
@@ -599,14 +599,14 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
         }
         if (s.elsebody)
         {
-            buf.writestring("else");
+            buf.put("else");
             if (!s.elsebody.isIfStatement())
             {
                 buf.writenl();
             }
             else
             {
-                buf.writeByte(' ');
+                buf.put(' ');
             }
             if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement())
             {
@@ -625,52 +625,52 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
     {
         s.condition.conditionToBuffer(buf, hgs);
         buf.writenl();
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         if (s.ifbody)
             s.ifbody.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
         if (s.elsebody)
         {
-            buf.writestring("else");
+            buf.put("else");
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.level++;
             buf.writenl();
             s.elsebody.statementToBuffer(buf, hgs);
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
         }
         buf.writenl();
     }
 
     void visitPragma(PragmaStatement s)
     {
-        buf.writestring("pragma (");
-        buf.writestring(s.ident.toString());
+        buf.put("pragma (");
+        buf.put(s.ident.toString());
         if (s.args && s.args.length)
         {
-            buf.writestring(", ");
+            buf.put(", ");
             argsToBuffer(s.args, buf, hgs);
         }
-        buf.writeByte(')');
+        buf.put(')');
         if (s._body)
         {
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.writenl();
             buf.level++;
             s._body.statementToBuffer(buf, hgs);
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
             buf.writenl();
         }
         else
         {
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
         }
     }
@@ -682,20 +682,20 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitSwitch(SwitchStatement s)
     {
-        buf.writestring(s.isFinal ? "final switch (" : "switch (");
+        buf.put(s.isFinal ? "final switch (" : "switch (");
         printConditionAssignment(s.param, s.condition);
-        buf.writeByte(')');
+        buf.put(')');
         buf.writenl();
         if (s._body)
         {
             if (!s._body.isScopeStatement())
             {
-                buf.writeByte('{');
+                buf.put('{');
                 buf.writenl();
                 buf.level++;
                 s._body.statementToBuffer(buf, hgs);
                 buf.level--;
-                buf.writeByte('}');
+                buf.put('}');
                 buf.writenl();
             }
             else
@@ -707,109 +707,109 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitCase(CaseStatement s)
     {
-        buf.writestring("case ");
+        buf.put("case ");
         s.exp.expressionToBuffer(buf, hgs);
-        buf.writeByte(':');
+        buf.put(':');
         buf.writenl();
         s.statement.statementToBuffer(buf, hgs);
     }
 
     void visitCaseRange(CaseRangeStatement s)
     {
-        buf.writestring("case ");
+        buf.put("case ");
         s.first.expressionToBuffer(buf, hgs);
-        buf.writestring(": .. case ");
+        buf.put(": .. case ");
         s.last.expressionToBuffer(buf, hgs);
-        buf.writeByte(':');
+        buf.put(':');
         buf.writenl();
         s.statement.statementToBuffer(buf, hgs);
     }
 
     void visitDefault(DefaultStatement s)
     {
-        buf.writestring("default:");
+        buf.put("default:");
         buf.writenl();
         s.statement.statementToBuffer(buf, hgs);
     }
 
     void visitGotoDefault(GotoDefaultStatement s)
     {
-        buf.writestring("goto default;");
+        buf.put("goto default;");
         buf.writenl();
     }
 
     void visitGotoCase(GotoCaseStatement s)
     {
-        buf.writestring("goto case");
+        buf.put("goto case");
         if (s.exp)
         {
-            buf.writeByte(' ');
+            buf.put(' ');
             s.exp.expressionToBuffer(buf, hgs);
         }
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitSwitchError(SwitchErrorStatement s)
     {
-        buf.writestring("SwitchErrorStatement::toCBuffer()");
+        buf.put("SwitchErrorStatement::toCBuffer()");
         buf.writenl();
     }
 
     void visitReturn(ReturnStatement s)
     {
-        buf.writestring("return ");
+        buf.put("return ");
         if (s.exp)
             s.exp.expressionToBuffer(buf, hgs);
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitBreak(BreakStatement s)
     {
-        buf.writestring("break");
+        buf.put("break");
         if (s.ident)
         {
-            buf.writeByte(' ');
-            buf.writestring(s.ident.toString());
+            buf.put(' ');
+            buf.put(s.ident.toString());
         }
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitContinue(ContinueStatement s)
     {
-        buf.writestring("continue");
+        buf.put("continue");
         if (s.ident)
         {
-            buf.writeByte(' ');
-            buf.writestring(s.ident.toString());
+            buf.put(' ');
+            buf.put(s.ident.toString());
         }
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitSynchronized(SynchronizedStatement s)
     {
-        buf.writestring("synchronized");
+        buf.put("synchronized");
         if (s.exp)
         {
-            buf.writeByte('(');
+            buf.put('(');
             s.exp.expressionToBuffer(buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
         }
         if (s._body)
         {
-            buf.writeByte(' ');
+            buf.put(' ');
             s._body.statementToBuffer(buf, hgs);
         }
     }
 
     void visitWith(WithStatement s)
     {
-        buf.writestring("with (");
+        buf.put("with (");
         s.exp.expressionToBuffer(buf, hgs);
-        buf.writestring(")");
+        buf.put(")");
         buf.writenl();
         if (s._body)
             s._body.statementToBuffer(buf, hgs);
@@ -817,7 +817,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitTryCatch(TryCatchStatement s)
     {
-        buf.writestring("try");
+        buf.put("try");
         buf.writenl();
         if (s._body)
         {
@@ -834,37 +834,37 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
         }
         foreach (c; *s.catches)
         {
-            buf.writestring("catch");
+            buf.put("catch");
             if (c.type)
             {
-                buf.writeByte('(');
+                buf.put('(');
                 typeToBuffer(c.type, c.ident, buf, hgs);
-                buf.writeByte(')');
+                buf.put(')');
             }
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.writenl();
             buf.level++;
             if (c.handler)
                 c.handler.statementToBuffer(buf, hgs);
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
             buf.writenl();
         }
     }
 
     void visitTryFinally(TryFinallyStatement s)
     {
-        buf.writestring("try");
+        buf.put("try");
         buf.writenl();
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         s._body.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
-        buf.writestring("finally");
+        buf.put("finally");
         buf.writenl();
         if (s.finalbody.isScopeStatement())
         {
@@ -880,17 +880,17 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitScopeGuard(ScopeGuardStatement s)
     {
-        buf.writestring(Token.toString(s.tok));
-        buf.writeByte(' ');
+        buf.put(Token.toString(s.tok));
+        buf.put(' ');
         if (s.statement)
             s.statement.statementToBuffer(buf, hgs);
     }
 
     void visitThrow(ThrowStatement s)
     {
-        buf.writestring("throw ");
+        buf.put("throw ");
         s.exp.expressionToBuffer(buf, hgs);
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
@@ -904,16 +904,16 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitGoto(GotoStatement s)
     {
-        buf.writestring("goto ");
-        buf.writestring(s.ident.toString());
-        buf.writeByte(';');
+        buf.put("goto ");
+        buf.put(s.ident.toString());
+        buf.put(';');
         buf.writenl();
     }
 
     void visitLabel(LabelStatement s)
     {
-        buf.writestring(s.ident.toString());
-        buf.writeByte(':');
+        buf.put(s.ident.toString());
+        buf.put(':');
         buf.writenl();
         if (s.statement)
             s.statement.statementToBuffer(buf, hgs);
@@ -921,12 +921,12 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
 
     void visitAsm(AsmStatement s)
     {
-        buf.writestring("asm { ");
+        buf.put("asm { ");
         Token* t = s.tokens;
         buf.level++;
         while (t)
         {
-            buf.writestring(t.toString());
+            buf.put(t.toString());
             if (t.next &&
                 t.value != TOK.min      &&
                 t.value != TOK.comma    && t.next.value != TOK.comma    &&
@@ -936,12 +936,12 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h
                                           t.next.value != TOK.rightParenthesis   &&
                 t.value != TOK.dot      && t.next.value != TOK.dot)
             {
-                buf.writeByte(' ');
+                buf.put(' ');
             }
             t = t.next;
         }
         buf.level--;
-        buf.writestring("; }");
+        buf.put("; }");
         buf.writenl();
     }
 
@@ -976,39 +976,39 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 {
     void visitDsymbol(Dsymbol s)
     {
-        buf.writestring(s.toChars());
+        buf.put(s.toChars());
     }
 
     void visitStaticAssert(StaticAssert s)
     {
-        buf.writestring(s.kind());
-        buf.writeByte('(');
+        buf.put(s.kind());
+        buf.put('(');
         s.exp.expressionToBuffer(buf, hgs);
         if (s.msgs)
         {
             foreach (m; (*s.msgs)[])
             {
-                buf.writestring(", ");
+                buf.put(", ");
                 m.expressionToBuffer(buf, hgs);
             }
         }
-        buf.writestring(");");
+        buf.put(");");
         buf.writenl();
     }
 
     void visitDebugSymbol(DebugSymbol s)
     {
-        buf.writestring("debug = ");
-        buf.writestring(s.ident.toString());
-        buf.writeByte(';');
+        buf.put("debug = ");
+        buf.put(s.ident.toString());
+        buf.put(';');
         buf.writenl();
     }
 
     void visitVersionSymbol(VersionSymbol s)
     {
-        buf.writestring("version = ");
-        buf.writestring(s.ident.toString());
-        buf.writeByte(';');
+        buf.put("version = ");
+        buf.put(s.ident.toString());
+        buf.put(';');
         buf.writenl();
     }
 
@@ -1017,10 +1017,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (em.type)
             typeToBuffer(em.type, em.ident, buf, hgs);
         else
-            buf.writestring(em.ident.toString());
+            buf.put(em.ident.toString());
         if (em.value)
         {
-            buf.writestring(" = ");
+            buf.put(" = ");
             em.value.expressionToBuffer(buf, hgs);
         }
     }
@@ -1030,8 +1030,8 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (hgs.hdrgen && imp.id == Id.object)
             return; // object is imported by default
         if (imp.isstatic)
-            buf.writestring("static ");
-        buf.writestring("import ");
+            buf.put("static ");
+        buf.put("import ");
         if (imp.aliasId)
         {
             buf.printf("%s = ", imp.aliasId.toChars());
@@ -1039,32 +1039,32 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         foreach (const pid; imp.packages)
         {
             buf.write(pid.toString());
-            buf.writeByte('.');
+            buf.put('.');
         }
-        buf.writestring(imp.id.toString());
+        buf.put(imp.id.toString());
         if (imp.names.length)
         {
-            buf.writestring(" : ");
+            buf.put(" : ");
             foreach (const i, const name; imp.names)
             {
                 if (i)
-                    buf.writestring(", ");
+                    buf.put(", ");
                 const _alias = imp.aliases[i];
                 if (_alias)
                     buf.printf("%s = %s", _alias.toChars(), name.toChars());
                 else
-                    buf.writestring(name.toChars());
+                    buf.put(name.toChars());
             }
         }
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitAliasThis(AliasThis d)
     {
-        buf.writestring("alias ");
-        buf.writestring(d.ident.toString());
-        buf.writestring(" this;\n");
+        buf.put("alias ");
+        buf.put(d.ident.toString());
+        buf.put(" this;\n");
     }
 
     void visitAttribDeclaration(AttribDeclaration d)
@@ -1077,32 +1077,32 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 
         if (!d.decl)
         {
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
             return;
         }
         if (d.decl.length == 0 || (hgs.hdrgen && d.decl.length == 1 && (*d.decl)[0].isUnitTestDeclaration()))
         {
             // hack for https://issues.dlang.org/show_bug.cgi?id=8081
-            if (hasSTC) buf.writeByte(' ');
-            buf.writestring("{}");
+            if (hasSTC) buf.put(' ');
+            buf.put("{}");
         }
         else if (d.decl.length == 1)
         {
-            if (hasSTC) buf.writeByte(' ');
+            if (hasSTC) buf.put(' ');
             toCBuffer((*d.decl)[0], buf, hgs);
             return;
         }
         else
         {
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.writenl();
             buf.level++;
             foreach (de; *d.decl)
                 toCBuffer(de, buf, hgs);
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
         }
         buf.writenl();
     }
@@ -1114,17 +1114,17 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 
     void visitDeprecatedDeclaration(DeprecatedDeclaration d)
     {
-        buf.writestring("deprecated(");
+        buf.put("deprecated(");
         d.msg.expressionToBuffer(buf, hgs);
-        buf.writestring(") ");
+        buf.put(") ");
         visitAttribDeclaration(d);
     }
 
     void visitLinkDeclaration(LinkDeclaration d)
     {
-        buf.writestring("extern (");
-        buf.writestring(linkageToString(d.linkage));
-        buf.writestring(") ");
+        buf.put("extern (");
+        buf.put(linkageToString(d.linkage));
+        buf.put(") ");
         visitAttribDeclaration(d);
     }
 
@@ -1142,9 +1142,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         case CPPMANGLE.def:
             break;
         }
-        buf.writestring("extern (C++, ");
-        buf.writestring(s);
-        buf.writestring(") ");
+        buf.put("extern (C++, ");
+        buf.put(s);
+        buf.put(") ");
         visitAttribDeclaration(d);
     }
 
@@ -1153,7 +1153,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         visibilityToBuffer(buf, d.visibility);
         AttribDeclaration ad = cast(AttribDeclaration)d;
         if (ad.decl.length <= 1)
-            buf.writeByte(' ');
+            buf.put(' ');
         if (ad.decl.length == 1 && (*ad.decl)[0].isVisibilityDeclaration)
             visitAttribDeclaration((*ad.decl)[0].isVisibilityDeclaration);
         else
@@ -1167,25 +1167,25 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             foreach (i, exp; (*d.exps)[])
             {
                 if (i)
-                    buf.writeByte(' ');
-                buf.writestring("align (");
+                    buf.put(' ');
+                buf.put("align (");
                 toCBuffer(exp, buf, hgs);
-                buf.writeByte(')');
+                buf.put(')');
             }
             if (d.decl && d.decl.length < 2)
-                buf.writeByte(' ');
+                buf.put(' ');
         }
         else
-            buf.writestring("align ");
+            buf.put("align ");
 
         visitAttribDeclaration(d.isAttribDeclaration());
     }
 
     void visitAnonDeclaration(AnonDeclaration d)
     {
-        buf.writestring(d.isunion ? "union" : "struct");
+        buf.put(d.isunion ? "union" : "struct");
         buf.writenl();
-        buf.writestring("{");
+        buf.put("{");
         buf.writenl();
         buf.level++;
         if (d.decl)
@@ -1194,21 +1194,21 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
                 toCBuffer(de, buf, hgs);
         }
         buf.level--;
-        buf.writestring("}");
+        buf.put("}");
         buf.writenl();
     }
 
     void visitPragmaDeclaration(PragmaDeclaration d)
     {
-        buf.writestring("pragma (");
-        buf.writestring(d.ident.toString());
+        buf.put("pragma (");
+        buf.put(d.ident.toString());
         if (d.args && d.args.length)
         {
-            buf.writestring(", ");
+            buf.put(", ");
             argsToBuffer(d.args, buf, hgs);
         }
 
-        buf.writeByte(')');
+        buf.put(')');
 
         // https://issues.dlang.org/show_bug.cgi?id=14690
         // Unconditionally perform a full output dump
@@ -1227,7 +1227,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (d.decl || d.elsedecl)
         {
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.writenl();
             buf.level++;
             if (d.decl)
@@ -1236,23 +1236,23 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
                     toCBuffer(de, buf, hgs);
             }
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
             if (d.elsedecl)
             {
                 buf.writenl();
-                buf.writestring("else");
+                buf.put("else");
                 buf.writenl();
-                buf.writeByte('{');
+                buf.put('{');
                 buf.writenl();
                 buf.level++;
                 foreach (de; *d.elsedecl)
                     toCBuffer(de, buf, hgs);
                 buf.level--;
-                buf.writeByte('}');
+                buf.put('}');
             }
         }
         else
-            buf.writeByte(':');
+            buf.put(':');
         buf.writenl();
     }
 
@@ -1260,22 +1260,22 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     {
         void foreachWithoutBody(ForeachStatement s)
         {
-            buf.writestring(Token.toString(s.op));
-            buf.writestring(" (");
+            buf.put(Token.toString(s.op));
+            buf.put(" (");
             foreach (i, p; *s.parameters)
             {
                 if (i)
-                    buf.writestring(", ");
+                    buf.put(", ");
                 if (stcToBuffer(buf, p.storageClass))
-                    buf.writeByte(' ');
+                    buf.put(' ');
                 if (p.type)
                     typeToBuffer(p.type, p.ident, buf, hgs);
                 else
-                    buf.writestring(p.ident.toString());
+                    buf.put(p.ident.toString());
             }
-            buf.writestring("; ");
+            buf.put("; ");
             s.aggr.expressionToBuffer(buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
             buf.writenl();
         }
 
@@ -1283,21 +1283,21 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         {
             /* s.op ( param ; lwr .. upr )
              */
-            buf.writestring(Token.toString(s.op));
-            buf.writestring(" (");
+            buf.put(Token.toString(s.op));
+            buf.put(" (");
             if (s.param.type)
                 typeToBuffer(s.param.type, s.param.ident, buf, hgs);
             else
-                buf.writestring(s.param.ident.toString());
-            buf.writestring("; ");
+                buf.put(s.param.ident.toString());
+            buf.put("; ");
             s.lwr.expressionToBuffer(buf, hgs);
-            buf.writestring(" .. ");
+            buf.put(" .. ");
             s.upr.expressionToBuffer(buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
             buf.writenl();
         }
 
-        buf.writestring("static ");
+        buf.put("static ");
         if (s.sfe.aggrfe)
         {
             foreachWithoutBody(s.sfe.aggrfe);
@@ -1307,29 +1307,29 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             assert(s.sfe.rangefe);
             foreachRangeWithoutBody(s.sfe.rangefe);
         }
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         visitAttribDeclaration(s);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
 
     }
 
     void visitMixinDeclaration(MixinDeclaration d)
     {
-        buf.writestring("mixin(");
+        buf.put("mixin(");
         argsToBuffer(d.exps, buf, hgs, null);
-        buf.writestring(");");
+        buf.put(");");
         buf.writenl();
     }
 
     void visitUserAttributeDeclaration(UserAttributeDeclaration d)
     {
-        buf.writestring("@(");
+        buf.put("@(");
         argsToBuffer(d.atts, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         visitAttribDeclaration(d);
     }
 
@@ -1337,9 +1337,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     {
         if (!constraint)
             return;
-        buf.writestring(" if (");
+        buf.put(" if (");
         constraint.expressionToBuffer(buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     /// Returns: whether `do` is needed to write the function body
@@ -1351,13 +1351,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         {
             foreach (frequire; *f.frequires)
             {
-                buf.writestring("in");
+                buf.put("in");
                 if (auto es = frequire.isExpStatement())
                 {
                     assert(es.exp && es.exp.op == EXP.assert_);
-                    buf.writestring(" (");
+                    buf.put(" (");
                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
-                    buf.writeByte(')');
+                    buf.put(')');
                     buf.writenl();
                     requireDo = false;
                 }
@@ -1374,18 +1374,18 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         {
             foreach (fensure; *f.fensures)
             {
-                buf.writestring("out");
+                buf.put("out");
                 if (auto es = fensure.ensure.isExpStatement())
                 {
                     assert(es.exp && es.exp.op == EXP.assert_);
-                    buf.writestring(" (");
+                    buf.put(" (");
                     if (fensure.id)
                     {
-                        buf.writestring(fensure.id.toString());
+                        buf.put(fensure.id.toString());
                     }
-                    buf.writestring("; ");
+                    buf.put("; ");
                     (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
-                    buf.writeByte(')');
+                    buf.put(')');
                     buf.writenl();
                     requireDo = false;
                 }
@@ -1393,9 +1393,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
                 {
                     if (fensure.id)
                     {
-                        buf.writeByte('(');
-                        buf.writestring(fensure.id.toString());
-                        buf.writeByte(')');
+                        buf.put('(');
+                        buf.put(fensure.id.toString());
+                        buf.put(')');
                     }
                     buf.writenl();
                     fensure.ensure.statementToBuffer(buf, hgs);
@@ -1415,7 +1415,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
                 buf.writenl();
                 contractsToBuffer(f);
             }
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
             return;
         }
@@ -1440,15 +1440,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 
         if (requireDo)
         {
-            buf.writestring("do");
+            buf.put("do");
             buf.writenl();
         }
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         f.fbody.statementToBuffer(buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
         hgs.tpltMember = savetlpt;
         hgs.autoMember = saveauto;
@@ -1459,11 +1459,11 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (!d || !d.baseclasses.length)
             return;
         if (!d.isAnonymous())
-            buf.writestring(" : ");
+            buf.put(" : ");
         foreach (i, b; *d.baseclasses)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             typeToBuffer(b.type, null, buf, hgs);
         }
     }
@@ -1479,7 +1479,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         {
             assert(fd.type);
             if (stcToBuffer(buf, fd.storage_class))
-                buf.writeByte(' ');
+                buf.put(' ');
             functionToBufferFull(cast(TypeFunction)fd.type, buf, d.ident, hgs, d);
             visitTemplateConstraint(d.constraint);
             hgs.tpltMember++;
@@ -1489,28 +1489,28 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         }
         if (AggregateDeclaration ad = onemember.isAggregateDeclaration())
         {
-            buf.writestring(ad.kind());
-            buf.writeByte(' ');
-            buf.writestring(ad.ident.toString());
-            buf.writeByte('(');
+            buf.put(ad.kind());
+            buf.put(' ');
+            buf.put(ad.ident.toString());
+            buf.put('(');
             visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
             visitTemplateConstraint(d.constraint);
             visitBaseClasses(ad.isClassDeclaration());
             hgs.tpltMember++;
             if (ad.members)
             {
                 buf.writenl();
-                buf.writeByte('{');
+                buf.put('{');
                 buf.writenl();
                 buf.level++;
                 foreach (s; *ad.members)
                     toCBuffer(s, buf, hgs);
                 buf.level--;
-                buf.writeByte('}');
+                buf.put('}');
             }
             else
-                buf.writeByte(';');
+                buf.put(';');
             buf.writenl();
             hgs.tpltMember--;
             return true;
@@ -1520,24 +1520,24 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             if (d.constraint)
                 return false;
             if (stcToBuffer(buf, vd.storage_class))
-                buf.writeByte(' ');
+                buf.put(' ');
             if (vd.type)
                 typeToBuffer(vd.type, vd.ident, buf, hgs);
             else
-                buf.writestring(vd.ident.toString());
-            buf.writeByte('(');
+                buf.put(vd.ident.toString());
+            buf.put('(');
             visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
             if (vd._init)
             {
-                buf.writestring(" = ");
+                buf.put(" = ");
                 ExpInitializer ie = vd._init.isExpInitializer();
                 if (ie && (ie.exp.op == EXP.construct || ie.exp.op == EXP.blit))
                     (cast(AssignExp)ie.exp).e2.expressionToBuffer(buf, hgs);
                 else
                     vd._init.initializerToBuffer(buf, hgs);
             }
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
             return true;
         }
@@ -1550,31 +1550,31 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         {
             // Should handle template functions for doc generation
             if (onemember && onemember.isFuncDeclaration())
-                buf.writestring("foo ");
+                buf.put("foo ");
         }
         if ((hgs.hdrgen || hgs.fullDump) && visitEponymousMember(d))
             return;
         if (hgs.ddoc)
-            buf.writestring(d.kind());
+            buf.put(d.kind());
         else
-            buf.writestring("template");
-        buf.writeByte(' ');
-        buf.writestring(d.ident.toString());
-        buf.writeByte('(');
+            buf.put("template");
+        buf.put(' ');
+        buf.put(d.ident.toString());
+        buf.put('(');
         visitTemplateParameters(hgs.ddoc ? d.origParameters : d.parameters, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         visitTemplateConstraint(d.constraint);
         if (hgs.hdrgen || hgs.fullDump)
         {
             hgs.tpltMember++;
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.writenl();
             buf.level++;
             foreach (s; *d.members)
                 toCBuffer(s, buf, hgs);
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
             buf.writenl();
             hgs.tpltMember--;
         }
@@ -1582,7 +1582,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 
     void visitTemplateInstance(TemplateInstance ti)
     {
-        buf.writestring(ti.name.toChars());
+        buf.put(ti.name.toChars());
         tiargsToBuffer(ti, buf, hgs);
 
         if (hgs.fullDump)
@@ -1594,15 +1594,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 
     void visitTemplateMixin(TemplateMixin tm)
     {
-        buf.writestring("mixin ");
+        buf.put("mixin ");
         typeToBuffer(tm.tqual, null, buf, hgs);
         tiargsToBuffer(tm, buf, hgs);
         if (tm.ident && memcmp(tm.ident.toString().ptr, cast(const(char)*) "__mixin", 7) != 0)
         {
-            buf.writeByte(' ');
-            buf.writestring(tm.ident.toString());
+            buf.put(' ');
+            buf.put(tm.ident.toString());
         }
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
         if (hgs.fullDump)
             dumpTemplateInstance(tm, buf, hgs);
@@ -1613,24 +1613,24 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         auto oldInEnumDecl = hgs.inEnumDecl;
         scope(exit) hgs.inEnumDecl = oldInEnumDecl;
         hgs.inEnumDecl = d;
-        buf.writestring("enum ");
+        buf.put("enum ");
         if (d.ident)
         {
-            buf.writestring(d.ident.toString());
+            buf.put(d.ident.toString());
         }
         if (d.memtype)
         {
-            buf.writestring(" : ");
+            buf.put(" : ");
             typeToBuffer(d.memtype, null, buf, hgs);
         }
         if (!d.members)
         {
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
             return;
         }
         buf.writenl();
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         foreach (em; *d.members)
@@ -1638,11 +1638,11 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             if (!em)
                 continue;
             toCBuffer(em, buf, hgs);
-            buf.writeByte(',');
+            buf.put(',');
             buf.writenl();
         }
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
 
         if (!hgs.importcHdr || !d.ident)
@@ -1657,48 +1657,48 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         {
             if (!em)
                 continue;
-            buf.writestring("alias ");
-            buf.writestring(em.ident.toString);
-            buf.writestring(" = ");
-            buf.writestring(d.ident.toString);
-            buf.writeByte('.');
-            buf.writestring(em.ident.toString);
-            buf.writeByte(';');
+            buf.put("alias ");
+            buf.put(em.ident.toString);
+            buf.put(" = ");
+            buf.put(d.ident.toString);
+            buf.put('.');
+            buf.put(em.ident.toString);
+            buf.put(';');
             buf.writenl();
         }
     }
 
     void visitNspace(Nspace d)
     {
-        buf.writestring("extern (C++, ");
-        buf.writestring(d.ident.toString());
-        buf.writeByte(')');
+        buf.put("extern (C++, ");
+        buf.put(d.ident.toString());
+        buf.put(')');
         buf.writenl();
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         foreach (s; *d.members)
             toCBuffer(s, buf, hgs);
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
     void visitStructDeclaration(StructDeclaration d)
     {
         //printf("visitStructDeclaration() %s\n", d.ident.toChars());
-        buf.writestring(d.kind());
-        buf.writeByte(' ');
+        buf.put(d.kind());
+        buf.put(' ');
         if (!d.isAnonymous())
-            buf.writestring(d.toChars());
+            buf.put(d.toChars());
         if (!d.members)
         {
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
             return;
         }
         buf.writenl();
-        buf.writeByte('{');
+        buf.put('{');
         buf.writenl();
         buf.level++;
         hgs.insideAggregate++;
@@ -1706,7 +1706,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             toCBuffer(s, buf, hgs);
         hgs.insideAggregate--;
         buf.level--;
-        buf.writeByte('}');
+        buf.put('}');
         buf.writenl();
     }
 
@@ -1714,15 +1714,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     {
         if (!d.isAnonymous())
         {
-            buf.writestring(d.kind());
-            buf.writeByte(' ');
-            buf.writestring(d.ident.toString());
+            buf.put(d.kind());
+            buf.put(' ');
+            buf.put(d.ident.toString());
         }
         visitBaseClasses(d);
         if (d.members)
         {
             buf.writenl();
-            buf.writeByte('{');
+            buf.put('{');
             buf.writenl();
             buf.level++;
             hgs.insideAggregate++;
@@ -1730,10 +1730,10 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
                 toCBuffer(s, buf, hgs);
             hgs.insideAggregate--;
             buf.level--;
-            buf.writeByte('}');
+            buf.put('}');
         }
         else
-            buf.writeByte(';');
+            buf.put(';');
         buf.writenl();
     }
 
@@ -1743,13 +1743,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             return;
         if (d.hidden)
             return;
-        buf.writestring("alias ");
+        buf.put("alias ");
         if (d.aliassym)
         {
-            buf.writestring(d.ident.toString());
-            buf.writestring(" = ");
+            buf.put(d.ident.toString());
+            buf.put(" = ");
             if (stcToBuffer(buf, d.storage_class))
-                buf.writeByte(' ');
+                buf.put(' ');
             /*
                 https://issues.dlang.org/show_bug.cgi?id=23223
                 https://issues.dlang.org/show_bug.cgi?id=23222
@@ -1758,7 +1758,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
             */
             if (d.aliassym.isModule())
             {
-                buf.writestring(d.aliassym.ident.toString());
+                buf.put(d.aliassym.ident.toString());
             }
             else
             {
@@ -1768,34 +1768,34 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         else if (d.type.ty == Tfunction)
         {
             if (stcToBuffer(buf, d.storage_class))
-                buf.writeByte(' ');
+                buf.put(' ');
             typeToBuffer(d.type, d.ident, buf, hgs);
         }
         else if (d.ident)
         {
             hgs.declstring = (d.ident == Id.string || d.ident == Id.wstring || d.ident == Id.dstring);
-            buf.writestring(d.ident.toString());
-            buf.writestring(" = ");
+            buf.put(d.ident.toString());
+            buf.put(" = ");
             if (stcToBuffer(buf, d.storage_class))
-                buf.writeByte(' ');
+                buf.put(' ');
             hgs.inCAlias = hgs.importcHdr;
             typeToBuffer(d.type, null, buf, hgs);
             hgs.inCAlias = false;
             hgs.declstring = false;
         }
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitAliasAssign(AliasAssign d)
     {
-        buf.writestring(d.ident.toString());
-        buf.writestring(" = ");
+        buf.put(d.ident.toString());
+        buf.put(" = ");
         if (d.aliassym)
             toCBuffer(d.aliassym, buf, hgs);
         else // d.type
             typeToBuffer(d.type, null, buf, hgs);
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
@@ -1804,7 +1804,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (d.storage_class & STC.local)
             return;
         visitVarDecl(d, false, buf, hgs);
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
@@ -1814,8 +1814,8 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 
         // https://issues.dlang.org/show_bug.cgi?id=24891
         // return/scope storage classes are printed as part of function type
-        if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope)))
-            buf.writeByte(' ');
+        if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef)))
+            buf.put(' ');
         typeToBuffer(f.type, f.ident, buf, hgs);
         auto tf = f.type.isTypeFunction();
 
@@ -1840,7 +1840,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
                         buf.writenl();
                     contractsToBuffer(f);
                 }
-                buf.writeByte(';');
+                buf.put(';');
                 buf.writenl();
             }
             else
@@ -1854,13 +1854,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     {
         if (f.type.ty == Terror)
         {
-            buf.writestring("__error");
+            buf.put("__error");
             return;
         }
         if (f.tok != TOK.reserved && !hgs.errorMsg)
         {
-            buf.writestring(f.kind());
-            buf.writeByte(' ');
+            buf.put(f.kind());
+            buf.put(' ');
         }
         TypeFunction tf = cast(TypeFunction)f.type;
 
@@ -1871,8 +1871,8 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         // https://issues.dlang.org/show_bug.cgi?id=20074
         void printAttribute(string str)
         {
-            buf.writeByte(' ');
-            buf.writestring(str);
+            buf.put(' ');
+            buf.put(str);
         }
 
         if (!hgs.errorMsg)
@@ -1889,7 +1889,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         ReturnStatement rs = s1 ? s1.endsWithReturnStatement() : null;
         if (rs && rs.exp)
         {
-            buf.writestring(" => ");
+            buf.put(" => ");
             rs.exp.expressionToBuffer(buf, hgs);
         }
         else
@@ -1903,29 +1903,29 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     void visitPostBlitDeclaration(PostBlitDeclaration d)
     {
         if (stcToBuffer(buf, d.storage_class))
-            buf.writeByte(' ');
-        buf.writestring("this(this)");
+            buf.put(' ');
+        buf.put("this(this)");
         bodyToBuffer(d);
     }
 
     void visitDtorDeclaration(DtorDeclaration d)
     {
         if (stcToBuffer(buf, d.storage_class))
-            buf.writeByte(' ');
-        buf.writestring("~this()");
+            buf.put(' ');
+        buf.put("~this()");
         bodyToBuffer(d);
     }
 
     void visitStaticCtorDeclaration(StaticCtorDeclaration d)
     {
         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
-            buf.writeByte(' ');
+            buf.put(' ');
         if (d.isSharedStaticCtorDeclaration())
-            buf.writestring("shared ");
-        buf.writestring("static this()");
+            buf.put("shared ");
+        buf.put("static this()");
         if (hgs.hdrgen && !hgs.tpltMember)
         {
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
         }
         else
@@ -1935,13 +1935,13 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
     void visitStaticDtorDeclaration(StaticDtorDeclaration d)
     {
         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
-            buf.writeByte(' ');
+            buf.put(' ');
         if (d.isSharedStaticDtorDeclaration())
-            buf.writestring("shared ");
-        buf.writestring("static ~this()");
+            buf.put("shared ");
+        buf.put("static ~this()");
         if (hgs.hdrgen && !hgs.tpltMember)
         {
-            buf.writeByte(';');
+            buf.put(';');
             buf.writenl();
         }
         else
@@ -1953,14 +1953,14 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (hgs.hdrgen)
             return;
         if (stcToBuffer(buf, d.storage_class))
-            buf.writeByte(' ');
-        buf.writestring("invariant");
+            buf.put(' ');
+        buf.put("invariant");
         auto es = d.fbody.isExpStatement();
         if (es && es.exp && es.exp.op == EXP.assert_)
         {
-            buf.writestring(" (");
+            buf.put(" (");
             (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs);
-            buf.writestring(");");
+            buf.put(");");
             buf.writenl();
         }
         else
@@ -1974,28 +1974,28 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
         if (hgs.hdrgen)
             return;
         if (stcToBuffer(buf, d.storage_class))
-            buf.writeByte(' ');
-        buf.writestring("unittest");
+            buf.put(' ');
+        buf.put("unittest");
         bodyToBuffer(d);
     }
 
     void visitBitFieldDeclaration(BitFieldDeclaration d)
     {
         if (stcToBuffer(buf, d.storage_class))
-            buf.writeByte(' ');
+            buf.put(' ');
         Identifier id = d.isAnonymous() ? null : d.ident;
         typeToBuffer(d.type, id, buf, hgs);
-        buf.writestring(" : ");
+        buf.put(" : ");
         d.width.expressionToBuffer(buf, hgs);
-        buf.writeByte(';');
+        buf.put(';');
         buf.writenl();
     }
 
     void visitNewDeclaration(NewDeclaration d)
     {
         if (stcToBuffer(buf, d.storage_class & ~STC.static_))
-            buf.writeByte(' ');
-        buf.writestring("new();");
+            buf.put(' ');
+        buf.put("new();");
     }
 
     void visitModule(Module m)
@@ -2062,15 +2062,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs)
 public
 void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, ref HdrGenState hgs)
 {
-    buf.writestring(td.ident == Id.ctor ? "this" : td.ident.toString());
-    buf.writeByte('(');
+    buf.put(td.ident == Id.ctor ? "this" : td.ident.toString());
+    buf.put('(');
     foreach (i, const tp; *td.parameters)
     {
         if (i)
-            buf.writestring(", ");
+            buf.put(", ");
         toCBuffer(tp, buf, hgs);
     }
-    buf.writeByte(')');
+    buf.put(')');
 
     if (hgs.showOneMember && td.onemember)
     {
@@ -2079,10 +2079,10 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re
             if (TypeFunction tf = cast(TypeFunction)fd.type.isTypeFunction())
             {
                 // !! Casted away const
-                buf.writestring(parametersTypeToChars(tf.parameterList));
+                buf.put(parametersTypeToChars(tf.parameterList));
                 if (tf.mod)
                 {
-                    buf.writeByte(' ');
+                    buf.put(' ');
                     buf.MODtoBuffer(tf.mod);
                 }
             }
@@ -2092,9 +2092,9 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re
     if (!hgs.skipConstraints &&
         td.constraint)
     {
-        buf.writestring(" if (");
+        buf.put(" if (");
         toCBuffer(td.constraint, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
 }
 
@@ -2109,7 +2109,7 @@ private void visitTemplateParameters(TemplateParameters* parameters, ref OutBuff
     foreach (i, p; *parameters)
     {
         if (i)
-            buf.writestring(", ");
+            buf.put(", ");
         toCBuffer(p, buf, hgs);
     }
 }
@@ -2137,12 +2137,12 @@ private void visitVarDecl(VarDeclaration v, bool anywritten, ref OutBuffer buf,
 
     const commentIt = hgs.importcHdr && isSpecialCName(v.ident);
     if (commentIt)
-        buf.writestring("/+");
+        buf.put("/+");
 
     if (anywritten)
     {
-        buf.writestring(", ");
-        buf.writestring(v.ident.toString());
+        buf.put(", ");
+        buf.put(v.ident.toString());
     }
     else
     {
@@ -2153,26 +2153,26 @@ private void visitVarDecl(VarDeclaration v, bool anywritten, ref OutBuffer buf,
         if (useTypeof)
             stc &= ~STC.auto_;
         if (stcToBuffer(buf, stc))
-            buf.writeByte(' ');
+            buf.put(' ');
         if (v.type)
             typeToBuffer(v.type, v.ident, buf, hgs);
         else if (useTypeof)
         {
-            buf.writestring("typeof(");
+            buf.put("typeof(");
             vinit(v);
-            buf.writestring(") ");
-            buf.writestring(v.ident.toString());
+            buf.put(") ");
+            buf.put(v.ident.toString());
         }
         else
-            buf.writestring(v.ident.toString());
+            buf.put(v.ident.toString());
     }
     if (v._init && !isextern)
     {
-        buf.writestring(" = ");
+        buf.put(" = ");
         vinit(v);
     }
     if (commentIt)
-        buf.writestring("+/");
+        buf.put("+/");
 }
 
 /*************************************
@@ -2204,7 +2204,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 {
     void visit(Expression e)
     {
-        buf.writestring(EXPtoString(e.op));
+        buf.put(EXPtoString(e.op));
     }
 
     void visitInteger(IntegerExp e)
@@ -2248,20 +2248,20 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
                     break;
                 }
             case Tint8:
-                buf.writestring("cast(byte)");
+                buf.put("cast(byte)");
                 goto L2;
             case Tint16:
-                buf.writestring("cast(short)");
+                buf.put("cast(short)");
                 goto L2;
             case Tint32:
             L2:
                 buf.printf("%d", cast(int)v);
                 break;
             case Tuns8:
-                buf.writestring("cast(ubyte)");
+                buf.put("cast(ubyte)");
                 goto case Tuns32;
             case Tuns16:
-                buf.writestring("cast(ushort)");
+                buf.put("cast(ushort)");
                 goto case Tuns32;
             case Tuns32:
                 buf.printf("%uu", cast(uint)v);
@@ -2272,7 +2272,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
                     // https://issues.dlang.org/show_bug.cgi?id=23173
                     // This is a special case because - is not part of the
                     // integer literal and 9223372036854775808L overflows a long
-                    buf.writestring("cast(long)-9223372036854775808");
+                    buf.put("cast(long)-9223372036854775808");
                 }
                 else
                 {
@@ -2283,20 +2283,20 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
                 buf.printf("%lluLU", v);
                 break;
             case Tbool:
-                buf.writestring(v ? "true" : "false");
+                buf.put(v ? "true" : "false");
                 break;
             case Tpointer:
-                buf.writestring("cast(");
+                buf.put("cast(");
 
                 HdrGenState hgs2;               // should re-examine need for new hgs
                 hgs2.fullQual = (t.ty == Tclass && !t.mod);
                 toCBuffer(t, buf, null, hgs2);
 
-                buf.writestring(")cast(size_t)");
+                buf.put(")cast(size_t)");
                 goto case Tuns64;
 
             case Tvoid:
-                buf.writestring("cast(void)0");
+                buf.put("cast(void)0");
                 break;
 
             default:
@@ -2315,12 +2315,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
     void visitError(ErrorExp e)
     {
-        buf.writestring("__error");
+        buf.put("__error");
     }
 
     void visitVoidInit(VoidInitExp e)
     {
-        buf.writestring("void");
+        buf.put("void");
     }
 
     void floatToBuffer(Type type, real_t value)
@@ -2338,19 +2338,19 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         /* Print as:
          *  (re+imi)
          */
-        buf.writeByte('(');
+        buf.put('(');
         floatToBuffer(e.type, creall(e.value));
-        buf.writeByte('+');
+        buf.put('+');
         floatToBuffer(e.type, cimagl(e.value));
-        buf.writestring("i)");
+        buf.put("i)");
     }
 
     void visitIdentifier(IdentifierExp e)
     {
         if (hgs.hdrgen || hgs.ddoc)
-            buf.writestring(e.ident.toHChars2());
+            buf.put(e.ident.toHChars2());
         else
-            buf.writestring(e.ident.toString());
+            buf.put(e.ident.toString());
     }
 
     void visitDsymbol(Dsymbol s)
@@ -2359,9 +2359,9 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         // This condition is a bit kludge, and can be cleaned up if the
         // mutual dependency `AST.toChars <> hdrgen.d` gets refactored
         if (hgs.vcg_ast && s.ident && !s.isTemplateInstance() && !s.isTemplateDeclaration())
-            buf.writestring(s.ident.toChars());
+            buf.put(s.ident.toChars());
         else
-            buf.writestring(s.toChars());
+            buf.put(s.toChars());
     }
 
     void visitDsymbolExp(DsymbolExp e)
@@ -2371,33 +2371,33 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
     void visitThis(ThisExp e)
     {
-        buf.writestring("this");
+        buf.put("this");
     }
 
     void visitSuper(SuperExp e)
     {
-        buf.writestring("super");
+        buf.put("super");
     }
 
     void visitNull(NullExp e)
     {
-        buf.writestring("null");
+        buf.put("null");
     }
 
     void visitString(StringExp e)
     {
         if (e.hexString || e.sz == 8)
         {
-            buf.writeByte('x');
-            buf.writeByte('"');
+            buf.put('x');
+            buf.put('"');
             foreach (i; 0 .. e.len)
                 buf.printf("%0*llX", e.sz, e.getIndex(i));
-            buf.writeByte('"');
+            buf.put('"');
             if (e.postfix)
-                buf.writeByte(e.postfix);
+                buf.put(e.postfix);
             return;
         }
-        buf.writeByte('"');
+        buf.put('"');
         const o = buf.length;
         foreach (i; 0 .. e.len)
         {
@@ -2405,15 +2405,15 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         }
         if (hgs.ddoc)
             escapeDdocString(buf, o);
-        buf.writeByte('"');
+        buf.put('"');
         if (e.postfix)
-            buf.writeByte(e.postfix);
+            buf.put(e.postfix);
     }
 
     void visitInterpolation(InterpExp e)
     {
-        buf.writeByte('i');
-        buf.writeByte('"');
+        buf.put('i');
+        buf.put('"');
         const o = buf.length;
 
         foreach (idx, str; e.interpolatedSet.parts)
@@ -2425,54 +2425,59 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
             }
             else
             {
-                buf.writeByte('$');
-                buf.writeByte('(');
+                buf.put('$');
+                buf.put('(');
                 foreach(ch; str)
-                    buf.writeByte(ch);
-                buf.writeByte(')');
+                    buf.put(ch);
+                buf.put(')');
             }
         }
 
         if (hgs.ddoc)
             escapeDdocString(buf, o);
-        buf.writeByte('"');
+        buf.put('"');
         if (e.postfix)
-            buf.writeByte(e.postfix);
+            buf.put(e.postfix);
 
     }
 
     void visitArrayLiteral(ArrayLiteralExp e)
     {
-        buf.writeByte('[');
+        buf.put('[');
         argsToBuffer(e.elements, buf, hgs, e.basis);
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitAssocArrayLiteral(AssocArrayLiteralExp e)
     {
-        buf.writeByte('[');
+        if (hgs.vcg_ast && e.lowering)
+        {
+            expToBuffer(e.lowering, PREC.assign, buf, hgs);
+            return;
+        }
+        buf.put('[');
         foreach (i, key; *e.keys)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             expToBuffer(key, PREC.assign, buf, hgs);
-            buf.writeByte(':');
+            buf.put(':');
             auto value = (*e.values)[i];
             expToBuffer(value, PREC.assign, buf, hgs);
         }
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitStructLiteral(StructLiteralExp e)
     {
-        buf.writestring(e.sd.toChars());
-        buf.writeByte('(');
+        buf.put(e.sd.toChars());
+        buf.put('(');
         // CTFE can generate struct literals that contain an AddrExp pointing
         // to themselves, need to avoid infinite recursion:
         // struct S { this(int){ this.s = &this; } S* s; }
         // const foo = new S(0);
         if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer)
-            buf.writestring("<recursion>");
+            buf.put("<recursion>");
         else
         {
             const old = e.stageflags;
@@ -2480,14 +2485,14 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
             argsToBuffer(e.elements, buf, hgs);
             e.stageflags = old;
         }
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitCompoundLiteral(CompoundLiteralExp e)
     {
-        buf.writeByte('(');
+        buf.put('(');
         typeToBuffer(e.type, null, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         e.initializer.initializerToBuffer(buf, hgs);
     }
 
@@ -2506,44 +2511,49 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         {
             // fixes bug 6491
             if (auto m = e.sds.isModule())
-                buf.writestring(m.md.toChars());
+                buf.put(m.md.toChars());
             else
-                buf.writestring(e.sds.toChars());
+                buf.put(e.sds.toChars());
         }
         else
         {
-            buf.writestring(e.sds.kind());
-            buf.writeByte(' ');
-            buf.writestring(e.sds.toChars());
+            buf.put(e.sds.kind());
+            buf.put(' ');
+            buf.put(e.sds.toChars());
         }
     }
 
     void visitTemplate(TemplateExp e)
     {
-        buf.writestring(e.td.toChars());
+        buf.put(e.td.toChars());
     }
 
     void visitNew(NewExp e)
     {
+        if (hgs.vcg_ast && e.lowering)
+        {
+            expToBuffer(e.lowering, PREC.primary, buf, hgs);
+            return;
+        }
         if (e.thisexp)
         {
             expToBuffer(e.thisexp, PREC.primary, buf, hgs);
-            buf.writeByte('.');
+            buf.put('.');
         }
-        buf.writestring("new ");
+        buf.put("new ");
         if (e.placement)
         {
-            buf.writeByte('(');
+            buf.put('(');
             expToBuffer(e.placement, PREC.assign, buf, hgs);
-            buf.writeByte(')');
-            buf.writeByte(' ');
+            buf.put(')');
+            buf.put(' ');
         }
         typeToBuffer(e.newtype, null, buf, hgs);
         if (e.arguments && e.arguments.length)
         {
-            buf.writeByte('(');
+            buf.put('(');
             argsToBuffer(e.arguments, buf, hgs, null, e.names);
-            buf.writeByte(')');
+            buf.put(')');
         }
     }
 
@@ -2552,22 +2562,22 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         if (e.thisexp)
         {
             expToBuffer(e.thisexp, PREC.primary, buf, hgs);
-            buf.writeByte('.');
+            buf.put('.');
         }
-        buf.writestring("new");
+        buf.put("new");
         if (e.placement)
         {
-            buf.writeByte(' ');
-            buf.writeByte('(');
+            buf.put(' ');
+            buf.put('(');
             expToBuffer(e.placement, PREC.assign, buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
         }
-        buf.writestring(" class ");
+        buf.put(" class ");
         if (e.arguments && e.arguments.length)
         {
-            buf.writeByte('(');
+            buf.put('(');
             argsToBuffer(e.arguments, buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
         }
         if (e.cd)
             e.cd.dsymbolToBuffer(buf, hgs);
@@ -2578,7 +2588,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         if (e.offset)
             buf.printf("(& %s + %llu)", e.var.toChars(), e.offset);
         else if (e.var.isTypeInfoDeclaration())
-            buf.writestring(e.var.toChars());
+            buf.put(e.var.toChars());
         else
             buf.printf("& %s", e.var.toChars());
     }
@@ -2590,31 +2600,31 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
     void visitOver(OverExp e)
     {
-        buf.writestring(e.vars.ident.toString());
+        buf.put(e.vars.ident.toString());
     }
 
     void visitTuple(TupleExp e)
     {
         if (e.e0)
         {
-            buf.writeByte('(');
+            buf.put('(');
             e.e0.expressionPrettyPrint(buf, hgs);
-            buf.writestring(", AliasSeq!(");
+            buf.put(", AliasSeq!(");
             argsToBuffer(e.exps, buf, hgs);
-            buf.writestring("))");
+            buf.put("))");
         }
         else
         {
-            buf.writestring("AliasSeq!(");
+            buf.put("AliasSeq!(");
             argsToBuffer(e.exps, buf, hgs);
-            buf.writeByte(')');
+            buf.put(')');
         }
     }
 
     void visitFunc(FuncExp e)
     {
         e.fd.dsymbolToBuffer(buf, hgs);
-        //buf.writestring(e.fd.toChars());
+        //buf.put(e.fd.toChars());
     }
 
     void visitDeclaration(DeclarationExp e)
@@ -2631,12 +2641,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
             // - Avoid printing newline.
             // - Intentionally use the format (Type var;)
             //   which isn't correct as regular D code.
-                buf.writeByte('(');
+                buf.put('(');
 
                 visitVarDecl(var, false, buf, hgs);
 
-                buf.writeByte(';');
-                buf.writeByte(')');
+                buf.put(';');
+                buf.put(')');
             }
             else e.declaration.dsymbolToBuffer(buf, hgs);
         }
@@ -2644,62 +2654,62 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
     void visitTypeid(TypeidExp e)
     {
-        buf.writestring("typeid(");
+        buf.put("typeid(");
         objectToBuffer(e.obj, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitTraits(TraitsExp e)
     {
-        buf.writestring("__traits(");
+        buf.put("__traits(");
         if (e.ident)
-            buf.writestring(e.ident.toString());
+            buf.put(e.ident.toString());
         if (e.args)
         {
             foreach (arg; *e.args)
             {
-                buf.writestring(", ");
+                buf.put(", ");
                 objectToBuffer(arg, buf, hgs);
             }
         }
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitHalt(HaltExp e)
     {
-        buf.writestring("halt");
+        buf.put("halt");
     }
 
     void visitIs(IsExp e)
     {
-        buf.writestring("is(");
+        buf.put("is(");
         typeToBuffer(e.targ, e.id, buf, hgs);
         if (e.tok2 != TOK.reserved)
         {
-            buf.writeByte(' ');
-            buf.writestring(Token.toString(e.tok));
-            buf.writeByte(' ');
-            buf.writestring(Token.toString(e.tok2));
+            buf.put(' ');
+            buf.put(Token.toString(e.tok));
+            buf.put(' ');
+            buf.put(Token.toString(e.tok2));
         }
         else if (e.tspec)
         {
             if (e.tok == TOK.colon)
-                buf.writestring(" : ");
+                buf.put(" : ");
             else
-                buf.writestring(" == ");
+                buf.put(" == ");
             typeToBuffer(e.tspec, null, buf, hgs);
         }
         if (e.parameters && e.parameters.length)
         {
-            buf.writestring(", ");
+            buf.put(", ");
             visitTemplateParameters(e.parameters, buf, hgs);
         }
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitUna(UnaExp e)
     {
-        buf.writestring(EXPtoString(e.op));
+        buf.put(EXPtoString(e.op));
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
@@ -2716,9 +2726,9 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
     void visitBin(BinExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
-        buf.writeByte(' ');
-        buf.writestring(EXPtoString(e.op));
-        buf.writeByte(' ');
+        buf.put(' ');
+        buf.put(EXPtoString(e.op));
+        buf.put(' ');
         expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
     }
 
@@ -2765,7 +2775,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
             if (commaExtract)
             {
-                expToBuffer(commaExtract, precedence[exp.op], buf, hgs);
+                expToBuffer(commaExtract, expPrecedence(hgs, exp), buf, hgs);
                 return;
             }
         }
@@ -2777,80 +2787,80 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
 
     void visitMixin(MixinExp e)
     {
-        buf.writestring("mixin(");
+        buf.put("mixin(");
         argsToBuffer(e.exps, buf, hgs, null);
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitImport(ImportExp e)
     {
-        buf.writestring("import(");
+        buf.put("import(");
         expToBuffer(e.e1, PREC.assign, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitAssert(AssertExp e)
     {
-        buf.writestring("assert(");
+        buf.put("assert(");
         expToBuffer(e.e1, PREC.assign, buf, hgs);
         if (e.msg)
         {
-            buf.writestring(", ");
+            buf.put(", ");
             expToBuffer(e.msg, PREC.assign, buf, hgs);
         }
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitThrow(ThrowExp e)
     {
-        buf.writestring("throw ");
+        buf.put("throw ");
         expToBuffer(e.e1, PREC.unary, buf, hgs);
     }
 
     void visitDotId(DotIdExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('.');
-        buf.writestring(e.ident.toString());
+        buf.put('.');
+        buf.put(e.ident.toString());
     }
 
     void visitDotTemplate(DotTemplateExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('.');
-        buf.writestring(e.td.toChars());
+        buf.put('.');
+        buf.put(e.td.toChars());
     }
 
     void visitDotVar(DotVarExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('.');
+        buf.put('.');
         visitDsymbol(e.var);
     }
 
     void visitDotTemplateInstance(DotTemplateInstanceExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('.');
+        buf.put('.');
         e.ti.dsymbolToBuffer(buf, hgs);
     }
 
     void visitDelegate(DelegateExp e)
     {
-        buf.writeByte('&');
+        buf.put('&');
         if (!e.func.isNested() || e.func.needThis())
         {
             expToBuffer(e.e1, PREC.primary, buf, hgs);
-            buf.writeByte('.');
+            buf.put('.');
         }
-        buf.writestring(e.func.toChars());
+        buf.put(e.func.toChars());
     }
 
     void visitDotType(DotTypeExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('.');
-        buf.writestring(e.sym.toChars());
+        buf.put('.');
+        buf.put(e.sym.toChars());
     }
 
     void visitCall(CallExp e)
@@ -2865,163 +2875,199 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
             e.e1.expressionPrettyPrint(buf, hgs);
         }
         else
+        {
+            if (!hgs.vcg_ast && e.loweredFrom)
+            {
+                // restore original syntax for expressions lowered to calls
+                expressionToBuffer(e.loweredFrom, buf, hgs);
+                return;
+            }
             expToBuffer(e.e1, precedence[e.op], buf, hgs);
-        buf.writeByte('(');
+        }
+        buf.put('(');
         argsToBuffer(e.arguments, buf, hgs, null, e.names);
-        buf.writeByte(')');
+        buf.put(')');
+    }
+
+    void visitNot(NotExp e)
+    {
+        if (!hgs.vcg_ast && e.loweredFrom)
+            return expressionToBuffer(e.loweredFrom, buf, hgs);
+        return visitUna(e);
     }
 
     void visitPtr(PtrExp e)
     {
-        buf.writeByte('*');
+        buf.put('*');
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
     void visitDelete(DeleteExp e)
     {
-        buf.writestring("delete ");
+        buf.put("delete ");
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
     void visitCast(CastExp e)
     {
-        buf.writestring("cast(");
+        buf.put("cast(");
         if (e.to)
             typeToBuffer(e.to, null, buf, hgs);
         else
         {
             MODtoBuffer(buf, e.mod);
         }
-        buf.writeByte(')');
+        buf.put(')');
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
     void visitVector(VectorExp e)
     {
-        buf.writestring("cast(");
+        buf.put("cast(");
         typeToBuffer(e.to, null, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
     void visitVectorArray(VectorArrayExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writestring(".array");
+        buf.put(".array");
     }
 
     void visitSlice(SliceExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
-        buf.writeByte('[');
+        buf.put('[');
         if (e.upr || e.lwr)
         {
             if (e.lwr)
                 sizeToBuffer(e.lwr, buf, hgs);
             else
-                buf.writeByte('0');
-            buf.writestring("..");
+                buf.put('0');
+            buf.put("..");
             if (e.upr)
                 sizeToBuffer(e.upr, buf, hgs);
             else
-                buf.writeByte('$');
+                buf.put('$');
         }
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitArrayLength(ArrayLengthExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writestring(".length");
+        buf.put(".length");
     }
 
     void visitInterval(IntervalExp e)
     {
         expToBuffer(e.lwr, PREC.assign, buf, hgs);
-        buf.writestring("..");
+        buf.put("..");
         expToBuffer(e.upr, PREC.assign, buf, hgs);
     }
 
     void visitDelegatePtr(DelegatePtrExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writestring(".ptr");
+        buf.put(".ptr");
     }
 
     void visitDelegateFuncptr(DelegateFuncptrExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writestring(".funcptr");
+        buf.put(".funcptr");
     }
 
     void visitArray(ArrayExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('[');
+        buf.put('[');
         argsToBuffer(e.arguments, buf, hgs);
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitDot(DotExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('.');
+        buf.put('.');
         expToBuffer(e.e2, PREC.primary, buf, hgs);
     }
 
+    void visitCat(CatExp e)
+    {
+        if (hgs.vcg_ast && e.lowering)
+            expressionToBuffer(e.lowering, buf, hgs);
+        else
+            visitBin(e);
+    }
+
+    void visitCatAssign(CatAssignExp e)
+    {
+        if (hgs.vcg_ast && e.lowering)
+            expressionToBuffer(e.lowering, buf, hgs);
+        else
+            visitBin(e);
+    }
+
     void visitIndex(IndexExp e)
     {
+        if (!hgs.vcg_ast && e.loweredFrom)
+        {
+            expressionToBuffer(e.loweredFrom, buf, hgs);
+            return;
+        }
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writeByte('[');
+        buf.put('[');
         sizeToBuffer(e.e2, buf, hgs);
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitPost(PostExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
-        buf.writestring(EXPtoString(e.op));
+        buf.put(EXPtoString(e.op));
     }
 
     void visitPre(PreExp e)
     {
-        buf.writestring(EXPtoString(e.op));
+        buf.put(EXPtoString(e.op));
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
     void visitRemove(RemoveExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
-        buf.writestring(".remove(");
+        buf.put(".remove(");
         expToBuffer(e.e2, PREC.assign, buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitCond(CondExp e)
     {
         expToBuffer(e.econd, PREC.oror, buf, hgs);
-        buf.writestring(" ? ");
+        buf.put(" ? ");
         expToBuffer(e.e1, PREC.expr, buf, hgs);
-        buf.writestring(" : ");
+        buf.put(" : ");
         expToBuffer(e.e2, PREC.cond, buf, hgs);
     }
 
     void visitDefaultInit(DefaultInitExp e)
     {
-        buf.writestring(EXPtoString(e.op));
+        buf.put(EXPtoString(e.op));
     }
 
     void visitClassReference(ClassReferenceExp e)
     {
-        buf.writestring(e.value.toChars());
+        buf.put(e.value.toChars());
     }
 
     if (e.rvalue)
-        buf.writestring("__rvalue(");
+        buf.put("__rvalue(");
 
     scope (exit)
         if (e.rvalue)
-            buf.writeByte(')');
+            buf.put(')');
 
     switch (e.op)
     {
@@ -3077,6 +3123,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         case EXP.delegate_:     return visitDelegate(e.isDelegateExp());
         case EXP.dotType:       return visitDotType(e.isDotTypeExp());
         case EXP.call:          return visitCall(e.isCallExp());
+        case EXP.not:           return visitNot(e.isNotExp());
         case EXP.star:          return visitPtr(e.isPtrExp());
         case EXP.delete_:       return visitDelete(e.isDeleteExp());
         case EXP.cast_:         return visitCast(e.isCastExp());
@@ -3090,6 +3137,10 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         case EXP.array:         return visitArray(e.isArrayExp());
         case EXP.dot:           return visitDot(e.isDotExp());
         case EXP.index:         return visitIndex(e.isIndexExp());
+        case EXP.concatenate:   return visitCat(e.isCatExp());
+        case EXP.concatenateAssign:     return visitCatAssign(e.isCatAssignExp());
+        case EXP.concatenateElemAssign: return visitCatAssign(e.isCatElemAssignExp());
+        case EXP.concatenateDcharAssign:        return visitCatAssign(e.isCatDcharAssignExp());
         case EXP.minusMinus:
         case EXP.plusPlus:      return visitPost(e.isPostExp());
         case EXP.preMinusMinus:
@@ -3129,7 +3180,7 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool
         if (r != value) // if exact duplication
             CTFloat.sprint(buffer.ptr, BUFFER_LEN, 'a', value);
     }
-    buf.writestring(buffer.ptr);
+    buf.put(buffer.ptr);
     if (buffer.ptr[strlen(buffer.ptr) - 1] == '.')
         buf.remove(buf.length() - 1, 1);
 
@@ -3141,18 +3192,18 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool
         case Tfloat32:
         case Timaginary32:
         case Tcomplex32:
-            buf.writeByte('F');
+            buf.put('F');
             break;
         case Tfloat80:
         case Timaginary80:
         case Tcomplex80:
-            buf.writeByte('L');
+            buf.put('L');
             break;
         default:
             break;
         }
         if (t.isImaginary())
-            buf.writeByte('i');
+            buf.put('i');
     }
 }
 
@@ -3177,40 +3228,40 @@ public:
 
     override void visit(TemplateTypeParameter tp)
     {
-        buf.writestring(tp.ident.toString());
+        buf.put(tp.ident.toString());
         if (tp.specType)
         {
-            buf.writestring(" : ");
+            buf.put(" : ");
             typeToBuffer(tp.specType, null, *buf, *hgs);
         }
         if (tp.defaultType)
         {
-            buf.writestring(" = ");
+            buf.put(" = ");
             typeToBuffer(tp.defaultType, null, *buf, *hgs);
         }
     }
 
     override void visit(TemplateThisParameter tp)
     {
-        buf.writestring("this ");
+        buf.put("this ");
         visit(cast(TemplateTypeParameter)tp);
     }
 
     override void visit(TemplateAliasParameter tp)
     {
-        buf.writestring("alias ");
+        buf.put("alias ");
         if (tp.specType)
             typeToBuffer(tp.specType, tp.ident, *buf, *hgs);
         else
-            buf.writestring(tp.ident.toString());
+            buf.put(tp.ident.toString());
         if (tp.specAlias)
         {
-            buf.writestring(" : ");
+            buf.put(" : ");
             objectToBuffer(tp.specAlias, *buf, *hgs);
         }
         if (tp.defaultAlias)
         {
-            buf.writestring(" = ");
+            buf.put(" = ");
             objectToBuffer(tp.defaultAlias, *buf, *hgs);
         }
     }
@@ -3220,20 +3271,20 @@ public:
         typeToBuffer(tp.valType, tp.ident, *buf, *hgs);
         if (tp.specValue)
         {
-            buf.writestring(" : ");
+            buf.put(" : ");
             tp.specValue.expressionToBuffer(*buf, *hgs);
         }
         if (tp.defaultValue)
         {
-            buf.writestring(" = ");
+            buf.put(" = ");
             tp.defaultValue.expressionToBuffer(*buf, *hgs);
         }
     }
 
     override void visit(TemplateTupleParameter tp)
     {
-        buf.writestring(tp.ident.toString());
-        buf.writestring("...");
+        buf.put(tp.ident.toString());
+        buf.put("...");
     }
 }
 
@@ -3258,23 +3309,27 @@ public:
 
     override void visit(DebugCondition c)
     {
-        buf.writestring("debug (");
-        buf.writestring(c.ident.toString());
-        buf.writeByte(')');
+        buf.put("debug");
+        if (c.ident)
+        {
+            buf.put(" (");
+            buf.put(c.ident.toString());
+            buf.put(')');
+        }
     }
 
     override void visit(VersionCondition c)
     {
-        buf.writestring("version (");
-        buf.writestring(c.ident.toString());
-        buf.writeByte(')');
+        buf.put("version (");
+        buf.put(c.ident.toString());
+        buf.put(')');
     }
 
     override void visit(StaticIfCondition c)
     {
-        buf.writestring("static if (");
+        buf.put("static if (");
         c.exp.expressionToBuffer(*buf, *hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
 }
 
@@ -3294,7 +3349,7 @@ void toCBufferInstance(const TemplateInstance ti, ref OutBuffer buf, bool qualif
     HdrGenState hgs;
     hgs.fullQual = qualifyTypes;
 
-    buf.writestring(ti.name.toChars());
+    buf.put(ti.name.toChars());
     tiargsToBuffer(cast() ti, buf, hgs);
 }
 
@@ -3310,19 +3365,19 @@ bool stcToBuffer(ref OutBuffer buf, STC stc) @safe
 
     if (stc & STC.scopeinferred)
     {
-        //buf.writestring("scope-inferred ");
+        //buf.put("scope-inferred ");
         stc &= ~(STC.scope_ | STC.scopeinferred);
     }
     if (stc & STC.returninferred)
     {
-        //buf.writestring((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred ");
+        //buf.put((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred ");
         stc &= ~(STC.return_ | STC.returninferred);
     }
 
     // ensure `auto ref` keywords are (almost) adjacent
     if (stc & STC.auto_)
     {
-        buf.writestring("auto ");
+        buf.put("auto ");
         stc &= ~STC.auto_;
     }
     /* Put scope ref return into a standard order
@@ -3344,7 +3399,7 @@ bool stcToBuffer(ref OutBuffer buf, STC stc) @safe
         case ScopeRef.ReturnRef_Scope:  rrs = isout ? "return out scope" : "return ref scope"; goto L1;
         case ScopeRef.Ref_ReturnScope:  rrs = isout ? "out return scope" : "ref return scope"; goto L1;
         L1:
-            buf.writestring(rrs);
+            buf.put(rrs);
             result = true;
             stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
             break;
@@ -3356,9 +3411,9 @@ bool stcToBuffer(ref OutBuffer buf, STC stc) @safe
         if (!s.length)
             break;
         if (result)
-            buf.writeByte(' ');
+            buf.put(' ');
         result = true;
-        buf.writestring(s);
+        buf.put(s);
     }
 
     return result;
@@ -3433,9 +3488,9 @@ private void linkageToBuffer(ref OutBuffer buf, LINK linkage) @safe
     const s = linkageToString(linkage);
     if (s.length)
     {
-        buf.writestring("extern (");
-        buf.writestring(s);
-        buf.writeByte(')');
+        buf.put("extern (");
+        buf.put(s);
+        buf.put(')');
     }
 }
 
@@ -3463,12 +3518,12 @@ string linkageToString(LINK linkage) pure nothrow @safe
 
 void visibilityToBuffer(ref OutBuffer buf, Visibility vis)
 {
-    buf.writestring(visibilityToString(vis.kind));
+    buf.put(visibilityToString(vis.kind));
     if (vis.kind == Visibility.Kind.package_ && vis.pkg)
     {
-        buf.writeByte('(');
-        buf.writestring(vis.pkg.toPrettyChars(true));
-        buf.writeByte(')');
+        buf.put('(');
+        buf.put(vis.pkg.toPrettyChars(true));
+        buf.put(')');
     }
 }
 
@@ -3528,7 +3583,7 @@ void argExpTypesToCBuffer(ref OutBuffer buf, Expressions* arguments)
     foreach (i, arg; *arguments)
     {
         if (i)
-            buf.writestring(", ");
+            buf.put(", ");
         typeToBuffer(arg.type, null, buf, hgs);
     }
 }
@@ -3541,7 +3596,7 @@ void arrayObjectsToBuffer(ref OutBuffer buf, Objects* objects)
     foreach (i, o; *objects)
     {
         if (i)
-            buf.writestring(", ");
+            buf.put(", ");
         objectToBuffer(o, buf, hgs);
     }
 }
@@ -3578,7 +3633,7 @@ const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQua
 
     if (tf.parameterList.varargs == VarArg.typesafe && parameter == tf.parameterList[tf.parameterList.parameters.length - 1])
     {
-        buf.writestring("...");
+        buf.put("...");
     }
     return buf.extractChars();
 }
@@ -3594,11 +3649,11 @@ const(char)* parameterToChars(Parameter parameter, TypeFunction tf, bool fullQua
 
 private void parametersToBuffer(ParameterList pl, ref OutBuffer buf, ref HdrGenState hgs)
 {
-    buf.writeByte('(');
+    buf.put('(');
     foreach (i; 0 .. pl.length)
     {
         if (i)
-            buf.writestring(", ");
+            buf.put(", ");
         pl[i].parameterToBuffer(buf, hgs);
     }
     final switch (pl.varargs)
@@ -3609,17 +3664,17 @@ private void parametersToBuffer(ParameterList pl, ref OutBuffer buf, ref HdrGenS
 
         case VarArg.variadic:
             if (pl.length)
-                buf.writestring(", ");
+                buf.put(", ");
 
             if (stcToBuffer(buf, pl.stc))
-                buf.writeByte(' ');
+                buf.put(' ');
             goto case VarArg.typesafe;
 
         case VarArg.typesafe:
-            buf.writestring("...");
+            buf.put("...");
             break;
     }
-    buf.writeByte(')');
+    buf.put(')');
 }
 
 
@@ -3634,52 +3689,52 @@ private void parameterToBuffer(Parameter p, ref OutBuffer buf, ref HdrGenState h
 {
     if (p.userAttribDecl)
     {
-        buf.writeByte('@');
+        buf.put('@');
 
         bool isAnonymous = p.userAttribDecl.atts.length > 0 && !(*p.userAttribDecl.atts)[0].isCallExp();
         if (isAnonymous)
-            buf.writeByte('(');
+            buf.put('(');
 
         argsToBuffer(p.userAttribDecl.atts, buf, hgs);
 
         if (isAnonymous)
-            buf.writeByte(')');
-        buf.writeByte(' ');
+            buf.put(')');
+        buf.put(' ');
     }
     if (p.storageClass & STC.auto_)
-        buf.writestring("auto ");
+        buf.put("auto ");
 
     STC stc = p.storageClass;
     if (p.storageClass & STC.in_)
     {
-        buf.writestring("in ");
+        buf.put("in ");
         if ((p.storageClass & (STC.constscoperef | STC.ref_)) == (STC.constscoperef | STC.ref_))
             stc &= ~STC.ref_;
     }
     else if (p.storageClass & STC.lazy_)
-        buf.writestring("lazy ");
+        buf.put("lazy ");
     else if (p.storageClass & STC.alias_)
-        buf.writestring("alias ");
+        buf.put("alias ");
 
     if (p.type && p.type.mod & MODFlags.shared_)
         stc &= ~STC.shared_;
 
     if (stcToBuffer(buf, stc & (STC.const_ | STC.immutable_ | STC.wild | STC.shared_ |
         STC.return_ | STC.returninferred | STC.scope_ | STC.scopeinferred | STC.out_ | STC.ref_ | STC.returnScope)))
-        buf.writeByte(' ');
+        buf.put(' ');
 
     const(char)[] s;
     if (p.storageClass & STC.alias_)
     {
         if (p.ident)
-            buf.writestring(p.ident.toString());
+            buf.put(p.ident.toString());
     }
     else if (p.type.isTypeIdentifier() &&
              (s = p.type.isTypeIdentifier().ident.toString()).length > 3 &&
              s[0..3] == "__T")
     {
         // print parameter name, instead of undetermined type parameter
-        buf.writestring(p.ident.toString());
+        buf.put(p.ident.toString());
     }
     else
     {
@@ -3688,7 +3743,7 @@ private void parameterToBuffer(Parameter p, ref OutBuffer buf, ref HdrGenState h
 
     if (p.defaultArg)
     {
-        buf.writestring(" = ");
+        buf.put(" = ");
         p.defaultArg.expToBuffer(PREC.assign, buf, hgs);
     }
 }
@@ -3712,12 +3767,12 @@ private void argsToBuffer(Expressions* expressions, ref OutBuffer buf, ref HdrGe
         foreach (i, el; *expressions)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
 
             if (names && i < names.length && (*names)[i].name)
             {
-                buf.writestring((*names)[i].name.toString());
-                buf.writestring(": ");
+                buf.put((*names)[i].name.toString());
+                buf.put(": ");
             }
             if (!el)
                 el = basis;
@@ -3731,9 +3786,9 @@ private void argsToBuffer(Expressions* expressions, ref OutBuffer buf, ref HdrGe
         //      [0..length: basis, 1: e1, 5: e5]
         if (basis)
         {
-            buf.writestring("0..");
+            buf.put("0..");
             buf.print(expressions.length);
-            buf.writestring(": ");
+            buf.put(": ");
             expToBuffer(basis, PREC.assign, buf, hgs);
         }
         foreach (i, el; *expressions)
@@ -3742,12 +3797,12 @@ private void argsToBuffer(Expressions* expressions, ref OutBuffer buf, ref HdrGe
             {
                 if (basis)
                 {
-                    buf.writestring(", ");
+                    buf.put(", ");
                     buf.print(i);
-                    buf.writestring(": ");
+                    buf.put(": ");
                 }
                 else if (i)
-                    buf.writestring(", ");
+                    buf.put(", ");
                 expToBuffer(el, PREC.assign, buf, hgs);
             }
         }
@@ -3770,7 +3825,7 @@ private void sizeToBuffer(Expression e, ref OutBuffer buf, ref HdrGenState hgs)
             }
             if (uval <= 0x7FFF_FFFF_FFFF_FFFFUL)
             {
-                buf.writestring("cast(size_t)");
+                buf.put("cast(size_t)");
                 buf.print(uval);
                 return;
             }
@@ -3784,33 +3839,51 @@ private void expressionToBuffer(Expression e, ref OutBuffer buf, ref HdrGenState
     expressionPrettyPrint(e, buf, hgs);
 }
 
+// to be called if e could be loweredFrom another expression instead of acessing precedence[e.op] directly
+private PREC expPrecedence(ref HdrGenState hgs, Expression e)
+{
+    if (!hgs.vcg_ast)
+    {
+        if (auto ce = e.isCallExp())
+        {
+            if (ce.loweredFrom)
+                e = ce.loweredFrom;
+        }
+        else if (auto ne = e.isNotExp())
+            if (ne.loweredFrom)
+                e = ne.loweredFrom;
+    }
+    return precedence[e.op];
+}
+
 /**************************************************
  * Write expression out to buf, but wrap it
  * in ( ) if its precedence is less than pr.
  */
 private void expToBuffer(Expression e, PREC pr, ref OutBuffer buf, ref HdrGenState hgs)
 {
+    auto prec = expPrecedence(hgs, e);
     debug
     {
-        if (precedence[e.op] == PREC.zero)
+        if (prec == PREC.zero)
             printf("precedence not defined for token '%s'\n", EXPtoString(e.op).ptr);
     }
     if (e.op == 0xFF)
     {
-        buf.writestring("<FF>");
+        buf.put("<FF>");
         return;
     }
-    assert(precedence[e.op] != PREC.zero);
+    assert(prec != PREC.zero);
     assert(pr != PREC.zero);
     /* Despite precedence, we don't allow a<b<c expressions.
      * They must be parenthesized.
      */
-    if (precedence[e.op] < pr || (pr == PREC.rel && precedence[e.op] == pr)
-        || (pr >= PREC.or && pr <= PREC.and && precedence[e.op] == PREC.rel))
+    if (prec < pr || (pr == PREC.rel && prec == pr)
+        || (pr >= PREC.or && pr <= PREC.and && prec == PREC.rel))
     {
-        buf.writeByte('(');
+        buf.put('(');
         e.expressionToBuffer(buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
     }
     else
     {
@@ -3833,8 +3906,8 @@ private void typeToBuffer(Type t, const Identifier ident, ref OutBuffer buf, ref
     visitWithMask(t, modMask, buf, hgs);
     if (ident)
     {
-        buf.writeByte(' ');
-        buf.writestring(ident.toString());
+        buf.put(' ');
+        buf.put(ident.toString());
     }
 }
 
@@ -3851,32 +3924,32 @@ private void visitWithMask(Type t, ubyte modMask, ref OutBuffer buf, ref HdrGenS
         if (m & MODFlags.shared_)
         {
             MODtoBuffer(buf, MODFlags.shared_);
-            buf.writeByte('(');
+            buf.put('(');
         }
         if (m & MODFlags.wild)
         {
             MODtoBuffer(buf, MODFlags.wild);
-            buf.writeByte('(');
+            buf.put('(');
         }
         if (m & (MODFlags.const_ | MODFlags.immutable_))
         {
             MODtoBuffer(buf, m & (MODFlags.const_ | MODFlags.immutable_));
-            buf.writeByte('(');
+            buf.put('(');
         }
         typeToBufferx(t, buf, hgs);
         if (m & (MODFlags.const_ | MODFlags.immutable_))
-            buf.writeByte(')');
+            buf.put(')');
         if (m & MODFlags.wild)
-            buf.writeByte(')');
+            buf.put(')');
         if (m & MODFlags.shared_)
-            buf.writeByte(')');
+            buf.put(')');
     }
 }
 
 
 private void dumpTemplateInstance(TemplateInstance ti, ref OutBuffer buf, ref HdrGenState hgs)
 {
-    buf.writeByte('{');
+    buf.put('{');
     buf.writenl();
     buf.level++;
 
@@ -3892,22 +3965,22 @@ private void dumpTemplateInstance(TemplateInstance ti, ref OutBuffer buf, ref Hd
     }
 
     buf.level--;
-    buf.writeByte('}');
+    buf.put('}');
     buf.writenl();
 
 }
 
 private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenState hgs)
 {
-    buf.writeByte('!');
+    buf.put('!');
     if (ti.nest)
     {
-        buf.writestring("(...)");
+        buf.put("(...)");
         return;
     }
     if (!ti.tiargs)
     {
-        buf.writestring("()");
+        buf.put("()");
         return;
     }
     if (ti.tiargs.length == 1)
@@ -3933,16 +4006,16 @@ private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenSt
             }
         }
     }
-    buf.writeByte('(');
+    buf.put('(');
     ti.nestUp();
     foreach (i, arg; *ti.tiargs)
     {
         if (i)
-            buf.writestring(", ");
+            buf.put(", ");
         objectToBuffer(arg, buf, hgs);
     }
     ti.nestDown();
-    buf.writeByte(')');
+    buf.put(')');
 }
 
 /****************************************
@@ -3971,9 +4044,9 @@ private void objectToBuffer(RootObject oarg, ref OutBuffer buf, ref HdrGenState
     else if (Dsymbol s = isDsymbol(oarg))
     {
         if (s.ident)
-            buf.writestring(s.ident.toString());
+            buf.put(s.ident.toString());
         else
-            buf.writestring(s.toChars());
+            buf.put(s.toChars());
     }
     else if (auto v = isTuple(oarg))
     {
@@ -3981,7 +4054,7 @@ private void objectToBuffer(RootObject oarg, ref OutBuffer buf, ref HdrGenState
         foreach (i, arg; *args)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             objectToBuffer(arg, buf, hgs);
         }
     }
@@ -3991,7 +4064,7 @@ private void objectToBuffer(RootObject oarg, ref OutBuffer buf, ref HdrGenState
     }
     else if (!oarg)
     {
-        buf.writestring("NULL");
+        buf.put("NULL");
     }
     else
     {
@@ -4015,7 +4088,7 @@ private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, ref O
     if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
     {
         linkageToBuffer(buf, t.linkage);
-        buf.writeByte(' ');
+        buf.put(' ');
     }
     if (t.linkage == LINK.objc && isStatic)
         buf.write("static ");
@@ -4023,25 +4096,25 @@ private void visitFuncIdentWithPostfix(TypeFunction t, const char[] ident, ref O
     {
         typeToBuffer(t.next, null, buf, hgs);
         if (ident)
-            buf.writeByte(' ');
+            buf.put(' ');
     }
     else if (hgs.ddoc)
-        buf.writestring("auto ");
+        buf.put("auto ");
     if (ident)
-        buf.writestring(ident);
+        buf.put(ident);
     parametersToBuffer(t.parameterList, buf, hgs);
     /* Use postfix style for attributes
      */
     if (t.mod)
     {
-        buf.writeByte(' ');
+        buf.put(' ');
         MODtoBuffer(buf, t.mod);
     }
 
     void dg(string str)
     {
-        buf.writeByte(' ');
-        buf.writestring(str);
+        buf.put(' ');
+        buf.put(str);
     }
     t.attributesApply(&dg);
 
@@ -4063,7 +4136,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te
     if (t.mod && !(hgs.ddoc || hgs.hdrgen))
     {
         MODtoBuffer(buf, t.mod);
-        buf.writeByte(' ');
+        buf.put(' ');
     }
 
     void dg(string str)
@@ -4073,8 +4146,8 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te
             // don't write 'ref' for ctors
             if ((ident == Id.ctor) && str == "ref")
                 return;
-            buf.writestring(str);
-            buf.writeByte(' ');
+            buf.put(str);
+            buf.put(' ');
         }
     }
     t.attributesApply(&dg);
@@ -4082,7 +4155,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te
     if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen)
     {
         linkageToBuffer(buf, t.linkage);
-        buf.writeByte(' ');
+        buf.put(' ');
     }
     if (ident && ident.toHChars2() != ident.toChars())
     {
@@ -4092,41 +4165,41 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te
     {
         typeToBuffer(t.next, null, buf, hgs);
         if (ident)
-            buf.writeByte(' ');
+            buf.put(' ');
     }
     else if (hgs.ddoc)
-        buf.writestring("auto ");
+        buf.put("auto ");
     if (ident)
-        buf.writestring(ident.toHChars2());
+        buf.put(ident.toHChars2());
     if (td)
     {
-        buf.writeByte('(');
+        buf.put('(');
         foreach (i, p; *td.origParameters)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             toCBuffer(p, buf, hgs);
         }
-        buf.writeByte(')');
+        buf.put(')');
     }
     parametersToBuffer(t.parameterList, buf, hgs);
     // postfix this attributes are more readable
     if (t.mod && (hgs.ddoc || hgs.hdrgen))
     {
-        buf.writeByte(' ');
+        buf.put(' ');
         MODtoBuffer(buf, t.mod);
     }
     if (t.isReturnScope && !t.isReturnInferred)
     {
-        buf.writestring(" return scope");
+        buf.put(" return scope");
     }
     else if (t.isScopeQual && !t.isScopeInferred)
     {
-        buf.writestring(" scope");
+        buf.put(" scope");
     }
     if (t.isReturn && !t.isReturnScope && !t.isReturnInferred)
     {
-        buf.writestring(" return");
+        buf.put(" return");
     }
     t.inuse--;
 }
@@ -4136,54 +4209,54 @@ private void initializerToBuffer(Initializer inx, ref OutBuffer buf, ref HdrGenS
 {
     void visitError(ErrorInitializer iz)
     {
-        buf.writestring("__error__");
+        buf.put("__error__");
     }
 
     void visitVoid(VoidInitializer iz)
     {
-        buf.writestring("void");
+        buf.put("void");
     }
 
     void visitDefault(DefaultInitializer iz)
     {
-        buf.writestring("{ }");
+        buf.put("{ }");
     }
 
     void visitStruct(StructInitializer si)
     {
         //printf("StructInitializer::toCBuffer()\n");
-        buf.writeByte('{');
+        buf.put('{');
         foreach (i, const id; si.field)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             if (id)
             {
-                buf.writestring(id.toString());
-                buf.writeByte(':');
+                buf.put(id.toString());
+                buf.put(':');
             }
             if (auto iz = si.value[i])
                 initializerToBuffer(iz, buf, hgs);
         }
-        buf.writeByte('}');
+        buf.put('}');
     }
 
     void visitArray(ArrayInitializer ai)
     {
-        buf.writeByte('[');
+        buf.put('[');
         foreach (i, ex; ai.index)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             if (ex)
             {
                 ex.expressionToBuffer(buf, hgs);
-                buf.writeByte(':');
+                buf.put(':');
             }
             if (auto iz = ai.value[i])
                 initializerToBuffer(iz, buf, hgs);
         }
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitExp(ExpInitializer ei)
@@ -4193,32 +4266,32 @@ private void initializerToBuffer(Initializer inx, ref OutBuffer buf, ref HdrGenS
 
     void visitC(CInitializer ci)
     {
-        buf.writeByte('{');
+        buf.put('{');
         foreach (i, ref DesigInit di; ci.initializerList)
         {
             if (i)
-                buf.writestring(", ");
+                buf.put(", ");
             if (di.designatorList)
             {
                 foreach (ref Designator d; (*di.designatorList)[])
                 {
                     if (d.exp)
                     {
-                        buf.writeByte('[');
+                        buf.put('[');
                         toCBuffer(d.exp, buf, hgs);
-                        buf.writeByte(']');
+                        buf.put(']');
                     }
                     else
                     {
-                        buf.writeByte('.');
-                        buf.writestring(d.ident.toString());
+                        buf.put('.');
+                        buf.put(d.ident.toString());
                     }
                 }
-                buf.writeByte('=');
+                buf.put('=');
             }
             initializerToBuffer(di.initializer, buf, hgs);
         }
-        buf.writeByte('}');
+        buf.put('}');
     }
 
     mixin VisitInitializer!void visit;
@@ -4236,13 +4309,13 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
 
     void visitError(TypeError t)
     {
-        buf.writestring("_error_");
+        buf.put("_error_");
     }
 
     void visitBasic(TypeBasic t)
     {
         //printf("TypeBasic::toCBuffer2(t.mod = %d)\n", t.mod);
-        buf.writestring(t.dstring);
+        buf.put(t.dstring);
     }
 
     void visitTraits(TypeTraits t)
@@ -4254,17 +4327,17 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
     void visitVector(TypeVector t)
     {
         //printf("TypeVector::toCBuffer2(t.mod = %d)\n", t.mod);
-        buf.writestring("__vector(");
+        buf.put("__vector(");
         visitWithMask(t.basetype, t.mod, buf, hgs);
-        buf.writestring(")");
+        buf.put(")");
     }
 
     void visitSArray(TypeSArray t)
     {
         visitWithMask(t.next, t.mod, buf, hgs);
-        buf.writeByte('[');
+        buf.put('[');
         sizeToBuffer(t.dim, buf, hgs);
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitDArray(TypeDArray t)
@@ -4273,25 +4346,25 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
         if (hgs.declstring)
             goto L1;
         if (ut.equals(Type.tstring))
-            buf.writestring("string");
+            buf.put("string");
         else if (ut.equals(Type.twstring))
-            buf.writestring("wstring");
+            buf.put("wstring");
         else if (ut.equals(Type.tdstring))
-            buf.writestring("dstring");
+            buf.put("dstring");
         else
         {
         L1:
             visitWithMask(t.next, t.mod, buf, hgs);
-            buf.writestring("[]");
+            buf.put("[]");
         }
     }
 
     void visitAArray(TypeAArray t)
     {
         visitWithMask(t.next, t.mod, buf, hgs);
-        buf.writeByte('[');
+        buf.put('[');
         visitWithMask(t.index, 0, buf, hgs);
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitPointer(TypePointer t)
@@ -4302,14 +4375,14 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
         else
         {
             visitWithMask(t.next, t.mod, buf, hgs);
-            buf.writeByte('*');
+            buf.put('*');
         }
     }
 
     void visitReference(TypeReference t)
     {
         visitWithMask(t.next, t.mod, buf, hgs);
-        buf.writeByte('&');
+        buf.put('&');
     }
 
     void visitFunction(TypeFunction t)
@@ -4330,23 +4403,23 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
             switch (id.dyncast()) with (DYNCAST)
             {
             case dsymbol:
-                buf.writeByte('.');
+                buf.put('.');
                 TemplateInstance ti = cast(TemplateInstance)id;
                 ti.dsymbolToBuffer(buf, hgs);
                 break;
             case expression:
-                buf.writeByte('[');
+                buf.put('[');
                 (cast(Expression)id).expressionToBuffer(buf, hgs);
-                buf.writeByte(']');
+                buf.put(']');
                 break;
             case type:
-                buf.writeByte('[');
+                buf.put('[');
                 typeToBufferx(cast(Type)id, buf, hgs);
-                buf.writeByte(']');
+                buf.put(']');
                 break;
             default:
-                buf.writeByte('.');
-                buf.writestring(id.toString());
+                buf.put('.');
+                buf.put(id.toString());
             }
         }
     }
@@ -4354,7 +4427,7 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
     void visitIdentifier(TypeIdentifier t)
     {
         //printf("visitTypeIdentifier() %s\n", t.ident.toChars());
-        buf.writestring(t.ident.toString());
+        buf.put(t.ident.toString());
         visitTypeQualifiedHelper(t);
     }
 
@@ -4366,22 +4439,22 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
 
     void visitTypeof(TypeTypeof t)
     {
-        buf.writestring("typeof(");
+        buf.put("typeof(");
         t.exp.expressionToBuffer(buf, hgs);
-        buf.writeByte(')');
+        buf.put(')');
         visitTypeQualifiedHelper(t);
     }
 
     void visitReturn(TypeReturn t)
     {
-        buf.writestring("typeof(return)");
+        buf.put("typeof(return)");
         visitTypeQualifiedHelper(t);
     }
 
     void visitEnum(TypeEnum t)
     {
         //printf("visitEnum: %s\n", t.sym.toChars());
-        buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+        buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
     }
 
     void visitStruct(TypeStruct t)
@@ -4393,9 +4466,9 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
         // while printing messages.
         TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
         if (ti && ti.aliasdecl == t.sym)
-            buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
+            buf.put(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
         else
-            buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+            buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
     }
 
     void visitClass(TypeClass t)
@@ -4405,9 +4478,9 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
         // while printing messages.
         TemplateInstance ti = t.sym.parent ? t.sym.parent.isTemplateInstance() : null;
         if (ti && ti.aliasdecl == t.sym)
-            buf.writestring(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
+            buf.put(hgs.fullQual ? ti.toPrettyChars() : ti.toChars());
         else
-            buf.writestring(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
+            buf.put(hgs.fullQual ? t.sym.toPrettyChars() : t.sym.toChars());
     }
 
     void visitTag(TypeTag t)
@@ -4417,25 +4490,25 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
             // https://issues.dlang.org/show_bug.cgi?id=24670
             // `const` must be parenthesized because it can be a return type
             if (t.mod & MODFlags.const_)
-                buf.writestring("const(");
+                buf.put("const(");
 
             // For C to D translation, `struct S` or `enum S` simply becomes `S`
-            buf.writestring(t.id.toString());
+            buf.put(t.id.toString());
 
             if (t.mod & MODFlags.const_)
-                buf.writestring(")");
+                buf.put(")");
             return;
         }
         // The following produces something like "const enum E : short"
         if (t.mod & MODFlags.const_)
-            buf.writestring("const ");
-        buf.writestring(Token.toString(t.tok));
-        buf.writeByte(' ');
+            buf.put("const ");
+        buf.put(Token.toString(t.tok));
+        buf.put(' ');
         if (t.id)
-            buf.writestring(t.id.toString());
+            buf.put(t.id.toString());
         if (t.tok == TOK.enum_ && t.base && t.base.ty != TY.Tint32)
         {
-            buf.writestring(" : ");
+            buf.put(" : ");
             visitWithMask(t.base, t.mod, buf, hgs);
         }
     }
@@ -4448,33 +4521,33 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs)
     void visitSlice(TypeSlice t)
     {
         visitWithMask(t.next, t.mod, buf, hgs);
-        buf.writeByte('[');
+        buf.put('[');
         sizeToBuffer(t.lwr, buf, hgs);
-        buf.writestring(" .. ");
+        buf.put(" .. ");
         sizeToBuffer(t.upr, buf, hgs);
-        buf.writeByte(']');
+        buf.put(']');
     }
 
     void visitNull(TypeNull t)
     {
-        buf.writestring("typeof(null)");
+        buf.put("typeof(null)");
     }
 
     void visitMixin(TypeMixin t)
     {
-        buf.writestring("mixin(");
+        buf.put("mixin(");
         argsToBuffer(t.exps, buf, hgs, null);
-        buf.writeByte(')');
+        buf.put(')');
     }
 
     void visitNoreturn(TypeNoreturn t)
     {
-        buf.writestring("noreturn");
+        buf.put("noreturn");
     }
 
     if (hgs.importcHdr && !hgs.inCAlias && t.mcache && t.mcache.typedefIdent)
     {
-        buf.writestring(t.mcache.typedefIdent.toString());
+        buf.put(t.mcache.typedefIdent.toString());
         return;
     }
 
diff --git a/gcc/d/dmd/iasm/gcc.d b/gcc/d/dmd/iasm/gcc.d
new file mode 100644 (file)
index 0000000..4be94d7
--- /dev/null
@@ -0,0 +1,780 @@
+/**
+ * Inline assembler for the GCC D compiler.
+ *
+ *              Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved
+ * Authors:     Iain Buclaw
+ * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d)
+ * Documentation:  https://dlang.org/phobos/dmd_iasmgcc.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d
+ */
+
+module dmd.iasm.gcc;
+
+import core.stdc.string;
+
+import dmd.arraytypes;
+import dmd.astcodegen;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.errors;
+import dmd.errorsink;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.identifier;
+import dmd.globals;
+import dmd.location;
+import dmd.cparse;
+import dmd.parse;
+import dmd.target;
+import dmd.tokens;
+import dmd.statement;
+import dmd.statementsem;
+
+/***********************************
+ * Parse and run semantic analysis on a GccAsmStatement.
+ * Params:
+ *      s  = gcc asm statement being parsed
+ *      sc = the scope where the asm statement is located
+ * Returns:
+ *      the completed gcc asm statement, or null if errors occurred
+ */
+public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc)
+{
+    //printf("GccAsmStatement.semantic()\n");
+    const bool doUnittests = global.params.parsingUnittestsRequired();
+    scope p = (sc && sc.inCfile)
+        ? new CParser!ASTCodegen(sc._module, "", false, global.errorSink, target.c, null, &global.compileEnv)
+        : new Parser!ASTCodegen(sc._module, "", false, global.errorSink, &global.compileEnv, doUnittests);
+
+    // Make a safe copy of the token list before parsing.
+    Token* toklist = null;
+    Token **ptoklist = &toklist;
+
+    for (Token* token = s.tokens; token; token = token.next)
+    {
+        *ptoklist = p.allocateToken();
+        memcpy(*ptoklist, token, Token.sizeof);
+        ptoklist = &(*ptoklist).next;
+        *ptoklist = null;
+    }
+    // Append closing `;` location.
+    *ptoklist = p.allocateToken();
+    (*ptoklist).value = TOK.semicolon;
+    (*ptoklist).loc = s.loc;
+    ptoklist = &(*ptoklist).next;
+    *ptoklist = null;
+
+    // Adjust starting line number of the parser.
+    p.token = *toklist;
+    p.baseLoc.startLine = s.loc.linnum;
+    p.linnum = s.loc.linnum;
+
+    // Parse the gcc asm statement.
+    const errors = global.errors;
+    s = p.parseGccAsm(s);
+    if (errors != global.errors)
+        return null;
+    s.stc = sc.stc;
+
+    // Fold the instruction template string.
+    s.insn = semanticAsmString(sc, s.insn, "asm instruction template");
+
+    if (s.labels && s.outputargs)
+        p.eSink.error(s.loc, "extended asm statements with labels cannot have output constraints");
+
+    // Analyse all input and output operands.
+    if (s.args)
+    {
+        foreach (i; 0 .. s.args.length)
+        {
+            Expression ec = (*s.constraints)[i];
+            (*s.constraints)[i] = semanticAsmString(sc, ec, "asm operand");
+
+            Expression earg = (*s.args)[i];
+            earg = earg.expressionSemantic(sc);
+            // Check argument is a valid lvalue/rvalue.
+            if (i < s.outputargs)
+                earg = earg.modifiableLvalue(sc);
+            else if (earg.checkValue())
+                earg = ErrorExp.get();
+            (*s.args)[i] = earg;
+        }
+    }
+
+    // Analyse all clobbers.
+    if (s.clobbers)
+    {
+        foreach (i; 0 .. s.clobbers.length)
+        {
+            Expression ec = (*s.clobbers)[i];
+            (*s.clobbers)[i] = semanticAsmString(sc, ec, "asm clobber");
+        }
+    }
+
+    // Analyse all goto labels.
+    if (s.labels)
+    {
+        foreach (i; 0 .. s.labels.length)
+        {
+            Identifier ident = (*s.labels)[i];
+            GotoStatement gs = new GotoStatement(s.loc, ident);
+            if (!s.gotos)
+                s.gotos = new GotoStatements();
+            s.gotos.push(gs);
+            gs.statementSemantic(sc);
+        }
+    }
+
+    return s;
+}
+
+/***********************************
+ * Run semantic analysis on an CAsmDeclaration.
+ * Params:
+ *      ad  = asm declaration
+ *      sc = the scope where the asm declaration is located
+ */
+public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc)
+{
+    import dmd.typesem : pointerTo;
+    ad.code = semanticString(sc, ad.code, "asm definition");
+    ad.code.type = ad.code.type.nextOf().pointerTo();
+
+    // Asm definition always needs emitting into the root module.
+    import dmd.dmodule : Module;
+    if (sc._module && sc._module.isRoot())
+        return;
+    if (Module m = Module.rootModule)
+        m.members.push(ad);
+}
+
+private:
+
+
+/***********************************
+ * Issue error if the current token is not `value`.
+ * Otherwise, advance to next token.
+ * Params:
+ *      p = parser state
+ *      value = token value to compare with
+ * Returns:
+ *      true    advanced to next token
+ *      false   error was issued
+ */
+bool requireToken(Parser)(Parser p, TOK value)
+{
+    if (p.token.value == value)
+    {
+        p.nextToken();
+        return true;
+    }
+
+    p.eSink.error(p.token.loc, "found `%s` when expecting `%s`",
+                  p.token.toChars(), Token.toChars(value));
+    return false;
+}
+
+/***********************************
+ * Run semantic analysis on `exp`, resolving it as a compile-time string.
+ * Params:
+ *      sc = scope
+ *      exp = Expression which expected as a string
+ *      s = What the string is expected for, used in error diagnostic
+ * Returns:
+ *      StringExp or ErrorExp
+ */
+Expression semanticAsmString(Scope* sc, Expression exp, const char *s)
+{
+    import dmd.dcast : implicitCastTo;
+    import dmd.dsymbolsem : resolveAliasThis;
+    import dmd.mtype : isAggregate, Type;
+
+    exp = expressionSemantic(exp, sc);
+
+    // Resolve `alias this` if we were given a struct literal.
+    if (auto ad = isAggregate(exp.type))
+    {
+        if (ad.aliasthis && ad.type && !ad.type.isTypeError())
+            exp = resolveAliasThis(sc, exp);
+    }
+
+    // Evaluate the expression as a string now or error trying.
+    if (auto se = semanticString(sc, exp, s))
+        exp = implicitCastTo(se, sc, Type.tstring);
+
+    return exp;
+}
+
+/***********************************
+ * Parse a D or ImportC assignment expression
+ */
+Expression parseAssignment(Parser)(Parser p)
+{
+    if (p.Ccompile)
+        return (cast(CParser!ASTCodegen)p).cparseAssignExp();
+
+    return p.parseAssignExp();
+}
+
+/***********************************
+ * Parse a D or ImportC conditional expression
+ */
+Expression parseConditional(Parser)(Parser p)
+{
+    if (p.Ccompile)
+        return (cast(CParser!ASTCodegen)p).cparseCondExp();
+
+    return p.parseCondExp();
+}
+
+/***********************************
+ * Parse a D or ImportC primary expression
+ */
+Expression parsePrimary(Parser)(Parser p)
+{
+    if (p.Ccompile)
+        return (cast(CParser!ASTCodegen)p).cparsePrimaryExp();
+
+    return p.parsePrimaryExp();
+}
+
+/***********************************
+ * Parse an expression that evaluates to a string.
+ * Grammar:
+ *      | AsmStringExpr:
+ *      |     StringLiteral
+ *      |     ( ConditionalExpression )
+ * Params:
+ *      p = parser state
+ * Returns:
+ *      the parsed string expression
+ */
+Expression parseAsmString(Parser)(Parser p)
+{
+    if (p.token.value == TOK.leftParenthesis)
+    {
+        // Skip over opening `(`
+        p.nextToken();
+        Expression insn = p.parseConditional();
+        if (insn.isErrorExp())
+            return insn;
+
+        // Look for closing `)`.
+        if (!p.requireToken(TOK.rightParenthesis))
+            return ErrorExp.get();
+
+        return insn;
+    }
+    else if (p.token.value != TOK.string_)
+    {
+        p.eSink.error(p.token.loc, "expected string literal or expression in parentheses");
+        return ErrorExp.get();
+    }
+
+    return p.parsePrimary();
+}
+
+/***********************************
+ * Parse list of extended asm input or output operands.
+ * Grammar:
+ *      | Operands:
+ *      |     SymbolicName(opt) AsmStringExpr ( AssignExpression )
+ *      |     SymbolicName(opt) AsmStringExpr ( AssignExpression ), Operands
+ *      |
+ *      | SymbolicName:
+ *      |     [ Identifier ]
+ * Params:
+ *      p = parser state
+ *      s = asm statement to parse
+ * Returns:
+ *      number of operands added to the gcc asm statement
+ */
+int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
+{
+    int numargs = 0;
+
+    if (p.token.value == TOK.colon ||
+        p.token.value == TOK.colonColon ||
+        p.token.value == TOK.semicolon ||
+        p.token.value == TOK.endOfFile)
+        return numargs;
+
+    while (1)
+    {
+        Expression arg;
+        Identifier name;
+
+        if (p.token.value == TOK.leftBracket)
+        {
+            // Skip over opening `[`
+            p.nextToken();
+            if (p.token.value == TOK.identifier)
+            {
+                // Store the symbolic name
+                name = p.token.ident;
+                p.nextToken();
+            }
+            else
+            {
+                p.eSink.error(p.token.loc, "identifier expected after `[`");
+                goto Lerror;
+            }
+            // Look for closing `]`
+            if (!p.requireToken(TOK.rightBracket))
+                goto Lerror;
+        }
+
+        // Look for the constraint string.
+        Expression constraint = p.parseAsmString();
+        if (constraint.isErrorExp())
+            goto Lerror;
+
+        // Look for the opening `(`
+        if (!p.requireToken(TOK.leftParenthesis))
+            goto Lerror;
+
+        // Parse the assign expression
+        arg = p.parseAssignment();
+        if (arg.isErrorExp())
+            goto Lerror;
+
+        // Look for the closing `)`
+        if (!p.requireToken(TOK.rightParenthesis))
+            goto Lerror;
+
+        // Add this operand to the list.
+        if (!s.args)
+        {
+            s.names = new Identifiers();
+            s.constraints = new Expressions();
+            s.args = new Expressions();
+        }
+        s.names.push(name);
+        s.args.push(arg);
+        s.constraints.push(constraint);
+        numargs++;
+
+        // If the next token is not a `,`, there are no more operands.
+        if (p.token.value != TOK.comma)
+            return numargs;
+
+        // Skip over the `,` token.
+        p.nextToken();
+    }
+Lerror:
+    while (p.token.value != TOK.semicolon &&
+           p.token.value != TOK.endOfFile)
+        p.nextToken();
+
+    return 0;
+}
+
+/***********************************
+ * Parse list of extended asm clobbers.
+ * Grammar:
+ *      | Clobbers:
+ *      |     AsmStringExpr
+ *      |     AsmStringExpr , Clobbers
+ * Params:
+ *      p = parser state
+ * Returns:
+ *      array of parsed clobber expressions
+ */
+Expressions* parseExtAsmClobbers(Parser)(Parser p)
+{
+    Expressions* clobbers;
+
+    if (p.token.value == TOK.colon ||
+        p.token.value == TOK.colonColon ||
+        p.token.value == TOK.semicolon ||
+        p.token.value == TOK.endOfFile)
+        return clobbers;
+
+    while (1)
+    {
+        // Look for the clobbers string
+        Expression clobber = p.parseAsmString();
+        if (clobber.isErrorExp())
+            goto Lerror;
+
+        // Add it to the list.
+        if (!clobbers)
+            clobbers = new Expressions();
+        clobbers.push(clobber);
+
+        // If the next token is not a `,`, there are no more clobbers.
+        if (p.token.value != TOK.comma)
+            return clobbers;
+
+        // Skip over the `,` token.
+        p.nextToken();
+    }
+Lerror:
+    while (p.token.value != TOK.semicolon &&
+           p.token.value != TOK.endOfFile)
+        p.nextToken();
+
+    return null;
+}
+
+/***********************************
+ * Parse list of extended asm goto labels.
+ * Grammar:
+ *      | GotoLabels:
+ *      |     Identifier
+ *      |     Identifier , GotoLabels
+ * Params:
+ *      p = parser state
+ * Returns:
+ *      array of parsed goto labels
+ */
+Identifiers* parseExtAsmGotoLabels(Parser)(Parser p)
+{
+    Identifiers* labels;
+
+    while (1)
+    {
+        if (p.token.value == TOK.identifier)
+        {
+            if (!labels)
+                labels = new Identifiers();
+            labels.push(p.token.ident);
+
+            // If the next token is not a `,`, there are no more labels.
+            if (p.nextToken() != TOK.comma)
+                return labels;
+
+            // Skip over the `,` token.
+            p.nextToken();
+        }
+        else
+        {
+            p.eSink.error(p.token.loc, "identifier expected for goto label name, not `%s`",
+                          p.token.toChars());
+            goto Lerror;
+        }
+    }
+Lerror:
+    while (p.token.value != TOK.semicolon &&
+           p.token.value != TOK.endOfFile)
+        p.nextToken();
+
+    return null;
+}
+
+/***********************************
+ * Parse a gcc asm statement.
+ * There are three forms of inline asm statements, basic, extended, and goto.
+ * Grammar:
+ *      | AsmInstruction:
+ *      |     BasicAsmInstruction
+ *      |     ExtAsmInstruction
+ *      |     GotoAsmInstruction
+ *      |
+ *      | BasicAsmInstruction:
+ *      |     AsmStringExpr
+ *      |
+ *      | ExtAsmInstruction:
+ *      |     AsmStringExpr : Operands(opt) : Operands(opt) : Clobbers(opt)
+ *      |
+ *      | GotoAsmInstruction:
+ *      |     AsmStringExpr : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
+ * Params:
+ *      p = parser state
+ *      s = asm statement to parse
+ * Returns:
+ *      the parsed gcc asm statement
+ */
+GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s)
+{
+    s.insn = p.parseAsmString();
+    if (s.insn.isErrorExp())
+        return s;
+
+    // No semicolon followed after instruction template, treat as extended asm.
+    if (p.token.value == TOK.colon || p.token.value == TOK.colonColon)
+    {
+        bool inputs;
+        bool clobbers;
+        bool labels;
+
+        // Look for outputs.
+        if (p.token.value == TOK.colon)
+        {
+            // Skip over the `:` token.
+            p.nextToken();
+            // Parse the output operands.
+            s.outputargs = p.parseExtAsmOperands(s);
+        }
+        else if (p.token.value == TOK.colonColon)
+            inputs = true;
+
+        // Look for inputs.
+        if (inputs || p.token.value == TOK.colon)
+        {
+            // Skip over the `:` or `::` token.
+            p.nextToken();
+            // Parse the input operands.
+            p.parseExtAsmOperands(s);
+        }
+        else if (p.token.value == TOK.colonColon)
+            clobbers = true;
+
+        // Look for clobbers.
+        if (clobbers || p.token.value == TOK.colon)
+        {
+            // Skip over the `:` or `::` token.
+            p.nextToken();
+            // Parse the clobbers.
+            s.clobbers = p.parseExtAsmClobbers();
+        }
+        else if (p.token.value == TOK.colonColon)
+            labels = true;
+
+        // Look for labels.
+        if (labels || p.token.value == TOK.colon)
+        {
+            // Skip over the `:` or `::` token.
+            p.nextToken();
+            // Parse the labels.
+            s.labels = p.parseExtAsmGotoLabels();
+        }
+    }
+
+    if (p.token.value == TOK.endOfFile)
+        assert(global.errors);
+    else
+        p.requireToken(TOK.semicolon);
+
+    return s;
+}
+
+unittest
+{
+    import dmd.mtype : TypeBasic;
+
+    if (!global.errorSink)
+        global.errorSink = new ErrorSinkCompiler;
+
+    const errors = global.startGagging();
+    scope(exit) global.endGagging(errors);
+
+    // If this check fails, then Type._init() was called before reaching here,
+    // and the entire chunk of code that follows can be removed.
+    assert(ASTCodegen.Type.tint32 is null);
+    // Minimally initialize the cached types in ASTCodegen.Type, as they are
+    // dependencies for some fail asm tests to succeed.
+    ASTCodegen.Type.stringtable._init();
+    scope(exit)
+    {
+        ASTCodegen.Type.deinitialize();
+        ASTCodegen.Type.tint32 = null;
+        ASTCodegen.Type.tchar = null;
+    }
+    scope tint32 = new TypeBasic(ASTCodegen.Tint32);
+    ASTCodegen.Type.tint32 = tint32;
+    scope tchar = new TypeBasic(ASTCodegen.Tchar);
+    ASTCodegen.Type.tchar = tchar;
+
+    // Imitates asmSemantic if version = IN_GCC.
+    static int semanticAsm(Token* tokens, bool importC)
+    {
+        const errors = global.errors;
+        scope gas = new GccAsmStatement(Loc.initial, tokens);
+        const bool doUnittests = false;
+        scope p = importC
+            ? new CParser!ASTCodegen(null, ";", false, global.errorSink, target.c, null, &global.compileEnv)
+            : new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests);
+        p.token = *tokens;
+        p.parseGccAsm(gas);
+        return global.errors - errors;
+    }
+
+    // Imitates parseStatement for asm statements.
+    static void parseAsm(string input, bool expectError, bool importC = false)
+    {
+        // Generate tokens from input test.
+        const bool doUnittests = false;
+        scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests);
+        p.nextToken();
+
+        Token* toklist = null;
+        Token** ptoklist = &toklist;
+        p.check(TOK.asm_);
+        p.check(TOK.leftCurly);
+        while (1)
+        {
+            if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile)
+                break;
+            if (p.token.value == TOK.colonColon)
+            {
+                *ptoklist = p.allocateToken();
+                memcpy(*ptoklist, &p.token, Token.sizeof);
+                (*ptoklist).value = TOK.colon;
+                ptoklist = &(*ptoklist).next;
+
+                *ptoklist = p.allocateToken();
+                memcpy(*ptoklist, &p.token, Token.sizeof);
+                (*ptoklist).value = TOK.colon;
+                ptoklist = &(*ptoklist).next;
+            }
+            else
+            {
+                *ptoklist = p.allocateToken();
+                memcpy(*ptoklist, &p.token, Token.sizeof);
+                ptoklist = &(*ptoklist).next;
+            }
+            *ptoklist = null;
+            p.nextToken();
+        }
+        p.check(TOK.rightCurly);
+
+        auto res = semanticAsm(toklist, importC);
+        // Checks for both unexpected passes and failures.
+        assert((res == 0) != expectError, input);
+    }
+
+    /// Assembly Tests, all should pass.
+    /// Note: Frontend is not initialized, use only strings and identifiers.
+    immutable string[] passAsmTests = [
+        // Basic asm statement
+        q{ asm { "nop";
+        } },
+
+        // Extended asm statement
+        q{ asm { "cpuid"
+               : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
+               : "a" (input);
+        } },
+
+        // Assembly with symbolic names
+        q{ asm { "bts %[base], %[offset]"
+               : [base] "+rm" (*ptr)
+               : [offset] "Ir" (bitnum);
+        } },
+
+        // Assembly with clobbers
+        q{ asm { "cpuid"
+               : "=a" (a)
+               : "a" (input)
+               : "ebx", "ecx", "edx";
+        } },
+
+        // Goto asm statement
+        q{ asm { "jmp %l0"
+               :
+               :
+               :
+               : Ljmplabel;
+        } },
+
+        // Any CTFE-able string allowed as instruction template.
+        q{ asm { (generateAsm);
+        } },
+
+        // Likewise mixins, permissible so long as the result is a string.
+        q{ asm { (mixin(`"repne"`, `~ "scasb"`));
+        } },
+
+        // :: token tests
+        q{ asm { "" : : : "memory"; } },
+        q{ asm { "" :: : "memory"; } },
+        q{ asm { "" : :: "memory"; } },
+        q{ asm { "" ::: "memory"; } },
+        q{ asm { "" :::: label; } },
+
+        // https://github.com/dlang/dmd/issues/21299
+        q{ asm { (insn) : (output) (a) : (input) (1) : (clobber); } },
+        q{ asm { (['t','e','s','t']) : (['=','r']) (a) : (['r']) (1) : (['m','e','m','o','r','y']); } },
+
+        // https://github.com/dlang/dmd/issues/21679
+        q{ asm { "" : "=r" (s.x); } },
+    ];
+
+    immutable string[] failAsmTests = [
+        // Found 'h' when expecting ';'
+        q{ asm { ""h;
+        } },
+
+        // https://issues.dlang.org/show_bug.cgi?id=20592
+        q{ asm { "nop" : [name] string (expr); } },
+
+        // Expression expected, not ';'
+        q{ asm { ""[;
+        } },
+
+        // Expression expected, not ':'
+        q{ asm { ""
+               :
+               : "g" (a ? b : : c);
+        } },
+
+        // Found ',' when expecting ':'
+        q{ asm { "", "";
+        } },
+
+        // Identifier expected, not ';'
+        q{ asm { "" : ::: ; } },
+        q{ asm { "" :: :: ; } },
+        q{ asm { "" ::: : ; } },
+        q{ asm { "" :::: ; } },
+
+        // https://issues.dlang.org/show_bug.cgi?id=20593
+        q{ asm { "instruction" : : "operand" 123; } },
+
+        // https://github.com/dlang/dmd/issues/21298
+        q{ asm { 1; }   },
+        q{ asm { int; } },
+        q{ asm { : "=r" (i); } },
+        q{ asm { (; } },
+        q{ asm { (""; } },
+        q{ asm { "" ,; } },
+        q{ asm { "" d; } },
+        q{ asm { "" : (; } },
+        q{ asm { "" : (""; } },
+        q{ asm { "" : ""; } },
+        q{ asm { "" : "" (; } },
+        q{ asm { "" : "" (a; } },
+        q{ asm { "" : "" (a) ,; } },
+        q{ asm { "" : "" (a) d; } },
+        q{ asm { "" : "" (a) : (; } },
+        q{ asm { "" : "" (a) : (""; } },
+        q{ asm { "" : "" (a) : ""; } },
+        q{ asm { "" : "" (a) : "" (; } },
+        q{ asm { "" : "" (a) : "" (b; } },
+        q{ asm { "" : "" (a) : "" (b) ,; } },
+        q{ asm { "" : "" (a) : "" (b) d; } },
+        q{ asm { "" : "" (a) : "" (b) : (; } },
+        q{ asm { "" : "" (a) : "" (b) : (""; } },
+        q{ asm { "" : "" (a) : "" (b) : "" ,; } },
+        q{ asm { "" : "" (a) : "" (b) : "" d; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : (; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c ,; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c d; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c :; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c : (; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c : (""; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c : "" (; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c : "" (d; } },
+        q{ asm { "" : "" (a) : "" (b) : "" : c : "" (d); } },
+
+        // https://github.com/dlang/dmd/issues/21679
+        q{ asm { "" : "=r" (s->x); } },
+    ];
+
+    immutable string[] passCAsmTests = [
+        // https://github.com/dlang/dmd/issues/21679
+        q{ asm { "" : "=r" (s->x); } },
+        q{ asm { "" : "=r" (s.x); } }
+    ];
+
+    foreach (test; passAsmTests)
+        parseAsm(test, false);
+
+    foreach (test; failAsmTests)
+        parseAsm(test, true);
+
+    foreach (test; passCAsmTests)
+        parseAsm(test, false, /*importC*/true);
+}
similarity index 87%
rename from gcc/d/dmd/iasm.d
rename to gcc/d/dmd/iasm/package.d
index 689ef0f9fee12957a3ebce4c508ac15ff59d40d9..33af0ca11dd5eb8e4ef854db1cf8b0533bd21923 100644 (file)
@@ -20,6 +20,7 @@ import dmd.dsymbol;
 import dmd.expression;
 import dmd.func;
 import dmd.mtype;
+import dmd.target;
 import dmd.tokens;
 import dmd.statement;
 import dmd.statementsem;
@@ -29,14 +30,24 @@ version (NoBackend)
 }
 else version (IN_GCC)
 {
-    import dmd.iasmgcc;
+    version = Asm_GCC;
+}
+else version (IN_LLVM)
+{
+    version = Asm_GCC;
 }
 else
 {
-    import dmd.iasmdmd;
+    import dmd.iasm.dmdx86;
+    import dmd.iasm.dmdaarch64;
     version = MARS;
 }
 
+version (Asm_GCC)
+{
+    import dmd.iasm.gcc;
+}
+
 /************************ AsmStatement ***************************************/
 
 Statement asmSemantic(AsmStatement s, Scope* sc)
@@ -73,9 +84,11 @@ Statement asmSemantic(AsmStatement s, Scope* sc)
         }
         auto ias = new InlineAsmStatement(s.loc, s.tokens);
         ias.caseSensitive = s.caseSensitive;
-        return inlineAsmSemantic(ias, sc);
+        return (target.isAArch64)
+            ? inlineAsmAArch64Semantic(ias, sc)
+            : inlineAsmSemantic(ias, sc);       // X86_64
     }
-    else version (IN_GCC)
+    else version (Asm_GCC)
     {
         auto eas = new GccAsmStatement(s.loc, s.tokens);
         return gccAsmSemantic(eas, sc);
@@ -94,7 +107,7 @@ void asmSemantic(CAsmDeclaration ad, Scope* sc)
     version (NoBackend)
     {
     }
-    else version (IN_GCC)
+    else version (Asm_GCC)
     {
         return gccAsmSemantic(ad, sc);
     }
diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d
deleted file mode 100644 (file)
index 4d9a758..0000000
+++ /dev/null
@@ -1,604 +0,0 @@
-/**
- * Inline assembler for the GCC D compiler.
- *
- *              Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved
- * Authors:     Iain Buclaw
- * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
- * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d)
- * Documentation:  https://dlang.org/phobos/dmd_iasmgcc.html
- * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d
- */
-
-module dmd.iasmgcc;
-
-import core.stdc.string;
-
-import dmd.arraytypes;
-import dmd.astcodegen;
-import dmd.dscope;
-import dmd.dsymbol;
-import dmd.errors;
-import dmd.errorsink;
-import dmd.expression;
-import dmd.expressionsem;
-import dmd.identifier;
-import dmd.globals;
-import dmd.location;
-import dmd.parse;
-import dmd.tokens;
-import dmd.statement;
-import dmd.statementsem;
-
-/***********************************
- * Parse and run semantic analysis on a GccAsmStatement.
- * Params:
- *      s  = gcc asm statement being parsed
- *      sc = the scope where the asm statement is located
- * Returns:
- *      the completed gcc asm statement, or null if errors occurred
- */
-public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc)
-{
-    //printf("GccAsmStatement.semantic()\n");
-    const bool doUnittests = global.params.parsingUnittestsRequired();
-    scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests);
-
-    // Make a safe copy of the token list before parsing.
-    Token* toklist = null;
-    Token **ptoklist = &toklist;
-
-    for (Token* token = s.tokens; token; token = token.next)
-    {
-        *ptoklist = p.allocateToken();
-        memcpy(*ptoklist, token, Token.sizeof);
-        ptoklist = &(*ptoklist).next;
-        *ptoklist = null;
-    }
-    p.token = *toklist;
-    p.baseLoc.startLine = s.loc.linnum;
-    p.linnum = s.loc.linnum;
-
-    // Parse the gcc asm statement.
-    const errors = global.errors;
-    s = p.parseGccAsm(s);
-    if (errors != global.errors)
-        return null;
-    s.stc = sc.stc;
-
-    // Fold the instruction template string.
-    s.insn = semanticString(sc, s.insn, "asm instruction template");
-
-    if (s.labels && s.outputargs)
-        p.eSink.error(s.loc, "extended asm statements with labels cannot have output constraints");
-
-    // Analyse all input and output operands.
-    if (s.args)
-    {
-        foreach (i; 0 .. s.args.length)
-        {
-            Expression e = (*s.args)[i];
-            e = e.expressionSemantic(sc);
-            // Check argument is a valid lvalue/rvalue.
-            if (i < s.outputargs)
-                e = e.modifiableLvalue(sc);
-            else if (e.checkValue())
-                e = ErrorExp.get();
-            (*s.args)[i] = e;
-
-            e = (*s.constraints)[i];
-            e = e.expressionSemantic(sc);
-            assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
-            (*s.constraints)[i] = e;
-        }
-    }
-
-    // Analyse all clobbers.
-    if (s.clobbers)
-    {
-        foreach (i; 0 .. s.clobbers.length)
-        {
-            Expression e = (*s.clobbers)[i];
-            e = e.expressionSemantic(sc);
-            assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1);
-            (*s.clobbers)[i] = e;
-        }
-    }
-
-    // Analyse all goto labels.
-    if (s.labels)
-    {
-        foreach (i; 0 .. s.labels.length)
-        {
-            Identifier ident = (*s.labels)[i];
-            GotoStatement gs = new GotoStatement(s.loc, ident);
-            if (!s.gotos)
-                s.gotos = new GotoStatements();
-            s.gotos.push(gs);
-            gs.statementSemantic(sc);
-        }
-    }
-
-    return s;
-}
-
-/***********************************
- * Run semantic analysis on an CAsmDeclaration.
- * Params:
- *      ad  = asm declaration
- *      sc = the scope where the asm declaration is located
- */
-public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc)
-{
-    import dmd.typesem : pointerTo;
-    ad.code = semanticString(sc, ad.code, "asm definition");
-    ad.code.type = ad.code.type.nextOf().pointerTo();
-
-    // Asm definition always needs emitting into the root module.
-    import dmd.dmodule : Module;
-    if (sc._module && sc._module.isRoot())
-        return;
-    if (Module m = Module.rootModule)
-        m.members.push(ad);
-}
-
-private:
-
-/***********************************
- * Parse an expression that evaluates to a string.
- * Grammar:
- *      | AsmStringExpr:
- *      |     StringLiteral
- *      |     ( AssignExpression )
- * Params:
- *      p = parser state
- * Returns:
- *      the parsed string expression
- */
-Expression parseAsmString(Parser)(Parser p)
-{
-    if (p.token.value == TOK.leftParenthesis)
-    {
-        p.nextToken();
-        Expression insn = p.parseAssignExp();
-        p.check(TOK.rightParenthesis);
-        return insn;
-    }
-    else if (p.token.value != TOK.string_)
-    {
-        p.eSink.error(p.token.loc, "expected string literal or expression in parentheses");
-        return ErrorExp.get();
-    }
-
-    return p.parsePrimaryExp();
-}
-
-/***********************************
- * Parse list of extended asm input or output operands.
- * Grammar:
- *      | Operands:
- *      |     SymbolicName(opt) StringLiteral ( AssignExpression )
- *      |     SymbolicName(opt) StringLiteral ( AssignExpression ), Operands
- *      |
- *      | SymbolicName:
- *      |     [ Identifier ]
- * Params:
- *      p = parser state
- *      s = asm statement to parse
- * Returns:
- *      number of operands added to the gcc asm statement
- */
-int parseExtAsmOperands(Parser)(Parser p, GccAsmStatement s)
-{
-    int numargs = 0;
-
-    while (1)
-    {
-        Expression arg;
-        Identifier name;
-        Expression constraint;
-
-        switch (p.token.value)
-        {
-            case TOK.semicolon:
-            case TOK.colon:
-            case TOK.endOfFile:
-                return numargs;
-
-            case TOK.leftBracket:
-                if (p.peekNext() == TOK.identifier)
-                {
-                    // Skip over opening `[`
-                    p.nextToken();
-                    // Store the symbolic name
-                    name = p.token.ident;
-                    p.nextToken();
-                }
-                else
-                {
-                    p.eSink.error(s.loc, "expected identifier after `[`");
-                    goto Lerror;
-                }
-                // Look for closing `]`
-                p.check(TOK.rightBracket);
-                // Look for the string literal and fall through
-                if (p.token.value == TOK.string_)
-                    goto case;
-                else
-                    goto default;
-
-            case TOK.string_:
-                constraint = p.parsePrimaryExp();
-                if (p.token.value != TOK.leftParenthesis)
-                {
-                    arg = p.parseAssignExp();
-                    p.eSink.error(arg.loc, "`%s` must be surrounded by parentheses", arg.toChars());
-                }
-                else
-                {
-                    // Look for the opening `(`
-                    p.check(TOK.leftParenthesis);
-                    // Parse the assign expression
-                    arg = p.parseAssignExp();
-                    // Look for the closing `)`
-                    p.check(TOK.rightParenthesis);
-                }
-
-                if (!s.args)
-                {
-                    s.names = new Identifiers();
-                    s.constraints = new Expressions();
-                    s.args = new Expressions();
-                }
-                s.names.push(name);
-                s.args.push(arg);
-                s.constraints.push(constraint);
-                numargs++;
-
-                if (p.token.value == TOK.comma)
-                    p.nextToken();
-                break;
-
-            default:
-                p.eSink.error(p.token.loc, "expected constant string constraint for operand, not `%s`",
-                        p.token.toChars());
-                goto Lerror;
-        }
-    }
-Lerror:
-    while (p.token.value != TOK.rightCurly &&
-           p.token.value != TOK.semicolon &&
-           p.token.value != TOK.endOfFile)
-        p.nextToken();
-
-    return numargs;
-}
-
-/***********************************
- * Parse list of extended asm clobbers.
- * Grammar:
- *      | Clobbers:
- *      |     StringLiteral
- *      |     StringLiteral , Clobbers
- * Params:
- *      p = parser state
- * Returns:
- *      array of parsed clobber expressions
- */
-Expressions* parseExtAsmClobbers(Parser)(Parser p)
-{
-    Expressions* clobbers;
-
-    while (1)
-    {
-        Expression clobber;
-
-        switch (p.token.value)
-        {
-            case TOK.semicolon:
-            case TOK.colon:
-            case TOK.endOfFile:
-                return clobbers;
-
-            case TOK.string_:
-                clobber = p.parsePrimaryExp();
-                if (!clobbers)
-                    clobbers = new Expressions();
-                clobbers.push(clobber);
-
-                if (p.token.value == TOK.comma)
-                    p.nextToken();
-                break;
-
-            default:
-                p.eSink.error(p.token.loc, "expected constant string constraint for clobber name, not `%s`",
-                        p.token.toChars());
-                goto Lerror;
-        }
-    }
-Lerror:
-    while (p.token.value != TOK.rightCurly &&
-           p.token.value != TOK.semicolon &&
-           p.token.value != TOK.endOfFile)
-        p.nextToken();
-
-    return clobbers;
-}
-
-/***********************************
- * Parse list of extended asm goto labels.
- * Grammar:
- *      | GotoLabels:
- *      |     Identifier
- *      |     Identifier , GotoLabels
- * Params:
- *      p = parser state
- * Returns:
- *      array of parsed goto labels
- */
-Identifiers* parseExtAsmGotoLabels(Parser)(Parser p)
-{
-    Identifiers* labels;
-
-    while (1)
-    {
-        switch (p.token.value)
-        {
-            case TOK.semicolon:
-            case TOK.endOfFile:
-                return labels;
-
-            case TOK.identifier:
-                if (!labels)
-                    labels = new Identifiers();
-                labels.push(p.token.ident);
-
-                if (p.nextToken() == TOK.comma)
-                    p.nextToken();
-                break;
-
-            default:
-                p.eSink.error(p.token.loc, "expected identifier for goto label name, not `%s`",
-                        p.token.toChars());
-                goto Lerror;
-        }
-    }
-Lerror:
-    while (p.token.value != TOK.rightCurly &&
-           p.token.value != TOK.semicolon &&
-           p.token.value != TOK.endOfFile)
-        p.nextToken();
-
-    return labels;
-}
-
-/***********************************
- * Parse a gcc asm statement.
- * There are three forms of inline asm statements, basic, extended, and goto.
- * Grammar:
- *      | AsmInstruction:
- *      |     BasicAsmInstruction
- *      |     ExtAsmInstruction
- *      |     GotoAsmInstruction
- *      |
- *      | BasicAsmInstruction:
- *      |     AsmStringExpr
- *      |
- *      | ExtAsmInstruction:
- *      |     AsmStringExpr : Operands(opt) : Operands(opt) : Clobbers(opt)
- *      |
- *      | GotoAsmInstruction:
- *      |     AsmStringExpr : : Operands(opt) : Clobbers(opt) : GotoLabels(opt)
- * Params:
- *      p = parser state
- *      s = asm statement to parse
- * Returns:
- *      the parsed gcc asm statement
- */
-GccAsmStatement parseGccAsm(Parser)(Parser p, GccAsmStatement s)
-{
-    s.insn = p.parseAsmString();
-    if (s.insn.isErrorExp())
-        return s;
-
-    if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
-        goto Ldone;
-
-    // No semicolon followed after instruction template, treat as extended asm.
-    foreach (section; 0 .. 4)
-    {
-        p.check(TOK.colon);
-
-        final switch (section)
-        {
-            case 0:
-                s.outputargs = p.parseExtAsmOperands(s);
-                break;
-
-            case 1:
-                p.parseExtAsmOperands(s);
-                break;
-
-            case 2:
-                s.clobbers = p.parseExtAsmClobbers();
-                break;
-
-            case 3:
-                s.labels = p.parseExtAsmGotoLabels();
-                break;
-        }
-
-        if (p.token.value == TOK.semicolon || p.token.value == TOK.endOfFile)
-            goto Ldone;
-    }
-Ldone:
-    p.check(TOK.semicolon);
-
-    return s;
-}
-
-unittest
-{
-    import dmd.mtype : TypeBasic;
-
-    if (!global.errorSink)
-        global.errorSink = new ErrorSinkCompiler;
-
-    const errors = global.startGagging();
-    scope(exit) global.endGagging(errors);
-
-    // If this check fails, then Type._init() was called before reaching here,
-    // and the entire chunk of code that follows can be removed.
-    assert(ASTCodegen.Type.tint32 is null);
-    // Minimally initialize the cached types in ASTCodegen.Type, as they are
-    // dependencies for some fail asm tests to succeed.
-    ASTCodegen.Type.stringtable._init();
-    scope(exit)
-    {
-        ASTCodegen.Type.deinitialize();
-        ASTCodegen.Type.tint32 = null;
-    }
-    scope tint32 = new TypeBasic(ASTCodegen.Tint32);
-    ASTCodegen.Type.tint32 = tint32;
-
-    // Imitates asmSemantic if version = IN_GCC.
-    static int semanticAsm(Token* tokens)
-    {
-        const errors = global.errors;
-        scope gas = new GccAsmStatement(Loc.initial, tokens);
-        const bool doUnittests = false;
-        scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests);
-        p.token = *tokens;
-        p.parseGccAsm(gas);
-        return global.errors - errors;
-    }
-
-    // Imitates parseStatement for asm statements.
-    static void parseAsm(string input, bool expectError)
-    {
-        // Generate tokens from input test.
-        const bool doUnittests = false;
-        scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests);
-        p.nextToken();
-
-        Token* toklist = null;
-        Token** ptoklist = &toklist;
-        p.check(TOK.asm_);
-        p.check(TOK.leftCurly);
-        while (1)
-        {
-            if (p.token.value == TOK.rightCurly || p.token.value == TOK.endOfFile)
-                break;
-            if (p.token.value == TOK.colonColon)
-            {
-                *ptoklist = p.allocateToken();
-                memcpy(*ptoklist, &p.token, Token.sizeof);
-                (*ptoklist).value = TOK.colon;
-                ptoklist = &(*ptoklist).next;
-
-                *ptoklist = p.allocateToken();
-                memcpy(*ptoklist, &p.token, Token.sizeof);
-                (*ptoklist).value = TOK.colon;
-                ptoklist = &(*ptoklist).next;
-            }
-            else
-            {
-                *ptoklist = p.allocateToken();
-                memcpy(*ptoklist, &p.token, Token.sizeof);
-                ptoklist = &(*ptoklist).next;
-            }
-            *ptoklist = null;
-            p.nextToken();
-        }
-        p.check(TOK.rightCurly);
-
-        auto res = semanticAsm(toklist);
-        // Checks for both unexpected passes and failures.
-        assert((res == 0) != expectError);
-    }
-
-    /// Assembly Tests, all should pass.
-    /// Note: Frontend is not initialized, use only strings and identifiers.
-    immutable string[] passAsmTests = [
-        // Basic asm statement
-        q{ asm { "nop";
-        } },
-
-        // Extended asm statement
-        q{ asm { "cpuid"
-               : "=a" (a), "=b" (b), "=c" (c), "=d" (d)
-               : "a" (input);
-        } },
-
-        // Assembly with symbolic names
-        q{ asm { "bts %[base], %[offset]"
-               : [base] "+rm" (*ptr),
-               : [offset] "Ir" (bitnum);
-        } },
-
-        // Assembly with clobbers
-        q{ asm { "cpuid"
-               : "=a" (a)
-               : "a" (input)
-               : "ebx", "ecx", "edx";
-        } },
-
-        // Goto asm statement
-        q{ asm { "jmp %l0"
-               :
-               :
-               :
-               : Ljmplabel;
-        } },
-
-        // Any CTFE-able string allowed as instruction template.
-        q{ asm { (generateAsm);
-        } },
-
-        // Likewise mixins, permissible so long as the result is a string.
-        q{ asm { (mixin(`"repne"`, `~ "scasb"`));
-        } },
-
-        // :: token tests
-        q{ asm { "" : : : "memory"; } },
-        q{ asm { "" :: : "memory"; } },
-        q{ asm { "" : :: "memory"; } },
-        q{ asm { "" ::: "memory"; } },
-    ];
-
-    immutable string[] failAsmTests = [
-        // Found 'h' when expecting ';'
-        q{ asm { ""h;
-        } },
-
-        // https://issues.dlang.org/show_bug.cgi?id=20592
-        q{ asm { "nop" : [name] string (expr); } },
-
-        // Expression expected, not ';'
-        q{ asm { ""[;
-        } },
-
-        // Expression expected, not ':'
-        q{ asm { ""
-               :
-               : "g" (a ? b : : c);
-        } },
-
-        // Found ',' when expecting ':'
-        q{ asm { "", "";
-        } },
-
-        // https://issues.dlang.org/show_bug.cgi?id=20593
-        q{ asm { "instruction" : : "operand" 123; } },
-
-        // https://github.com/dlang/dmd/issues/21298
-        q{ asm { 1; }   },
-        q{ asm { int; } },
-        q{ asm { : "=r" (i); } },
-    ];
-
-    foreach (test; passAsmTests)
-        parseAsm(test, false);
-
-    foreach (test; failAsmTests)
-        parseAsm(test, true);
-}
index 2d10e6f991aff0f78963c660b7d17074361b0b22..c5988203d1b2314488b3f716699414cba6dc1547 100644 (file)
@@ -206,6 +206,7 @@ immutable Msgtable[] msgtable =
     { "keys" },
     { "values" },
     { "rehash" },
+    { "dup" },
 
     { "future", "__future" },
     { "property" },
@@ -259,11 +260,15 @@ immutable Msgtable[] msgtable =
     { "FpopBack", "popBack" },
 
     // For internal functions
-    { "aaLen", "_aaLen" },
-    { "aaKeys", "_aaKeys" },
-    { "aaValues", "_aaValues" },
-    { "aaRehash", "_aaRehash" },
-    { "_aaAsStruct" },
+    { "_d_aaGetY" },
+    { "_d_aaGetRvalueX" },
+    { "_d_aaDel" },
+    { "_d_aaEqual" },
+    { "_d_aaIn" },
+    { "_d_aaNew" },
+    { "_d_aaLen" },
+    { "_d_aaApply" },
+    { "_d_aaApply2" },
     { "monitorenter", "_d_monitorenter" },
     { "monitorexit", "_d_monitorexit" },
     { "criticalenter", "_d_criticalenter2" },
@@ -280,15 +285,15 @@ immutable Msgtable[] msgtable =
     { "_d_newarrayTTrace" },
     { "_d_newarraymTX" },
     { "_d_newarraymTXTrace" },
+    { "_d_arrayliteralTX" },
+    { "_d_arrayliteralTXTrace" },
     { "_d_assert_fail" },
-    { "dup" },
-    { "_aaApply" },
-    { "_aaApply2" },
     { "_d_arrayctor" },
     { "_d_arraysetctor" },
     { "_d_arraysetassign" },
     { "_d_arrayassign_l" },
     { "_d_arrayassign_r" },
+    { "_d_cast" },
 
     { "imported" },
     { "InterpolationHeader" },
@@ -332,6 +337,7 @@ immutable Msgtable[] msgtable =
     { "_d_arrayappendcTXTrace" },
     { "_d_arraycatnTX" },
     { "_d_arraycatnTXTrace" },
+    { "_d_assocarrayliteralTX" },
 
     // varargs implementation
     { "stdc" },
index c213597eb0396ef48964ac860bf641833839608f..92d3a8c79505b7d5aa42c174c65f6e1048e32740 100644 (file)
@@ -221,7 +221,7 @@ nothrow:
      *      Identifier (inside Identifier.idPool) with deterministic name based
      *      on the source location.
      */
-    extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, string parent = "")
+    extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, const void* parent = null)
     {
         // generate `<prefix>_L<line>_C<col>`
         auto sl = SourceLoc(loc);
@@ -248,14 +248,15 @@ nothrow:
          * directly, but that would unnecessary lengthen symbols names. See issue:
          * https://issues.dlang.org/show_bug.cgi?id=23722
          */
-        static struct Key { string locKey; string prefix; string parent; }
+        static struct Key { string locKey; string prefix; const(void)* parent; }
         __gshared uint[Key] counters;
 
-        string locKey = cast(string) (sl.filename ~ idBuf[]);
+        const locKey = cast(string) (sl.filename ~ idBuf[]);
+        const key = Key(locKey, prefix, parent);
         static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u)))
         {
             // 2.082+
-            counters.update(Key(locKey, prefix, parent),
+            counters.update(key,
                 () => 1u,          // insertion
                 (ref uint counter) // update
                 {
@@ -267,7 +268,6 @@ nothrow:
         }
         else
         {
-            const key = Key(locKey, prefix, parent);
             if (auto pCounter = key in counters)
             {
                 idBuf.writestring("_");
index 14bd889bd6a1a0abc88eb1393897e48fd3783407..54efbd24cc020d062e6f79a2dd2de3ef34498e4e 100644 (file)
@@ -41,7 +41,6 @@ public:
     const char *kind() const override;
     Visibility visible() override;
     Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees
-    Dsymbol *toAlias() override;
     bool overloadInsert(Dsymbol *s) override;
 
     void accept(Visitor *v) override { v->visit(this); }
index 059de62b1f9c9f1e3221f88b6f21c263b77aeef6..3f5fe86dc9770c26d62e9182b6d45a7a42dc625e 100644 (file)
@@ -89,13 +89,13 @@ Expression arrayFuncConv(Expression e, Scope* sc)
     auto t = e.type.toBasetype();
     if (auto ta = t.isTypeDArray())
     {
-        if (!checkAddressable(e, sc))
+        if (!checkAddressable(e, sc, "take address of"))
             return ErrorExp.get();
         e = e.castTo(sc, ta.next.pointerTo());
     }
     else if (auto ts = t.isTypeSArray())
     {
-        if (!checkAddressable(e, sc))
+        if (!checkAddressable(e, sc, "take address of"))
             return ErrorExp.get();
         e = e.castTo(sc, ts.next.pointerTo());
     }
@@ -553,10 +553,10 @@ Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsy
     auto vd = s.isVarDeclaration(); // new declaration
     auto vd2 = s2.isVarDeclaration(); // existing declaration
 
-    if (vd && vd.isCmacro())
+    if (vd && vd.isCmacro)
         return s2;
 
-    assert(!(vd2 && vd2.isCmacro()));
+    assert(!(vd2 && vd2.isCmacro));
 
     if (vd && vd2)
     {
index 9f9fd77b7e2689f106571cb422639ad7ec1c95b3..2b8a8ed4987ac3f1d5b85750979d69a03979e7d9 100644 (file)
@@ -25,6 +25,7 @@ import dmd.dsymbol;
 import dmd.dsymbolsem;
 import dmd.dtemplate;
 import dmd.expression;
+import dmd.expressionsem : getConstInitializer;
 import dmd.func;
 import dmd.hdrgen;
 import dmd.mangle;
index ed9f7f1ce775b411f35110bb39f02f56aea8d5e0..62855fe93f9340888f5fe8fbd7d3b1e2ef7afd4e 100644 (file)
@@ -1706,7 +1706,7 @@ class Lexer
                 }
                 break;
             }
-            stringbuffer.writeByte(c);
+            stringbuffer.writeByte(cast(char)c);
         }
     }
 
@@ -1750,7 +1750,7 @@ class Lexer
                 if (n & 1)
                 {
                     error("odd number (%d) of hex characters in hex string", n);
-                    stringbuffer.writeByte(v);
+                    stringbuffer.writeByte(cast(char)v);
                 }
                 t.setString(stringbuffer);
                 stringPostfix(t);
@@ -1777,7 +1777,7 @@ class Lexer
                 if (n & 1)
                 {
                     v = (v << 4) | c;
-                    stringbuffer.writeByte(v);
+                    stringbuffer.writeByte(cast(char)v);
                 }
                 else
                     v = c;
@@ -2195,7 +2195,7 @@ class Lexer
                 }
                 break;
             }
-            stringbuffer.writeByte(c);
+            stringbuffer.writeByte(cast(char)c);
         }
     }
 
index 7e9f0205d1c3765dec6d9e51b165e49e3e141c81..8a429037dfce6b17aee5933a207635d96ba8b04e 100644 (file)
@@ -26,7 +26,7 @@ import dmd.astenums;
 import dmd.attrib;
 import dmd.declaration;
 import dmd.dsymbol;
-import dmd.dsymbolsem : isGNUABITag;
+import dmd.dsymbolsem : isGNUABITag, toAlias;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
index cc797614ed0b5ebbaa514930a0289d2c8370d186..a6a80b7be7be4a01df9a77a33f62d4826a3b01a5 100644 (file)
@@ -145,6 +145,7 @@ import dmd.declaration;
 import dmd.dinterpret;
 import dmd.dmodule;
 import dmd.dsymbol;
+import dmd.dsymbolsem : toAlias;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
@@ -318,6 +319,14 @@ void mangleType(Type t, ubyte modMask, ref OutBuffer buf, ref Backref backref)
 
 
 /*************************************************************
+ * Mangle type of function. writing it to `buf`
+ * Params:
+ *     t = function type
+ *     ta = consult original function type for attributes
+ *     modMask = type modifiers
+ *     tret = function return type
+ *     buf = sink for mangling characters
+ *     backref = back reference
  */
 void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, ref OutBuffer buf, ref Backref backref)
 {
@@ -401,7 +410,7 @@ void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, r
     foreach (idx, param; t.parameterList)
         mangleParameter(param, buf, backref);
     //if (buf.data[buf.length - 1] == '@') assert(0);
-    buf.writeByte('Z' - t.parameterList.varargs); // mark end of arg list
+    buf.writeByte(cast(ubyte)('Z' - t.parameterList.varargs)); // mark end of arg list
     if (tret !is null)
         mangleType(tret, 0, buf, backref);
     t.inuse--;
@@ -1205,11 +1214,11 @@ void writeBackRef(ref OutBuffer buf, size_t pos) @safe
     while (mul >= base)
     {
         auto dig = cast(ubyte)(pos / mul);
-        buf.writeByte('A' + dig);
+        buf.writeByte(cast(char)('A' + dig));
         pos -= dig * mul;
         mul /= base;
     }
-    buf.writeByte('a' + cast(ubyte)pos);
+    buf.writeByte(cast(char)('a' + pos));
 }
 
 
index e1780146402a6cccb0429fcbea2cc2321f842ee5..c81b6d7531ef0555cb6cc6e7392ebe6b0c7a10ea 100644 (file)
@@ -10,6 +10,8 @@
 
 #pragma once
 
+#include <stdint.h>
+
 #include "dsymbol.h"
 
 struct ModuleDeclaration;
@@ -28,12 +30,11 @@ enum PKG
     PKGpackage  // already determined that's an actual package
 };
 
-enum class Edition : unsigned char
+enum class Edition : uint16_t
 {
-    none = 0u,
-    legacy = 1u,
-    v2024 = 2u,
-    latest = 2u,
+    v2023 = 2023,
+    v2024,
+    v2025,
 };
 
 class Package : public ScopeDsymbol
@@ -98,6 +99,8 @@ public:
     SearchOptFlags searchCacheFlags;       // cached flags
     d_bool insearch;
 
+    d_bool isExplicitlyOutOfBinary; // Is this module known to be out of binary, and must be DllImport'd?
+
     // module from command line we're imported from,
     // i.e. a module that will be taken all the
     // way to an object file
@@ -120,7 +123,6 @@ public:
     size_t namelen;             // length of module name in characters
 
     static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen);
-    static const char *find(const char *filename);
     static Module *load(Loc loc, Identifiers *packages, Identifier *ident);
 
     const char *kind() const override;
@@ -140,19 +142,11 @@ public:
     bool isCoreModule(Identifier *ident);
 
     // Back end
-
-    int doppelganger;           // sub-module
     Symbol *cov;                // private uint[] __coverage;
     DArray<unsigned> 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
+    bool hasCDtor;
 
     void *ctfe_cov;             // stores coverage information from ctfe
 
index c9b57449658ae04af3965d91ee79251259127f53..c11ce9d390054d3f35921453c314686769273e42 100644 (file)
@@ -30,8 +30,6 @@ import dmd.dtemplate;
 import dmd.enumsem;
 import dmd.errors;
 import dmd.expression;
-import dmd.dsymbolsem : determineSize;
-import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
@@ -47,9 +45,8 @@ import dmd.typesem;
 import dmd.visitor;
 
 enum LOGDOTEXP = 0;         // log ::dotExp()
-enum LOGDEFAULTINIT = 0;    // log ::defaultInit()
 
-enum SIZE_INVALID = (~cast(uinteger_t)0);   // error return from size() functions
+enum SIZE_INVALID = (~cast(ulong)0);   // error return from size() functions
 
 static if (__VERSION__ < 2095)
 {
@@ -688,19 +685,6 @@ extern (C++) abstract class Type : ASTNode
         return false;
     }
 
-    /**************************
-     * When T is mutable,
-     * Given:
-     *      T a, b;
-     * Can we bitwise assign:
-     *      a = b;
-     * ?
-     */
-    bool isAssignable()
-    {
-        return true;
-    }
-
     /**************************
      * Returns true if T can be converted to boolean value.
      */
@@ -1323,19 +1307,6 @@ extern (C++) abstract class Type : ASTNode
         return s;
     }
 
-    /***************************************
-     * Use when we prefer the default initializer to be a literal,
-     * rather than a global immutable variable.
-     */
-    Expression defaultInitLiteral(Loc loc)
-    {
-        static if (LOGDEFAULTINIT)
-        {
-            printf("Type::defaultInitLiteral() '%s'\n", toChars());
-        }
-        return defaultInit(this, loc);
-    }
-
     /***************************************
      * Return !=0 if the type or any of its subtypes is wild.
      */
@@ -1399,9 +1370,9 @@ extern (C++) abstract class Type : ASTNode
      * Return the mask that an integral type will
      * fit into.
      */
-    extern (D) final uinteger_t sizemask()
+    extern (D) final ulong sizemask()
     {
-        uinteger_t m;
+        ulong m;
         switch (toBasetype().ty)
         {
         case Tbool:
@@ -1572,11 +1543,6 @@ extern (C++) final class TypeError : Type
         return this;
     }
 
-    override Expression defaultInitLiteral(Loc loc)
-    {
-        return ErrorExp.get();
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -2118,17 +2084,6 @@ extern (C++) final class TypeVector : Type
         return false;
     }
 
-    override Expression defaultInitLiteral(Loc loc)
-    {
-        //printf("TypeVector::defaultInitLiteral()\n");
-        assert(basetype.ty == Tsarray);
-        Expression e = basetype.defaultInitLiteral(loc);
-        auto ve = new VectorExp(loc, e, this);
-        ve.type = this;
-        ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc));
-        return ve;
-    }
-
     TypeBasic elementType()
     {
         assert(basetype.ty == Tsarray);
@@ -2219,25 +2174,6 @@ extern (C++) final class TypeSArray : TypeArray
         return next.alignment();
     }
 
-    override Expression defaultInitLiteral(Loc loc)
-    {
-        static if (LOGDEFAULTINIT)
-        {
-            printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
-        }
-        size_t d = cast(size_t)dim.toInteger();
-        Expression elementinit;
-        if (next.ty == Tvoid)
-            elementinit = tuns8.defaultInitLiteral(loc);
-        else
-            elementinit = next.defaultInitLiteral(loc);
-        auto elements = new Expressions(d);
-        foreach (ref e; *elements)
-            e = null;
-        auto ae = new ArrayLiteralExp(loc, this, elementinit, elements);
-        return ae;
-    }
-
     override bool hasUnsafeBitpatterns()
     {
         return next.hasUnsafeBitpatterns();
@@ -2603,110 +2539,6 @@ extern (C++) final class TypeFunction : TypeNext
         return linkage == LINK.d && parameterList.varargs == VarArg.variadic;
     }
 
-    /*********************************
-     * Append error message to buf.
-     * Input:
-     *  buf = message sink
-     *  format = printf format
-     */
-    extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...)
-    {
-        if (global.gag && !global.params.v.showGaggedErrors)
-            return;
-        va_list ap;
-        va_start(ap, format);
-        buf.vprintf(format, ap);
-        va_end(ap);
-    }
-
-    /********************************
-     * Convert an `argumentList`, which may contain named arguments, into
-     * a list of arguments in the order of the parameter list.
-     *
-     * Params:
-     *      argumentList = array of function arguments
-     *      buf = if not null, append error message to it
-     * Returns: re-ordered argument list, or `null` on error
-     */
-    extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf)
-    {
-        Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null;
-        ArgumentLabel[] names = argumentList.names ? (*argumentList.names)[] : null;
-        const nParams = parameterList.length(); // cached because O(n)
-        auto newArgs = new Expressions(nParams);
-        newArgs.zero();
-        size_t ci = 0;
-        bool hasNamedArgs = false;
-        const bool isVariadic = parameterList.varargs != VarArg.none;
-        foreach (i, arg; args)
-        {
-            if (!arg)
-            {
-                ci++;
-                continue;
-            }
-            auto name = i < names.length ? names[i].name : null;
-            if (name)
-            {
-                hasNamedArgs = true;
-                const pi = findParameterIndex(name);
-                if (pi == -1)
-                {
-                    if (buf)
-                        getMatchError(*buf, "no parameter named `%s`", name.toChars());
-                    return null;
-                }
-                ci = pi;
-            }
-            if (ci >= newArgs.length)
-            {
-                if (!isVariadic)
-                {
-                    // Without named args, let the caller diagnose argument overflow
-                    if (hasNamedArgs && buf)
-                        getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars());
-                    return null;
-                }
-                while (ci >= newArgs.length)
-                    newArgs.push(null);
-            }
-
-            if ((*newArgs)[ci])
-            {
-                if (buf)
-                    getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars());
-                return null;
-            }
-            (*newArgs)[ci++] = arg;
-        }
-        foreach (i, arg; (*newArgs)[])
-        {
-            if (arg || parameterList[i].defaultArg)
-                continue;
-
-            if (isVariadic && i + 1 == newArgs.length)
-                continue;
-
-            // dtemplate sets `defaultArg=null` to avoid semantic on default arguments,
-            // don't complain about missing arguments in that case
-            if (this.incomplete)
-                continue;
-
-            if (buf)
-                getMatchError(*buf, "missing argument for parameter #%d: `%s`",
-                    i + 1, parameterToChars(parameterList[i], this, false));
-            return null;
-        }
-        // strip trailing nulls from default arguments
-        size_t e = newArgs.length;
-        while (e > 0 && (*newArgs)[e - 1] is null)
-        {
-            --e;
-        }
-        newArgs.setDim(e);
-        return newArgs;
-    }
-
     /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise.
     bool iswild() const pure nothrow @safe @nogc
     {
@@ -2736,23 +2568,6 @@ extern (C++) final class TypeFunction : TypeNext
     {
         v.visit(this);
     }
-
-    /**
-     * Look for the index of parameter `ident` in the parameter list
-     *
-     * Params:
-     *   ident = identifier of parameter to search for
-     * Returns: index of parameter with name `ident` or -1 if not found
-     */
-    private extern(D) ptrdiff_t findParameterIndex(Identifier ident)
-    {
-        foreach (i, p; this.parameterList)
-        {
-            if (p.ident == ident)
-                return i;
-        }
-        return -1;
-    }
 }
 
 /***********************************************************
@@ -3129,99 +2944,6 @@ extern (C++) final class TypeStruct : Type
         return sym.alignment;
     }
 
-    /***************************************
-     * Use when we prefer the default initializer to be a literal,
-     * rather than a global immutable variable.
-     */
-    override Expression defaultInitLiteral(Loc loc)
-    {
-        static if (LOGDEFAULTINIT)
-        {
-            printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
-        }
-        sym.size(loc);
-        if (sym.sizeok != Sizeok.done)
-            return ErrorExp.get();
-
-        auto structelems = new Expressions(sym.nonHiddenFields());
-        uint offset = 0;
-        foreach (j; 0 .. structelems.length)
-        {
-            VarDeclaration vd = sym.fields[j];
-            Expression e;
-            if (vd.inuse)
-            {
-                error(loc, "circular reference to `%s`", vd.toPrettyChars());
-                return ErrorExp.get();
-            }
-            if (vd.offset < offset || vd.type.size() == 0)
-                e = null;
-            else if (vd._init)
-            {
-                if (vd._init.isVoidInitializer())
-                    e = null;
-                else
-                    e = vd.getConstInitializer(false);
-            }
-            else
-                e = vd.type.defaultInitLiteral(loc);
-            if (e && e.op == EXP.error)
-                return e;
-            if (e)
-                offset = vd.offset + cast(uint)vd.type.size();
-            (*structelems)[j] = e;
-        }
-        auto structinit = new StructLiteralExp(loc, sym, structelems);
-
-        /* Copy from the initializer symbol for larger symbols,
-         * otherwise the literals expressed as code get excessively large.
-         */
-        if (size(this, loc) > target.ptrsize * 4 && !needsNested())
-            structinit.useStaticInit = true;
-
-        structinit.type = this;
-        return structinit;
-    }
-
-    override bool isAssignable()
-    {
-        bool assignable = true;
-        uint offset = ~0; // dead-store initialize to prevent spurious warning
-
-        sym.determineSize(sym.loc);
-
-        /* If any of the fields are const or immutable,
-         * then one cannot assign this struct.
-         */
-        for (size_t i = 0; i < sym.fields.length; i++)
-        {
-            VarDeclaration v = sym.fields[i];
-            //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind());
-            if (i == 0)
-            {
-            }
-            else if (v.offset == offset)
-            {
-                /* If any fields of anonymous union are assignable,
-                 * then regard union as assignable.
-                 * This is to support unsafe things like Rebindable templates.
-                 */
-                if (assignable)
-                    continue;
-            }
-            else
-            {
-                if (!assignable)
-                    return false;
-            }
-            assignable = v.type.isMutable() && v.type.isAssignable();
-            offset = v.offset;
-            //printf(" -> assignable = %d\n", assignable);
-        }
-
-        return assignable;
-    }
-
     override bool isBoolean()
     {
         return false;
@@ -3383,11 +3105,6 @@ extern (C++) final class TypeEnum : Type
         return memType().isString();
     }
 
-    override bool isAssignable()
-    {
-        return memType().isAssignable();
-    }
-
     override bool needsDestruction()
     {
         return memType().needsDestruction();
@@ -3576,16 +3293,14 @@ extern (C++) final class TypeTuple : Type
     extern (D) this(Type t1)
     {
         super(Ttuple);
-        arguments = new Parameters();
-        arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null));
+        arguments = new Parameters(new Parameter(Loc.initial, STC.none, t1, null, null, null));
     }
 
     extern (D) this(Type t1, Type t2)
     {
         super(Ttuple);
-        arguments = new Parameters();
-        arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null));
-        arguments.push(new Parameter(Loc.initial, STC.none, t2, null, null, null));
+        arguments = new Parameters(new Parameter(Loc.initial, STC.none, t1, null, null, null),
+                                   new Parameter(Loc.initial, STC.none, t2, null, null, null));
     }
 
     static TypeTuple create() @safe
@@ -4294,12 +4009,32 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma
         dg("@nogc");
     if (tf.isProperty)
         dg("@property");
+
+    /* The following is more or less like dmd.hdrgen.stcToBuffer(), in the future
+     * it should be merged. The idea is consistent ordering
+     */
+    STC stc;
     if (tf.isRef)
-        dg("ref");
+        stc |= STC.ref_;
     if (tf.isReturn && !tf.isReturnInferred)
-        dg("return");
+        stc |= STC.return_;
     if (tf.isScopeQual && !tf.isScopeInferred)
-        dg("scope");
+        stc |= STC.scope_;
+    if (tf.isReturnScope)
+        stc |= STC.returnScope;
+    final switch (buildScopeRef(stc))
+    {
+        case ScopeRef.None:                                                      break;
+        case ScopeRef.Scope:            dg("scope");                             break;
+        case ScopeRef.Return:           dg("return");                            break;
+        case ScopeRef.ReturnScope:      dg("return"); dg("scope");               break;
+        case ScopeRef.ReturnRef:        dg("return"); dg("ref");                 break;
+        case ScopeRef.Ref:              dg("ref");                               break;
+        case ScopeRef.RefScope:         dg("ref");    dg("scope");               break;
+        case ScopeRef.ReturnRef_Scope:  dg("return"); dg("ref");    dg("scope"); break;
+        case ScopeRef.Ref_ReturnScope:  dg("ref");    dg("return"); dg("scope"); break;
+    }
+
     if (tf.isLive)
         dg("@live");
 
index 4d303acf3c3c59216c5c277466216f64e9241e5e..7f9c026918f62ed3b41fa3910860a33807a62b0b 100644 (file)
@@ -44,6 +44,7 @@ namespace dmd
 {
     Type *typeSemantic(Type *t, Loc loc, Scope *sc);
     Type *merge(Type *type);
+    Expression *defaultInitLiteral(Type *t, Loc loc);
 }
 
 enum class TY : uint8_t
@@ -238,7 +239,6 @@ public:
     virtual bool isUnsigned();
     virtual bool isScopeClass();
     virtual bool isString();
-    virtual bool isAssignable();
     virtual bool isBoolean();
     bool isConst() const       { return (mod & MODconst) != 0; }
     bool isImmutable() const   { return (mod & MODimmutable) != 0; }
@@ -265,7 +265,6 @@ public:
 
     virtual ClassDeclaration *isClassHandle();
     virtual structalign_t alignment();
-    virtual Expression *defaultInitLiteral(Loc loc);
     virtual int hasWild() const;
     virtual bool hasVoidInitPointers();
     virtual bool hasUnsafeBitpatterns();
@@ -315,7 +314,6 @@ public:
     const char *kind() override;
     TypeError *syntaxCopy() override;
 
-    Expression *defaultInitLiteral(Loc loc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -376,7 +374,6 @@ public:
     bool isScalar() override;
     bool isUnsigned() override;
     bool isBoolean() override;
-    Expression *defaultInitLiteral(Loc loc) override;
     TypeBasic *elementType();
 
     void accept(Visitor *v) override { v->visit(this); }
@@ -400,7 +397,6 @@ public:
     unsigned alignsize() override;
     bool isString() override;
     structalign_t alignment() override;
-    Expression *defaultInitLiteral(Loc loc) override;
     bool hasUnsafeBitpatterns() override;
     bool hasVoidInitPointers() override;
     bool hasInvariant() override;
@@ -691,8 +687,6 @@ public:
     unsigned alignsize() override;
     TypeStruct *syntaxCopy() override;
     structalign_t alignment() override;
-    Expression *defaultInitLiteral(Loc loc) override;
-    bool isAssignable() override;
     bool isBoolean() override;
     bool needsDestruction() override;
     bool needsCopyOrPostblit() override;
@@ -723,7 +717,6 @@ public:
     bool isUnsigned() override;
     bool isBoolean() override;
     bool isString() override;
-    bool isAssignable() override;
     bool needsDestruction() override;
     bool needsCopyOrPostblit() override;
     bool needsNested() override;
index fab9723636cbd03780b87fca58f37b77a17816e7..84a991e69323f3bb05dab3b405fbe58e50dadfca 100644 (file)
@@ -14,7 +14,6 @@ import dmd.dscope;
 import dmd.dsymbol;
 import dmd.errors;
 import dmd.expression;
-import dmd.globals;
 import dmd.identifier;
 import dmd.location;
 
index 5e3c164acbb41c62c4d2a0f2a6586ddd70b88b88..f528d411bfd5632e2b53fe7ce2f245809e7245d7 100644 (file)
@@ -16,18 +16,22 @@ module dmd.nogc;
 import core.stdc.stdio;
 
 import dmd.aggregate;
+import dmd.arraytypes;
 import dmd.astenums;
-import dmd.declaration;
 import dmd.common.outbuffer;
+import dmd.declaration;
 import dmd.dmodule;
 import dmd.dscope;
-import dmd.dtemplate : isDsymbol;
 import dmd.dsymbol : PASS;
+import dmd.dtemplate : isDsymbol;
 import dmd.errors;
 import dmd.escape;
 import dmd.expression;
+import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
+import dmd.id;
+import dmd.identifier;
 import dmd.init;
 import dmd.location;
 import dmd.mtype;
@@ -35,6 +39,7 @@ import dmd.rootobject : RootObject, DYNCAST;
 import dmd.semantic2;
 import dmd.semantic3;
 import dmd.tokens;
+import dmd.typesem : unqualify;
 import dmd.visitor;
 import dmd.visitor.postorder;
 
@@ -124,7 +129,8 @@ public:
 
     override void visit(ArrayLiteralExp e)
     {
-        if (e.type.toBasetype().isTypeSArray() || !e.elements || !e.elements.length || e.onstack)
+        const dim = e.elements ? e.elements.length : 0;
+        if (e.type.toBasetype().isTypeSArray() || dim == 0 || e.onstack)
             return;
         if (setGC(e, "this array literal"))
             return;
@@ -135,6 +141,25 @@ public:
             return;
         }
 
+        if (!global.params.useGC)
+        {
+            if (!checkOnly)
+            {
+                version (IN_GCC)
+                    error(e.loc, "this array literal requires the GC and cannot be used with `???`");
+                else
+                    error(e.loc, "this array literal requires the GC and cannot be used with `-betterC`");
+            }
+            err = true;
+            return;
+        }
+
+        if (!lowerArrayLiteral(e, sc))
+        {
+            err = true;
+            return;
+        }
+
         f.printGCUsage(e.loc, "array literal may cause a GC allocation");
     }
 
index 099f8115898e62fc77ebb00574ce5a9fbf7a6b55..7e48b28150f9e31a8f8e00beed98e875106646de 100644 (file)
@@ -26,13 +26,12 @@ import dmd.astenums;
 import dmd.declaration;
 import dmd.dscope;
 import dmd.dsymbol;
+import dmd.dsymbolsem : toAlias;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.escape;
 import dmd.expression;
-
 import dmd.func;
-import dmd.globals;
 import dmd.hdrgen;
 import dmd.identifier;
 import dmd.init;
@@ -79,6 +78,8 @@ void oblive(FuncDeclaration funcdecl)
     checkObErrors(obstate);
 }
 
+private:
+
 alias ObNodes = Array!(ObNode*);
 
 alias StmtState = dmd.stmtstate.StmtState!ObNode;
index 32878b57af9ba354473935d76e72c0b68122b472..5b7afd5dd57a9a72cb6cc3264c5efdac89a81d00 100644 (file)
@@ -31,8 +31,6 @@ import dmd.errors;
 import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
-import dmd.globals;
-import dmd.gluelayer;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
@@ -433,7 +431,7 @@ extern(C++) private final class Unsupported : Objc
 {
     extern(D) final this()
     {
-        ObjcGlue.initialize();
+
     }
 
     override void setObjc(ClassDeclaration cd)
@@ -545,7 +543,6 @@ extern(C++) private final class Supported : Objc
     {
         VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC");
 
-        ObjcGlue.initialize();
         ObjcSelector._init();
     }
 
index 7baaeaa7fcfa673c0dd026aed162a9237ec52f91..791762ac18b64d540fe7cfe36c9d95da46a92352 100644 (file)
@@ -30,7 +30,6 @@ import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
 import dmd.funcsem;
-import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
@@ -129,8 +128,7 @@ Objects* opToArg(Scope* sc, EXP op)
 {
     Expression e = new StringExp(Loc.initial, EXPtoString(stripAssignOp(op)));
     e = e.expressionSemantic(sc);
-    auto tiargs = new Objects();
-    tiargs.push(e);
+    auto tiargs = new Objects(e);
     return tiargs;
 }
 
@@ -340,7 +338,11 @@ Expression opOverloadArray(ArrayExp ae, Scope* sc)
 
                 // Convert to IndexExp
                 if (ae.arguments.length == 1)
-                    return new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]).expressionSemantic(sc);
+                {
+                    auto idxexp = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
+                    idxexp.modifiable = ae.modifiable;
+                    return idxexp.expressionSemantic(sc);
+                }
             }
             break;
         }
@@ -439,8 +441,7 @@ Expression opOverloadCast(CastExp e, Scope* sc, Type att = null)
                 return build_overload(e.loc, sc, e.e1, null, fd);
             }
         }
-        auto tiargs = new Objects();
-        tiargs.push(e.to);
+        auto tiargs = new Objects(e.to);
         return dotTemplateCall(e.e1, Id.opCast, tiargs).expressionSemantic(sc);
     }
     // Didn't find it. Forward to aliasthis
@@ -551,14 +552,15 @@ Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop)
 
     // Try opBinary and opBinaryRight
     Dsymbol s = search_function(ad1, Id.opBinary);
-    if (s && !s.isTemplateDeclaration())
+
+    if (s && !(s.isTemplateDeclaration() || s.isOverloadSet))
     {
         error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
         return ErrorExp.get();
     }
 
     Dsymbol s_r = search_function(ad2, Id.opBinaryRight);
-    if (s_r && !s_r.isTemplateDeclaration())
+    if (s_r && !(s_r.isTemplateDeclaration() || s_r.isOverloadSet()))
     {
         error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
         return ErrorExp.get();
@@ -906,6 +908,8 @@ Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisSt
 {
     if (auto ae = e.e1.isArrayExp())
     {
+        markArrayExpModifiable(ae);
+
         ae.e1 = ae.e1.expressionSemantic(sc);
         ae.e1 = resolveProperties(sc, ae.e1);
         Expression ae1old = ae.e1;
@@ -993,13 +997,16 @@ Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisSt
     if (Expression result = e.binSemanticProp(sc))
         return result;
 
+    if (auto result = rewriteIndexAssign(e, sc, aliasThisStop))
+        return result;
+
     // Don't attempt 'alias this' if an error occurred
     if (e.e1.type.isTypeError() || e.e2.type.isTypeError())
         return ErrorExp.get();
 
     AggregateDeclaration ad1 = isAggregate(e.e1.type);
     Dsymbol s = search_function(ad1, Id.opOpAssign);
-    if (s && !s.isTemplateDeclaration())
+    if (s && !(s.isTemplateDeclaration() || s.isOverloadSet()))
     {
         error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars());
         return ErrorExp.get();
@@ -1046,7 +1053,7 @@ private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s,
     if (s)
     {
         functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2), null);
-        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors))
             return ErrorExp.get();
     }
     FuncDeclaration lastf = m.lastf;
@@ -1054,7 +1061,7 @@ private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s,
     if (s_r)
     {
         functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1), null);
-        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors))
             return ErrorExp.get();
     }
     if (m.count > 1)
@@ -1187,6 +1194,8 @@ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
             return fd;
         if (TemplateDeclaration td = s2.isTemplateDeclaration())
             return td;
+        if (OverloadSet os = s2.isOverloadSet())
+            return os;
     }
     return null;
 }
index b13f8c64584f9d32d407bb5dc9a7ce3d1c4cd706..88a947f6b02a8506ae46d8b01bb8ed0ffe7d9270 100644 (file)
@@ -1138,9 +1138,16 @@ Expression optimize(Expression e, int result, bool keepLvalue = false)
 
     void visitEqual(EqualExp e)
     {
-        //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
+        //printf("EqualExp::optimize(result = %d) %s\n", result, e.toChars());
+        if (auto lowering = e.lowering)
+        {
+            optimize(lowering, result, keepLvalue);
+            return;
+        }
+
         if (binOptimize(e, WANTvalue))
             return;
+
         Expression e1 = fromConstInitializer(result, e.e1);
         Expression e2 = fromConstInitializer(result, e.e2);
         if (e1.op == EXP.error)
index c57f1d3f7f74d4a1b59fa50990ebd5513dc51a14..ee078f359d774993e23f8d3733b553af9273f94d 100644 (file)
@@ -142,6 +142,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
 
         md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
 
+        if (token.value == TOK.int32Literal)
+        {
+            auto edition = token.intvalue;
+            if (edition < Edition.min || Edition.max < edition)
+            {
+                error("module edition %lld must be in the range %d ... %d", edition, Edition.min, Edition.max);
+                edition = edition.min;
+            }
+            mod.edition = cast(Edition)edition;
+            nextToken();
+        }
+
         if (token.value != TOK.semicolon)
             error("`;` expected following module declaration instead of `%s`", token.toChars());
         nextToken();
@@ -238,7 +250,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                                 if (auto id = (*exps)[0].isIdentifierExp())
                                     if (id.ident == Id.__edition_latest_do_not_use)
                                     {
-                                        mod.edition = Edition.latest;
+                                        mod.edition = Edition.max;      // latest edition
                                         continue;
                                     }
 
@@ -1287,6 +1299,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             return orig;
         }
 
+        if (0) // replace this with -preview=return in next PR
+            switch (orig & (STC.out_ | STC.ref_ | STC.scope_ | STC.return_))
+            {
+                case STC.return_ | STC.ref_:
+                    if (!(orig & STC.returnRef))
+                        error("`return` `ref` attribute pair must be written as '`return ref`'");
+                    break;
+
+                case STC.return_ | STC.out_:
+                    if (!(orig & STC.returnRef))
+                        error("`return` `out` attribute pair must be written as '`return out`'");
+                    break;
+
+                case STC.return_ | STC.scope_:
+                    if (!(orig & STC.returnScope))
+                        error("`return` `scope` attribute pair must be written as '`return scope`'");
+                    break;
+
+                case STC.return_ | STC.ref_ | STC.scope_:
+                    if (!(orig & (STC.returnRef | STC.returnScope)))
+                        error("`return` `ref` `scope` attribute triple must be written as '`return ref`' and '`scope`' or '`return scope`' and '`ref`'");
+                    break;
+
+                case STC.return_ | STC.out_ | STC.scope_:
+                    if (!(orig & (STC.returnRef | STC.returnScope)))
+                        error("`return` `out` `scope` attribute triple must be written as '`return out`' and '`scope`' or '`return scope`' and '`out`'");
+                    break;
+
+                default:
+                    break;
+            }
+
         checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest);
         checkConflictSTCGroup(STC.gshared | STC.shared_);
         checkConflictSTCGroup!true(STC.safeGroup);
@@ -1398,6 +1442,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
      */
     private STC parsePostfix(STC storageClass, AST.Expressions** pudas)
     {
+        const STC prefix = storageClass;
         while (1)
         {
             STC stc;
@@ -1428,10 +1473,23 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 break;
 
             case TOK.return_:
+            {
                 stc = STC.return_;
-                if (peekNext() == TOK.scope_)
-                    stc |= STC.returnScope;     // recognize `return scope`
+                TOK next = peekNext();
+                if (next == TOK.scope_)     // recognize the `return scope` pair
+                {
+                    stc |= STC.scope_ | STC.returnScope;
+                    nextToken();            // consume it
+                }
+                else if (next == TOK.ref_)  // recognize the `return ref` pair
+                {
+                    stc |= STC.ref_ | STC.returnRef;
+                    nextToken();            // consume it
+                }
+                else if (prefix & STC.ref_) // https://dlang.org/spec/function.html#struct-return-methods
+                    stc |= STC.returnRef;   // treat member function `ref int fp() return;` as `int fp() return ref;`
                 break;
+            }
 
             case TOK.scope_:
                 stc = STC.scope_;
@@ -1470,6 +1528,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     nextToken();
                     continue;
                 }
+
+                /* This is so we can enforce `return ref`, but not foul things up with an extra `ref`
+                 * which is not accounted for in the semantic routines. The problem comes about because
+                 * there's no way to attach `return ref` to the implicit `this` parameter but not the return value.
+                 */
+                if (!(prefix & STC.ref_) && storageClass & STC.ref_ && storageClass & STC.returnRef)
+                    storageClass &= ~STC.ref_;
+
                 return storageClass;
             }
             storageClass = appendStorageClass(storageClass, stc);
@@ -2814,7 +2880,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         STC varargsStc;
 
         // Attributes allowed for ...
-        enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope;
+        enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope | STC.returnRef;
 
         check(TOK.leftParenthesis);
         while (1)
@@ -2925,10 +2991,26 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     goto L2;
 
                 case TOK.return_:
+                {
                     stc = STC.return_;
-                    if (peekNext() == TOK.scope_)
-                        stc |= STC.returnScope;
+                    TOK next = peekNext();
+                    if (next == TOK.scope_)    // recognize the `return scope` pair
+                    {
+                        stc |= STC.scope_ | STC.returnScope;
+                        nextToken();           // consume it
+                    }
+                    else if (next == TOK.ref_) // recognize the `return ref` pair
+                    {
+                        stc |= STC.ref_ | STC.returnRef;
+                        nextToken();           // consume it
+                    }
+                    else if (next == TOK.out_) // recognize the `return out` pair
+                    {
+                        stc |= STC.out_ | STC.returnRef;
+                        nextToken();           // consume it
+                    }
                     goto L2;
+                }
                 L2:
                     storageClass = appendStorageClass(storageClass, stc);
                     continue;
@@ -4707,9 +4789,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 if (width)
                 {
                     if (_init)
-                        error("initializer not allowed for bit-field declaration");
+                        error("initializer not allowed for bitfield declaration");
                     if (storage_class)
-                        error("storage class not allowed for bit-field declaration");
+                        error("storage class not allowed for bitfield declaration");
                     s = new AST.BitFieldDeclaration(width.loc, t, ident, width);
                 }
                 else
@@ -7104,21 +7186,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 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;
@@ -9307,7 +9374,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         return e;
     }
 
-    private AST.Expression parseCondExp()
+    AST.Expression parseCondExp()
     {
         auto e = parseOrOrExp();
         if (token.value == TOK.question)
index a80fc804cbde09ee665e7d52c4dc94d5058eace0..ee35e805d66770cbb5274f71cedb7f45a05f632f 100644 (file)
@@ -55,6 +55,18 @@ public:
         if (data.ptr && data.ptr != &smallarray[0])
             mem.xfree(data.ptr);
     }
+
+    // this is using a template constraint because of ambiguity with this(size_t) when T is
+    // int, and c++ header generation doesn't accept wrapping this in static if
+    extern(D) this()(T[] elems ...) pure nothrow if (is(T == struct) || is(T == class))
+    {
+        this(elems.length);
+        foreach(i; 0 .. elems.length)
+        {
+            this[i] = elems[i];
+        }
+    }
+
     ///returns elements comma separated in []
     extern(D) const(char)[] toString() const
     {
@@ -1181,3 +1193,19 @@ pure nothrow @nogc @safe unittest
     b.popFront();
     assert(b == expected[]);
 }
+
+
+/// Test Array array constructor
+pure nothrow unittest
+{
+    //check to make sure that this works with the aliases in arraytypes.d
+    import dmd.rootobject;
+    alias Objects = Array!RootObject;
+
+    auto ro1 = new RootObject();
+    auto ro2 = new RootObject();
+
+    auto aoo = new Objects(ro1, ro2);
+    assert((*aoo)[0] is ro1);
+    assert((*aoo)[1] is ro2);
+}
index 5ad07750b3b0facc496a484e10ac4b63e850aecb..b25392b6386b40355c854a5272bde41fb641ff11 100644 (file)
@@ -468,7 +468,20 @@ nothrow:
             assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc");
     }
 
-    // Split a path and append the results to `array`
+    /****
+     * Splits a delimiter-separated path string (e.g., from an environment variable like `PATH`)
+     * into individual path fragments and appends them to the given array.
+     *
+     * This is a convenience wrapper around `splitPath` that collects all fragments
+     * into an `Array!(const(char)*)`.
+     *
+     * Params:
+     *   path  = A null-terminated string containing path fragments separated by platform-specific delimiters.
+     *   array = The array to which each extracted fragment will be appended.
+     *
+     * See_Also:
+     *   `splitPath`
+     */
     extern (C++) static void appendSplitPath(const(char)* path, ref Strings array)
     {
         int sink(const(char)* p) nothrow
@@ -487,7 +500,7 @@ nothrow:
      *  sink = send the path pieces here, end when sink() returns !=0
      *  path = the path to split up.
      */
-    static void splitPath(int delegate(const(char)*) nothrow sink, const(char)* path)
+    static void splitPath(scope int delegate(const(char)*) nothrow sink, const(char)* path)
     {
         if (!path)
             return;
index d327f4b258d843e5c5e02d4930fe6cbee334eda6..40dc2eb1979aa4baac10383582222f51d6acf6d6 100644 (file)
@@ -13,7 +13,7 @@ module dmd.root.hash;
 
 // MurmurHash2 was written by Austin Appleby, and is placed in the public
 // domain. The author hereby disclaims copyright to this source code.
-// https://sites.google.com/site/murmurhash/
+// https://github.com/aappleby/smhasher/
 uint calcHash(scope const(char)[] data) @nogc nothrow pure @safe
 {
     return calcHash(cast(const(ubyte)[])data);
index 3be9efecc2c62b4a511bf3de01b5116909111669..4a3eeb77d0334139a14e2d70b9059a3a98ea28e5 100644 (file)
@@ -28,7 +28,7 @@ import dmd.errors;
 import dmd.expression;
 import dmd.func;
 import dmd.funcsem : isRootTraitsCompilesScope;
-import dmd.globals : FeatureState, global;
+import dmd.globals : FeatureState;
 import dmd.id;
 import dmd.identifier;
 import dmd.location;
@@ -85,7 +85,6 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
     if (ad.sizeok != Sizeok.done)
         ad.determineSize(ad.loc);
 
-    import dmd.globals : FeatureState;
     const hasPointers = v.type.hasPointers();
     if (hasPointers)
     {
index b57d77d9882f418f307595fbf7cd88141949fb7a..0b1d9b66bd0dab5611f6cf096464215fc63d8440 100644 (file)
@@ -67,10 +67,6 @@ struct Scope final
     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
-    d_bool nofree;                // true if shouldn't free it
-    d_bool inLoop;                // true if inside a loop (where constructor calls aren't allowed)
-    d_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
 
@@ -96,13 +92,12 @@ struct Scope final
     PragmaDeclaration *inlining; // inlining strategy for functions
 
     Visibility visibility;            // visibility for class members
-    int explicitVisibility;     // set if in an explicit visibility attribute
 
     StorageClass stc;           // storage class
 
     DeprecatedDeclaration *depdecl; // customized deprecation message
 
-    uint16_t flags;
+    uint16_t flags, flags2;
     uint16_t previews; // state of preview switches
 
     bool ctor() const;
index 26f0fa8a254ff0b168da89c292ff79c77263831d..6e4c7d10d3c19b1741ca8501edfa9ef46acdf578 100644 (file)
@@ -813,10 +813,11 @@ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag)
  */
 void lowerStaticAAs(VarDeclaration vd, Scope* sc)
 {
-    if (vd.storage_class & STC.manifest)
-        return;
     if (auto ei = vd._init.isExpInitializer())
-        lowerStaticAAs(ei.exp, sc);
+    {
+        scope v = new StaticAAVisitor(sc, vd.storage_class);
+        ei.exp.accept(v);
+    }
 }
 
 /**
@@ -828,7 +829,7 @@ void lowerStaticAAs(VarDeclaration vd, Scope* sc)
  */
 void lowerStaticAAs(Expression e, Scope* sc)
 {
-    scope v = new StaticAAVisitor(sc);
+    scope v = new StaticAAVisitor(sc, STC.none);
     e.accept(v);
 }
 
@@ -837,33 +838,26 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor
 {
     alias visit = SemanticTimeTransitiveVisitor.visit;
     Scope* sc;
+    STC storage_class;
 
-    this(Scope* sc) scope @safe
+    this(Scope* sc, STC storage_class) scope @safe
     {
         this.sc = sc;
+        this.storage_class = storage_class;
     }
 
     override void visit(AssocArrayLiteralExp aaExp)
     {
-        if (!verifyHookExist(aaExp.loc, *sc, Id._aaAsStruct, "initializing static associative arrays", Id.object))
-            return;
-
-        Expression hookFunc = new IdentifierExp(aaExp.loc, Id.empty);
-        hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id.object);
-        hookFunc = new DotIdExp(aaExp.loc, hookFunc, Id._aaAsStruct);
-        auto arguments = new Expressions();
-        arguments.push(aaExp);
-        Expression loweredExp = new CallExp(aaExp.loc, hookFunc, arguments);
-
-        sc = sc.startCTFE();
-        loweredExp = loweredExp.expressionSemantic(sc);
-        loweredExp = resolveProperties(sc, loweredExp);
-        sc = sc.endCTFE();
-        loweredExp = loweredExp.ctfeInterpret();
-
-        aaExp.lowering = loweredExp;
-
-        semanticTypeInfo(sc, loweredExp.type);
+        if (!aaExp.lowering)
+            expressionSemantic(aaExp, sc);
+        assert(aaExp.lowering);
+        if (!(storage_class & STC.manifest)) // manifest constants create runtime copies
+            if (!aaExp.loweringCtfe)
+            {
+                aaExp.loweringCtfe = aaExp.lowering.ctfeInterpret();
+                aaExp.loweringCtfe.accept(this); // lower AAs in keys and values
+            }
+        semanticTypeInfo(sc, aaExp.lowering.type);
     }
 
     // https://issues.dlang.org/show_bug.cgi?id=24602
index bafb2e7d8719364cd8e806967fe1791e0c2def52..4c4c0ec2b355026d556b5efc09ee245d04f17ea5 100644 (file)
@@ -1,5 +1,7 @@
 /**
- * Performs the semantic3 stage, which deals with function bodies.
+ * Performs the semantic3 stage of semantic analysis, which finalizes
+ * function bodies and late semantic checks for templates, mixins,
+ * aggregates, and special members.
  *
  * Copyright:   Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved
  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
@@ -343,7 +345,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
             sc2.linkage = funcdecl.isCsymbol() ? LINK.c : LINK.d;
             sc2.stc &= STC.flowThruFunction;
             sc2.visibility = Visibility(Visibility.Kind.public_);
-            sc2.explicitVisibility = 0;
+            sc2.explicitVisibility = false;
             sc2.aligndecl = null;
             if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure)
             {
@@ -402,7 +404,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
             // functions may be widely used by dmd-compiled projects.
             // It also gives more time for the implementation of dual-context
             // functions to be reworked as a frontend-only feature.
-            if (funcdecl.hasDualContext())
+            if (funcdecl.hasDualContext)
             {
                 .deprecation(funcdecl.loc, "%s `%s` function requires a dual-context, which is deprecated", funcdecl.kind, funcdecl.toPrettyChars);
                 if (auto ti = sc2.parent ? sc2.parent.isInstantiated() : null)
@@ -615,7 +617,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 if (!funcdecl.fbody)
                     funcdecl.fbody = new CompoundStatement(Loc.initial, new Statements());
 
-                if (funcdecl.isNaked())
+                if (funcdecl.isNaked)
                 {
                     fpreinv = null;         // can't accommodate with no stack frame
                     fpostinv = null;
@@ -925,6 +927,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                                 exp = exp.implicitCastTo(sc2, tret);
 
                             exp = exp.toLvalue(sc2, "`ref` return");
+                            checkAddressable(exp, sc2, "`ref` return");
                             checkReturnEscapeRef(*sc2, exp, false);
                             exp = exp.optimize(WANTvalue, /*keepLvalue*/ true);
                         }
@@ -970,7 +973,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                             /* https://issues.dlang.org/show_bug.cgi?id=10789
                              * If NRVO is not possible, all returned lvalues should call their postblits.
                              */
-                            if (!funcdecl.isNRVO())
+                            if (!funcdecl.isNRVO)
                                 exp = doCopyOrMove(sc2, exp, f.next, true, true);
 
                             if (tret.hasPointers())
@@ -1255,7 +1258,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                             {
                                 // 'this' is the monitor
                                 vsync = new VarExp(funcdecl.loc, funcdecl.vthis);
-                                if (funcdecl.hasDualContext())
+                                if (funcdecl.hasDualContext)
                                 {
                                     vsync = new PtrExp(funcdecl.loc, vsync);
                                     vsync = new IndexExp(funcdecl.loc, vsync, IntegerExp.literal!0);
@@ -1298,7 +1301,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
                 }
             }
 
-            if (funcdecl.isNaked() && (funcdecl.fensures || funcdecl.frequires))
+            if (funcdecl.isNaked && (funcdecl.fensures || funcdecl.frequires))
                 .error(funcdecl.loc, "%s `%s` naked assembly functions with contracts are not supported", funcdecl.kind, funcdecl.toPrettyChars);
 
             sc2.ctorflow.callSuper = CSX.none;
@@ -1516,9 +1519,8 @@ private extern(C++) final class Semantic3Visitor : Visitor
             auto ts = new ThrowStatement(ctor.loc, new IdentifierExp(ctor.loc, id));
             auto handler = new CompoundStatement(ctor.loc, ss, ts);
 
-            auto catches = new Catches();
             auto ctch = new Catch(ctor.loc, getException(), id, handler);
-            catches.push(ctch);
+            auto catches = new Catches(ctch);
 
             ctor.fbody = new TryCatchStatement(ctor.loc, ctor.fbody, catches);
         }
@@ -1604,8 +1606,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
             (ad.type && ad.type.ty != Terror))
         {
             // Evaluate: RTinfo!type
-            auto tiargs = new Objects();
-            tiargs.push(ad.type);
+            auto tiargs = new Objects(ad.type);
             auto ti = new TemplateInstance(ad.loc, Type.rtinfo, tiargs);
 
             Scope* sc3 = ti.tempdecl._scope.startCTFE();
@@ -1628,8 +1629,83 @@ private extern(C++) final class Semantic3Visitor : Visitor
             sd.semanticTypeInfoMembers();
         ad.semanticRun = PASS.semantic3done;
     }
+
+    override void visit(TypeInfoAssociativeArrayDeclaration ti)
+    {
+        if (ti.semanticRun >= PASS.semantic3)
+            return;
+        ti.semanticRun = PASS.semantic3;
+
+        auto t = ti.tinfo.isTypeAArray();
+        Loc loc = t.loc;
+        auto sc2 = sc ? sc : ti._scope;
+
+        auto makeDotExp(Identifier hook)
+        {
+            // always called with naked types
+            auto tiargs = new Objects(t.index, t.next);
+
+            Expression id = new IdentifierExp(loc, Id.empty);
+            id = new DotIdExp(loc, id, Id.object);
+            id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray);
+            return new DotTemplateInstanceExp(loc, id, hook, tiargs);
+        }
+
+        void notTemplateFunction(Loc loc, Identifier id)
+        {
+            error(loc, "`%s` isn't a template function", id.toChars());
+        }
+
+        // generate ti.entry
+        auto tempinst = makeDotExp(Id.Entry);
+        auto e = expressionSemantic(tempinst, sc2);
+        assert(e.type);
+        ti.entry = e.type;
+        if (auto ts = ti.entry.isTypeStruct())
+        {
+            ts.sym.requestTypeInfo = true;
+            if (auto tmpl = ts.sym.isInstantiated())
+                tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted
+        }
+        semanticTypeInfo(sc2, ti.entry); // might get deferred
+
+        // generate ti.xtoHash
+        auto aaGetHash = Identifier.idPool("aaGetHash");
+        auto hashinst = makeDotExp(aaGetHash);
+        e = expressionSemantic(hashinst, sc2);
+        if (!e.isErrorExp())
+        {
+            if (!e.isVarExp() || !e.type.isTypeFunction())
+                notTemplateFunction(e.loc, aaGetHash);
+            else
+            {
+                ti.xtoHash = e.isVarExp().var;
+                if (auto tmpl = ti.xtoHash.parent.isTemplateInstance())
+                    tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted
+            }
+        }
+
+        // generate ti.xopEqual
+        auto aaOpEqual = Identifier.idPool("aaOpEqual");
+        auto equalinst = makeDotExp(aaOpEqual);
+        e = expressionSemantic(equalinst, sc2);
+        if (!e.isErrorExp())
+        {
+            if (!e.isVarExp() || !e.type.isTypeFunction())
+                notTemplateFunction(e.loc, aaOpEqual);
+            else
+            {
+                ti.xopEqual = e.isVarExp().var;
+                if (auto tmpl = ti.xopEqual.parent.isTemplateInstance())
+                    tmpl.minst = sc2._module.importedFrom; // ensure it gets emitted
+            }
+        }
+        visit(cast(ASTCodegen.TypeInfoDeclaration)ti);
+    }
 }
 
+/// Helper for semantic3 analysis of functions.
+/// This struct is part of a WIP refactoring to simplify large `visit(FuncDeclaration)` logic.
 private struct FuncDeclSem3
 {
     // The FuncDeclaration subject to Semantic analysis
@@ -1663,6 +1739,37 @@ private struct FuncDeclSem3
     }
 }
 
+/***************************************
+ * 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)
+        return null;
+
+    __gshared TypeFunction tftostring;
+    if (!tftostring)
+    {
+        tftostring = new TypeFunction(ParameterList(), Type.tstring, LINK.d);
+        tftostring = tftostring.merge().toTypeFunction();
+    }
+    return fd.overloadExactMatch(tftostring);
+}
+
+/**
+ * Ensures special members of a struct are fully analysed
+ * before the backend emits TypeInfo.
+ *
+ * Handles late semantic analysis for members like `opEquals`, `opCmp`,
+ * `toString`, `toHash`, postblit, and destructor.
+ */
 void semanticTypeInfoMembers(StructDeclaration sd)
 {
     if (sd.xeq &&
@@ -1715,13 +1822,22 @@ void semanticTypeInfoMembers(StructDeclaration sd)
     }
 }
 
-/***********************************************
- * Check that the function contains any closure.
- * If it's @nogc, report suitable errors.
- * This is mostly consistent with FuncDeclaration::needsClosure().
+/**
+ * Determine whether the given function will need to allocate a _closure_ and
+ * verify that such an allocation is allowed under the current compilation
+ * settings.
  *
- * Returns:
- *      true if any errors occur.
+ * Whenever an error is emitted, every nested function that actually closes
+ * over a variable is listed in a supplemental diagnostic, together with the
+ * location of the captured variable’s declaration.  (This extra walk is
+ * skipped when the compiler is gagged.)
+ *
+ * See_Also:
+ *      $(UL
+ *        $(LI `FuncDeclaration.needsClosure`)
+ *        $(LI `FuncDeclaration.setGC`)
+ *        $(LI `FuncDeclaration.printGCUsage`)
+ *      )
  */
 extern (D) bool checkClosure(FuncDeclaration fd)
 {
index 5984466d3b96791c290cbc9e2e3d21dbe3fbc9a7..402a74c0f9b23ba8b97402fee9ce4ff5c5470ad6 100644 (file)
@@ -19,7 +19,6 @@ import dmd.expression;
 import dmd.expressionsem;
 import dmd.func;
 import dmd.funcsem;
-import dmd.globals;
 import dmd.hdrgen;
 import dmd.id;
 import dmd.identifier;
@@ -392,6 +391,7 @@ VarDeclaration copyToTemp(STC stc, const char[] name, Expression e)
  *  e0 = a new side effect part will be appended to it.
  *  e = original expression
  *  alwaysCopy = if true, build new temporary variable even if e is trivial.
+ *  stc = storage class flags to add to new temporary variable
  * Returns:
  *  When e is trivial and alwaysCopy == false, e itself is returned.
  *  Otherwise, a new VarExp is returned.
@@ -399,7 +399,7 @@ VarDeclaration copyToTemp(STC stc, const char[] name, Expression e)
  *  e's lvalue-ness will be handled well by STC.ref_ or STC.rvalue.
  */
 Expression extractSideEffect(Scope* sc, const char[] name,
-    ref Expression e0, Expression e, bool alwaysCopy = false)
+    ref Expression e0, Expression e, bool alwaysCopy = false, STC stc = STC.exptemp)
 {
     //printf("extractSideEffect(e: %s)\n", e.toChars());
 
@@ -413,8 +413,8 @@ Expression extractSideEffect(Scope* sc, const char[] name,
         (sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e)))
         return e;
 
-    auto vd = copyToTemp(STC.none, name, e);
-    vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue;
+    stc |= (e.isLvalue() ? STC.ref_ : STC.rvalue);
+    auto vd = copyToTemp(stc, name, e);
 
     e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd)
                                 .expressionSemantic(sc));
index 2ade0a9a9bbc2aeaa3035d25fe119f0d01587d7e..baf928a13f0137fead31219b2950f5c130b03b35 100644 (file)
@@ -439,8 +439,7 @@ extern (C++) final class MixinStatement : Statement
 
     extern (D) this(Loc loc, Expression exp)
     {
-        Expressions* exps = new Expressions();
-        exps.push(exp);
+        Expressions* exps = new Expressions(exp);
         this(loc, exps);
     }
 
@@ -1740,8 +1739,15 @@ extern (C++) final class LabelDsymbol : Dsymbol
  */
 extern (C++) class AsmStatement : Statement
 {
-    Token* tokens;
-    bool caseSensitive;  // for register names
+    Token* tokens;       // linked list of tokens for one instruction or pseudo op
+    static struct BitFields
+    {
+    bool caseSensitive;  // for register names, and only turned on when doing MASM style inline asm
+    bool isVolatile;     // ImportC asm "volatile"
+    bool isInline;       // ImportC asm "inline"
+    }
+    import dmd.common.bitfields;
+    mixin(generateBitFields!(BitFields, ubyte));
 
     extern (D) this(Loc loc, Token* tokens) @safe
     {
@@ -1772,10 +1778,10 @@ extern (C++) class AsmStatement : Statement
 extern (C++) final class InlineAsmStatement : AsmStatement
 {
     void* asmcode;
+    ulong regs;     // mask of registers modified (must match regm_t in back end)
     uint asmalign;  // alignment of this statement
-    uint regs;      // mask of registers modified (must match regm_t in back end)
     bool refparam;  // true if function parameter is referenced
-    bool naked;     // true if function is to be naked
+    bool naked;     // true if function is to be naked (no prolog/epilog)
 
     extern (D) this(Loc loc, Token* tokens) @safe
     {
index 5f962b26d6335b6d2e57f2b17569418416faad2e..c7d941d34338ad8cc3753c186bf291b2a3f8140c 100644 (file)
@@ -715,7 +715,15 @@ class AsmStatement : public Statement
 {
 public:
     Token *tokens;
-    d_bool caseSensitive;  // for register names
+private:
+    uint8_t bitFields;
+public:
+    bool caseSensitive() const; // for register names
+    bool caseSensitive(bool v);
+    bool isVolatile() const;    // importC asm volatile
+    bool isVolatile(bool v);
+    bool isInline() const;      // importC asm inline
+    bool isInline(bool v);
 
     AsmStatement *syntaxCopy() override;
     void accept(Visitor *v) override { v->visit(this); }
@@ -725,8 +733,8 @@ class InlineAsmStatement final : public AsmStatement
 {
 public:
     void *asmcode;
-    unsigned asmalign;          // alignment of this statement
-    unsigned regs;              // mask of registers modified (must match regm_t in back end)
+    unsigned long long regs;      // mask of registers modified (must match regm_t in back end)
+    unsigned asmalign;            // alignment of this statement
     d_bool refparam;              // true if function parameter is referenced
     d_bool naked;                 // true if function is to be naked
 
index 523ed7b39781839d5acc22f3648a94b192a46323..9a9079943b552c4b4045e10f2928b8f9a912ea1b 100644 (file)
@@ -365,10 +365,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                         handler = new CompoundStatement(Loc.initial, handler, ts);
                     }
 
-                    auto catches = new Catches();
                     auto ctch = new Catch(Loc.initial, getThrowable(), id, handler);
                     ctch.internalCatch = true;
-                    catches.push(ctch);
+                    auto catches = new Catches(ctch);
 
                     Statement st = new TryCatchStatement(Loc.initial, _body, catches);
                     if (sfinally)
@@ -621,8 +620,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
              *    } finally { v2.~this(); }
              *  } finally { v1.~this(); }
              */
-            auto ainit = new Statements();
-            ainit.push(fs._init);
+            auto ainit = new Statements(fs._init);
             fs._init = null;
             ainit.push(fs);
             Statement s = new CompoundStatement(fs.loc, ainit);
@@ -1013,7 +1011,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                     Type tindex = (*fs.parameters)[0].type;
                     if (!tindex.isIntegral())
                     {
-                        error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars());
+                        error(fs.loc, "foreach: index cannot be of non-integral type `%s`", tindex.toChars());
                         return retError();
                     }
                     /* What cases to deprecate implicit conversions for:
@@ -1382,8 +1380,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                     Expression ve = new VarExp(loc, vd);
                     ve.type = tfront;
 
-                    auto exps = new Expressions();
-                    exps.push(ve);
+                    auto exps = new Expressions(ve);
                     int pos = 0;
                     while (exps.length < dim)
                     {
@@ -1461,6 +1458,16 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
          */
 
         //printf("ForeachRangeStatement::semantic() %p\n", fs);
+
+        if (fs.param.storageClass & STC.manifest)
+        {
+            error(fs.loc, "cannot declare `enum` loop variables for non-unrolled foreach");
+        }
+        if (fs.param.storageClass & STC.alias_)
+        {
+            error(fs.loc, "cannot declare `alias` loop variables for non-unrolled foreach");
+        }
+
         auto loc = fs.loc;
         fs.lwr = fs.lwr.expressionSemantic(sc);
         fs.lwr = resolveProperties(sc, fs.lwr);
@@ -2126,10 +2133,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
         auto arguments = new Expressions();
         arguments.push(ss.condition);
 
-        auto compileTimeArgs = new Objects();
 
         // The type & label no.
-        compileTimeArgs.push(new TypeExp(ss.loc, ss.condition.type.nextOf()));
+        auto compileTimeArgs = new Objects(new TypeExp(ss.loc, ss.condition.type.nextOf()));
 
         // The switch labels
         foreach (caseString; *csCopy)
@@ -2709,10 +2715,13 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                      */
                     Scope* sc2 = sc.push();
                     sc2.eSink = global.errorSinkNull;
-                    bool err = checkReturnEscapeRef(*sc2, rs.exp, true);
+                    bool addressable = checkAddressable(rs.exp, sc2, "`ref` return");
+                    bool escapes = checkReturnEscapeRef(*sc2, rs.exp, true);
                     sc2.pop();
 
-                    if (err)
+                    if (!addressable)
+                        turnOffRef(() { checkAddressable(rs.exp, sc, "`ref` return"); });
+                    else if (escapes)
                         turnOffRef(() { checkReturnEscapeRef(*sc, rs.exp, false); });
                     else if (!rs.exp.type.constConv(tf.next))
                         turnOffRef(
@@ -3096,11 +3105,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
                 auto tmp = copyToTemp(STC.none, "__sync", ss.exp);
                 tmp.dsymbolSemantic(sc);
 
-                auto cs = new Statements();
-                cs.push(new ExpStatement(ss.loc, tmp));
-
-                auto args = new Parameters();
-                args.push(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type, null, null, null));
+                auto cs = new Statements(new ExpStatement(ss.loc, tmp));
+                auto args = new Parameters(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type,
+                                                         null, null, null));
 
                 FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
                 Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp));
@@ -3131,8 +3138,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             tmp.storage_class |= STC.temp | STC.shared_ | STC.static_;
             Expression tmpExp = new VarExp(ss.loc, tmp);
 
-            auto cs = new Statements();
-            cs.push(new ExpStatement(ss.loc, tmp));
+            auto cs = new Statements(new ExpStatement(ss.loc, tmp));
 
             /* This is just a dummy variable for "goto skips declaration" error.
              * Backend optimizer could remove this unused variable.
@@ -3141,8 +3147,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             v.dsymbolSemantic(sc);
             cs.push(new ExpStatement(ss.loc, v));
 
-            auto enterArgs = new Parameters();
-            enterArgs.push(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null));
+            auto enterArgs = new Parameters(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null));
 
             FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_);
             Expression e = new AddrExp(ss.loc, tmpExp);
@@ -3151,8 +3156,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
             e.type = Type.tvoid; // do not run semantic on e
             cs.push(new ExpStatement(ss.loc, e));
 
-            auto exitArgs = new Parameters();
-            exitArgs.push(new Parameter(Loc.initial, STC.none, t, null, null, null));
+            auto exitArgs = new Parameters(new Parameter(Loc.initial, STC.none, t, null, null, null));
 
             FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_);
             e = new CallExp(ss.loc, fdexit, tmpExp);
@@ -3743,7 +3747,7 @@ public bool throwSemantic(Loc loc, ref Expression exp, Scope* sc)
     exp = exp.checkGC(sc);
     if (exp.isErrorExp())
         return false;
-    if (!exp.type.isNaked())
+    if (!exp.type.isNaked)
     {
         // @@@DEPRECATED_2.112@@@
         // Deprecated in 2.102, change into an error & return false in 2.112
@@ -3864,10 +3868,8 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
 
     FuncDeclaration fdapply;
     TypeDelegate dgty;
-    auto params = new Parameters();
-    params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null));
-    auto dgparams = new Parameters();
-    dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
+    auto params = new Parameters(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null));
+    auto dgparams = new Parameters(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
     if (dim == 2)
         dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
     dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
@@ -3892,7 +3894,6 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde,
 private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab)
 {
     auto taa = tab.isTypeAArray();
-    Expression ec;
     const dim = fs.parameters.length;
     // Check types
     Parameter p = (*fs.parameters)[0];
@@ -3903,8 +3904,8 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld
         Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index);
         if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta))
         {
-            error(fs.loc, "`foreach`: index must be type `%s`, not `%s`",
-                     ti.toChars(), ta.toChars());
+            error(fs.loc, "`foreach`: index parameter `%s%s` must be type `%s`, not `%s`",
+                 isRef ? "ref ".ptr : "".ptr, p.toChars(), ti.toChars(), ta.toChars());
             return null;
         }
         p = (*fs.parameters)[1];
@@ -3914,55 +3915,28 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld
     Type taav = taa.nextOf();
     if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
     {
-        error(fs.loc, "`foreach`: value must be type `%s`, not `%s`",
-                 taav.toChars(), ta.toChars());
+        error(fs.loc, "`foreach`: value parameter `%s%s` must be type `%s`, not `%s`",
+            isRef ? "ref ".ptr : "".ptr, p.toChars(), taav.toChars(), ta.toChars());
         return null;
     }
 
     /* Call:
-     *  extern(C) int _aaApply(void*, in size_t, int delegate(void*))
-     *      _aaApply(aggr, keysize, flde)
-     *
-     *  extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*))
-     *      _aaApply2(aggr, keysize, flde)
+     *   int _d_aaApply(V[K] aa, int delegate(V*))
+     * or
+     *   int _d_aaApply2(V[K] aa, int delegate(K*, V*))
      */
-    __gshared FuncDeclaration* fdapply = [null, null];
-    __gshared TypeDelegate* fldeTy = [null, null];
-    ubyte i = (dim == 2 ? 1 : 0);
-    if (!fdapply[i])
-    {
-        auto params = new Parameters();
-        params.push(new Parameter(Loc.initial, STC.none, Type.tvoid.pointerTo(), null, null, null));
-        params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null));
-        auto dgparams = new Parameters();
-        dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
-        if (dim == 2)
-            dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null));
-        fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d));
-        params.push(new Parameter(Loc.initial, STC.none, fldeTy[i], null, null, null));
-        fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply);
-    }
-
-    auto exps = new Expressions();
-    exps.push(fs.aggr);
-    auto keysize = taa.index.size();
-    if (keysize == SIZE_INVALID)
-        return null;
-    assert(keysize < keysize.max - target.ptrsize);
-    keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1);
-    // paint delegate argument to the type runtime expects
-    Expression fexp = flde;
-    if (!fldeTy[i].equals(flde.type))
-    {
-        fexp = new CastExp(fs.loc, flde, flde.type);
-        fexp.type = fldeTy[i];
-    }
-    exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t));
-    exps.push(fexp);
-    ec = new VarExp(Loc.initial, fdapply[i], false);
-    ec = new CallExp(fs.loc, ec, exps);
-    ec.type = Type.tint32; // don't run semantic() on ec
-    return ec;
+    auto loc = fs.loc;
+    Identifier hook = dim == 2 ? Id._d_aaApply2 : Id._d_aaApply;
+    Expression func = new IdentifierExp(loc, Id.empty);
+    func = new DotIdExp(loc, func, Id.object);
+    auto tiargs = new Objects(taa.index.substWildTo(MODFlags.const_),
+                              taav.substWildTo(MODFlags.const_),
+                              flde.type.substWildTo(MODFlags.const_));
+    func = new DotTemplateInstanceExp(loc, func, hook, tiargs);
+
+    auto arguments = new Expressions(fs.aggr, flde);
+    auto call = new CallExp(loc, func, arguments);
+    return call;
 }
 
 private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc)
@@ -3976,12 +3950,11 @@ private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc)
     // Construct a switch statement around the return value
     // of the apply function.
     Statement s;
-    auto a = new Statements();
 
     // default: break; takes care of cases 0 and 1
     s = new BreakStatement(Loc.initial, null);
     s = new DefaultStatement(Loc.initial, s);
-    a.push(s);
+    auto a = new Statements(s);
 
     // cases 2...
     foreach (i, c; *cases)
@@ -4007,6 +3980,10 @@ private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc)
  */
 private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld)
 {
+    auto tab = fs.aggr.type.toBasetype();
+    auto taa = tab.isTypeAArray();
+    const isStaticOrDynamicArray = tab.isTypeSArray() || tab.isTypeDArray();
+
     auto params = new Parameters();
     foreach (i, p; *fs.parameters)
     {
@@ -4015,6 +3992,7 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti
 
         p.type = p.type.typeSemantic(fs.loc, sc);
         p.type = p.type.addStorageClass(p.storageClass);
+        p.type = p.type.substWildTo(MODFlags.const_);
         if (tfld)
         {
             Parameter param = tfld.parameterList[i];
@@ -4044,8 +4022,26 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti
         LcopyArg:
             id = Identifier.generateId("__applyArg", cast(int)i);
 
-            Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id));
-            auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie);
+            // Make sure `p.type` matches the signature expected by the druntime helpers.
+            const isIndexParam = i == 0 && fs.parameters.length == 2;
+            Type userType = p.type;
+            Expression initExp = new IdentifierExp(fs.loc, id);
+            if (taa)
+            {
+                p.type = isIndexParam ? taa.index : taa.nextOf();
+                p.type = p.type.substWildTo(MODFlags.const_);
+            }
+            else if (isStaticOrDynamicArray && isIndexParam)
+            {
+                p.type = Type.tsize_t;
+                // If the user used an incompatible index type (e.g., `int` instead
+                // of `size_t` - which is deprecated nowadays), then add a cast.
+                if (!p.type.implicitConvTo(userType))
+                    initExp = new CastExp(fs.loc, initExp, userType);
+            }
+
+            Initializer initializer = new ExpInitializer(fs.loc, initExp);
+            auto v = new VarDeclaration(fs.loc, userType, p.ident, initializer);
             v.storage_class |= STC.temp | (stc & STC.scope_);
             Statement s = new ExpStatement(fs.loc, v);
             fs._body = new CompoundStatement(fs.loc, s, fs._body);
@@ -4104,7 +4100,7 @@ void catchSemantic(Catch c, Scope* sc)
         // reference .object.Throwable
         c.type = getThrowable();
     }
-    else if (!c.type.isNaked() && !c.type.isConst())
+    else if (!c.type.isNaked && !c.type.isConst())
     {
         // @@@DEPRECATED_2.115@@@
         // Deprecated in 2.105, change into an error & uncomment assign in 2.115
@@ -4352,7 +4348,8 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState
     const bool skipCheck = isStatic && needExpansion;
     if (!skipCheck && (dim < 1 || dim > 2))
     {
-        error(fs.loc, "only one (value) or two (key,value) arguments allowed for sequence `foreach`");
+        error(fs.loc, "only one (element) or two (index, element) arguments allowed for sequence `foreach`, not %llu",
+            ulong(dim));
         return returnEarly();
     }
 
@@ -4411,10 +4408,11 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState
         const bool skip = isStatic && needExpansion;
         if (!skip && dim == 2)
         {
-            // Declare key
+            // Declare index
             if (p.isReference() || p.isLazy())
             {
-                error(fs.loc, "no storage class for key `%s`", p.ident.toChars());
+                error(fs.loc, "invalid storage class `%s` for index `%s`",
+                    stcToString(p.storageClass).ptr, p.ident.toChars());
                 return returnEarly();
             }
 
@@ -4429,7 +4427,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState
 
             if (!p.type.isIntegral())
             {
-                error(fs.loc, "foreach: key cannot be of non-integral type `%s`",
+                error(fs.loc, "foreach: index cannot be of non-integral type `%s`",
                          p.type.toChars());
                 return returnEarly();
             }
@@ -4474,7 +4472,8 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState
             if (storageClass & (STC.out_ | STC.lazy_) ||
                 storageClass & STC.ref_ && !te)
             {
-                error(fs.loc, "no storage class for value `%s`", ident.toChars());
+                error(fs.loc, "invalid storage class `%s` for element `%s`",
+                    stcToString(p.storageClass).ptr, ident.toChars());
                 return false;
             }
             Declaration var;
@@ -4572,7 +4571,13 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState
                 var = new AliasDeclaration(loc, ident, t);
                 if (paramtype)
                 {
-                    error(fs.loc, "cannot specify element type for symbol `%s`", fs.toChars());
+                    error(fs.loc, "cannot specify element type for symbol `%s`", ident.toChars());
+                    return false;
+                }
+                if (storageClass & STC.manifest)
+                {
+                    error(fs.loc, "invalid storage class `enum` for element `%s`",
+                        ident.toChars());
                     return false;
                 }
             }
@@ -4689,8 +4694,7 @@ private Statements* flatten(Statement statement, Scope* sc)
 {
     static auto errorStatements()
     {
-        auto a = new Statements();
-        a.push(new ErrorStatement());
+        auto a = new Statements(new ErrorStatement());
         return a;
     }
 
@@ -4734,8 +4738,7 @@ private Statements* flatten(Statement statement, Scope* sc)
                 toCBuffer(s, &buf, &hgs);
                 printf("tm ==> s = %s\n", buf.peekChars());
             }
-            auto a = new Statements();
-            a.push(s);
+            auto a = new Statements(s);
             return a;
 
         case STMT.Forwarding:
@@ -4787,8 +4790,7 @@ private Statements* flatten(Statement statement, Scope* sc)
             else
                 s = cs.elsebody;
 
-            auto a = new Statements();
-            a.push(s);
+            auto a = new Statements(s);
             return a;
 
         case STMT.StaticForeach:
@@ -4802,8 +4804,7 @@ private Statements* flatten(Statement statement, Scope* sc)
                 {
                     return result;
                 }
-                result = new Statements();
-                result.push(s);
+                result = new Statements(s);
                 return result;
             }
             else
index 46926cd141953b938781ee3e478de2ae485f4130..5f5d284a64c9e9df8c86663d37033f7b2412189f 100644 (file)
@@ -335,10 +335,10 @@ struct TargetC
     BitFieldStyle bitFieldStyle; /// different C compilers do it differently
 
     /**
-     * Indicates whether the specified bit-field contributes to the alignment
+     * Indicates whether the specified bitfield contributes to the alignment
      * of the containing aggregate.
      * E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length)
-     * bit-fields.
+     * bitfields.
      */
     extern (C++) bool contributesToAggregateAlignment(BitFieldDeclaration bfd);
 }
index bfc41514804394570a4f83a1f3b9055ab72665de..db810511e62b8ee0f6c086f6d703ac9d8ffd8e9c 100644 (file)
@@ -29,6 +29,12 @@ class Expression;
 class FuncDeclaration;
 class Parameter;
 
+namespace dmd
+{
+    bool isDiscardable(TemplateInstance*);
+    bool needsCodegen(TemplateInstance*);
+}
+
 class Tuple final : public RootObject
 {
 public:
@@ -126,7 +132,6 @@ public:
     virtual bool declareParameter(Scope *sc) = 0;
     virtual void print(RootObject *oarg, RootObject *oded) = 0;
     virtual RootObject *specialization() = 0;
-    virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0;
     virtual bool hasDefaultArg() = 0;
 
     DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; }
@@ -148,7 +153,6 @@ public:
     bool declareParameter(Scope *sc) override final;
     void print(RootObject *oarg, RootObject *oded) override final;
     RootObject *specialization() override final;
-    RootObject *defaultArg(Loc instLoc, Scope *sc) override final;
     bool hasDefaultArg() override final;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -179,7 +183,6 @@ public:
     bool declareParameter(Scope *sc) override;
     void print(RootObject *oarg, RootObject *oded) override;
     RootObject *specialization() override;
-    RootObject *defaultArg(Loc instLoc, Scope *sc) override;
     bool hasDefaultArg() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -199,7 +202,6 @@ public:
     bool declareParameter(Scope *sc) override;
     void print(RootObject *oarg, RootObject *oded) override;
     RootObject *specialization() override;
-    RootObject *defaultArg(Loc instLoc, Scope *sc) override;
     bool hasDefaultArg() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -215,7 +217,6 @@ public:
     bool declareParameter(Scope *sc) override;
     void print(RootObject *oarg, RootObject *oded) override;
     RootObject *specialization() override;
-    RootObject *defaultArg(Loc instLoc, Scope *sc) override;
     bool hasDefaultArg() override;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -267,14 +268,10 @@ public:
     unsigned char inuse;                 // for recursive expansion detection
 
     TemplateInstance *syntaxCopy(Dsymbol *) override;
-    Dsymbol *toAlias() override final;   // resolve real symbol
     const char *kind() const override;
     const char* toPrettyCharsHelper() override final;
     Identifier *getIdent() override final;
 
-    bool isDiscardable();
-    bool needsCodegen();
-
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -302,4 +299,5 @@ namespace dmd
     TemplateParameter *isTemplateParameter(RootObject *o);
     bool isError(const RootObject *const o);
     void printTemplateStats(bool listInstances, ErrorSink* eSink);
+    void printInstantiationTrace(TemplateInstance *ti);
 }
index a9cf867f11475c674db9b09c7e962c0845309edd..b392476a44bcb01e9c42f872ae10ae5f33d58c04 100644 (file)
@@ -57,6 +57,8 @@ import dmd.visitor;
 
 alias funcLeastAsSpecialized = dmd.funcsem.leastAsSpecialized;
 
+enum LOG = false;
+
 /************************************
  * Perform semantic analysis on template.
  * Params:
@@ -205,6 +207,583 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl)
     tempdecl.semanticRun = PASS.semanticdone;
 }
 
+/*******************************************
+ * 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);
+}
+
+/**********************************************
+ * 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.
+ */
+bool updateTempDecl(TemplateInstance ti, Scope* sc, Dsymbol s)
+{
+    if (!s)
+        return ti.tempdecl !is null;
+
+    Identifier id = ti.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)
+                {
+                    ti.tempdecl = os;
+                    return true;
+                }
+                s = s2;
+            }
+        }
+        if (!s)
+        {
+            .error(ti.loc, "%s `%s` template `%s` is not defined", ti.kind, ti.toPrettyChars, id.toChars());
+            return false;
+        }
+    }
+
+    if (OverDeclaration od = s.isOverDeclaration())
+    {
+        ti.tempdecl = od; // TODO: more strict check
+        return true;
+    }
+
+    /* It should be a TemplateDeclaration, not some other symbol
+     */
+    if (FuncDeclaration f = s.isFuncDeclaration())
+        ti.tempdecl = f.findTemplateDeclRoot();
+    else
+        ti.tempdecl = s.isTemplateDeclaration();
+
+    // We're done
+    if (ti.tempdecl)
+        return true;
+
+    // Error already issued, just return `false`
+    if (!s.parent && global.errors)
+        return false;
+
+    if (!s.parent && s.getType())
+    {
+        Dsymbol s2 = s.getType().toDsymbol(sc);
+        if (!s2)
+        {
+            .error(ti.loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", ti.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)
+        {
+            if (ad && ad.isAliasedTemplateParameter())
+                printf("`%s` is an alias created from a template parameter\n", s.toChars());
+        }
+        if (!ad || !ad.isAliasedTemplateParameter())
+            s = s2;
+    }
+
+    TemplateInstance ti2 = 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 (ti2 && (ti2.name == s.ident || matchId(ti2, s.ident)) && ti2.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 = ti2.tempdecl.isTemplateDeclaration();
+        assert(td);
+        if (td.overroot) // if not start of overloaded list of TemplateDeclaration's
+            td = td.overroot; // then get the start
+        ti.tempdecl = td;
+        return true;
+    }
+    else
+    {
+        .error(ti.loc, "%s `%s` `%s` is not a template declaration, it is a %s",
+               ti.kind, ti.toPrettyChars, id.toChars(), s.kind());
+        return false;
+    }
+}
 
 /***************************************
  * Given that ti is an instance of this TemplateDeclaration,
@@ -402,30 +981,256 @@ MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti,
     return m;
 }
 
-/****************************
- * Check to see if constraint is satisfied.
+/**
+    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.
+*/
+bool isDiscardable(TemplateInstance ti)
+{
+    if (ti.aliasdecl is null)
+        return false;
+
+    auto v = ti.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; *ti.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.
  */
-bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+bool needsCodegen(TemplateInstance ti)
 {
-    /* Detect recursive attempts to instantiate this template declaration,
-     * https://issues.dlang.org/show_bug.cgi?id=4072
-     *  void foo(T)(T x) if (is(typeof(foo(x)))) { }
-     *  static assert(!is(typeof(foo(7))));
-     * Recursive attempts are regarded as a constraint failure.
-     */
-    /* There's a chicken-and-egg problem here. We don't know yet if this template
-     * instantiation will be a local one (enclosing is set), and we won't know until
-     * after selecting the correct template. Thus, function we're nesting inside
-     * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel().
-     * Workaround the problem by setting a flag to relax the checking on frame errors.
-     */
+    //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 = ti.tnext;
+    TemplateInstance tinst = ti.tinst;
+    ti.tnext = null;
+
+    // Don't do codegen if the instance has errors,
+    // is a dummy instance (see evaluateConstraint),
+    // or is determined to be discardable.
+    if (ti.errors || ti.inst is null || ti.inst.isDiscardable())
+    {
+        ti.minst = null; // mark as speculative
+        return false;
+    }
 
-    for (TemplatePrevious* p = td.previous; p; p = p.prev)
+    // This should only be called on the primary instantiation.
+    assert(ti is ti.inst);
+
+    /* Hack for suppressing linking errors against template instances
+    * of `_d_arrayliteralTX` (e.g. `_d_arrayliteralTX!(int[])`).
+    *
+    * This happens, for example, when a lib module is compiled with `preview=dip1000` and
+    * the array literal is placed on stack instead of using the lowering. In this case,
+    * if the root module is compiled without `preview=dip1000`, the compiler will consider
+    * the template already instantiated within the lib module, and thus skip the codegen for it
+    * in the root module object file, thinking that the linker will find the instance in the lib module.
+    *
+    * To bypass this edge case, we always do codegen for `_d_arrayliteralTX` template instances,
+    * even if an instance already exists in non-root module.
+    */
+    if (ti.inst && ti.inst.name == Id._d_arrayliteralTX)
     {
-        if (!arrayObjectMatch(*p.dedargs, *dedargs))
-            continue;
-        //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
-        /* It must be a subscope of p.sc, other scope chains are not recursive
+        return true;
+    }
+
+    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(ti, 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)
+            {
+                ti.minst = tnext.minst; // cache result
+                assert(ti.minst);
+                assert(ti.minst.isRoot());
+                return true;
+            }
+            else if (!ti.minst && tnext.minst)
+            {
+                ti.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(ti, 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)
+            {
+                ti.minst = tnext.minst; // cache result
+                assert(!ti.minst.isRoot() && !ti.minst.rootImports());
+                return false;
+            }
+            else if (!ti.minst)
+            {
+                ti.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 ti.minst !is null;
+}
+
+/****************************
+ * Check to see if constraint is satisfied.
+ */
+bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+{
+    /* Detect recursive attempts to instantiate this template declaration,
+     * https://issues.dlang.org/show_bug.cgi?id=4072
+     *  void foo(T)(T x) if (is(typeof(foo(x)))) { }
+     *  static assert(!is(typeof(foo(7))));
+     * Recursive attempts are regarded as a constraint failure.
+     */
+    /* There's a chicken-and-egg problem here. We don't know yet if this template
+     * instantiation will be a local one (enclosing is set), and we won't know until
+     * after selecting the correct template. Thus, function we're nesting inside
+     * is not on the sc scope chain, and this can cause errors in FuncDeclaration.getLevel().
+     * Workaround the problem by setting a flag to relax the checking on frame errors.
+     */
+
+    for (TemplatePrevious* p = td.previous; p; p = p.prev)
+    {
+        if (!arrayObjectMatch(*p.dedargs, *dedargs))
+            continue;
+        //printf("recursive, no match p.sc=%p %p %s\n", p.sc, this, this.toChars());
+        /* It must be a subscope of p.sc, other scope chains are not recursive
          * instantiations.
          * the chain of enclosing scopes is broken by paramscope (its enclosing
          * scope is _scope, but paramscope.callsc is the instantiating scope). So
@@ -528,6 +1333,317 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc,
     return result;
 }
 
+/****************************
+ * Destructively get the error message from the last constraint evaluation
+ * Params:
+ *      td = TemplateDeclaration
+ *      tip = tip to show after printing all overloads
+ */
+const(char)* getConstraintEvalError(TemplateDeclaration td, 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(td.constraint, td.lastConstraint, td.lastConstraintNegs[], full, count);
+    scope (exit)
+    {
+        td.lastConstraint = null;
+        td.lastConstraintTiargs = null;
+        td.lastConstraintNegs.setDim(0);
+    }
+    if (!msg)
+        return null;
+
+    OutBuffer buf;
+
+    assert(td.parameters && td.lastConstraintTiargs);
+    if (td.parameters.length > 0)
+    {
+        formatParamsWithTiargs(*td.parameters, *td.lastConstraintTiargs, td.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();
+}
+
+/**********************************
+ * Find the TemplateDeclaration that matches this TemplateInstance best.
+ *
+ * Params:
+ *   ti    = TemplateInstance
+ *   sc    = the scope this TemplateInstance resides in
+ *   argumentList = function arguments in case of a template function
+ *
+ * Returns:
+ *   `true` if a match was found, `false` otherwise
+ */
+bool findBestMatch(TemplateInstance ti, Scope* sc, ArgumentList argumentList)
+{
+    if (ti.havetempdecl)
+    {
+        TemplateDeclaration tempdecl = ti.tempdecl.isTemplateDeclaration();
+        assert(tempdecl);
+        assert(tempdecl._scope);
+        // Deduce tdtypes
+        ti.tdtypes.setDim(tempdecl.parameters.length);
+        if (!matchWithInstance(sc, tempdecl, ti, ti.tdtypes, argumentList, 2))
+        {
+            .error(ti.loc, "%s `%s` incompatible arguments for template instantiation",
+                   ti.kind, ti.toPrettyChars);
+            return false;
+        }
+        // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary?
+        return true;
+    }
+
+    static if (LOG)
+    {
+        printf("TemplateInstance.findBestMatch()\n");
+    }
+
+    const errs = global.errors;
+    TemplateDeclaration td_last = null;
+    Objects dedtypes;
+
+    /* Since there can be multiple TemplateDeclaration's with the same
+     * name, look for the best match.
+     */
+    auto tovers = ti.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] : ti.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 < ti.tiargs.length)
+            {
+                if (!td.isVariadic())
+                    return 0;
+            }
+
+            dedtypes.setDim(td.parameters.length);
+            dedtypes.zero();
+            assert(td.semanticRun != PASS.initial);
+
+            MATCH m = matchWithInstance(sc, td, ti, 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;
+            }
+
+            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;
+            ti.tdtypes.setDim(dedtypes.length);
+            memcpy(ti.tdtypes.tdata(), dedtypes.tdata(), ti.tdtypes.length * (void*).sizeof);
+            return 0;
+        });
+
+        if (td_ambig)
+        {
+            .error(ti.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(ti.loc, td_last, td_best);
+                return false;
+            }
+        }
+    }
+
+    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 (ti.tiargs.length <= i)
+                ti.tiargs.push(ti.tdtypes[i]);
+            assert(i < ti.tiargs.length);
+
+            auto tvp = (*td_last.parameters)[i].isTemplateValueParameter();
+            if (!tvp)
+                continue;
+            assert(ti.tdtypes[i]);
+            // tdtypes[i] is already normalized to the required type in matchArg
+
+            (*ti.tiargs)[i] = ti.tdtypes[i];
+        }
+        if (td_last.isVariadic() && ti.tiargs.length == dim && ti.tdtypes[dim])
+        {
+            Tuple va = isTuple(ti.tdtypes[dim]);
+            assert(va);
+            ti.tiargs.pushSlice(va.objects[]);
+        }
+    }
+    else if (ti.errors && ti.inst)
+    {
+        // instantiation was failed with error reporting
+        assert(global.errors);
+        return false;
+    }
+    else
+    {
+        auto tdecl = ti.tempdecl.isTemplateDeclaration();
+
+        if (errs != global.errors)
+            errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars());
+        else if (tdecl && !tdecl.overnext)
+        {
+            // 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(ti.loc, "%s `%s` %s `%s`\n%s", ti.kind, ti.toPrettyChars, msg, tmsg, cmsg);
+                if (tip)
+                    .tip(tip);
+            }
+            else
+            {
+                .error(ti.loc, "%s `%s` %s `%s`", ti.kind, ti.toPrettyChars, msg, tmsg);
+
+                if (tdecl.parameters.length == ti.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(ti.loc, sc, ti.tiargs, i, tdecl.parameters, dedtypes, null);
+                        auto arg = (*ti.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(ti.loc, "`%s` is not a type", arg.toChars);
+                            else if (auto tvp = param.isTemplateValueParameter)
+                                errorSupplemental(ti.loc, "`%s` is not of a value of type `%s`",
+                                                  arg.toChars, tvp.valType.toChars);
+
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            .error(ti.loc, "%s `%s` does not match any template declaration", ti.kind(), ti.toPrettyChars());
+            bool found;
+            overloadApply(ti.tempdecl, (s){
+                if (!found)
+                    errorSupplemental(ti.loc, "Candidates are:");
+                found = true;
+                errorSupplemental(s.loc, "%s", s.toChars());
+                return 0;
+            });
+        }
+        return false;
+    }
+
+    /* The best match is td_last
+     */
+    ti.tempdecl = td_last;
+
+    static if (LOG)
+    {
+        printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars());
+    }
+    return (errs == global.errors);
+}
+
 /*******************************************
  * Append to buf a textual representation of template parameters with their arguments.
  * Params:
@@ -536,7 +1652,7 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc,
  *  variadic = if it's a variadic argument list
  *  buf = where the text output goes
  */
-void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf)
+private void formatParamsWithTiargs(ref TemplateParameters parameters, ref Objects tiargs, bool variadic, ref OutBuffer buf)
 {
     buf.writestring("  with `");
 
@@ -677,6 +1793,79 @@ L1:
     return MATCH.nomatch;
 }
 
+RootObject defaultArg(TemplateParameter tp, Loc instLoc, Scope* sc)
+{
+    if (tp.isTemplateTupleParameter())
+        return null;
+    if (auto tpp = tp.isTemplateTypeParameter())
+    {
+        Type t = tpp.defaultType;
+        if (t)
+        {
+            t = t.syntaxCopy();
+            t = t.typeSemantic(tpp.loc, sc); // use the parameter loc
+        }
+        return t;
+    }
+    if (auto tap = tp.isTemplateAliasParameter())
+    {
+        RootObject da = tap.defaultAlias;
+        if (auto ta = isType(tap.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(tap.loc, sc, da, null); // use the parameter loc
+        return o;
+    }
+    if (auto tvp = tp.isTemplateValueParameter())
+    {
+        Expression e = tvp.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;
+    }
+    assert(0);
+}
+
 /*************************************************
  * Match function arguments against a specific template function.
  *
@@ -1873,43 +3062,322 @@ extern (D) RootObject declareParameter(TemplateDeclaration td, Scope* sc, Templa
     return o;
 }
 
-/*************************************************
- * Given function arguments, figure out which template function
- * to expand, and return matching result.
- * Params:
- *      m           = matching result
- *      dstart      = the root of overloaded function templates
- *      loc         = instantiation location
- *      sc          = instantiation scope
- *      tiargs      = initial list of template arguments
- *      tthis       = if !NULL, the 'this' pointer argument
- *      argumentList= arguments to function
- *      errorHelper = delegate to send error message to if not null
+/**********************************
+ * 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.
  */
-void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
-    Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null)
+bool TemplateInstance_semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null)
 {
-    version (none)
+    // 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++)
     {
-        printf("functionResolve() dstart: %s (", dstart.toChars());
-        for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++)
-        {
-            if (i) printf(", ");
-            RootObject arg = (*tiargs)[i];
-            printf("%s", arg.toChars());
-        }
-        printf(")(");
-        for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++)
-        {
-            if (i) printf(", ");
-            Expression arg = (*argumentList.arguments)[i];
-            printf("%s %s", arg.type.toChars(), arg.toChars());
-            //printf("\tty = %d\n", arg.type.ty);
-        }
-        printf(")\n");
-        //printf("stc = %llx\n", dstart._scope.stc);
-        //printf("match:t/f = %d/%d\n", ta_last, m.last);
-    }
+        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;
+
+            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;
+            }
+
+            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())
+            {
+                FuncDeclaration f = fa.toAliasFunc();
+                if (!fa.hasOverloads && f.isUnique())
+                {
+                    // Strip FuncAlias only when the aliased function
+                    // does not have any overloads.
+                    sa = f;
+                }
+            }
+            (*tiargs)[j] = sa;
+
+            TemplateDeclaration td = sa.isTemplateDeclaration();
+            if (td && td.semanticRun == PASS.initial && td.literal)
+            {
+                td.dsymbolSemantic(sc);
+            }
+            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)
+    {
+        printf("-TemplateInstance.semanticTiargs()\n");
+        for (size_t j = 0; j < tiargs.length; j++)
+        {
+            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);
+        }
+    }
+    return !err;
+}
+
+
+/*************************************************
+ * Given function arguments, figure out which template function
+ * to expand, and return matching result.
+ * Params:
+ *      m           = matching result
+ *      dstart      = the root of overloaded function templates
+ *      loc         = instantiation location
+ *      sc          = instantiation scope
+ *      tiargs      = initial list of template arguments
+ *      tthis       = if !NULL, the 'this' pointer argument
+ *      argumentList= arguments to function
+ *      errorHelper = delegate to send error message to if not null
+ */
+void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
+    Type tthis, ArgumentList argumentList, void delegate(const(char)*) scope errorHelper = null)
+{
+    version (none)
+    {
+        printf("functionResolve() dstart: %s (", dstart.toChars());
+        for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++)
+        {
+            if (i) printf(", ");
+            RootObject arg = (*tiargs)[i];
+            printf("%s", arg.toChars());
+        }
+        printf(")(");
+        for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++)
+        {
+            if (i) printf(", ");
+            Expression arg = (*argumentList.arguments)[i];
+            printf("%s %s", arg.type.toChars(), arg.toChars());
+            //printf("\tty = %d\n", arg.type.ty);
+        }
+        printf(")\n");
+        //printf("stc = %llx\n", dstart._scope.stc);
+        //printf("match:t/f = %d/%d\n", ta_last, m.last);
+    }
 
     // results
     int property = 0;   // 0: uninitialized
@@ -2501,3 +3969,1865 @@ private extern(C++) class DummyArgVisitor : Visitor
         result = null;
     }
 }
+
+/**
+ * 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;
+}
+
+private auto X(T, U)(T m, U n)
+{
+    return (m << 4) | n;
+}
+
+private ubyte deduceWildHelper(Type t, Type* at, Type tparam)
+{
+    if ((tparam.mod & MODFlags.wild) == 0)
+        return 0;
+
+    *at = null;
+
+    switch (X(tparam.mod, t.mod))
+    {
+    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):
+        {
+            *at = t.unqualify(tparam.mod & t.mod);
+            return MODFlags.wild;
+        }
+    default:
+        return 0;
+    }
+}
+
+private MATCH deduceTypeHelper(Type t, out Type at, Type tparam)
+{
+    // 9*9 == 81 cases
+    switch (X(tparam.mod, t.mod))
+    {
+    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
+        {
+            at = t.unSharedOf().mutableOf();
+            return MATCH.constant;
+        }
+    case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+        // foo(shared(const(U)))        shared(inout(T))        => T
+        {
+            at = t.unSharedOf().mutableOf();
+            return MATCH.constant;
+        }
+    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);
+    }
+}
+
+__gshared Expression emptyArrayElement = null;
+
+/*
+ * Returns `true` if `t` is a reference type, or an array of reference types.
+ */
+private bool isTopRef(Type t)
+{
+    auto tb = t.baseElemOf();
+    return tb.ty == Tclass ||
+           tb.ty == Taarray ||
+           tb.ty == Tstruct && tb.hasPointers();
+}
+
+/*
+ * Returns a valid `Loc` for semantic routines.
+ * Some paths require a location derived from the first
+ * template parameter when available.
+ */
+private Loc semanticLoc(scope ref TemplateParameters parameters)
+{
+    Loc loc;
+    if (parameters.length)
+        loc = parameters[0].loc;
+    return loc;
+}
+
+private MATCH deduceAliasThis(Type t, Scope* sc, Type tparam,
+    ref TemplateParameters parameters, ref Objects dedtypes, uint* wm)
+{
+    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);
+                auto m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+                tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT);
+                return m;
+            }
+        }
+    }
+    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);
+                auto m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+                ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT);
+                return m;
+            }
+        }
+    }
+    return MATCH.nomatch;
+}
+
+private MATCH deduceParentInstance(Scope* sc, Dsymbol sym, TypeInstance tpi,
+    ref TemplateParameters parameters, ref Objects dedtypes, uint* wm)
+{
+    if (tpi.idents.length)
+    {
+        RootObject id = tpi.idents[tpi.idents.length - 1];
+        if (id.dyncast() == DYNCAST.identifier && sym.ident.equals(cast(Identifier)id))
+        {
+            Type tparent = sym.parent.getType();
+            if (tparent)
+            {
+                tpi.idents.length--;
+                auto m = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+                tpi.idents.length++;
+                return m;
+            }
+        }
+    }
+    return MATCH.nomatch;
+}
+
+private MATCH matchAll(TypeDeduced td, Type tt)
+{
+    MATCH match = MATCH.exact;
+    foreach (j, e; td.argexps)
+    {
+        assert(e);
+        if (e == emptyArrayElement)
+            continue;
+
+        Type t = tt.addMod(td.tparams[j].mod).substWildTo(MODFlags.const_);
+
+        MATCH m = e.implicitConvTo(t);
+        if (match > m)
+            match = m;
+        if (match == MATCH.nomatch)
+            break;
+    }
+    return match;
+}
+/****
+ * Given an identifier, figure out which TemplateParameter it is.
+ * Return IDX_NOTFOUND if not found.
+ */
+private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters)
+{
+    for (size_t i = 0; i < parameters.length; i++)
+    {
+        TemplateParameter tp = (*parameters)[i];
+        if (tp.ident.equals(id))
+            return i;
+    }
+    return IDX_NOTFOUND;
+}
+
+private size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
+{
+    if (TypeIdentifier tident = tparam.isTypeIdentifier())
+    {
+        //printf("\ttident = '%s'\n", tident.toChars());
+        return templateIdentifierLookup(tident.ident, parameters);
+    }
+    return IDX_NOTFOUND;
+}
+/* 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
+ */
+MATCH deduceType(scope RootObject o, scope Scope* sc, scope Type tparam,
+    scope ref TemplateParameters parameters, scope ref Objects dedtypes,
+    scope uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false)
+{
+    extern (C++) final class DeduceType : Visitor
+    {
+        alias visit = Visitor.visit;
+    public:
+        MATCH result;
+
+        extern (D) this() @safe
+        {
+            result = MATCH.nomatch;
+        }
+
+        override void visit(Type t)
+        {
+            if (!tparam)
+                goto Lnomatch;
+
+            if (t == tparam)
+                goto Lexact;
+
+            if (tparam.ty == Tident)
+            {
+                // Determine which parameter tparam is
+                size_t i = templateParameterLookup(tparam, &parameters);
+                if (i == IDX_NOTFOUND)
+                {
+                    if (!sc)
+                        goto Lnomatch;
+
+                    /* Need a loc to go with the semantic routine. */
+                    Loc loc = semanticLoc(parameters);
+
+                    /* 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);
+                    return;
+                }
+
+                TemplateParameter tp = parameters[i];
+
+                TypeIdentifier tident = tparam.isTypeIdentifier();
+                if (tident.idents.length > 0)
+                {
+                    //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;
+                }
+
+                // 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())
+                {
+                    goto Lnomatch;
+                }
+
+                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 = semanticLoc(parameters);
+
+                tparam = tparam.typeSemantic(loc, sc);
+            }
+            if (t.ty != tparam.ty)
+            {
+                if (Dsymbol sym = t.toDsymbol(sc))
+                {
+                    if (sym.isforwardRef() && !tparam.deco)
+                        goto Lnomatch;
+                }
+
+                MATCH m = t.implicitConvTo(tparam);
+                if (m == MATCH.nomatch && !ignoreAliasThis)
+                {
+                    m = deduceAliasThis(t, sc, tparam, parameters, dedtypes, wm);
+                }
+                result = m;
+                return;
+            }
+
+            if (t.nextOf())
+            {
+                if (tparam.deco && !tparam.hasWild())
+                {
+                    result = t.implicitConvTo(tparam);
+                    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)
+        {
+            if (auto tp = tparam.isTypeVector())
+            {
+                result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm);
+                return;
+            }
+            visit(cast(Type)t);
+        }
+
+        override void visit(TypeDArray t)
+        {
+            visit(cast(Type)t);
+        }
+
+        override void visit(TypeSArray t)
+        {
+            // 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())
+            {
+                if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter)
+                {
+                    Identifier id = tsa.dim.isVarExp().var.ident;
+                    i = templateIdentifierLookup(id, &parameters);
+                    assert(i != IDX_NOTFOUND);
+                    tp = parameters[i];
+                }
+                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;
+            }
+
+            visit(cast(Type)t);
+        }
+
+        override void visit(TypeAArray t)
+        {
+            // Extra check that index type must match
+            if (tparam && tparam.ty == Taarray)
+            {
+                TypeAArray tp = tparam.isTypeAArray();
+                if (!deduceType(t.index, sc, tp.index, parameters, dedtypes))
+                {
+                    result = MATCH.nomatch;
+                    return;
+                }
+            }
+            visit(cast(Type)t);
+        }
+
+        override void visit(TypeFunction t)
+        {
+            // 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;
+            }
+
+            if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
+            {
+                result = MATCH.nomatch;
+                return;
+            }
+
+            foreach (fparam; *tp.parameterList.parameters)
+            {
+                // 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]))
+                {
+                    auto tx = fparam.type.typeSemantic(Loc.initial, sc);
+                    if (tx.ty == Terror)
+                    {
+                        result = MATCH.nomatch;
+                        return;
+                    }
+                    fparam.type = tx;
+                }
+            }
+
+            const size_t nfargs = t.parameterList.length;
+            size_t nfparams = tp.parameterList.length;
+
+            if (!deduceFunctionTuple(t, tp, parameters, dedtypes, 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;
+                }
+            }
+
+            visit(cast(Type)t);
+        }
+
+        override void visit(TypeIdentifier t)
+        {
+            // 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);
+        }
+
+        override void visit(TypeInstance t)
+        {
+            // Extra check
+            if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl)
+            {
+                visit(cast(Type)t);
+                return;
+            }
+
+            TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
+            assert(tempdecl);
+
+            TypeInstance tp = tparam.isTypeInstance();
+
+            //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;
+                }
+
+                TemplateParameter tpx = parameters[i];
+                if (!tpx.matchArg(sc, tempdecl, i, &parameters, dedtypes, null))
+                    goto Lnomatch;
+            }
+            else if (tempdecl != tp.tempinst.tempdecl)
+                goto Lnomatch;
+
+        L2:
+            if (!resolveTemplateInstantiation(sc, &parameters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes))
+                goto Lnomatch;
+
+            visit(cast(Type)t);
+            return;
+
+        Lnomatch:
+            //printf("no match\n");
+            result = MATCH.nomatch;
+        }
+
+        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();
+
+            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;
+                    }
+                }
+
+                TypeInstance tpi = tparam.isTypeInstance();
+                auto m = deduceParentInstance(sc, t.sym, tpi, parameters, dedtypes, wm);
+                if (m != MATCH.nomatch)
+                {
+                    result = m;
+                    return;
+                }
+            }
+
+            // Extra check
+            if (tparam && tparam.ty == Tstruct)
+            {
+                TypeStruct tp = tparam.isTypeStruct();
+
+                //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 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);
+        }
+
+        override void visit(TypeClass t)
+        {
+            //printf("TypeClass.deduceType(this = %s)\n", t.toChars());
+
+            /* 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();
+
+            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;
+                    }
+                }
+
+                TypeInstance tpi = tparam.isTypeInstance();
+                auto m = deduceParentInstance(sc, t.sym, tpi, parameters, dedtypes, wm);
+                if (m != MATCH.nomatch)
+                {
+                    result = m;
+                    return;
+                }
+
+                // If it matches exactly or via implicit conversion, we're done
+                visit(cast(Type)t);
+                if (result != MATCH.nomatch)
+                    return;
+
+                /* 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.
+                 */
+
+                int numBaseClassMatches = 0; // Have we found an interface match?
+
+                // Our best guess at dedtypes
+                auto best = new Objects(dedtypes.length);
+
+                ClassDeclaration s = t.sym;
+                while (s && s.baseclasses.length > 0)
+                {
+                    // Test the base class
+                    deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, *best, numBaseClassMatches);
+
+                    // 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;
+                }
+
+                if (numBaseClassMatches == 0)
+                {
+                    result = MATCH.nomatch;
+                    return;
+                }
+
+                // 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;
+            }
+
+            // Extra check
+            if (tparam && tparam.ty == Tclass)
+            {
+                TypeClass tp = tparam.isTypeClass();
+
+                //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 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;
+            }
+
+            TemplateTypeParameter tp = parameters[i].isTemplateTypeParameter();
+            if (!tp)
+                return; // nomatch
+
+            if (e == emptyArrayElement)
+            {
+                if (dedtypes[i])
+                {
+                    result = MATCH.exact;
+                    return;
+                }
+                if (tp.defaultType)
+                {
+                    tp.defaultType.accept(this);
+                    return;
+                }
+            }
+
+            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
+
+            // expression vs (none)
+            if (!at)
+            {
+                dedtypes[i] = new TypeDeduced(tt, e, tparam);
+                return;
+            }
+
+            TypeDeduced xt = null;
+            if (at.ty == Tnone)
+            {
+                xt = cast(TypeDeduced)at;
+                at = xt.tded;
+            }
+
+            // From previous matched expressions to current deduced type
+            MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch;
+
+            // From current expressions to previous deduced type
+            Type pt = at.addMod(tparam.mod);
+            if (*wm)
+                pt = pt.substWildTo(*wm);
+            MATCH match2 = e.implicitConvTo(pt);
+
+            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;
+        }
+
+        private 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);
+        }
+
+        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);
+        }
+
+        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);
+        }
+
+        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;
+            }
+
+            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;
+            }
+
+            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 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);
+        }
+
+        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;
+
+                // 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;
+
+                if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
+                    return;
+
+                auto tiargs = new Objects();
+                tiargs.reserve(e.td.parameters.length);
+
+                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);
+                }
+
+                // Set target of return type inference
+                if (!tf.next && tof.next)
+                    e.fd.treq = tparam;
+
+                auto ti = new TemplateInstance(e.loc, e.td, tiargs);
+                Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope);
+
+                // Reset inference target for the later re-semantic
+                e.fd.treq = null;
+
+                if (ex.op == EXP.error)
+                    return;
+                if (ex.op != EXP.function_)
+                    return;
+                visit(ex.type);
+                return;
+            }
+
+            Type t = e.type;
+
+            if (t.ty == Tdelegate && tparam.ty == Tpointer)
+                return;
+
+            // 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 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 void visit(CommaExp e)
+        {
+            e.e2.accept(this);
+        }
+    }
+
+    scope DeduceType v = new DeduceType();
+    if (Type t = isType(o))
+        t.accept(v);
+    else if (Expression e = isExpression(o))
+    {
+        assert(wm);
+        e.accept(v);
+    }
+    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.
+ */
+private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches)
+{
+    if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null)
+    {
+        // 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);
+
+        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;
+        }
+    }
+
+    // Now recursively test the inherited interfaces
+    foreach (ref bi; b.baseInterfaces)
+    {
+        deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+    }
+}
+
+/*
+ * Handle tuple matching for function parameters.
+ * If the last parameter of `tp` is a template tuple parameter,
+ * collect the corresponding argument types from `t`.
+ * Params:
+ *     t          = actual function type
+ *     tp         = template function type
+ *     parameters = template parameters
+ *     dedtypes   = deduced types array
+ *     nfargs     = number of arguments in `t`
+ *     nfparams   = number of parameters in `tp` (updated on success)
+ * Returns: `true` on success, `false` on mismatch.
+ */
+private bool deduceFunctionTuple(TypeFunction t, TypeFunction tp,
+    ref TemplateParameters parameters, ref Objects dedtypes,
+    size_t nfargs, ref size_t nfparams)
+{
+    if (nfparams > 0 && nfargs >= nfparams - 1)
+    {
+        Parameter fparam = tp.parameterList[nfparams - 1];
+        assert(fparam && fparam.type);
+        if (fparam.type.ty == Tident)
+        {
+            TypeIdentifier tid = fparam.type.isTypeIdentifier();
+            if (tid.idents.length == 0)
+            {
+                size_t tupi = 0;
+                for (; tupi < parameters.length; ++tupi)
+                {
+                    TemplateParameter tx = parameters[tupi];
+                    TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+                    if (tup && tup.ident.equals(tid.ident))
+                        break;
+                }
+                if (tupi == parameters.length)
+                    return nfargs == nfparams;
+
+                size_t tuple_dim = nfargs - (nfparams - 1);
+
+                RootObject o = dedtypes[tupi];
+                if (o)
+                {
+                    Tuple tup = isTuple(o);
+                    if (!tup || tup.objects.length != tuple_dim)
+                        return false;
+                    for (size_t i = 0; i < tuple_dim; ++i)
+                    {
+                        if (!t.parameterList[nfparams - 1 + i].type.equals(tup.objects[i]))
+                            return false;
+                    }
+                }
+                else
+                {
+                    auto tup = new Tuple(tuple_dim);
+                    for (size_t i = 0; i < tuple_dim; ++i)
+                        tup.objects[i] = t.parameterList[nfparams - 1 + i].type;
+                    dedtypes[tupi] = tup;
+                }
+                --nfparams; // ignore tuple parameter for further deduction
+                return true;
+            }
+        }
+    }
+    return nfargs == nfparams;
+}
+
+/********************
+ * 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++)
+    {
+        //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;
+        }
+
+        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;
+            }
+
+            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)
+        {
+            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 (t1 && t2)
+        {
+            if (!deduceType(t1, sc, t2, *parameters, *dedtypes))
+                return false;
+        }
+        else if (e1 && e2)
+        {
+        Le:
+            e1 = e1.ctfeInterpret();
+
+            /* 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)
+            }
+
+            e2 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=13417
+            e2 = e2.ctfeInterpret();
+
+            //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;
+
+                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;
+    }
+    return true;
+}
+
+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;
+}
+
+Expression getExpression(RootObject o)
+{
+    auto s = isDsymbol(o);
+    return s ? .getValue(s) : .getValue(isExpression(o));
+}
index a10620772ac5495fe84e66532bca917f0b5e4724..d304436766050b9e36a60d53d1f024cb1dbbd9f0 100644 (file)
@@ -1180,7 +1180,7 @@ void writeCharLiteral(ref OutBuffer buf, dchar c)
             if (c <= 0xFF)
             {
                 if (isprint(c))
-                    buf.writeByte(c);
+                    buf.writeByte(cast(char)c);
                 else
                     buf.printf("\\x%02x", c);
             }
index 8cce1732e87ee6ced8230992007ddf1a1b7d6990..40195aa2fa184bc39099e2a5d92b604401909ab5 100644 (file)
@@ -51,6 +51,7 @@ import dmd.root.array;
 import dmd.root.speller;
 import dmd.root.stringtable;
 import dmd.target;
+import dmd.templatesem : TemplateInstance_semanticTiargs;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
@@ -340,7 +341,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
             sc.stc |= STC.deprecated_;
         Scope* sc2 = sc.startCTFE();
         scope(exit) { sc2.endCTFE(); }
-        if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1))
+        if (!TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1))
         {
             sc.stc = save;
             return ErrorExp.get();
@@ -735,7 +736,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
          * a symbol should not be folded to a constant.
          * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
          */
-        if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 2))
+        if (!TemplateInstance_semanticTiargs(e.loc, sc, e.args, 2))
             return ErrorExp.get();
         if (dim != 1)
             return dimError(1);
@@ -774,7 +775,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
         sc2.copyFlagsFrom(sc);
         sc2.noAccessCheck = true;
         sc2.ignoresymbolvisibility = true;
-        bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
+        bool ok = TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1);
         sc2.pop();
         if (!ok)
             return ErrorExp.get();
@@ -849,7 +850,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
         sc2.copyFlagsFrom(sc);
         sc2.noAccessCheck = true;
         sc2.ignoresymbolvisibility = true;
-        bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1);
+        bool ok = TemplateInstance_semanticTiargs(e.loc, sc2, e.args, 1);
         sc2.pop();
         if (!ok)
             return ErrorExp.get();
@@ -1304,7 +1305,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
          * a symbol should not be folded to a constant.
          * Bit 1 means don't convert Parameter to Type if Parameter has an identifier
          */
-        if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 3))
+        if (!TemplateInstance_semanticTiargs(e.loc, sc, e.args, 3))
             return ErrorExp.get();
 
         if (dim != 1)
@@ -1882,9 +1883,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
         ob1.push((*e.args)[0]);
         Objects ob2;
         ob2.push((*e.args)[1]);
-        if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob1, 0))
+        if (!TemplateInstance_semanticTiargs(e.loc, sc, &ob1, 0))
             return ErrorExp.get();
-        if (!TemplateInstance.semanticTiargs(e.loc, sc, &ob2, 0))
+        if (!TemplateInstance_semanticTiargs(e.loc, sc, &ob2, 0))
             return ErrorExp.get();
         if (ob1.length != ob2.length)
             return False();
index ffbdfed6eb37eb7f74a7144b7f4221bb7d6ae273..caddc5e0c894e0e337794f54a0c0327a1d83a170 100644 (file)
@@ -467,8 +467,7 @@ bool isCopyable(Type t)
             assert(ctor);
             scope el = new IdentifierExp(Loc.initial, Id.p); // dummy lvalue
             el.type = cast() ts;
-            Expressions* args = new Expressions();
-            args.push(el);
+            Expressions* args = new Expressions(el);
             FuncDeclaration f = resolveFuncCall(Loc.initial, null, ctor, null, cast()ts, ArgumentList(args), FuncResolveFlag.quiet);
             if (!f || f.storage_class & STC.disable)
                 return false;
@@ -477,6 +476,60 @@ bool isCopyable(Type t)
     return true;
 }
 
+/**************************
+ * When T is mutable,
+ * Given:
+ *      T a, b;
+ * Can we bitwise assign:
+ *      a = b;
+ * ?
+ */
+bool isAssignable(Type t)
+{
+    if (auto te = t.isTypeEnum())
+        t = te.memType();
+    TypeStruct ts = t.isTypeStruct();
+    if (!ts)
+        return true;
+
+    bool assignable = true;
+    uint offset = ~0; // dead-store initialize to prevent spurious warning
+
+    auto sym = ts.sym;
+    sym.determineSize(sym.loc);
+
+    /* If any of the fields are const or immutable,
+     * then one cannot assign this struct.
+     */
+    for (size_t i = 0; i < sym.fields.length; i++)
+    {
+        VarDeclaration v = sym.fields[i];
+        //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s\n", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind());
+        if (i == 0)
+        {
+        }
+        else if (v.offset == offset)
+        {
+            /* If any fields of anonymous union are assignable,
+             * then regard union as assignable.
+             * This is to support unsafe things like Rebindable templates.
+             */
+            if (assignable)
+                continue;
+        }
+        else
+        {
+            if (!assignable)
+                return false;
+        }
+        assignable = v.type.isMutable() && v.type.isAssignable();
+        offset = v.offset;
+        //printf(" -> assignable = %d\n", assignable);
+    }
+
+    return assignable;
+}
+
 /************************************
  * Determine mutability of indirections in (ref) t.
  *
@@ -688,6 +741,129 @@ extern (D) bool checkComplexTransition(Type type, Loc loc, Scope* sc)
     return false;
 }
 
+/**
+ * Look for the index of parameter `ident` in the parameter list
+ *
+ * Params:
+ *   tf = function type
+ *   ident = identifier of parameter to search for
+ * Returns: index of parameter with name `ident` or -1 if not found
+ */
+private extern(D) ptrdiff_t findParameterIndex(TypeFunction tf, Identifier ident)
+{
+    foreach (i, p; tf.parameterList)
+    {
+        if (p.ident == ident)
+            return i;
+    }
+    return -1;
+}
+
+/*********************************
+ * Append error message to buf.
+ * Input:
+ *  buf = message sink
+ *  format = printf format
+ */
+private extern(C) void getMatchError(ref OutBuffer buf, const(char)* format, ...)
+{
+    if (global.gag && !global.params.v.showGaggedErrors)
+        return;
+    va_list ap;
+    va_start(ap, format);
+    buf.vprintf(format, ap);
+    va_end(ap);
+}
+
+/********************************
+ * Convert an `argumentList`, which may contain named arguments, into
+ * a list of arguments in the order of the parameter list.
+ *
+ * Params:
+ *   tf = function type
+ *      argumentList = array of function arguments
+ *      buf = if not null, append error message to it
+ * Returns: re-ordered argument list, or `null` on error
+ */
+extern(D) Expressions* resolveNamedArgs(TypeFunction tf, ArgumentList argumentList, OutBuffer* buf)
+{
+    Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null;
+    ArgumentLabel[] names = argumentList.names ? (*argumentList.names)[] : null;
+    const nParams = tf.parameterList.length(); // cached because O(n)
+    auto newArgs = new Expressions(nParams);
+    newArgs.zero();
+    size_t ci = 0;
+    bool hasNamedArgs = false;
+    const bool isVariadic = tf.parameterList.varargs != VarArg.none;
+    foreach (i, arg; args)
+    {
+        if (!arg)
+        {
+            ci++;
+            continue;
+        }
+        auto name = i < names.length ? names[i].name : null;
+        if (name)
+        {
+            hasNamedArgs = true;
+            const pi = tf.findParameterIndex(name);
+            if (pi == -1)
+            {
+                if (buf)
+                    getMatchError(*buf, "no parameter named `%s`", name.toChars());
+                return null;
+            }
+            ci = pi;
+        }
+        if (ci >= newArgs.length)
+        {
+            if (!isVariadic)
+            {
+                // Without named args, let the caller diagnose argument overflow
+                if (hasNamedArgs && buf)
+                    getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars());
+                return null;
+            }
+            while (ci >= newArgs.length)
+                newArgs.push(null);
+        }
+
+        if ((*newArgs)[ci])
+        {
+            if (buf)
+                getMatchError(*buf, "parameter `%s` assigned twice", tf.parameterList[ci].toChars());
+            return null;
+        }
+        (*newArgs)[ci++] = arg;
+    }
+    foreach (i, arg; (*newArgs)[])
+    {
+        if (arg || tf.parameterList[i].defaultArg)
+            continue;
+
+        if (isVariadic && i + 1 == newArgs.length)
+            continue;
+
+        // dtemplate sets `defaultArg=null` to avoid semantic on default arguments,
+        // don't complain about missing arguments in that case
+        if (tf.incomplete)
+            continue;
+
+        if (buf)
+            getMatchError(*buf, "missing argument for parameter #%d: `%s`",
+                          i + 1, parameterToChars(tf.parameterList[i], tf, false));
+        return null;
+    }
+    // strip trailing nulls from default arguments
+    size_t e = newArgs.length;
+    while (e > 0 && (*newArgs)[e - 1] is null)
+    {
+        --e;
+    }
+    newArgs.setDim(e);
+    return newArgs;
+}
+
 /********************************
  * 'args' are being matched to function type 'tf'
  * Determine match level.
@@ -868,8 +1044,8 @@ extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, Argu
             if (errorHelper)
             {
                 if (u >= args.length)
-                    TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`",
-                        u + 1, parameterToChars(p, tf, false));
+                    getMatchError(buf, "missing argument for parameter #%d: `%s`",
+                                  u + 1, parameterToChars(p, tf, false));
                 // If an error happened previously, `pMessage` was already filled
                 else if (buf.length == 0)
                     buf.writestring(tf.getParamError(args[u], p));
@@ -886,7 +1062,7 @@ extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, Argu
     {
         // all parameters had a match, but there are surplus args
         OutBuffer buf2;
-        TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length);
+        getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length);
         errorHelper(buf2.extractChars());
         return MATCH.nomatch;
     }
@@ -944,7 +1120,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
     if (!f)
         return nocpctor();
 
-    if (f.isDisabled() && !f.isGenerated())
+    if (f.isDisabled() && !f.isGenerated)
     {
         /* https://issues.dlang.org/show_bug.cgi?id=24301
          * Copy constructor is explicitly disabled
@@ -967,7 +1143,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
             bsafe ? "@safe ".ptr : nullptr,
             bnogc ? "nogc"  .ptr : nullptr);
     }
-    else if (f.isGenerated() && f.isDisabled())
+    else if (f.isGenerated && f.isDisabled())
     {
         /* https://issues.dlang.org/show_bug.cgi?id=23097
          * Compiler generated copy constructor failed.
@@ -1180,6 +1356,12 @@ private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction
         return MATCH.nomatch;
     }
 
+    if (arg.isBitField())
+    {
+        if (pMessage) *pMessage = tf.getParamError(arg, p);
+        return MATCH.nomatch;
+    }
+
     return m;
 }
 
@@ -1193,13 +1375,21 @@ private const(char)* getParamError(TypeFunction tf, Expression arg, Parameter pa
     // when comparing the type with strcmp, we need to drop the qualifier
     bool qual = !arg.type.mutableOf().equals(par.type.mutableOf()) &&
         strcmp(arg.type.mutableOf().toChars(), par.type.mutableOf().toChars()) == 0;
-    auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars();
     OutBuffer buf;
-    // only mention rvalue if it's relevant
-    const rv = !arg.isLvalue() && par.isReference();
-    buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`",
-        rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at,
-        parameterToChars(par, tf, qual));
+    // only mention rvalue or bitfield if it's relevant
+    if (arg.isBitField())
+    {
+        buf.printf("cannot pass bitfield argument `%s` to parameter `%s`",
+                   arg.toErrMsg(), parameterToChars(par, tf, qual));
+    }
+    else
+    {
+        auto at = qual ? arg.type.toPrettyChars(true) : arg.type.toChars();
+        const rv = !arg.isLvalue() && par.isReference() && !(par.storageClass & STC.constscoperef);
+        buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`",
+            rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at,
+            parameterToChars(par, tf, qual));
+    }
     return buf.extractChars();
 }
 
@@ -1232,7 +1422,7 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p,
             if (pMessage)
             {
                 OutBuffer buf;
-                TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu",
+                getMatchError(buf, "expected %llu variadic argument(s), not %zu",
                     sz, trailingArgs.length);
                 *pMessage = buf.extractChars();
             }
@@ -1290,6 +1480,35 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p,
     }
 }
 
+/// Compute cached type properties for `TypeStruct`
+void determineTypeProperties(StructDeclaration sd)
+{
+    import dmd.dsymbolsem : hasPointers;
+    if (sd.computedTypeProperties)
+        return;
+    foreach (vd; sd.fields)
+    {
+        if (vd.storage_class & STC.ref_ || vd.hasPointers())
+        {
+            sd.hasPointerField = true;
+            sd.hasUnsafeBitpatterns = true;
+        }
+
+        if (vd._init && vd._init.isVoidInitializer() && vd.hasPointers())
+            sd.hasVoidInitPointers = true;
+
+        if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns())
+            sd.hasUnsafeBitpatterns = true;
+
+        if (!vd._init && vd.type.hasVoidInitPointers())
+            sd.hasVoidInitPointers = true;
+
+        if (vd.type.hasInvariant())
+            sd.hasFieldWithInvariant = true;
+    }
+    sd.computedTypeProperties = true;
+}
+
 /***************************************
  * Return !=0 if type has pointers that need to
  * be scanned by the GC during a collection cycle.
@@ -2027,7 +2246,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
             // duplicate a part of StructDeclaration::semanticTypeInfoMembers
             //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash);
 
-            if (sd.xeq && sd.xeq.isGenerated() && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
+            if (sd.xeq && sd.xeq.isGenerated && sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done)
             {
                 uint errors = global.startGagging();
                 sd.xeq.semantic3(sd.xeq._scope);
@@ -2658,7 +2877,7 @@ Type typeSemantic(Type type, Loc loc, Scope* sc)
 
                     if (farg && (eparam.storageClass & STC.ref_))
                     {
-                        if (!farg.isLvalue())
+                        if (!farg.isLvalue() || farg.isBitField())
                             eparam.storageClass &= ~STC.ref_; // value parameter
                         eparam.storageClass &= ~STC.auto_;    // https://issues.dlang.org/show_bug.cgi?id=14656
                         eparam.storageClass |= STC.autoref;
@@ -3315,6 +3534,11 @@ Type merge(Type type)
                 return type;
             goto default;
 
+        case Tfunction:
+            if (!type.nextOf()) // don't merge if return type is unknown
+                return type;
+            goto default;
+
         default:
             if (type.nextOf() && !type.nextOf().deco)
                 return type;
@@ -3377,6 +3601,95 @@ Type merge2(Type type)
     return t;
 }
 
+private enum LOGDEFAULTINIT = 0;
+/***************************************
+ * Use when we prefer the default initializer to be a literal,
+ * rather than a global immutable variable.
+ */
+Expression defaultInitLiteral(Type t, Loc loc)
+{
+
+    if (t.isTypeError())
+        return ErrorExp.get();
+    if (auto ts = t.isTypeStruct())
+    {
+        static if (LOGDEFAULTINIT)
+        {
+            printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
+        }
+        ts.sym.size(loc);
+        if (ts.sym.sizeok != Sizeok.done)
+            return ErrorExp.get();
+
+        auto structelems = new Expressions(ts.sym.nonHiddenFields());
+        uint offset = 0;
+        foreach (j; 0 .. structelems.length)
+        {
+            VarDeclaration vd = ts.sym.fields[j];
+            Expression e;
+            if (vd.inuse)
+            {
+                error(loc, "circular reference to `%s`", vd.toPrettyChars());
+                return ErrorExp.get();
+            }
+            if (vd.offset < offset || vd.type.size() == 0)
+                e = null;
+            else if (vd._init)
+            {
+                if (vd._init.isVoidInitializer())
+                    e = null;
+                else
+                    e = vd.getConstInitializer(false);
+            }
+            else
+                e = vd.type.defaultInitLiteral(loc);
+            if (e && e.op == EXP.error)
+                return e;
+            if (e)
+                offset = vd.offset + cast(uint)vd.type.size();
+            (*structelems)[j] = e;
+        }
+        auto structinit = new StructLiteralExp(loc, ts.sym, structelems);
+
+        /* Copy from the initializer symbol for larger symbols,
+         * otherwise the literals expressed as code get excessively large.
+         */
+        if (size(ts, loc) > target.ptrsize * 4 && !ts.needsNested())
+            structinit.useStaticInit = true;
+
+        structinit.type = ts;
+        return structinit;
+    }
+    if (auto tv = t.isTypeVector())
+    {
+        //printf("TypeVector::defaultInitLiteral()\n");
+        assert(tv.basetype.ty == Tsarray);
+        Expression e = tv.basetype.defaultInitLiteral(loc);
+        auto ve = new VectorExp(loc, e, tv);
+        ve.type = tv;
+        ve.dim = cast(int)(tv.basetype.size(loc) / tv.elementType().size(loc));
+        return ve;
+    }
+    if (auto tsa = t.isTypeSArray())
+    {
+        static if (LOGDEFAULTINIT)
+        {
+            printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
+        }
+        size_t d = cast(size_t)tsa.dim.toInteger();
+        Expression elementinit;
+        if (tsa.next.ty == Tvoid)
+            elementinit = Type.tuns8.defaultInitLiteral(loc);
+        else
+            elementinit = tsa.next.defaultInitLiteral(loc);
+        auto elements = new Expressions(d);
+        foreach (ref e; *elements)
+            e = null;
+        auto ae = new ArrayLiteralExp(loc, tsa, elementinit, elements);
+        return ae;
+    }
+    return defaultInit(t, loc);
+}
 /***************************************
  * Calculate built-in properties which just the type is necessary.
  *
@@ -3516,8 +3829,7 @@ Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int fla
                         {
                             e = mt.defaultInitLiteral(loc);
                             auto se = new StringExp(e.loc, ident.toString());
-                            auto tiargs = new Objects();
-                            tiargs.push(se);
+                            auto tiargs = new Objects(se);
                             auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
                             dti.ti.tempdecl = td;
                             dti.dotTemplateSemanticProp(scope_, DotExpFlag.none);
@@ -4888,20 +5200,15 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
         }
         if (ident == Id.length)
         {
-            __gshared FuncDeclaration fd_aaLen = null;
-            if (fd_aaLen is null)
-            {
-                auto fparams = new Parameters();
-                fparams.push(new Parameter(Loc.initial, STC.const_ | STC.scope_, mt, null, null, null));
-                fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen);
-                TypeFunction tf = fd_aaLen.type.toTypeFunction();
-                tf.purity = PURE.const_;
-                tf.isNothrow = true;
-                tf.isNogc = false;
-            }
-            Expression ev = new VarExp(e.loc, fd_aaLen, false);
-            e = new CallExp(e.loc, ev, e);
-            e.type = fd_aaLen.type.toTypeFunction().next;
+            auto loc = e.loc;
+            Expression hookFunc = new IdentifierExp(loc, Id.empty);
+            hookFunc = new DotIdExp(loc, hookFunc, Id.object);
+            auto keytype = mt.index.substWildTo(MODFlags.const_);
+            auto valtype = mt.nextOf().substWildTo(MODFlags.const_);
+            auto tiargs = new Objects(keytype, valtype);
+            hookFunc = new DotTemplateInstanceExp(loc, hookFunc, Id._d_aaLen, tiargs);
+            Expression e = new CallExp(loc, hookFunc, e);
+            e = e.expressionSemantic(sc);
             return e;
         }
         else
@@ -5013,17 +5320,31 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
                 /* Rewrite e.ident as:
                  *  e.opDispatch!("ident")
                  */
-                TemplateDeclaration td = fd.isTemplateDeclaration();
-                if (!td)
-                {
-                    .error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s", fd.kind, fd.toPrettyChars, fd.kind());
-                    return returnExp(ErrorExp.get());
-                }
+
                 auto se = new StringExp(e.loc, ident.toString());
-                auto tiargs = new Objects();
-                tiargs.push(se);
+                auto tiargs = new Objects(se);
                 auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
-                dti.ti.tempdecl = td;
+
+                if (OverloadSet os = fd.isOverloadSet())
+                {
+                    if (!findTempDecl(dti, sc))
+                    {
+                        .error(fd.loc, "Couldn't find template declaration for opDispatch");
+                        return returnExp(ErrorExp.get());
+                    }
+                }
+                else
+                {
+                    TemplateDeclaration td = fd.isTemplateDeclaration();
+                    if (!td)
+                    {
+                        .error(fd.loc, "%s `%s` must be a template `opDispatch(string s)`, not a %s",
+                               fd.kind, fd.toPrettyChars, fd.kind());
+                        return returnExp(ErrorExp.get());
+                    }
+                    dti.ti.tempdecl = td;
+                }
+
                 /* opDispatch, which doesn't need IFTI,  may occur instantiate error.
                  * e.g.
                  *  template opDispatch(name) if (isValid!name) { ... }
@@ -5743,7 +6064,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
                     Expression e1;
                     Type t;
                     /* returns: true to continue, false to return */
-                    if (f.hasDualContext())
+                    if (f.hasDualContext)
                     {
                         if (f.followInstantiationContext(ad))
                         {
@@ -6345,7 +6666,7 @@ Type getComplexLibraryType(Loc loc, Scope* sc, TY ty)
         return *pt;
     *pt = Type.terror;
 
-    Module mConfig = Module.loadCoreStdcConfig();
+    Module mConfig = loadCoreStdcConfig();
     if (!mConfig)
     {
         error(loc, "`core.stdc.config` is required for complex numbers");
@@ -6466,9 +6787,9 @@ Covariant covariant(Type src, Type t, STC* pstc = null, bool cppCovariant = fals
              */
             if (t1.linkage == LINK.cpp && cppCovariant)
             {
-                notcovariant |= tp1.isNaked() != tp2.isNaked();
+                notcovariant |= tp1.isNaked != tp2.isNaked;
                 if (auto tpn1 = tp1.nextOf())
-                    notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked();
+                    notcovariant |= tpn1.isNaked != tp2.nextOf().isNaked;
             }
         }
     }
@@ -6574,7 +6895,7 @@ Lcovariant:
     /* https://issues.dlang.org/show_bug.cgi?id=23135
      * extern(C++) mutable member functions are not covariant with const.
      */
-    if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked())
+    if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked != t2.isNaked)
         goto Lnotcovariant;
 
     /* Can convert mutable to const
@@ -6663,7 +6984,7 @@ STC parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarati
 
     /* If haven't inferred the return type yet, can't infer storage classes
      */
-    if (!tf.nextOf() || !tf.isNothrow())
+    if (!tf.nextOf() || !tf.isNothrow)
         return stc;
 
     tf.purityLevel();
@@ -7346,7 +7667,7 @@ Type substWildTo(Type type, uint mod)
             t = t.addMod(MODFlags.shared_);
 
         //printf("-Type.substWildTo t = %s\n", t.toChars());
-        return t;
+        return t.merge();
     }
 
     if (!tf.iswild && !(tf.mod & MODFlags.wild))
index 1c7ed320a96867dff67d77dbc29b00e20fa55d7f..503d00bfd340e29b28b51e7e28ae5330d7c95e91 100644 (file)
@@ -22,6 +22,7 @@ import dmd.expression;
 import dmd.globals;
 import dmd.location;
 import dmd.mtype;
+import dmd.templatesem;
 import dmd.typesem;
 import core.stdc.stdio;
 
@@ -165,27 +166,9 @@ TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc)
 
     auto ti = TypeInfoAssociativeArrayDeclaration.create(t);
     t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic
-    Loc loc = t.loc;
-    auto tiargs = new Objects();
-    tiargs.push(t.index); // always called with naked types
-    tiargs.push(t.next);
-
-    Expression id = new IdentifierExp(loc, Id.empty);
-    id = new DotIdExp(loc, id, Id.object);
-    id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray);
-    auto tempinst = new DotTemplateInstanceExp(loc, id, Id.Entry, tiargs);
-    auto e = expressionSemantic(tempinst, sc);
-    assert(e.type);
-    ti.entry = e.type;
-    if (auto ts = ti.entry.isTypeStruct())
-    {
-        ts.sym.requestTypeInfo = true;
-        if (auto tmpl = ts.sym.isInstantiated())
-            tmpl.minst = sc._module.importedFrom; // ensure it get's emitted
-    }
-    getTypeInfoType(loc, ti.entry, sc);
-    assert(ti.entry.vtinfo);
-
+    ti._scope = sc;
+    sc.setNoFree();
+    Module.addDeferredSemantic3(ti);
     return ti;
 }
 
index 80611d6f26a24714bdcd75a3152327d04d3f1032..73d7c3ef9d69557dfc3ec089623e1b010114901b 100644 (file)
@@ -45,7 +45,7 @@ import dmd.visitor.postorder;
  *      e = expression tree to visit
  *      dgVar = call when a variable is declared
  */
-void foreachVar(Expression e, void delegate(VarDeclaration) dgVar)
+void foreachVar(Expression e, scope void delegate(VarDeclaration) dgVar)
 {
     if (!e)
         return;
@@ -110,8 +110,8 @@ void foreachVar(Expression e, void delegate(VarDeclaration) dgVar)
  *      dgVar = delegate to pass found VarDeclarations to
  */
 void foreachExpAndVar(Statement s,
-        void delegate(Expression) dgExp,
-        void delegate(VarDeclaration) dgVar)
+        scope void delegate(Expression) dgExp,
+        scope void delegate(VarDeclaration) dgVar)
 {
     void visit(Statement s)
     {
index 22549da45d5f41e32d13b1a9359ff563c441531b..f7c1f8dccf05898722ab41f063cd455f8e167507 100644 (file)
@@ -106,6 +106,42 @@ public:
         doCond(e.e1) || doCond(e.e2) || applyTo(e);
     }
 
+    override void visit(CatExp e)
+    {
+        if (auto lowering = e.lowering)
+        {
+            doCond(lowering) || applyTo(e);
+        }
+        else
+        {
+            visit(cast(BinExp)e);
+        }
+    }
+
+    override void visit(CatAssignExp e)
+    {
+        if (auto lowering = e.lowering)
+        {
+            doCond(lowering) || applyTo(e);
+        }
+        else
+        {
+            visit(cast(BinExp)e);
+        }
+    }
+
+    override void visit(EqualExp e)
+    {
+        if (auto lowering = e.lowering)
+        {
+            doCond(lowering) || applyTo(e);
+        }
+        else
+        {
+            visit(cast(BinExp)e);
+        }
+    }
+
     override void visit(AssertExp e)
     {
         //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
index 21a41fe2c4c9723648fa4b6d29cedda609855ee0..c9ee5a6696f86d6c7a1b68177c35e61355b84bce 100644 (file)
@@ -381,101 +381,87 @@ public:
        /* For static and dynamic arrays, equality is defined as the lengths of
           the arrays matching, and all the elements are equal.  */
        Type *t1elem = tb1->nextOf ()->toBasetype ();
-       Type *t2elem = tb1->nextOf ()->toBasetype ();
 
-       /* Check if comparisons of arrays can be optimized using memcmp.
+       /* Use lowering if it has already been handled by the front-end.  */
+       if (e->lowering != NULL)
+         {
+           this->result_ = build_expr (e->lowering);
+           return;
+         }
+
+       /* For all other arrays, comparisons can be optimized using memcmp.
           This will inline EQ expressions as:
                e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0;
            Or when generating a NE expression:
                e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0;  */
-       if ((t1elem->isIntegral () || t1elem->ty == TY::Tvoid
-            || (t1elem->ty == TY::Tstruct
-                && !t1elem->isTypeStruct ()->sym->xeq))
-           && t1elem->ty == t2elem->ty)
-         {
-           tree t1 = d_array_convert (e->e1);
-           tree t2 = d_array_convert (e->e2);
-           tree result;
-
-           /* Make temporaries to prevent multiple evaluations.  */
-           tree t1saved = d_save_expr (t1);
-           tree t2saved = d_save_expr (t2);
-
-           /* Length of arrays, for comparisons done before calling memcmp.  */
-           tree t1len = d_array_length (t1saved);
-           tree t2len = d_array_length (t2saved);
-
-           /* Reference to array data.  */
-           tree t1ptr = d_array_ptr (t1saved);
-           tree t2ptr = d_array_ptr (t2saved);
+       tree t1 = d_array_convert (e->e1);
+       tree t2 = d_array_convert (e->e2);
+       tree result;
 
-           /* Compare arrays using memcmp if possible, otherwise for structs,
-              each field is compared inline.  */
-           if (t1elem->ty != TY::Tstruct
-               || identity_compare_p (t1elem->isTypeStruct ()->sym))
-             {
-               tree size =
-                 size_mult_expr (t1len, size_int (dmd::size (t1elem)));
+       /* Make temporaries to prevent multiple evaluations.  */
+       tree t1saved = d_save_expr (t1);
+       tree t2saved = d_save_expr (t2);
 
-               result = build_memcmp_call (t1ptr, t2ptr, size);
-               result = build_boolop (code, result, integer_zero_node);
-             }
-           else
-             {
-               StructDeclaration *sd = t1elem->isTypeStruct ()->sym;
+       /* Length of arrays, for comparisons done before calling memcmp.  */
+       tree t1len = d_array_length (t1saved);
+       tree t2len = d_array_length (t2saved);
 
-               result = build_array_struct_comparison (code, sd, t1len,
-                                                       t1ptr, t2ptr);
-             }
+       /* Reference to array data.  */
+       tree t1ptr = d_array_ptr (t1saved);
+       tree t2ptr = d_array_ptr (t2saved);
 
-           /* Check array length first before passing to memcmp.
-              For equality expressions, this becomes:
-                   (e1.length == 0 || memcmp);
-              Otherwise for inequality:
-                   (e1.length != 0 && memcmp);  */
-           tree tsizecmp = build_boolop (code, t1len, size_zero_node);
-           if (e->op == EXP::equal)
-             result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result);
-           else
-             result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result);
+       /* Compare arrays using memcmp if possible, otherwise for structs,
+          each field is compared inline.  */
+       if (t1elem->ty != TY::Tstruct
+           || identity_compare_p (t1elem->isTypeStruct ()->sym))
+         {
+           tree size =
+             size_mult_expr (t1len, size_int (dmd::size (t1elem)));
 
-           /* Finally, check if lengths of both arrays match if dynamic.
-              The frontend should have already guaranteed that static arrays
-              have same size.  */
-           if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray)
-             gcc_assert (dmd::size (tb1) == dmd::size (tb2));
-           else
-             {
-               tree tlencmp = build_boolop (code, t1len, t2len);
-               if (e->op == EXP::equal)
-                 result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result);
-               else
-                 result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result);
-             }
+           result = build_memcmp_call (t1ptr, t2ptr, size);
+           result = build_boolop (code, result, integer_zero_node);
+         }
+       else
+         {
+           StructDeclaration *sd = t1elem->isTypeStruct ()->sym;
 
-           /* Ensure left-to-right order of evaluation.  */
-           if (TREE_SIDE_EFFECTS (t2))
-             result = compound_expr (t2saved, result);
+           result = build_array_struct_comparison (code, sd, t1len,
+                                                   t1ptr, t2ptr);
+         }
 
-           if (TREE_SIDE_EFFECTS (t1))
-             result = compound_expr (t1saved, result);
+       /* Check array length first before passing to memcmp.
+          For equality expressions, this becomes:
+          (e1.length == 0 || memcmp);
+          Otherwise for inequality:
+          (e1.length != 0 && memcmp);  */
+       tree tsizecmp = build_boolop (code, t1len, size_zero_node);
+       if (e->op == EXP::equal)
+         result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result);
+       else
+         result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result);
 
-           this->result_ = result;
-         }
+       /* Finally, check if lengths of both arrays match if dynamic.
+          The frontend should have already guaranteed that static arrays
+          have same size.  */
+       if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray)
+         gcc_assert (dmd::size (tb1) == dmd::size (tb2));
        else
          {
-           /* Use _adEq2() to compare each element.  */
-           Type *t1array = dmd::arrayOf (t1elem);
-           tree result = build_libcall (LIBCALL_ADEQ2, e->type, 3,
-                                        d_array_convert (e->e1),
-                                        d_array_convert (e->e2),
-                                        build_typeinfo (e, t1array));
+           tree tlencmp = build_boolop (code, t1len, t2len);
+           if (e->op == EXP::equal)
+             result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result);
+           else
+             result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result);
+         }
 
-           if (e->op == EXP::notEqual)
-             result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result);
+       /* Ensure left-to-right order of evaluation.  */
+       if (TREE_SIDE_EFFECTS (t2))
+         result = compound_expr (t2saved, result);
 
-           this->result_ = result;
-         }
+       if (TREE_SIDE_EFFECTS (t1))
+         result = compound_expr (t1saved, result);
+
+       this->result_ = result;
       }
     else if (TypeStruct *ts = tb1->isTypeStruct ())
       {
@@ -490,16 +476,9 @@ public:
       }
     else if (tb1->ty == TY::Taarray && tb2->ty == TY::Taarray)
       {
-       /* Use _aaEqual() for associative arrays.  */
-       tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3,
-                                    build_typeinfo (e, tb1),
-                                    build_expr (e->e1),
-                                    build_expr (e->e2));
-
-       if (e->op == EXP::notEqual)
-         result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result);
-
-       this->result_ = result;
+       /* Call to `_d_aaEqual' for associative arrays has already been handled
+          by the front-end.  */
+       gcc_unreachable ();
       }
     else
       {
@@ -517,17 +496,10 @@ public:
      exists in an associative array.  The result is a pointer to the
      element, or null if false.  */
 
-  void visit (InExp *e) final override
+  void visit (InExp *) final override
   {
-    Type *tb2 = e->e2->type->toBasetype ();
-    Type *tkey = tb2->isTypeAArray ()->index->toBasetype ();
-    tree key = convert_expr (build_expr (e->e1), e->e1->type, tkey);
-
-    /* Build a call to _aaInX().  */
-    this->result_ = build_libcall (LIBCALL_AAINX, e->type, 3,
-                                  build_expr (e->e2),
-                                  build_typeinfo (e, tkey),
-                                  build_address (key));
+    /* Call to `_d_aaIn' has already been handled by the front-end.  */
+    gcc_unreachable ();
   }
 
   /* Build a relational expression.  The result type is bool.  */
@@ -1152,42 +1124,8 @@ public:
 
     if (tb1->ty == TY::Taarray)
       {
-       /* Get the key for the associative array.  */
-       Type *tkey = tb1->isTypeAArray ()->index->toBasetype ();
-       tree key = convert_expr (build_expr (e->e2), e->e2->type, tkey);
-       libcall_fn libcall;
-       tree tinfo, ptr;
-
-       if (e->modifiable)
-         {
-           libcall = LIBCALL_AAGETY;
-           ptr = build_address (build_expr (e->e1));
-           tinfo = build_typeinfo (e, dmd::mutableOf (dmd::unSharedOf (tb1)));
-         }
-       else
-         {
-           libcall = LIBCALL_AAGETRVALUEX;
-           ptr = build_expr (e->e1);
-           tinfo = build_typeinfo (e, tkey);
-         }
-
-       /* Index the associative array.  */
-       tree result = build_libcall (libcall, dmd::pointerTo (e->type), 4,
-                                    ptr, tinfo,
-                                    size_int (dmd::size (tb1->nextOf ())),
-                                    build_address (key));
-
-       if (!e->indexIsInBounds && array_bounds_check ())
-         {
-           tree tassert = build_array_bounds_call (e->loc);
-
-           result = d_save_expr (result);
-           result = build_condition (TREE_TYPE (result),
-                                     d_truthvalue_conversion (result),
-                                     result, tassert);
-         }
-
-       this->result_ = indirect_ref (build_ctype (e->type), result);
+       /* Associative arrays have already been handled by the front-end.  */
+       gcc_unreachable ();
       }
     else
       {
@@ -1369,10 +1307,14 @@ public:
   {
     Type *ebtype = e->e1->type->toBasetype ();
     Type *tbtype = e->to->toBasetype ();
-    tree result = build_expr (e->e1, this->constp_, this->literalp_);
 
-    /* Just evaluate e1 if it has any side effects.  */
-    if (tbtype->ty == TY::Tvoid)
+    /* Use lowering if it has already been handled by the front-end.  */
+    Expression *cast = (e->lowering != NULL) ? e->lowering : e->e1;
+    tree result = build_expr (cast, this->constp_, this->literalp_);
+
+    /* When expression has been rewritten or is a cast to `void', just evaluate
+       the result if it has any side effects.  */
+    if (e->lowering != NULL || tbtype->ty == TY::Tvoid)
       this->result_ = build_nop (build_ctype (tbtype), result);
     else
       this->result_ = convert_for_rvalue (result, ebtype, tbtype);
@@ -1415,14 +1357,8 @@ public:
     /* Check that the array is actually an associative array.  */
     if (e->e1->type->toBasetype ()->ty == TY::Taarray)
       {
-       Type *tb = e->e1->type->toBasetype ();
-       Type *tkey = tb->isTypeAArray ()->index->toBasetype ();
-       tree index = convert_expr (build_expr (e->e2), e->e2->type, tkey);
-
-       this->result_ = build_libcall (LIBCALL_AADELX, Type::tbool, 3,
-                                      build_expr (e->e1),
-                                      build_typeinfo (e, tkey),
-                                      build_address (index));
+       /* Call to `_d_aaDel' has already been handled by the front-end.  */
+       gcc_unreachable ();
       }
     else
       {
@@ -1550,8 +1486,12 @@ public:
        Taking the address of a struct literal is otherwise illegal.  */
     if (e->e1->op == EXP::structLiteral)
       {
-       StructLiteralExp *sle = e->e1->isStructLiteralExp ()->origin;
-       gcc_assert (sle != NULL);
+       StructLiteralExp *sle = e->e1->isStructLiteralExp ();
+       if (!this->constp_)
+         {
+           gcc_assert (sle->origin != NULL);
+           sle = sle->origin;
+         }
 
        /* Build the reference symbol, the decl is built first as the
           initializer may have recursive references.  */
@@ -2429,17 +2369,10 @@ public:
       }
     else if (tb->ty == TY::Taarray)
       {
-       /* Allocating memory for a new associative array.  */
-       tree arg = build_typeinfo (e, e->newtype);
-       tree mem = build_libcall (LIBCALL_AANEW, Type::tvoidptr, 1, arg);
-
-       /* Return an associative array pointed to by MEM.  */
-       tree aatype = build_ctype (tb);
-       vec <constructor_elt, va_gc> *ce = NULL;
-       CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem);
-
-       result = build_nop (build_ctype (e->type),
-                           build_padded_constructor (aatype, ce));
+       /* Allocating memory for a new associative array has already been
+          handled by the front-end.  */
+       gcc_assert (e->lowering);
+       result = build_expr (e->lowering);
       }
     else
       gcc_unreachable ();
@@ -2684,10 +2617,9 @@ public:
     else
       {
        /* Allocate space on the memory managed heap.  */
-       tree mem = build_libcall (LIBCALL_ARRAYLITERALTX,
-                                 dmd::pointerTo (etype), 2,
-                                 build_typeinfo (e, dmd::arrayOf (etype)),
-                                 size_int (e->elements->length));
+       gcc_assert (e->lowering);
+       tree mem = build_nop (build_pointer_type (satype),
+                             build_expr (e->lowering));
        mem = d_save_expr (mem);
 
        /* Now copy the constructor into memory.  */
@@ -2715,55 +2647,24 @@ public:
 
   void visit (AssocArrayLiteralExp *e) final override
   {
-    if (this->constp_ && e->lowering != NULL)
+    if (this->constp_ && e->loweringCtfe != NULL)
       {
        /* When an associative array literal gets lowered, it's converted into a
           struct literal suitable for static initialization.  */
-       this->result_ = build_expr (e->lowering, this->constp_, true);
-       return ;
+       this->result_ = build_expr (e->loweringCtfe, this->constp_, true);
+       return;
       }
 
-    /* Want the mutable type for typeinfo reference.  */
-    Type *tb = dmd::mutableOf (e->type->toBasetype ());
-
     /* Handle empty assoc array literals.  */
-    TypeAArray *ta = tb->isTypeAArray ();
     if (e->keys->length == 0)
       {
-       this->result_ = build_padded_constructor (build_ctype (ta), NULL);
+       this->result_ = build_padded_constructor (build_ctype (e->type), NULL);
        return;
       }
 
-    /* Build an expression that assigns all expressions in KEYS
-       to a constructor.  */
-    Type *tkarray = dmd::sarrayOf (ta->index, e->keys->length);
-    tree akeys = build_array_from_exprs (tkarray, e->keys, this->constp_);
-    tree init = stabilize_expr (&akeys);
-
-    /* Do the same with all expressions in VALUES.  */
-    Type *tvarray = dmd::sarrayOf (ta->next, e->values->length);
-    tree avals = build_array_from_exprs (tvarray, e->values, this->constp_);
-    init = compound_expr (init, stabilize_expr (&avals));
-
     /* Generate: _d_assocarrayliteralTX (ti, keys, vals);  */
-    tree keys = d_array_value (build_ctype (dmd::arrayOf (ta->index)),
-                              size_int (e->keys->length),
-                              build_address (akeys));
-    tree vals = d_array_value (build_ctype (dmd::arrayOf (ta->next)),
-                              size_int (e->values->length),
-                              build_address (avals));
-
-    tree mem = build_libcall (LIBCALL_ASSOCARRAYLITERALTX, Type::tvoidptr, 3,
-                             build_typeinfo (e, ta), keys, vals);
-
-    /* Return an associative array pointed to by MEM.  */
-    tree aatype = build_ctype (ta);
-    vec <constructor_elt, va_gc> *ce = NULL;
-    CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem);
-
-    tree result = build_nop (build_ctype (e->type),
-                            build_padded_constructor (aatype, ce));
-    this->result_ = compound_expr (init, result);
+    gcc_assert (e->lowering);
+    this->result_ = build_expr (e->lowering);
   }
 
   /* Build a struct literal.  */
index e41e4c18c47c834cfdc29ebe3f89d34d2ff30fe7..e2e2ba49b7d1995bf5e4fa05b2391507204c7e47 100644 (file)
@@ -144,7 +144,7 @@ public:
   /* Alias symbols aren't imported, but their targets are.  */
   void visit (AliasDeclaration *d) final override
   {
-    Dsymbol *dsym = d->toAlias ();
+    Dsymbol *dsym = dmd::toAlias (d);
 
     if (dsym == d)
       {
index 8db5672c9c32392e0ff4a874e493d228d902265c..9651c07dc8ba71fef01a57e6ff1737e0c0140822 100644 (file)
@@ -64,21 +64,6 @@ DEF_D_RUNTIME (CALLFINALIZER, "_d_callfinalizer", RT(VOID), P1(VOIDPTR), 0)
 DEF_D_RUNTIME (CALLINTERFACEFINALIZER, "_d_callinterfacefinalizer", RT(VOID),
               P1(VOIDPTR), 0)
 
-/* Used for casting to a class or interface.  */
-DEF_D_RUNTIME (DYNAMIC_CAST, "_d_dynamic_cast", RT(OBJECT),
-              P2(OBJECT, CLASSINFO), 0)
-DEF_D_RUNTIME (INTERFACE_CAST, "_d_interface_cast", RT(OBJECT),
-              P2(OBJECT, CLASSINFO), 0)
-
-/* Used for allocating an array literal on the GC heap.  */
-DEF_D_RUNTIME (ARRAYLITERALTX, "_d_arrayliteralTX", RT(VOIDPTR),
-              P2(CONST_TYPEINFO, SIZE_T), 0)
-
-/* Used for value equality (x == y) and comparisons (x < y) of non-trivial
-   arrays.  Such as an array of structs or classes.  */
-DEF_D_RUNTIME (ADEQ2, "_adEq2", RT(INT),
-              P3(ARRAY_VOID, ARRAY_VOID, CONST_TYPEINFO), 0)
-
 /* Used for (array.length = n) expressions.  The `i' variant is for when the
    initializer is nonzero.  */
 DEF_D_RUNTIME (ARRAYSETLENGTHT, "_d_arraysetlengthT", RT(ARRAY_VOID),
@@ -106,31 +91,6 @@ DEF_D_RUNTIME (ARRAYAPPENDCD, "_d_arrayappendcd", RT(ARRAY_VOID),
 DEF_D_RUNTIME (ARRAYAPPENDWD, "_d_arrayappendwd", RT(ARRAY_VOID),
               P2(ARRAYPTR_BYTE, DCHAR), 0)
 
-/* Used for allocating a new associative array.  */
-DEF_D_RUNTIME (ASSOCARRAYLITERALTX, "_d_assocarrayliteralTX", RT(VOIDPTR),
-              P3(CONST_TYPEINFO, ARRAY_VOID, ARRAY_VOID), 0)
-DEF_D_RUNTIME (AANEW, "_aaNew", RT(VOIDPTR), P1(CONST_TYPEINFO), 0)
-
-/* Used for value equality of two associative arrays.  */
-DEF_D_RUNTIME (AAEQUAL, "_aaEqual", RT(INT),
-              P3(CONST_TYPEINFO, ASSOCARRAY, ASSOCARRAY), 0)
-
-/* Used to determine is a key exists in an associative array.  */
-DEF_D_RUNTIME (AAINX, "_aaInX", RT(VOIDPTR),
-              P3(ASSOCARRAY, CONST_TYPEINFO, VOIDPTR), 0)
-
-/* Used to retrieve a value from an associative array index by a key.  The
-   `Rvalue' variant returns null if the key is not found, where as aaGetY
-   will create new key entry for assignment.  */
-DEF_D_RUNTIME (AAGETY, "_aaGetY", RT(VOIDPTR),
-              P4(POINTER_ASSOCARRAY, CONST_TYPEINFO, SIZE_T, VOIDPTR), 0)
-DEF_D_RUNTIME (AAGETRVALUEX, "_aaGetRvalueX", RT(VOIDPTR),
-              P4(ASSOCARRAY, CONST_TYPEINFO, SIZE_T, VOIDPTR), 0)
-
-/* Used when calling delete on a key entry in an associative array.  */
-DEF_D_RUNTIME (AADELX, "_aaDelX", RT(BOOL),
-              P3(ASSOCARRAY, CONST_TYPEINFO, VOIDPTR), 0)
-
 /* Used for throw() expressions.  */
 DEF_D_RUNTIME (THROW, "_d_throw", RT(VOID), P1(OBJECT), ECF_NORETURN)
 DEF_D_RUNTIME (BEGIN_CATCH, "__gdc_begin_catch", RT(VOIDPTR), P1(VOIDPTR), 0)
index 160d37846f29511227347e465f6f48f0d39fc4c1..b6792dfe5cae9763b789cda7619e4fc0e0772017 100644 (file)
@@ -730,7 +730,9 @@ public:
        void *__monitor;
        TypeInfo value;
        TypeInfo key;
-       TypeInfo entry;  */
+       TypeInfo entry;
+       bool function(in void*, in void*) xopEquals;
+       hash_t function(in void*) xtoHash;  */
 
   void visit (TypeInfoAssociativeArrayDeclaration *d) final override
   {
@@ -750,6 +752,16 @@ public:
       this->layout_field (build_typeinfo (d->loc, d->entry));
     else
       this->layout_field (null_pointer_node);
+
+    /* bool function(in void*, in void*) xopEquals;  */
+    tree xeq = (d->xopEqual) ? build_address (get_symbol_decl (d->xopEqual))
+      : null_pointer_node;
+    this->layout_field (xeq);
+
+    /* hash_t function (in void*) xtoHash;  */
+    tree xhash = (d->xtoHash) ? build_address (get_symbol_decl (d->xtoHash))
+      : null_pointer_node;
+    this->layout_field (xhash);
   }
 
   /* Layout of TypeInfo_Vector is:
@@ -1442,6 +1454,15 @@ check_typeinfo_type (const Loc &loc, Scope *sc, Expression *expr)
            error_at (make_location_t (loc),
                      "%<object.TypeInfo%> cannot be used with %<-fno-rtti%>");
 
+         if (expr != NULL || !warned)
+           {
+             /* Print the location of where the error came from.  */
+             if (sc && sc->tinst)
+               dmd::printInstantiationTrace (sc->tinst);
+
+             global.errors++;
+           }
+
          warned = 1;
        }
     }
@@ -1653,6 +1674,7 @@ create_typeinfo (Type *type, Scope *sc)
            {
              ident = Identifier::idPool ("TypeInfo_AssociativeArray");
              make_internal_typeinfo (tk, ident, ptr_type_node, ptr_type_node,
+                                     ptr_type_node, ptr_type_node,
                                      ptr_type_node, NULL);
            }
          t->vtinfo = sc && have_typeinfo_p (Type::typeinfoassociativearray)
index 30d696290950509ef6081fe4fcc8f03e51184056..cab9b6c800ac4b6d7bfa980038d5b1666a042fa6 100644 (file)
@@ -1259,7 +1259,7 @@ public:
     /* For structs with a user defined postblit, copy constructor, or a
        destructor, also set TREE_ADDRESSABLE on the type and all variants.
        This will make the struct be passed around by reference.  */
-    if (!t->sym->isPOD ())
+    if (!dmd::isPOD (t->sym))
       {
        for (tree tv = t->ctype; tv != NULL_TREE; tv = TYPE_NEXT_VARIANT (tv))
          {
index e2f36b01c94ac44289b0d79c3476c8e2e64beda9..95cacbeaa854a124c27345e4764855c585f0b53a 100644 (file)
@@ -5,7 +5,7 @@ void parse1()
 {
     asm
     {
-        ""h;    // { dg-error "found 'h' when expecting ':'" }
+        ""h;    // { dg-error "found 'h' when expecting ';'" }
     }
 }
 
@@ -16,13 +16,13 @@ void parse2()
         "" : : "g" (1 ? 2 : 3);
         "" : : "g" (1 ? 2 : :) 3;
         // { dg-error "expression expected, not ':'" "" { target *-*-* } .-1 }
-        // { dg-error "expected constant string constraint for operand" "" { target *-*-* } .-2 }
+        // { dg-error "found '3' when expecting ';'" "" { target *-*-* } .-2 }
     }
 }
 
 void parse3()
 {
-    asm { "" [; } // { dg-error "found '\\\[' when expecting ':'" }
+    asm { "" [; } // { dg-error "found '\\\[' when expecting ';'" }
 }
 
 void parse4()
@@ -30,7 +30,7 @@ void parse4()
     int expr;
     asm
     {
-        "%name" : [name] string (expr); // { dg-error "expected constant string constraint for operand, not 'string'" }
+        "%name" : [name] string (expr); // { dg-error "expected string literal or expression in parentheses" }
     }
 }
 
index b525a2131ce100e57881a9ad35509386c8e1814a..1b44c976de3bb9bde694e0439c8accf9c80f5690 100644 (file)
@@ -7,6 +7,6 @@ void test(int a)
 {
     asm
     {
-        "cpuid" : : "a" a;  // { dg-error "'a' must be surrounded by parentheses" }
+        "cpuid" : : "a" a;  // { dg-error "found 'a' when expecting '\\\('" }
     }
 }
index 3861a37899cc5b18062ada0393d0bd69ea3c46e1..473ce752a8b93d540863d8cd825e4a9cff49c099 100644 (file)
@@ -3,9 +3,13 @@
 
 module object;
 
+class Object {}
+class TypeInfo {}
+class TypeInfo_AssociativeArray : TypeInfo {} // { dg-note "defined here" }
+
 extern(C) int main()
 {
-    int[int] aa; // { dg-error ".object.TypeInfo. could not be found" }
-    aa[0] = 1;  // { dg-error ".object.TypeInfo. could not be found, but is implicitly used" }
+    int[int] aa; // { dg-error "no property" }
+    aa[0] = 1;  // { dg-error "'object._d_aaGetY' not found" }
     return 0;
 }
index 29b8265c82130b24171e6b99d4f8f11945a2b33e..32391066b0b6a060e4b677b29439601e9ac29e1a 100644 (file)
@@ -2,12 +2,27 @@
 // { dg-options "-fno-rtti" }
 // { dg-shouldfail "expressions depend on TypeInfo" }
 
-int* testInExp(int key, int[int] aa)
+module object;
+
+class Object {}
+class TypeInfo {}
+
+struct AA(K, V)
 {
-    return key in aa; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." }
+    this(int sz) nothrow
+    {
+        keyTI = typeid(K); // { dg-error "'object.TypeInfo' cannot be used with '-fno-rtti'" }
+    }
+    TypeInfo keyTI;
 }
 
-bool testAAEqual(int[string] aa1, int[string] aa2)
+auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key)
+{
+    auto aa = *(cast(inout(AA!(K, V))*)&a); // { dg-note "instantiated from here" }
+    return null;
+}
+
+int* testInExp(int key, int[int] aa)
 {
-    return aa1 == aa2; // { dg-error "requires .object.TypeInfo. and cannot be used with .-fno-rtti." }
+    return key in aa; // { dg-note "instantiated from here" }
 }
diff --git a/gcc/testsuite/gdc.dg/rtti2.d b/gcc/testsuite/gdc.dg/rtti2.d
new file mode 100644 (file)
index 0000000..56cb04e
--- /dev/null
@@ -0,0 +1,29 @@
+// { dg-do compile }
+// { dg-options "-fno-rtti" }
+// { dg-shouldfail "expressions depend on TypeInfo" }
+
+module object;
+
+class Object {}
+class TypeInfo {}
+class TypeInfo_Array : TypeInfo {}
+
+struct AA(K, V)
+{
+    this(int sz) nothrow
+    {
+        keyTI = typeid(K); // { dg-error "'object.TypeInfo' cannot be used with '-fno-rtti'" }
+    }
+    TypeInfo keyTI;
+}
+
+bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2)
+{
+    auto aa1 = *(cast(const(AA!(K, V))*)&a1); // { dg-note "instantiated from here" }
+    return false;
+}
+
+bool testAAEqual(int[immutable(char)[]] aa1, int[immutable(char)[]] aa2)
+{
+    return aa1 == aa2; // { dg-note "instantiated from here" }
+}
index 682881969ed6e38cce76a0133ed3ee192901c242..6f331dda9c93af5aa9a10d5ff242929d33dab88c 100644 (file)
@@ -134,7 +134,7 @@ struct Bar14986
 
 Bar14986 test14986()
 {
-    Foo14986[] types;
+    Foo14986[] types = [Foo14986(1)];
     auto a1 = new void[types[0].tsize];                 // TypeIdentifier::toExpression
     auto a2 = new void[Id14986!types[0].tsize];         // TypeInstance::toExpression
 
index ee698f16dcf8bb2283c31e8b3eacd4d45ceb5c90..e2408c971c0a0b5d98df4582753059fd7469f324 100644 (file)
@@ -566,7 +566,7 @@ struct SafeS
 {
     this(int[1] x) scope {}
        this(int[2] x) return scope {}
-       this(int[3] x) scope return {}
+       this(int[3] x) return ref scope {}
        this(int[4] x) return {}
 
 @safe:
@@ -627,3 +627,5 @@ interface I12344
 {
     int i12344(int x) in(x > 0) out(result) {assert(result > 0);};
 }
+
+debug enum issue21406 = 1;
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/test21331.d b/gcc/testsuite/gdc.test/compilable/extra-files/test21331.d
new file mode 100644 (file)
index 0000000..59f30d1
--- /dev/null
@@ -0,0 +1,17 @@
+struct S(alias fun) {
+    int foo() {
+        int r = fun(0);
+        static foreach (i; 0..2)
+            r += (x => 2)(0);
+        return r;
+    }
+}
+
+int bar() {
+    int r;
+    static foreach (i; 0..2)
+        r += S!(x => 1)().foo();
+    return r;
+}
+
+// nm test21331.o | grep __lambda_L5_C19
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/test21359a.d b/gcc/testsuite/gdc.test/compilable/extra-files/test21359a.d
new file mode 100644 (file)
index 0000000..b52d9db
--- /dev/null
@@ -0,0 +1,3 @@
+void registerAll() {
+    import test21359b;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/test21359b.d b/gcc/testsuite/gdc.test/compilable/extra-files/test21359b.d
new file mode 100644 (file)
index 0000000..f9aca7f
--- /dev/null
@@ -0,0 +1,11 @@
+struct SumType(Types...)
+{
+    static foreach (T; Types)
+        T values_;
+}
+
+SumType!(string[int]) convertObserver;
+
+void hackAroundLinkerError() {
+    auto t = typeid(const(immutable(char)[][]));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/h20184.h b/gcc/testsuite/gdc.test/compilable/imports/h20184.h
new file mode 100644 (file)
index 0000000..5342a1d
--- /dev/null
@@ -0,0 +1 @@
+int f20184(void);
index 019ff226ea8952948eade60552461b6e4852c1de..98ebcdff745aab836ef5ad8bc66ea0d9927a587a 100644 (file)
@@ -29,3 +29,20 @@ static if (__traits(compiles, int4))
     enum int4 F = D * E;
     static assert(F.array == [1, 4, 9, 16]);
 }
+
+// https://github.com/dlang/dmd/issues/20114
+
+int* find(int[] arr, int needle)
+{
+       foreach(ref a; arr)
+               if(a == needle)
+                       return &a;
+       return null;
+}
+
+enum int[int] aa = [0: 0];
+enum int[] da = [0, 1, 2];
+static assert(0 in aa);
+static assert(&da[1]);
+static assert(find(da, 1));
+static assert(!find(da, 3));
index a53805b4066f912e1ee3c613f7a161456b86c8e5..119c366ed39588493393db6542dbacb56339eea2 100644 (file)
@@ -4,9 +4,9 @@ alias Lint = mixin("Int");
 
 int test1(mixin("int")* p)
 {
-    mixin("int")[] a;
-    mixin("int[]") b;
-    mixin("int[] c;");
+    mixin("int")[] a = [1];
+    mixin("int[]") b = [1];
+    mixin("int[] c = [1];");
     mixin("*p = c[0];");
     *p = mixin("c[0]");
     return *p + a[0] + b[0] + c[0];
index b9d11eb9b8e539231d9527e16d17e350a353041e..73921e96cb2ab4a40a06c4a63fff1c37f90519fa 100644 (file)
@@ -6,7 +6,8 @@ void main(){
     *(x)++=0;
     (*(x)--)=0;
     (*x++)=0; // ok
-    int*[] z;
+    int a = 1, b = 2;
+    int*[] z = [&a, &b];
     *(z[0])++=0; //ok
     (y[0])--;
     *x++=0;
@@ -16,7 +17,7 @@ void f()
 {
     int b;
     (b)++;
-    int[] a;
+    int[] a = [1];
     b = (a)[0]++; //ok
     (a[0])--;
     b = (int).init; //ok
index 647910ecf161cf7d1e31c0405fc5183789007f18..a9392d7bcf9b1d87197d54cb21837a5ebec0b16c 100644 (file)
@@ -5,7 +5,7 @@ pure nothrow @nogc ref @safe shared(C1)(return ref shared(C1) c)
 pure nothrow @nogc ref @safe shared(int)(return ref shared(C3) c)
 ---
 */
-ref shared(int) f(return shared ref int y)
+ref shared(int) f(return ref shared int y)
 {
     return y;
 }
@@ -97,7 +97,7 @@ shared(C2)* test_dotvarexp_4(return ref shared C3 c)
     return &c.c2;
 }
 
-ref shared(int) test_dotvarexp_5(return shared ref C3 c)
+ref shared(int) test_dotvarexp_5(return ref shared C3 c)
 {
     return c.c1.c1.value;
 }
index 766406898e6d060881103f6d2b86b43d8231f60a..aa6249af55bcfd6a847032168b6fcd76f44fb5f3 100644 (file)
@@ -5,7 +5,7 @@ class C { A!int[1] array; }
 
 void test14838() pure nothrow @nogc @safe
 {
-    C c;
+    scope C c = new C;
     c.__xdtor();    // C.~this() will also be inferred to
                     // pure nothrow @nogc @safe
 
index 098bfe4c5d04e5c9498d1a7827f7f09609c1afc8..3299f1521c7eed3ad2f8144596999e2fe67b582c 100644 (file)
@@ -6,7 +6,7 @@ struct S { int[] a; int b; }
 \r
 void foo()\r
 {\r
-    S[] s;\r
+    S[] s = [S([],0)];\r
     if (s[$-1] == S.init) {}\r
 }\r
 \r
index bc8ad2c40e62ca2e028ef31f6a6421ad5a003380..0e455ec61772ec97462a7df2530db9cf2bcb031b 100644 (file)
@@ -36,9 +36,11 @@ void main() {
  void test20989() @safe
 {
     uint[] arr = [1, 2, 3];
+    uint[] arr2 = null;
     assert(arr.ptr);
-    assert(!arr.ptr);
+    assert(!arr2.ptr);
     assert(arr.ptr is arr.ptr);
+    assert(arr2.ptr is arr2.ptr);
 }
 
 // https://issues.dlang.org/show_bug.cgi?id=21765
diff --git a/gcc/testsuite/gdc.test/compilable/test20184.d b/gcc/testsuite/gdc.test/compilable/test20184.d
new file mode 100644 (file)
index 0000000..1039065
--- /dev/null
@@ -0,0 +1,5 @@
+// EXTRA_FILES: imports/h20184.h
+import imports.h20184;
+
+alias fun_signature = extern (C) int();
+static assert(is(typeof(f20184) : fun_signature));
diff --git a/gcc/testsuite/gdc.test/compilable/test20365.d b/gcc/testsuite/gdc.test/compilable/test20365.d
new file mode 100644 (file)
index 0000000..045e991
--- /dev/null
@@ -0,0 +1,20 @@
+// REQUIRED_ARGS: -preview=bitfields
+
+struct A { int x : 16, y : 16; }
+
+void autoref_assign(A a)
+{
+    auto ref int x = a.x;
+}
+
+void f()(auto ref int);
+
+void autoref_param(A a)
+{
+    f(a.y);
+}
+
+auto ref int autoref_return(ref A a)
+{
+    return a.y;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21331.sh b/gcc/testsuite/gdc.test/compilable/test21331.sh
new file mode 100755 (executable)
index 0000000..855e563
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+objfile="${OUTPUT_BASE}${OBJ}"
+$DMD -c -m${MODEL} -of${objfile} ${EXTRA_FILES}/${TEST_NAME}.d
+
+nm ${objfile} | grep -E '16__lambda_L13_C17.+15__lambda_L5_C19'
+nm ${objfile} | grep -E '16__lambda_L13_C17.+17__lambda_L5_C19_1'
+nm ${objfile} | grep -E '18__lambda_L13_C17_1.+15__lambda_L5_C19'
+nm ${objfile} | grep -E '18__lambda_L13_C17_1.+17__lambda_L5_C19_1'
+
+rm_retry ${objfile}
diff --git a/gcc/testsuite/gdc.test/compilable/test21359.sh b/gcc/testsuite/gdc.test/compilable/test21359.sh
new file mode 100755 (executable)
index 0000000..ed4719c
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+objfile="${OUTPUT_BASE}${OBJ}"
+$DMD -c -m${MODEL} -allinst -of${objfile} ${EXTRA_FILES}/${TEST_NAME}a.d -I${EXTRA_FILES}
+
+# This test is brittle and might break in the future.
+# For now, ensure a specific AA TypeInfo isn't defined multiple times, as happened with DMD v2.111.0.
+mangled_sym='_D14TypeInfo_HiAya6__initZ'
+num_typeinfo_syms=$(nm --defined-only ${objfile} | grep -F ${mangled_sym} | wc -l)
+if [[ "${num_typeinfo_syms}" -ne 1 ]]; then
+    echo "Expected 1 '${mangled_sym}' symbol definition but got: ${num_typeinfo_syms}"
+    exit 1
+fi
+
+rm_retry ${objfile}
diff --git a/gcc/testsuite/gdc.test/compilable/test21416.d b/gcc/testsuite/gdc.test/compilable/test21416.d
new file mode 100644 (file)
index 0000000..e24f729
--- /dev/null
@@ -0,0 +1,17 @@
+// https://github.com/dlang/dmd/issues/21416
+
+struct S
+{
+       @disable this(ref S);
+}
+
+ref id(ref S s) => s;
+
+S* p;
+void foo(S s) { }
+
+void main()
+{
+       S s;
+       foo(__rvalue(id(s)));
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test21476.d b/gcc/testsuite/gdc.test/compilable/test21476.d
new file mode 100644 (file)
index 0000000..a2e307e
--- /dev/null
@@ -0,0 +1,13 @@
+// https://github.com/dlang/dmd/issues/21476
+
+struct S21476
+{
+    string field;
+    this(ref return scope S21476);
+    this(return scope S21476);
+}
+
+void test21476()
+{
+    auto o = S21476("aoe");
+}
index 6237661a923ae904b80fdac66e7738f22a62e376..f8223549ea9d8d44c3d6d73436ccde560dda9287 100644 (file)
@@ -5,7 +5,7 @@ struct Ptr
 {
        int* impl;
        void* fun0() return scope {return impl;}
-       void* fun1() scope return {return impl;}
+       void* fun1() scope return ref {return impl;}
        void* fun2() return {return &this;}
 }
 
index 664452ef4e686f9770255115c06a32164c54e045..aefc76cddb586ed81019bc4539d7c4a8ab129ab3 100644 (file)
@@ -1,7 +1,7 @@
 // https://issues.dlang.org/show_bug.cgi?id=3004
 /*
 REQUIRED_ARGS: -ignore -v
-TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|import|\(imported|semantic|entry|library|function  object|function  core|\s*$)")
+TRANSFORM_OUTPUT: remove_lines("^(predefs|binary|version|config|DFLAG|parse|inline|.*_d_newarrayU|import|\(imported|semantic|entry|library|function  object|function  core|\s*$)")
 TEST_OUTPUT:
 ---
 pragma    GNU_attribute (__error)
index 659b5f776a59758bc5936044977ef51953df8a07..190feb4767c975f68567eaa690d41478e55c31c7 100644 (file)
@@ -15,3 +15,13 @@ class A {
                 return;
         }
 }
+
+// https://github.com/dlang/dmd/issues/18018
+int main ()
+{
+   string[string] aa;
+   goto A;               // line 4
+   aa["X"] = "Y";        // line 5
+A:
+   return 0;
+}
index 0d0af2e61f0609eea2b932bd796dac68c3f7317c..45b42c9e89539203b1f24e1384a1cff653fdae67 100644 (file)
@@ -27,7 +27,7 @@ struct S
     int i;
     int* ptr;
 
-    int* wannabeReturnRef() scope return
+    int* wannabeReturnRef() return ref scope
     {
         return &i;
     }
index f4defb48095a9b3c1fe9988f99f39ed53a1ad24d..7c1fdcb16cc980a65c8395150e721d5f570074f1 100644 (file)
@@ -44,8 +44,8 @@ void test_getFunctionAttributes()
     static assert(__traits(getFunctionAttributes, S.sharedF) == tuple!("shared", "@system"));
     static assert(__traits(getFunctionAttributes, typeof(S.sharedF)) == tuple!("shared", "@system"));
 
-    static assert(__traits(getFunctionAttributes, S.refF) == tuple!("ref", "return", "@system"));
-    static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("ref", "return", "@system"));
+    static assert(__traits(getFunctionAttributes, S.refF) == tuple!("return", "ref", "@system"));
+    static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("return", "ref", "@system"));
 
     static assert(__traits(getFunctionAttributes, S.propertyF) == tuple!("@property", "@system"));
     static assert(__traits(getFunctionAttributes, typeof(&S.propertyF)) == tuple!("@property", "@system"));
index bcb7256723c6921f7409a48fa8b255505861eaa5..6a9055574008a33ce1565b8aea9a51606c99f39f 100644 (file)
@@ -4,6 +4,8 @@ PERMUTE_ARGS:
 OUTPUT_FILES: compilable/vcg-ast.d.cg
 EXTRA_FILES: imports/vcg_ast_import.d
 TEST_OUTPUT_FILE: extra-files/vcg-ast.d.cg
+// size_t currently must be ulong in this test, not uint
+DISABLED: freebsd32 openbsd32 linux32 osx32 win32
 */
 
 module vcg;
index 0474315be57e62efa6e70d6af5a89c585693a40e..592ee315ce343a727d80cf8c9ce93c50c08f1a8b 100644 (file)
@@ -76,3 +76,41 @@ void test13899()
     {
     }
 }
+
+import core.checkedint;
+
+// check inlining of checkedint with -wi
+T testCheckedSigned(T)(T x, T y)
+{
+    bool overflow;
+    T z = adds(x, y, overflow);
+    z = subs(z, x, overflow);
+    z = muls(z, x, overflow);
+    z = negs(z, overflow);
+    return z;
+}
+
+T testCheckedUnsigned(T)(T x, T y)
+{
+    bool overflow;
+    T z = addu(x, y, overflow);
+    z = subu(z, x, overflow);
+    z = mulu(z, x, overflow);
+    return z;
+}
+
+void testCkeckedInt()
+{
+    assert(testCheckedSigned!int(3,4) == -12);
+    assert(testCheckedSigned!long(3,4) == -12);
+    static if (is(cent))
+        assert(testCheckedSigned!cent(3,4) == -12);
+
+    bool overflow;
+    assert(mulu(cast(long)3, cast(uint)4, overflow) == 12);
+
+    assert(testCheckedUnsigned!uint(3,4) == 12);
+    assert(testCheckedUnsigned!ulong(3,4) == 12);
+    static if (is(ucent))
+        assert(testCheckedUnsigned!ucent(3,4) == 12);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/aa_assign.d b/gcc/testsuite/gdc.test/fail_compilation/aa_assign.d
new file mode 100644 (file)
index 0000000..3c6c5d2
--- /dev/null
@@ -0,0 +1,13 @@
+/* TEST_OUTPUT:
+---
+fail_compilation\aa_assign.d(11): Error: associative arrays can only be assigned values with immutable keys, not `char[]`
+---
+*/
+
+void test_mutable_key()
+{
+    int[char[]] aa;
+    char[] str = "123".dup;
+    aa[str] = 3;
+    assert(str in aa);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/aaerrors.d b/gcc/testsuite/gdc.test/fail_compilation/aaerrors.d
new file mode 100644 (file)
index 0000000..571f472
--- /dev/null
@@ -0,0 +1,43 @@
+/* TEST_OUTPUT:
+TRANSFORM_OUTPUT: remove_lines(called from here)
+---
+fail_compilation\aaerrors.d-mixin-29(29): Error: `assert(aai[1] == 0)` failed
+fail_compilation\aaerrors.d-mixin-30(30): Error: `assert((aai[1] = 1) == 0)` failed
+fail_compilation\aaerrors.d-mixin-31(31): Error: `assert(*(1 in aai) == 3)` failed
+fail_compilation\aaerrors.d-mixin-32(32): Error: `assert(aai.remove(2))` failed
+fail_compilation\aaerrors.d-mixin-33(33): Error: `assert(aai != [1:2])` failed
+fail_compilation\aaerrors.d-mixin-34(34): Error: `assert(aai == [1:3])` failed
+fail_compilation\aaerrors.d-mixin-41(41): Error: `assert(aas[1].x == 0)` failed
+fail_compilation\aaerrors.d-mixin-42(42): Error: `assert((aas[1] = 1).x == 0)` failed
+fail_compilation\aaerrors.d-mixin-43(43): Error: `assert((*(1 in aas)).x == 0)` failed
+---
+*/
+
+
+struct S
+{
+    int x;
+    this(int _x){ x = _x; }
+    ref S opAssign(int _x){ x = _x; return this; }
+}
+
+string gentest_ii(string expr)
+{
+    return "() { int[int] aai = [ 1 : 2 ]; assert(" ~ expr ~ ");\n return true; }()\n";
+}
+
+const ii1 = mixin(gentest_ii("aai[1] == 0"));
+const ii2 = mixin(gentest_ii("(aai[1] = 1) == 0"));
+const ii3 = mixin(gentest_ii("*(1 in aai) == 3"));
+const ii4 = mixin(gentest_ii("aai.remove(2)"));
+const ii5 = mixin(gentest_ii("aai != [1:2]"));
+const ii6 = mixin(gentest_ii("aai == [1:3]"));
+
+string gentest_is(string expr)
+{
+    return "() { S[int] aas = [ 1 : S(2) ]; assert(" ~ expr ~ ");\n return true; }()\n";
+}
+
+const is1 = mixin(gentest_is("aas[1].x == 0"));
+const is2 = mixin(gentest_is("(aas[1] = 1).x == 0"));
+const is3 = mixin(gentest_is("(1 in aas).x == 0"));
index a8f0faa073926a41b8fa43b8c275926e998969d6..9515039a721ce476e95856b4bc3086968fd9831e 100644 (file)
@@ -1,8 +1,8 @@
 /* REQUIRED_ARGS: -preview=bitfields
  * TEST_OUTPUT:
 ---
-fail_compilation/biterrors.d(103): Error: initializer not allowed for bit-field declaration
-fail_compilation/biterrors.d(104): Error: storage class not allowed for bit-field declaration
+fail_compilation/biterrors.d(103): Error: initializer not allowed for bitfield declaration
+fail_compilation/biterrors.d(104): Error: storage class not allowed for bitfield declaration
 ---
  */
 
index 77671eb853ff90f1a0d9e1c797c5315f24e52e49..85554931d133004eafed753724c39916eef11c54 100644 (file)
@@ -1,9 +1,9 @@
 /* REQUIRED_ARGS: -preview=bitfields
  * TEST_OUTPUT:
 ---
-fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bit-field must be member of struct, union, or class
-fail_compilation/biterrors2.d(104): Error: bit-field `b` has zero width
-fail_compilation/biterrors2.d(105): Error: bit-field type `float` is not an integer type
+fail_compilation/biterrors2.d(100): Error: variable `biterrors2.a` - bitfield must be member of struct, union, or class
+fail_compilation/biterrors2.d(104): Error: bitfield `b` has zero width
+fail_compilation/biterrors2.d(105): Error: bitfield type `float` is not an integer type
 ---
 */
 
index 09d7be66d6430c8749083462063919b85e554910..e956fc2f6eba547439c1e89db82b5b617a6b911b 100644 (file)
@@ -1,7 +1,7 @@
 /* REQUIRED_ARGS: -preview=bitfields
  * TEST_OUTPUT:
 ---
-fail_compilation/biterrors3.d(103): Error: storage class not allowed for bit-field declaration
+fail_compilation/biterrors3.d(103): Error: storage class not allowed for bitfield declaration
 fail_compilation/biterrors3.d(106): Error: expected `,` or `=` after identifier, not `:`
 fail_compilation/biterrors3.d(106): Error: found `:` when expecting `,`
 fail_compilation/biterrors3.d(106): Error: found `3` when expecting `identifier`
index 0f2ca2d0d3fccffc9c330b8b958abcbc3bb0b6c0..53b5cdc232ff68ae73eee793387f6c3a00b66275 100644 (file)
@@ -1,7 +1,7 @@
 /* REQUIRED_ARGS: -preview=bitfields
  * TEST_OUTPUT:
 ---
-fail_compilation/biterrors4.d(109): Error: cannot take address of bit-field `a`
+fail_compilation/biterrors4.d(109): Error: cannot take address of bitfield `s.a`
 ---
 */
 
index 2665d69cc98e7608d5d6021e936674755d5f5e10..e17516b8f89b7e5a7d22f3d6b6e29562191323eb 100644 (file)
@@ -1,8 +1,10 @@
 /* REQUIRED_ARGS: -preview=bitfields
  * TEST_OUTPUT:
 ---
-fail_compilation/biterrors5.d(23): Error: bitfield symbol expected not struct `biterrors5.S`
-fail_compilation/biterrors5.d(24): Error: bitfield symbol expected not variable `biterrors5.test0.i`
+fail_compilation/biterrors5.d(25): Error: bitfield symbol expected not struct `biterrors5.S`
+fail_compilation/biterrors5.d(26): Error: bitfield symbol expected not variable `biterrors5.test0.i`
+fail_compilation/biterrors5.d(35): Error: cannot take address of bitfield `a.x`
+fail_compilation/biterrors5.d(35): Error: cannot take address of bitfield `a.y`
 ---
 */
 
@@ -23,3 +25,12 @@ void test0()
     i = __traits(getBitfieldOffset, S);
     i = __traits(getBitfieldOffset, i);
 }
+
+/****************************************/
+
+struct B { int x: 3; int y: 5; }
+
+void test1(int val, int choice, B a)
+{
+    (choice ? a.x : a.y) = val;
+}
index 40b99ab39d09baeef3115a1e4e1e075109bb673d..3608d9f4991d94cfe4cae025aafcfb57216eb7bd 100644 (file)
@@ -1,15 +1,23 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag13320.d(14): Error: operator `++` not supported for `f` of type `Foo`
-fail_compilation/diag13320.d(9):        perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}`
+fail_compilation/diag13320.d(20): Error: operator `++` not supported for `f` of type `Foo`
+fail_compilation/diag13320.d(14):        perhaps implement `auto opUnary(string op : "++")() {}` or `auto opOpAssign(string op : "+")(int) {}`
+fail_compilation/diag13320.d(21): Error: expression `f` of type `Foo` does not have a boolean value
+fail_compilation/diag13320.d(14):        perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`
+fail_compilation/diag13320.d(22): Error: expression `Foo()` of type `E` does not have a boolean value
+fail_compilation/diag13320.d(14):        perhaps add Cast Operator Overloading with `bool opCast(T : bool)() => ...`
 ---
 */
 
+
 struct Foo {}
+enum E : Foo { a = Foo.init }
 
 void main()
 {
     Foo f;
     ++f;
+    if (f) {}
+    assert(E.a);
 }
index 1dbacfd3e54a17149268b8454557af67189a3c3e..92032bcac019254790aebc676b2cd9fb84d1a7e4 100644 (file)
@@ -1,37 +1,37 @@
 /* TEST_OUTPUT:
 ---
-fail_compilation/diag16976.d(44): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(45): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(46): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(47): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(48): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(49): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(50): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(51): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(52): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(53): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(54): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(55): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(56): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(57): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(58): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(59): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(65): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(66): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(67): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(68): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(69): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(70): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(71): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(72): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(73): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(74): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(75): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(76): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(77): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(78): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(79): Error: foreach: key cannot be of non-integral type `float`
-fail_compilation/diag16976.d(80): Error: foreach: key cannot be of non-integral type `float`
+fail_compilation/diag16976.d(44): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(45): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(46): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(47): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(48): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(49): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(50): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(51): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(52): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(53): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(54): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(55): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(56): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(57): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(58): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(59): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(65): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(66): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(67): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(68): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(69): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(70): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(71): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(72): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(73): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(74): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(75): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(76): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(77): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(78): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(79): Error: foreach: index cannot be of non-integral type `float`
+fail_compilation/diag16976.d(80): Error: foreach: index cannot be of non-integral type `float`
 ---
 */
 
index 3267e6692432e5d5ec6a2abbb2d57c17c3322ecc..a19796d5d9b6be2d6600067da40ccd2db0c4d51a 100644 (file)
@@ -2,17 +2,16 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/diag7420.d(21): Error: static variable `x` cannot be read at compile time
-fail_compilation/diag7420.d(21):        while evaluating: `static assert(x < 4)`
+fail_compilation/diag7420.d(20): Error: static variable `x` cannot be read at compile time
+fail_compilation/diag7420.d(20):        while evaluating: `static assert(x < 4)`
+fail_compilation/diag7420.d(21): Error: static variable `y` cannot be read at compile time
+fail_compilation/diag7420.d(21):        while evaluating: `static assert(y == "abc")`
 fail_compilation/diag7420.d(22): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(22):        called from here: `__equals(y, "abc")`
-fail_compilation/diag7420.d(22):        while evaluating: `static assert(y == "abc")`
+fail_compilation/diag7420.d(22):        while evaluating: `static assert(cast(ubyte[])y != null)`
 fail_compilation/diag7420.d(23): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(23):        while evaluating: `static assert(cast(ubyte[])y != null)`
+fail_compilation/diag7420.d(23):        while evaluating: `static assert(cast(int)y[0] == 1)`
 fail_compilation/diag7420.d(24): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(24):        while evaluating: `static assert(cast(int)y[0] == 1)`
-fail_compilation/diag7420.d(25): Error: static variable `y` cannot be read at compile time
-fail_compilation/diag7420.d(25):        while evaluating: `static assert(y[0..1].length == 1u)`
+fail_compilation/diag7420.d(24):        while evaluating: `static assert(y[0..1].length == 1u)`
 ---
 */
 
index 7fe30a687065d065e98f75d41d17fd298592e8ec..848fb19536c2023e36d0fac719191a56e61aeb8d 100644 (file)
@@ -5,7 +5,7 @@ fail_compilation/discard_value.d(24): Error: the result of the equality expressi
 fail_compilation/discard_value.d(25): Error: the result of the equality expression `null !is null` is discarded
 fail_compilation/discard_value.d(26): Error: the result of the equality expression `v == 0` is discarded
 fail_compilation/discard_value.d(27): Error: the result of the equality expression `v == 0` is discarded
-fail_compilation/discard_value.d(28): Error: `!__equals("", "")` has no effect
+fail_compilation/discard_value.d(28): Error: the result of the equality expression `"" != ""` is discarded
 fail_compilation/discard_value.d(29): Error: the result of the equality expression `"" == ""` is discarded
 fail_compilation/discard_value.d(30): Error: the result of the equality expression `fun().i == 4` is discarded
 fail_compilation/discard_value.d(30):        note that `fun().i` may have a side effect
index cdf0e854d54f4928336c924c4980fe65bcac865a..85d31db0cf139866b865841533c3262a8750c96b 100644 (file)
@@ -1,7 +1,10 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail13756.d(11): Error: `foreach`: index must be type `const(int)`, not `int`
+fail_compilation/fail13756.d(14): Error: `foreach`: index parameter `ref k` must be type `const(int)`, not `int`
+fail_compilation/fail13756.d(17): Error: cannot implicitly convert expression `__applyArg0` of type `int` to `string`
+fail_compilation/fail13756.d(19): Error: cannot implicitly convert expression `__applyArg1` of type `int` to `char`
+fail_compilation/fail13756.d(20): Error: `foreach`: value parameter `ref val` must be type `int`, not `dchar`
 ---
 */
 
@@ -11,4 +14,8 @@ void maiin()
     foreach (ref int k, v; aa)
     {
     }
+    foreach (string key, val; aa) {}
+
+    foreach (key, char val; aa) {}
+    foreach (key, ref dchar val; aa) {}
 }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20365.d b/gcc/testsuite/gdc.test/fail_compilation/fail20365.d
new file mode 100644 (file)
index 0000000..a7ce476
--- /dev/null
@@ -0,0 +1,29 @@
+/* REQUIRED_ARGS: -preview=bitfields
+TEST_OUTPUT:
+---
+fail_compilation/fail20365.d(16): Error: bitfield `a.x` cannot be assigned to `ref x`
+fail_compilation/fail20365.d(23): Error: function `f` is not callable using argument types `(int)`
+fail_compilation/fail20365.d(23):        cannot pass bitfield argument `a.y` to parameter `ref int`
+fail_compilation/fail20365.d(19):        `fail20365.f(ref int)` declared here
+fail_compilation/fail20365.d(28): Error: cannot `ref` return bitfield `a.y`
+---
+*/
+
+struct A { int x : 16, y : 16; }
+
+void ref_assign(A a)
+{
+    ref int x = a.x;
+}
+
+void f(ref int);
+
+void ref_param(A a)
+{
+    f(a.y);
+}
+
+ref int ref_return(ref A a)
+{
+    return a.y;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21414.d b/gcc/testsuite/gdc.test/fail_compilation/fail21414.d
new file mode 100644 (file)
index 0000000..d07b2e2
--- /dev/null
@@ -0,0 +1,47 @@
+// https://github.com/dlang/dmd/issues/21414
+
+/* REQUIRED_ARGS: -o-
+TEST_OUTPUT:
+---
+fail_compilation/fail21414.d(42): Error: moving variable `__rvalue(s)` with `__rvalue` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(42): Error: moving variable `__rvalue(s)` with `__rvalue` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(44): Error: calling `__rvalue`-annotated function `unsafeMove` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(44): Error: calling `__rvalue`-annotated function `unsafeMove` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(46): Error: moving result of `ref` function `id` with `__rvalue` is not allowed in a `@safe` function
+fail_compilation/fail21414.d(46): Error: moving result of `ref` function `id` with `__rvalue` is not allowed in a `@safe` function
+---
+*/
+
+@safe:
+
+struct S
+{
+    int x;
+    this(int x)
+    {
+        this.x = x;
+    }
+    ~this() { }
+    this(S s) { }
+}
+
+void foo(S s, immutable(S) t)
+{
+    assert(t.x == 2);
+    s.x = 3;
+    assert(t.x == 2);
+}
+
+ref unsafeMove(T)(ref T arg) __rvalue => arg;
+
+ref id(T)(return ref T arg) => arg;
+
+void main()
+{
+    auto s = S(2);
+    foo(__rvalue(s), __rvalue(s));
+    auto t = S(2);
+    foo(unsafeMove(t), unsafeMove(t));
+    auto u = S(2);
+    foo(__rvalue(id(u)), __rvalue(id(u)));
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21660.d b/gcc/testsuite/gdc.test/fail_compilation/fail21660.d
new file mode 100644 (file)
index 0000000..50dfa8e
--- /dev/null
@@ -0,0 +1,57 @@
+// REQUIRED_ARGS: -preview=bitfields
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21660.d(29): Error: overlapping initialization for field `b` and `c`
+fail_compilation/fail21660.d(29):        `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
+fail_compilation/fail21660.d(30): Error: overlapping initialization for field `b` and `c`
+fail_compilation/fail21660.d(30):        `struct` initializers that contain anonymous unions must initialize only the first member of a `union`. All subsequent non-overlapping fields are default initialized
+fail_compilation/fail21660.d(52): Error: overlapping initialization for field `a` and `b`
+fail_compilation/fail21660.d(52): Error: overlapping initialization for field `a` and `d`
+fail_compilation/fail21660.d(52): Error: overlapping initialization for field `c` and `d`
+fail_compilation/fail21660.d(54): Error: overlapping initialization for field `a` and `d`
+---
+*/
+
+struct S
+{
+    uint a : 1;
+    union {
+        uint b : 2;
+        struct {
+            uint c : 3;
+        }
+    }
+}
+
+void testS()
+{
+    S s = S(1, 2, 3);       // b + c overlap
+    S t = S(a:1, b:2, c:3); // b + c overlap
+    S u = S(a:1, c:3);      // ok
+}
+
+union U
+{
+    union {
+        uint a : 5;
+        uint b : 4;
+    }
+    struct {
+        uint : 5;
+        uint c : 11;
+    }
+    struct {
+        uint : 4;
+        uint d : 12;
+    }
+}
+
+void testU()
+{
+    U s = U(1, 2, 3, 4); // a + b, a + d, c + d overlap
+    U t = U(a:1, c:3);   // ok
+    U u = U(a:1, d:2);   // a + d overlap
+    U v = U(b:1, c:3);   // ok
+    U w = U(b:1, d:2);   // ok
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21693.d b/gcc/testsuite/gdc.test/fail_compilation/fail21693.d
new file mode 100644 (file)
index 0000000..77cf20c
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/fail21693.d(15): Error: function `fail` is not callable using argument types `(uint)`
+fail_compilation/fail21693.d(15):        cannot pass rvalue argument `__rvalue(s.x)` of type `uint` to parameter `ref uint`
+fail_compilation/fail21693.d(11):        `fail21693.fail(ref uint)` declared here
+fail_compilation/fail21693.d(16): Error: rvalue `__rvalue(s.x)` cannot be assigned to `ref x`
+---
+*/
+struct S { uint x; }
+void fail(ref uint);
+
+void test21693(S s)
+{
+    fail(__rvalue(s.x));
+    ref x = __rvalue(s.x);
+}
index 56ccac64fae2a551ebc8b51c5c5233e4b77ccfea..8057e1ccb15292e7bbec104abc4bd860bfe78dec 100644 (file)
@@ -1,7 +1,7 @@
 // EXTRA_FILES: imports/imp22749.c
 /* TEST_OUTPUT:
 ---
-fail_compilation/fail22749.d(12): Error: cannot take address of bit-field `field`
+fail_compilation/fail22749.d(12): Error: cannot take address of bitfield `s.field`
 ---
 */
 import imports.imp22749;
index 00c66a6574f411d3c1b458d96534c3c3a9f08a4a..0fd1ff2f4eef851cdde05c83be1cd98cf1ac61ed 100644 (file)
@@ -1,8 +1,9 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
-fail_compilation/fail6497.d(12): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(13): Error: taking the address of stack-allocated local variable `n` is not allowed in a `@safe` function
+fail_compilation/fail6497.d(19): Error: taking the address of local variable `i` is not allowed in a `@safe` function
 ---
 */
 
@@ -11,3 +12,9 @@ void main() @safe
     int n;
     auto b = &(0 ? n : n);
 }
+
+void f() @safe
+{
+    ref i = *new int;
+    auto b = &i;
+}
index dbdd5d5666b5eba1bb22f0f167c0adcb0ae7d77e..e566aff570a9f198009e015a3ac0f4463649230b 100644 (file)
@@ -7,10 +7,10 @@ fail_compilation/fail6795.d(20): Error: cannot modify expression `[0:0][0]` beca
 fail_compilation/fail6795.d(22): Error: cannot modify expression `[0][0]` because it is not an lvalue
 fail_compilation/fail6795.d(23): Error: cannot modify expression `[0:0][0]` because it is not an lvalue
 fail_compilation/fail6795.d(25): Error: cannot take address of expression `[0][0]` because it is not an lvalue
-fail_compilation/fail6795.d(26): Error: cannot take address of expression `[0:0][0]` because it is not an lvalue
 fail_compilation/fail6795.d(30): Error: cannot modify expression `Some["zz"]` because it is not an lvalue
 ---
 */
+
 void test_wrong_line_num()
 {
     enum int[1] sa = [0];
@@ -23,7 +23,7 @@ void test_wrong_line_num()
     aa[0] /= 3;
 
     auto ps = &sa[0];
-    auto pa = &aa[0];
+    auto pa = &aa[0]; // ok with AA lowering, just as `pa = 0 in aa`
 
     // https://issues.dlang.org/show_bug.cgi?id=24845
     enum Maps : int[string] { Some = ["aa" : 12], Other = ["bb" : 24] }
index 8fa75cbf6f1a9fe7c15fb4a13d617a5607c966db..175e582592073c261559c0e47cd01b33c75de628 100644 (file)
@@ -7,7 +7,7 @@ fail_compilation/fix22108.d(12): Error: returning scope variable `p` is not allo
 
 // https://issues.dlang.org/show_bug.cgi?id=22108
 
-@safe ref int test(ref scope return int* p)
+@safe ref int test(return ref scope int* p)
 {
     return *p;
 }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/foreach_seq.d b/gcc/testsuite/gdc.test/fail_compilation/foreach_seq.d
new file mode 100644 (file)
index 0000000..9291e20
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/foreach_seq.d(25): Error: only one (element) or two (index, element) arguments allowed for sequence `foreach`, not 3
+fail_compilation/foreach_seq.d(26): Error: invalid storage class `ref` for index `i`
+fail_compilation/foreach_seq.d(27): Error: foreach: index cannot be of non-integral type `void`
+fail_compilation/foreach_seq.d(28): Error: index type `bool` cannot cover index range 0..3
+fail_compilation/foreach_seq.d(29): Error: `foreach` loop variable cannot be both `enum` and `alias`
+fail_compilation/foreach_seq.d(30): Error: constant value `1` cannot be `ref`
+fail_compilation/foreach_seq.d(31): Error: invalid storage class `enum` for index `i`
+fail_compilation/foreach_seq.d(33): Error: invalid storage class `ref` for element `e`
+fail_compilation/foreach_seq.d(34): Error: symbol `object` cannot be `ref`
+fail_compilation/foreach_seq.d(35): Error: cannot specify element type for symbol `e`
+fail_compilation/foreach_seq.d(36): Error: cannot specify element type for symbol `object`
+fail_compilation/foreach_seq.d(37): Error: invalid storage class `enum` for element `e`
+---
+*/
+
+// test semantic errors on foreach parameters
+void main()
+{
+    alias AliasSeq(A...) = A;
+    alias seq = AliasSeq!(1, 2, 3);
+
+    foreach (a, b, c; seq) {}
+    foreach (ref i, e; seq) {}
+    foreach (void i, e; seq) {}
+    foreach (bool i, e; seq) {}
+    foreach (enum alias e; seq) {}
+    foreach (ref enum e; seq) {}
+    foreach (ref enum i, e; seq) {}
+
+    foreach (ref e; AliasSeq!int) {}
+    foreach (ref e; AliasSeq!object) {}
+    foreach (int e; AliasSeq!int) {}
+    foreach (int e; AliasSeq!object) {}
+    foreach (enum e; AliasSeq!int) {}
+}
index 16eaa99b3d813515335643936871366f1cb5b013..c1e6cffc9e8f282f1d51bd2d826eeb9e9b67fbd1 100644 (file)
@@ -2,7 +2,7 @@
 TEST_OUTPUT:
 ---
 fail_compilation/ice10938.d(14): Error: no property `opts` for `this` of type `ice10938.C`
-fail_compilation/ice10938.d(19): Error: forward reference to inferred return type of function call `this.opDispatch()`
+fail_compilation/ice10938.d(19): Error: can't infer return type in function `opDispatch`
 fail_compilation/ice10938.d(14): Error: template instance `ice10938.C.opDispatch!"opts"` error instantiating
 ---
 */
index aa44709124a31d3fbb86b42572e1a836617a95fd..4d1673fba9fa734c0502198a86fa081889a4a1c9 100644 (file)
@@ -1,7 +1,7 @@
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/ice13644.d(22): Error: foreach: key cannot be of non-integral type `string`
+fail_compilation/ice13644.d(22): Error: foreach: index cannot be of non-integral type `string`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/issue21630.d b/gcc/testsuite/gdc.test/fail_compilation/issue21630.d
new file mode 100644 (file)
index 0000000..832ce3b
--- /dev/null
@@ -0,0 +1,18 @@
+/+
+TEST_OUTPUT:
+---
+fail_compilation/issue21630.d(14): Error: cannot declare `enum` loop variables for non-unrolled foreach
+fail_compilation/issue21630.d(15): Error: cannot declare `alias` loop variables for non-unrolled foreach
+fail_compilation/issue21630.d(16): Error: cannot declare `enum` loop variables for non-unrolled foreach
+fail_compilation/issue21630.d(17): Error: cannot declare `alias` loop variables for non-unrolled foreach
+---
++/
+
+void main()
+{
+    enum a = [1, 2, 3];
+    foreach(enum i; a) { } // error
+    foreach(alias i; a) { } // error
+    foreach(enum i; 0 .. 3) {  } // error
+    foreach(alias i; 0 .. 3) {  } // error
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/previewin3.d b/gcc/testsuite/gdc.test/fail_compilation/previewin3.d
new file mode 100644 (file)
index 0000000..cd1ac73
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+REQUIRED_ARGS: -preview=in -preview=dip1000
+TEST_OUTPUT:
+----
+fail_compilation/previewin3.d(2): Error: function `foo` is not callable using argument types `(int)`
+fail_compilation/previewin3.d(2):        cannot pass argument `42` of type `int` to parameter `in WithDtor`
+fail_compilation/previewin3.d(8):        `previewin3.foo(in WithDtor)` declared here
+----
+ */
+
+#line 1
+void rvalueErrorMsg () {
+    foo(42);
+}
+
+// Add a dtor to ensure things are passed by ref
+struct WithDtor { ~this() @safe pure nothrow @nogc {} }
+
+void foo(in WithDtor) {}
index 4d14993ef5a4e4d21f9d13c4a8117603cbbed357..6b3ac69a236840e01e5728adf123b318361844a5 100644 (file)
@@ -18,11 +18,11 @@ fail_compilation/pull12941.d(105):        `pull12941.abc(return ref int* p)` dec
 
 #line 100
 
-int* foo(ref scope return int* p);
-int* foo(out scope return int* p);
+int* foo(return ref scope int* p);
+int* foo(return out scope int* p);
 
-int* bar(scope return int* p);
-int* abc(ref return int* p);
+int* bar(return scope int* p);
+int* abc(return ref int* p);
 
 void test()
 {
index f663e4f4fb06c77ccdf83928d5d1c689d179e2fc..18c7fe64cc9a1c6dcbe7152e827e5f84e83c10d4 100644 (file)
@@ -130,7 +130,7 @@ fail_compilation/retscope2.d(721): Error: returning `s.get1()` escapes a referen
 
 struct S700
 {
-    @safe S700* get1() scope return
+    @safe S700* get1() return ref scope
     {
         return &this;
     }
index 7ab1d7cd17379124b131259f12aca2d7d8e1d2bc..075d96370a9b09262fefadf474ebcd266f3f538a 100644 (file)
@@ -153,13 +153,13 @@ shared(int)* test_dotvarexp_3(return shared C1* c)
 }
 
 // First level DotVarExp dereferencing
-ref shared(int) test_dotvarexp_4(return shared ref C2 c)
+ref shared(int) test_dotvarexp_4(return ref shared C2 c)
 {
     return c.c1.value;
 }
 
 // Second level DotVarExp dereferencing
-ref shared(int) test_dotvarexp_5(return shared ref C3 c)
+ref shared(int) test_dotvarexp_5(return ref shared C3 c)
 {
     return c.c1.c1.value;
 }
@@ -201,7 +201,7 @@ ref shared(K1) test_newexp_1()
 // Inference tests
 
 // Fails because no `ref`
-auto test_inference_1(return shared ref C3 c)
+auto test_inference_1(return ref shared C3 c)
 {
     return c;
 }
@@ -221,7 +221,7 @@ auto ref test_inference_3()
 }
 
 // Fails because `const` conversion
-auto ref Object test_inference_4(const return shared ref Object a)
+auto ref Object test_inference_4(const return ref shared Object a)
 {
     return a;
 }
index dcd6f4dd2329e22044c470d01e53606438d9d798..a696945099dc93da57e725ade813584ce21ba8a3 100644 (file)
@@ -25,8 +25,8 @@ int* bar(return ref int s)
     return &foo(s);
 }
 
-ref int identity(ref return int x) {return x;}
-ref int* identityPtr(ref return int* x) {return x;}
+ref int identity(return ref int x) {return x;}
+ref int* identityPtr(return ref int* x) {return x;}
 
 int* addrOfRefEscape()
 {
index ce9e5bb188540858a90608020d6f7ba55b2e420e..70e50251a9d74044e9ccd48a2e9dcc8dcc6ee36b 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * TEST_OUTPUT:
 ---
-fail_compilation/test15704.d(17): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function
-fail_compilation/test15704.d(18): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function
-fail_compilation/test15704.d(19): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release
+fail_compilation/test15704.d(19): Error: copying `void[]` to `void[]` is not allowed in a `@safe` function
+fail_compilation/test15704.d(20): Error: copying `const(void)[]` to `void[]` is not allowed in a `@safe` function
+fail_compilation/test15704.d(21): Deprecation: copying `int[]` to `void[]` will become `@system` in a future release
+fail_compilation/test15704.d(22): Error: cannot implicitly convert expression `cast(byte)0` of type `byte` to `void[]`
+fail_compilation/test15704.d(22): Error: cannot copy `byte` to `void[]`
 ---
  */
 
@@ -17,4 +19,5 @@ void main() @safe {
     arr1[] = arr2[];  // overwrites pointers with arbitrary ints
     arr1[] = new const(void)[3];
     arr1[] = [5];
+    arr1[] = byte(); // filling not allowed
 }
index b5db07fd9385e57b515337d5f2a0a6d46b90afb7..f06b1bfc601a1f568a8ff27e709bf66dc073f6be 100644 (file)
@@ -15,7 +15,7 @@ fail_compilation/test18282.d(53): Error: escaping a reference to local variable
 
 string* f() @safe
 {
-    scope string*[] ls;
+    scope string*[] ls = [null];
     return ls[0];
 }
 
index add4d738ce0237e41f4999b0321806047a9df979..0f3ac298537afbd984180618ff4cb0deefae84fd 100644 (file)
@@ -17,7 +17,7 @@ struct S
 
     auto borrowA() return /*scope inferred*/ { return ptr; }
     int* borrowB() return { return ptr; }
-    int* borrowC() scope return { return ptr; }
+    int* borrowC() scope return ref { return ptr; }
 }
 
 void main()
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21408.d b/gcc/testsuite/gdc.test/fail_compilation/test21408.d
new file mode 100644 (file)
index 0000000..43fd2bf
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21408.d(12): Error: can't infer return type in function `opEquals`
+fail_compilation/test21408.d(19): Error: can't infer return type in function `opEquals`
+---
+*/
+
+struct A {
+    A[] as;
+    auto opEquals(A x) {
+        return as == x.as;
+    }
+}
+
+struct B {
+    B[] as;
+    auto opEquals(B x) {
+        return x == this;
+    }
+}
+
+void main() {}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21429.d b/gcc/testsuite/gdc.test/fail_compilation/test21429.d
new file mode 100644 (file)
index 0000000..00ce850
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+TEST_OUTPUT:
+---
+fail_compilation/test21429.d(20): Error: no property `z` for `s` of type `test21429.S`
+fail_compilation/test21429.d(13):        struct `S` defined here
+---
+*/
+//make sure this fails properly after fixing bug 21429.d
+template mixinOpDispatch(string id){
+    string opDispatch(string s)() if(s == id){ return id; }
+}
+
+struct S {
+    mixin mixinOpDispatch!"x";
+    mixin mixinOpDispatch!"y";
+}
+
+void main(){
+    S s;
+    auto fail = s.z;
+}
index c9c7c7fcd507603d7754d8f35bd20907a1d61658..b523cccb6796c2a4306a9cd9c459e4e03f9c0bc5 100644 (file)
@@ -1,7 +1,7 @@
 /* REQUIRED_ARGS: -betterC
 TEST_OUTPUT:
 ---
-fail_compilation/test21477.d(103): Error: expression `[1]` uses the GC and cannot be used with switch `-betterC`
+fail_compilation/test21477.d(103): Error: this array literal requires the GC and cannot be used with `-betterC`
 ---
 */
 
index 22565e4a8a2e378a17e147f1d1f86e2eef7fc05c..7a831ebaaebed5756dd3b71d3dea9abb4d14daa5 100644 (file)
@@ -3,10 +3,10 @@
 fail_compilation/test21546.d(113): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `int* delegate() return`
 fail_compilation/test21546.d(114): Error: cannot implicitly convert expression `pc` of type `const(int)* delegate() return` to `immutable(int)* delegate() return`
 fail_compilation/test21546.d(115): Error: cannot implicitly convert expression `pi` of type `immutable(int)* delegate() return` to `int* delegate() return`
-fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `int delegate() ref return`
-fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() ref return` to `immutable(int) delegate() ref return`
-fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() ref return` to `int delegate() ref return`
-fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() ref return[]` to `int delegate() ref return[]`
+fail_compilation/test21546.d(213): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() return ref` to `int delegate() return ref`
+fail_compilation/test21546.d(214): Error: cannot implicitly convert expression `dc` of type `const(int) delegate() return ref` to `immutable(int) delegate() return ref`
+fail_compilation/test21546.d(215): Error: cannot implicitly convert expression `di` of type `immutable(int) delegate() return ref` to `int delegate() return ref`
+fail_compilation/test21546.d(305): Error: cannot implicitly convert expression `[dgi]` of type `immutable(int) delegate() return ref[]` to `int delegate() return ref[]`
 ---
  */
 // https://issues.dlang.org/show_bug.cgi?id=21546
index e834b78f7d57f3a2f4222ad7b3ccbafeae91d79c..e7b6ea7d701fc5ec17502bc8e87751a0a57a0717 100644 (file)
@@ -1,7 +1,7 @@
 /* REQUIRED_ARGS: -betterC
 TEST_OUTPUT:
 ---
-fail_compilation/test23710.d(111): Error: array concatenation of expression `foo ~ [1, 2, 3]` requires the GC which is not available with -betterC
+fail_compilation/test23710.d(112): Error: array concatenation of expression `foo ~ cast(int[])a` requires the GC which is not available with -betterC
 ---
  */
 // https://issues.dlang.org/show_bug.cgi?id=23710
@@ -12,13 +12,14 @@ int test(int i)
 {
     int j;
     int[] foo;
+    int[3] a = [1,2,3];
     if (0)
     {
         for (;;)
         {
             import core.stdc.stdio;
             printf("start body\n");
-            foo = foo ~ [1,2,3];
+            foo = foo ~ a;
 L1:
             printf("foo.length = %zu\n", foo.length);
            j += foo.length;
index 2345a66764e18d7abaad6a46d2f6d699d3bf68c0..5f34c0c10ce3ec0fa2086625475b8a7059f899f0 100644 (file)
@@ -7,6 +7,6 @@ fail_compilation/varargsstc.d(103): Error: variadic parameter cannot have attrib
 
 #line 100
 
-int printf(const(char)*, const scope shared return ...);
-int printf(const(char)*, ref out scope immutable shared return ...);
-int printf(const(char)*, ref scope immutable shared return ...);
+int printf(const(char)*, const return scope shared ...);
+int printf(const(char)*, return ref out scope immutable shared ...);
+int printf(const(char)*, return ref scope immutable shared ...);
index d7b8f6646c3b393a40c0177aedcdbd4d3968d48c..a05641ebd51c0e01c7ab3cce90f03a4a5bef79f6 100644 (file)
@@ -8,12 +8,11 @@ EXTRA_SOURCES: extra-files/minimal/object.d
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/verifyhookexist.d(22): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(28): Error: `object.__equals` not found. The current runtime does not support equal checks on arrays, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(29): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(33): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(36): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt.
-fail_compilation/verifyhookexist.d(41): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(21): Error: `object.__ArrayCast` not found. The current runtime does not support casting array of structs, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(28): Error: `object.__cmp` not found. The current runtime does not support comparing arrays, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(32): Error: `object._d_assert_fail` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(35): Error: `object.__switch` not found. The current runtime does not support switch cases on strings, or the runtime is corrupt.
+fail_compilation/verifyhookexist.d(40): Error: `object.__switch_error` not found. The current runtime does not support generating assert messages, or the runtime is corrupt.
 ---
 */
 
index 289e1bcfbd6a1c5b10d2bce0855a509d1febecae..73fe52eb4c2843696f4d7ba9cfad1b5dc941ff4f 100644 (file)
@@ -95,6 +95,21 @@ void test3()
     assert(__traits(getMember, u, "b") == 2);
 }
 
+/********************************************/
+// https://github.com/dlang/dmd/issues/18247
+
+struct S4
+{
+    ulong a:64;
+}
+
+void test4()
+{
+    S4 s;
+    s.a = 1;
+    assert(s.a == 1);
+}
+
 /********************************************/
 
 int main()
@@ -102,5 +117,6 @@ int main()
     test1();
     test2();
     test3();
+    test4();
     return 0;
 }
index e922b5926a4119968138b6ecef457e94e50defb7..d605294ae3f33815a14554762d8e9507119ef63f 100644 (file)
@@ -17,6 +17,6 @@ auto otherTest(inout ref Base block) @nogc { assert(0); }
 auto otherTest(inout ref A block) @nogc {}
 
 void main() {
-    B* thingie;
+    B* thingie = new B;
     otherTest(*thingie);
 }
index 7857eaf544af56eacf5c0c19b05d87129c2dd857..4aa7a968ae0a68dab74d7dbcc21f1bd8164a8cbb 100644 (file)
@@ -418,7 +418,7 @@ class C8939regression
         string[2] str;
         refValue(str[n1]);
 
-        int[] da;
+        int[] da = [1];
         refValue(da[n2]);
 
         int n; int* p = &n;
index 59b88ec88cb696521c4196716888adf8c6bf66a1..b0befcceb62d2e41cec57248c337325b46014ff5 100644 (file)
@@ -1,5 +1,6 @@
 /*
 EXTRA_FILES: imports/test15777a.d imports/test15777b.d
+REQUIRED_ARGS: -verrors=simple
 TEST_OUTPUT:
 ---
 int
@@ -10,6 +11,7 @@ int
 foobar7406(T)
 int
 test7406()
+runnable/foreach5.d(1200): Deprecation: foreach: loop index implicitly converted from `size_t` to `short`
 ---
 */
 
@@ -1046,6 +1048,21 @@ void test13756()
     {
         static assert(is(typeof(k) == const int));
     }
+
+    // https://github.com/dlang/dmd/issues/21456
+    foreach (long k, long v; aa)
+    {
+        assert(k == 1 && v == 20);
+    }
+    static struct S
+    {
+        int x, y, z;
+        this(int a) { z = a; }
+    }
+    foreach (long k, S s; aa)
+    {
+        assert(k == 1 && s.z == 20);
+    }
 }
 
 /***************************************/
@@ -1175,6 +1192,22 @@ void test17041()
     assert(foo17041(cast(int[2]) [10, 20]) == [31, 41]); // test rvalue
 }
 
+/***************************************/
+// https://github.com/ldc-developers/ldc/issues/326
+
+void testLDC326()
+{
+    foreach (short i, dchar c; "ab")
+    {
+        if (c == 'a')
+            assert(i == 0);
+        else if (c == 'b')
+            assert(i == 1);
+        else
+            assert(0);
+    }
+}
+
 /***************************************/
 
 int main()
@@ -1207,6 +1240,7 @@ int main()
     test13756();
     test14653();
     test17041();
+    testLDC326();
 
     printf("Success\n");
     return 0;
index 1c2ae55b79c4ed6a4941cd65f4e1608828b1cf87..04731ac90857b2ecb537e1960062f1cbb5c30118 100644 (file)
@@ -2549,6 +2549,21 @@ void test9()
 
 ////////////////////////////////////////////////////////////////////////
 
+void test10()
+{
+    double a,b,c;
+    double* pa,pb;
+
+    a = 5;
+    b = a++;
+    assert(a == 6);
+    pa = &a;
+    pb = &b;
+    *pb = (*pa)++;
+}
+
+////////////////////////////////////////////////////////////////////////
+
 int main()
 {
     // All the various integer divide tests
@@ -2651,6 +2666,7 @@ int main()
     test20574();
     test8();
     test9();
+    test10();
 
     printf("Success\n");
     return 0;
index 834a1f76bcbf3b9edf00369ea464a82f59d0f30b..6c8bc56535292fc22429d1886360ad59d3dbeaac 100644 (file)
@@ -132,6 +132,31 @@ void test8()
     assert(s.m.a == 3);
 }
 
+/*************************************************/
+// https://github.com/dlang/dmd/issues/21549
+
+struct Test9
+{
+    size_t a, b = 3;
+    this(int x)
+       {
+               a = x;
+       }
+}
+
+void new9(ref Test9 p)
+{
+    new(p) Test9(1);
+}
+
+void test9()
+{
+       Test9 t9 = void;
+       new9(t9);
+       assert(t9.a == 1);
+       // assert(t9.b == 3);  // Test9.init not copied yet
+}
+
 /*************************************************/
 
 int main()
@@ -144,6 +169,7 @@ int main()
     test6();
     test7();
     test8();
+    test9();
 
     return 0;
 }
index 507c1143feca93569901af011ebbdfa6c3761303..e5727006d3ba76212c8bbe872f10cd4630315690 100644 (file)
@@ -4848,6 +4848,22 @@ void test24010()
     assert(x24010 == 71);
 }
 
+/**********************************/
+extern (C) struct S21303
+{
+    static bool dtorCalled = false;
+
+    ~this() { dtorCalled = true; }
+}
+
+void test21303()
+{
+    {
+        S21303 s;
+    }
+    assert(S21303.dtorCalled);
+}
+
 /**********************************/
 
 int main()
@@ -4990,6 +5006,7 @@ int main()
     test68();
     testPR12012();
     test24010();
+    test21303();
 
     printf("Success\n");
     return 0;
index 6b8fb458075affac7042d6fd947528c9cb0a2844..9311a2796432235483d0411c898d38820247b915 100644 (file)
@@ -206,6 +206,18 @@ void testClassLiteral()
 
 /////////////////////////////////////////////
 
+// https://github.com/dlang/dmd/issues/21690
+void testMultiDim()
+{
+    // int[int][int] aa1 = [ 1: [2: 3] ]; // Error: invalid value `[2:3]` in initializer (see #17804)
+    int[int][int] aa2 = ([ 1: [2: 3] ]); // workaround
+    auto aa3 = [ 1: [2: 3] ]; // works, too
+    static auto aa4 = [ 1: [2: 3] ]; // Error: internal compiler error: failed to detect static initialization of associative array
+    assert(aa2 == aa3);
+    assert(aa3 == aa4);
+}
+
+/////////////////////////////////////////////
 
 void main()
 {
@@ -219,4 +231,5 @@ void main()
     testEnumInit();
     testStaticArray();
     testClassLiteral();
+    testMultiDim();
 }
diff --git a/gcc/testsuite/gdc.test/runnable/test17487.d b/gcc/testsuite/gdc.test/runnable/test17487.d
new file mode 100644 (file)
index 0000000..e0428e2
--- /dev/null
@@ -0,0 +1,179 @@
+// https://github.com/dlang/dmd/issues/17487
+import core.stdc.stdio;
+
+// version = PRINT;
+
+void testKey()
+{
+    version(PRINT) printf("--- testKey\n");
+    auto count = new Count;
+
+    string[S] aa;
+    aa[S(count, 42)] = "foo";
+
+    version(PRINT) printf("--- after init\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 1);
+
+    version(PRINT) printf("---\n");
+    assert(S(count, 42) in aa);
+
+    version(PRINT) printf("--- after in\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 1);
+
+    version(PRINT) printf("---\n");
+    assert(S(new Count, 22) !in aa);
+    version(PRINT) printf("--- after !in\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 1);
+
+    version(PRINT) printf("---\n");
+    aa[S(count, 42)] = "foo2";
+
+    version(PRINT) printf("--- after assign value\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 1);
+    assert(count.copyConstructed == 1); // copy ctor used when adding key, not during lookup
+}
+
+void testValue()
+{
+    version(PRINT) printf("--- testValue\n");
+    auto count = new Count;
+
+    S[int] aa;
+    aa[42] = S(count, 42);
+
+    version(PRINT) printf("--- after init\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 1);
+    assert(count.constructed == 1);
+
+    version(PRINT) printf("---\n");
+    aa[22] = S(count, 22);
+
+    version(PRINT) printf("--- after create by value\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 2);
+    assert(count.constructed == 2);
+
+    version(PRINT) printf("---\n");
+    aa[2] = aa[22];
+
+    version(PRINT) printf("--- after create by copy ctor\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 3);
+    assert(count.copyConstructed == 1);
+
+    version(PRINT) printf("---\n");
+    aa[22] = aa[42];
+
+    version(PRINT) printf("--- after assign by copy\n");
+    version(PRINT) count.print();
+    assert(count.refCount == 3);
+    assert(count.assignedFrom == 1);
+    assert(count.assignedTo == 1);
+
+    version(PRINT) printf("---\n");
+}
+
+void main()
+{
+    testKey();
+    testValue();
+    version(PRINT) printf("--- cleanup\n");
+}
+
+struct Count
+{
+    int constructed;
+    int copyConstructed;
+    int assignedFrom;
+    int assignedTo;
+    int destroyed;
+    int refCount;
+
+    void print()
+    {
+        printf("Constructed: %d times\n", constructed);
+        printf("Copy Constructed: %d times\n", copyConstructed);
+        printf("Assigned from: %d times\n", assignedFrom);
+        printf("Assigned To: %d times\n", assignedTo);
+        printf("Destroyed: %d times\n", destroyed);
+        printf("refCount: %d\n", refCount);
+    }
+}
+
+struct S
+{
+    Count* count;
+    int i;
+    bool destroyed;
+
+    this(Count* count, int i)
+    {
+        this.count = count;
+        this.i = i;
+        ++count.constructed;
+        ++count.refCount;
+        version(PRINT) printf("Constructing: %d, count(%p)\n", i, count);
+    }
+
+    this(ref S rhs)
+    {
+        this.count = rhs.count;
+        this.i = rhs.i;
+
+        ++count.copyConstructed;
+        ++count.refCount;
+        version(PRINT) printf("Copy Constructing: %d, count(%p)\n", i, count);
+    }
+
+    ~this()
+    {
+        version(PRINT) printf("Destroying: %d, count(%p)\n", i, count);
+
+        if(count)
+        {
+            ++count.destroyed;
+            --count.refCount;
+        }
+
+        destroyed = true;
+    }
+
+    void opAssign()(auto ref S rhs)
+    {
+        version(PRINT) printf("Assigning %d, count(%p) to %d, count(%p)\n",
+            rhs.i, rhs.count, this.i, this.count);
+
+        if(this.count)
+        {
+            ++this.count.assignedTo;
+            --this.count.refCount;
+        }
+
+        if(rhs.count)
+        {
+            ++rhs.count.assignedFrom;
+            ++rhs.count.refCount;
+        }
+
+        this.count = rhs.count;
+        this.i = rhs.i;
+    }
+
+    invariant
+    {
+        assert(!destroyed);
+    }
+
+    bool opEquals()(const auto ref S rhs) const {
+        return this.i == rhs.i;
+    }
+
+    size_t toHash() const {
+        return 0;
+    }
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test20473.d b/gcc/testsuite/gdc.test/runnable/test20473.d
new file mode 100644 (file)
index 0000000..faba795
--- /dev/null
@@ -0,0 +1,110 @@
+// https://github.com/dlang/dmd/issues/20473
+// REQUIRED_ARGS: -preview=bitfields
+
+struct S1
+{
+    char[] slice;
+    bool flag:1;
+}
+
+void test1()
+{
+    auto a = S1(['1','2'], false);
+    auto b = S1(['1','2'], true);
+    auto c = S1(['1','2'], true);
+    assert(a != b);
+    assert(b == c);
+    assert(typeid(S1).getHash(&a) != typeid(S1).getHash(&b));
+    assert(typeid(S1).getHash(&b) == typeid(S1).getHash(&c));
+}
+
+struct S2
+{
+    bool flag:1;
+    Object o;
+}
+
+void test2()
+{
+    auto o = new Object;
+    auto a = S2(false, o);
+    auto b = S2(true, o);
+    auto c = S2(true, o);
+    assert(a != b);
+    assert(b == c);
+    assert(typeid(S2).getHash(&a) != typeid(S2).getHash(&b));
+    assert(typeid(S2).getHash(&b) == typeid(S2).getHash(&c));
+}
+
+struct Wrapper3
+{
+    void[] wrapped;
+}
+struct S3
+{
+    bool flag:1;
+    Wrapper3 wrapper;
+}
+
+void test3()
+{
+    auto a = S3(false, Wrapper3([1,2,3]));
+    auto b = S3(true, Wrapper3([1,2,3]));
+    auto c = S3(true, Wrapper3([1,2,3]));
+    assert(a != b);
+    assert(b == c);
+    assert(typeid(S3).getHash(&a) != typeid(S3).getHash(&b));
+    assert(typeid(S3).getHash(&b) == typeid(S3).getHash(&c));
+}
+
+struct Wrapper4
+{
+    Object wrapped;
+}
+struct S4
+{
+    bool flag:1;
+    Wrapper4 wrapper;
+}
+
+void test4()
+{
+    auto o = new Object;
+    auto a = S4(false, Wrapper4(o));
+    auto b = S4(true, Wrapper4(o));
+    auto c = S4(true, Wrapper4(o));
+    assert(a != b);
+    assert(b == c);
+    assert(typeid(S4).getHash(&a) != typeid(S4).getHash(&b));
+    assert(typeid(S4).getHash(&b) == typeid(S4).getHash(&c));
+}
+
+enum Wrapper5 : string
+{
+    empty = ""
+}
+struct S5
+{
+    bool flag:1;
+    Wrapper5 wrapper;
+}
+
+void test5()
+{
+    auto a = S5(false, Wrapper5.empty);
+    auto b = S5(true, Wrapper5.empty);
+    auto c = S5(true, Wrapper5.empty);
+    assert(a != b);
+    assert(b == c);
+    assert(typeid(S5).getHash(&a) != typeid(S5).getHash(&b));
+    assert(typeid(S5).getHash(&b) == typeid(S5).getHash(&c));
+}
+
+void main()
+{
+    test1();
+    test2();
+    test3();
+    test4();
+    test5();
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21429.d b/gcc/testsuite/gdc.test/runnable/test21429.d
new file mode 100644 (file)
index 0000000..c1d5811
--- /dev/null
@@ -0,0 +1,131 @@
+mixin template OD(string s)
+{
+
+    string opDispatch(string name)() if (name == s)
+    {
+        return name;
+    }
+}
+
+mixin template ODA(string s)
+{
+    void opDispatch(string name)(int x) if (name == s)
+    {
+        this.val = x;
+    }
+}
+
+struct T
+{
+    mixin OD!"x";
+    mixin OD!"y";
+}
+
+struct TAssign
+{
+    int val;
+    mixin ODA!"x";
+    mixin ODA!"y";
+}
+
+struct U
+{
+    mixin OD!"z";
+}
+
+
+template adder()
+{
+    int opBinary(string s : "+")(int x) { return x; }
+}
+
+template subtracter()
+{
+    int opBinary(string s : "-")(int x) { return -x; }
+}
+
+
+struct Arithmetic
+{
+    mixin adder;
+    mixin subtracter;
+
+}
+
+template adderRight()
+{
+    int opBinaryRight(string s : "+")(int x){ return x; }
+}
+
+
+template subtracterRight()
+{
+    int opBinaryRight(string s: "-")(int x){ return -x; }
+}
+
+struct ArithmeticRight
+{
+    mixin adderRight;
+    mixin subtracterRight;
+}
+
+template mixinOpAssign(string op)
+{
+    void opOpAssign(string s)(int x) if(s == op)
+    {
+        val = x;
+        lastOp = s;
+    }
+}
+
+struct AssignOverloads
+{
+    int val;
+    string lastOp;
+    mixin mixinOpAssign!"+";
+    mixin mixinOpAssign!"-";
+}
+
+
+void main()
+{
+
+    T t;
+    string s = t.x();
+    assert(s == "x");
+    assert(t.y == "y");
+
+    //explicit call should work
+    assert(t.opDispatch!"x" == "x");
+
+
+    //TODO: fix these
+    Arithmetic a;
+    assert((a + 5) == 5);
+    assert((a - 7) == -7);
+
+
+    ArithmeticRight ar;
+    assert((5 + ar) == 5);
+    assert((7 - ar) == -7);
+
+
+    U u;
+    //should work for a single mixin
+    assert(u.z == "z");
+
+    TAssign ta;
+    ta.x = 5;
+    assert(ta.val == 5);
+    ta.y = 10;
+    assert(ta.val == 10);
+
+    AssignOverloads oa;
+    oa += 5;
+    assert(oa.val == 5);
+    assert(oa.lastOp == "+");
+    oa -= 10;
+    assert(oa.val == 10);
+    assert(oa.lastOp == "-");
+
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21435.d b/gcc/testsuite/gdc.test/runnable/test21435.d
new file mode 100644 (file)
index 0000000..e7c2eac
--- /dev/null
@@ -0,0 +1,73 @@
+// https://github.com/dlang/dmd/issues/21435
+
+void check_align()
+{
+    pragma(inline, false);
+    int x;
+    (cast(size_t)&x & 3) == 0 || assert(false);
+}
+
+char* getenv_setargv(char* env)
+{
+    auto p = env;
+    auto q = env;
+    auto r = env;
+    auto s = env;
+    auto t = env;
+    auto u = env;
+    auto ptr = env;
+
+    while (1)
+    {
+        auto c = *ptr++;
+        switch (c)
+        {
+            case 0:
+                check_align();
+                return env;
+
+            case 'a':
+                *p++ = 'A';
+                continue;
+
+            case 'b':
+                *q++ = 'B';
+                continue;
+
+            case 'c':
+                *r++ = 'C';
+                continue;
+
+            case 'd':
+                *s++ = 'D';
+                continue;
+
+            case 'e':
+                *t++ = 'E';
+                continue;
+
+            case 'f':
+                *u++ = 'F';
+                continue;
+
+            case 'g':
+                *p++ = 'G';
+                continue;
+
+            case 'h':
+                *p++ = 'H';
+                continue;
+
+            default:
+                *p++ = c;
+                continue;
+        }
+    }
+}
+
+void main()
+{
+    char[10] m = "abcdefghi";
+    auto str = getenv_setargv(m.ptr);
+    str[0] || assert(false);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/test21660.d b/gcc/testsuite/gdc.test/runnable/test21660.d
new file mode 100644 (file)
index 0000000..9a0efd8
--- /dev/null
@@ -0,0 +1,31 @@
+// REQUIRED_ARGS: -preview=bitfields
+struct S
+{
+    int a : 3;
+    int b : 3;
+}
+
+struct T
+{
+    uint a : 1;
+    union {
+        uint b : 2;
+        struct {
+            uint : 2;
+            uint c : 3;
+        }
+    }
+}
+
+void main()
+{
+    S s = S(1, 2);
+    assert(s.a == 1 && s.b == 2);
+    S t = S(b:1, a:2);
+    assert(t.a == 2 && t.b == 1);
+
+    T u = T(1, 2, 3);
+    assert(u.a == 1 && u.b == 2 && u.c == 3);
+    T v = T(a:1, b:2, c:3);
+    assert(v.a == 1 && v.b == 2 && v.c == 3);
+}
index d89c152aa48c88dac31137665fc5d6e8aa19da4a..1ccd9c347f454c0506703089a2caf0f24ccc1896 100644 (file)
@@ -3638,10 +3638,10 @@ shared class Bug5504b
 
 void test5504()
 {
-    immutable Bug5504 c;
+    immutable Bug5504 c = new immutable Bug5504;
     c.foo(10);
     c.xx!(int).hoo(10);
-    shared Bug5504b d;
+    shared Bug5504b d = new shared Bug5504b;
     d.foo(10);
     d.xx!(int).hoo(10);
 }
index 3d199d9841251fa0b19ea36a6521c0725435e1bc..74da97d55e709027af06b5c3cdfc94349149c456 100644 (file)
@@ -310,9 +310,9 @@ void testinenum()
 // https://github.com/dlang/dmd/issues/21258
 void test21258()
 {
-       alias AliasSeq(TList...) = TList;
+    alias AliasSeq(TList...) = TList;
 
-       struct S { int x; } // use a local type to not generate required TypeInfo elsewhere
+    struct S { int x; } // use a local type to not generate required TypeInfo elsewhere
     foreach (T; AliasSeq!(S[int]))
         enum E { a = T.init, } // bug report uses bad syntax here, but this crashed, too
 }
@@ -329,6 +329,53 @@ void test21207()
 
 /************************************************/
 
+void testEvaluationOrder()
+{
+       static int last;
+       int[int] aa;
+       int[4] arr;
+
+       int seqi(int n, int i, int ln = __LINE__)
+       {
+               n += ln * 100;
+               assert(n > last);
+               last = n;
+               return i;
+       }
+       ref int[int] seqaa(int n, int ln = __LINE__)
+       {
+               n += ln * 100;
+               assert(n > last);
+               last = n;
+               return aa;
+       }
+       seqaa(1)[seqi(2, 0)] = seqi(3, 1); // aa[0] = 1
+       int x = seqaa(1)[seqi(2, 0)];  // x = aa[0]
+       seqaa(1)[seqi(2, 1)] = seqaa(3)[seqi(4, 0)]; // aa[1] = aa[0]
+       assert(seqi(1, 0) in seqaa(2)); // 0 in aa
+
+       // only executed once?
+       auto naa = seqaa(1).dup;
+       auto len = seqaa(1).length;
+       auto keys = seqaa(1).keys;
+       auto values = seqaa(1).values;
+       auto hash = hashOf(seqaa(1));
+       seqaa(1).rehash;
+       seqaa(1).clear;
+
+       version (none)
+               seqaa(1) = [seqi(2, 1) : seqi(3, 1), seqi(4, 2) : seqi(5, 4)]; // aa = [1:1, 2:4]
+       else
+               aa = [1:1, 2:4];
+
+       assert(seqaa(1).remove(seqi(2, 1))); // aa.remove(1)
+
+       assert(seqaa(1) == seqaa(2)); // aa == aa
+       assert(!(seqaa(1) != seqaa(2))); // aa != aa
+}
+
+/************************************************/
+
 int main()
 {
     testaa();
@@ -339,6 +386,7 @@ int main()
     testinout();
     testinenum();
     test21258();
+       testEvaluationOrder();
 
     printf("Success\n");
     return 0;
index 4aac1fba1a4e1953e5519b72dee42e853569909b..2aadc35afc0a6cfb5fd21dad6bd374de15fa2648 100644 (file)
@@ -314,6 +314,29 @@ int[int] testRetx()
 
 void aafunc(int[int] aa) {}
 
+void testTypeinfo()
+{
+    int[int] a = [ 1:1, 2:4, 3:9, 4:16 ];
+    int[int] b = [ 1:1, 2:4, 3:9, 4:16 ];
+    assert(a == b);
+
+    hash_t ahash1 = hashOf(a);
+    hash_t ahash2 = typeid(a).getHash(&a);
+    assert(ahash1 == ahash2);
+    hash_t bhash1 = hashOf(a);
+    hash_t bhash2 = typeid(a).getHash(&a);
+    assert(bhash1 == bhash2);
+    assert(ahash1 == bhash1);
+    assert(typeid(a).equals(&a, &b));
+
+    string[string] c = [ "1":"one", "2":"two", "3":"three" ];
+    hash_t chash1 = hashOf(c);
+    hash_t chash2 = typeid(c).getHash(&c);
+    assert(chash1 == chash2);
+    assert(chash1 != ahash2);
+    assert(typeid(c).equals(&c, &c));
+}
+
 /***************************************************/
 // https://issues.dlang.org/show_bug.cgi?id=12214
 
@@ -370,6 +393,7 @@ void main()
     assert(testRet());
     static assert(testRet());
 
+    testTypeinfo();
     test12220();
     test12403();
 }
index a1164dc4beb945cb7f11b41c5c1f75ca166b2be9..6c520e3196f8779f276182155daabf22c8000a46 100644 (file)
@@ -2,7 +2,7 @@
 /*
 TEST_OUTPUT:
 ---
-foo1 ulong function(return ref int* delegate() return p) ref return
+foo1 ulong function(return ref int* delegate() return p) return ref
 foo2 int function(return ref int delegate() p) ref
 foo3 int function(ref inout(int*) p) ref
 foo4 int function(return ref inout(int*) p) ref
@@ -18,7 +18,7 @@ struct SS
     ref ulong foo1(return ref int* delegate() return p) return;
     ref int foo2(return ref int delegate() p);
     ref int foo3(inout ref int* p);
-    ref int foo4(return inout ref int* p);
+    ref int foo4(return ref inout int* p);
 }
 
 pragma(msg, "foo1 ", typeof(&SS.foo1));
index 62d90e5770b5d58c127b0a718df0f874f6231a15..a9235246e938eb70da44bd52b8e67e746c5e2913 100644 (file)
@@ -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.
index 0e9b71bfa5d2ba5b0c50d99e6bb9832049553906..17fa2ceaa96048551409a51d7dd91beae80d5dfa 100644 (file)
@@ -184,13 +184,13 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
        core/internal/array/construction.d core/internal/array/duplication.d \
        core/internal/array/equality.d core/internal/array/operations.d \
        core/internal/array/utils.d core/internal/atomic.d \
-       core/internal/attributes.d core/internal/container/array.d \
-       core/internal/container/common.d core/internal/container/hashtab.d \
-       core/internal/container/treap.d core/internal/convert.d \
-       core/internal/dassert.d core/internal/destruction.d \
-       core/internal/entrypoint.d core/internal/gc/bits.d \
-       core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \
-       core/internal/gc/impl/conservative/gc.d \
+       core/internal/attributes.d core/internal/cast_.d \
+       core/internal/container/array.d core/internal/container/common.d \
+       core/internal/container/hashtab.d core/internal/container/treap.d \
+       core/internal/convert.d core/internal/dassert.d \
+       core/internal/destruction.d core/internal/entrypoint.d \
+       core/internal/gc/bits.d core/internal/gc/blkcache.d \
+       core/internal/gc/blockmeta.d core/internal/gc/impl/conservative/gc.d \
        core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \
        core/internal/gc/os.d core/internal/gc/pooltable.d \
        core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \
@@ -221,11 +221,10 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
        gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \
        gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
        gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
-       rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
-       rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
-       rt/invariant_.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
-       rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
-       rt/util/utility.d
+       rt/aApply.d rt/aApplyR.d rt/arraycat.d rt/config.d rt/critical_.d \
+       rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant_.d rt/lifetime.d \
+       rt/memory.d rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d \
+       rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d
 
 DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
        core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
@@ -324,15 +323,15 @@ DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \
 
 DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
        core/sys/posix/arpa/inet.d core/sys/posix/config.d \
-       core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/fcntl.d \
-       core/sys/posix/grp.d core/sys/posix/iconv.d core/sys/posix/inttypes.d \
-       core/sys/posix/libgen.d core/sys/posix/locale.d \
-       core/sys/posix/mqueue.d core/sys/posix/net/if_.d \
-       core/sys/posix/netdb.d core/sys/posix/netinet/in_.d \
-       core/sys/posix/netinet/tcp.d core/sys/posix/poll.d \
-       core/sys/posix/pthread.d core/sys/posix/pwd.d core/sys/posix/sched.d \
-       core/sys/posix/semaphore.d core/sys/posix/setjmp.d \
-       core/sys/posix/signal.d core/sys/posix/spawn.d \
+       core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/endian.d \
+       core/sys/posix/fcntl.d core/sys/posix/grp.d core/sys/posix/iconv.d \
+       core/sys/posix/inttypes.d core/sys/posix/libgen.d \
+       core/sys/posix/locale.d core/sys/posix/mqueue.d \
+       core/sys/posix/net/if_.d core/sys/posix/netdb.d \
+       core/sys/posix/netinet/in_.d core/sys/posix/netinet/tcp.d \
+       core/sys/posix/poll.d core/sys/posix/pthread.d core/sys/posix/pwd.d \
+       core/sys/posix/sched.d core/sys/posix/semaphore.d \
+       core/sys/posix/setjmp.d core/sys/posix/signal.d core/sys/posix/spawn.d \
        core/sys/posix/stdc/time.d core/sys/posix/stdio.d \
        core/sys/posix/stdlib.d core/sys/posix/string.d \
        core/sys/posix/strings.d core/sys/posix/sys/filio.d \
index 8543c6e53edcb2d751dd1ec7d007cef462860bbf..bf86bc0e1aef32455fbacc6951d8e02a142566cb 100644 (file)
@@ -205,7 +205,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
        core/internal/array/equality.lo \
        core/internal/array/operations.lo core/internal/array/utils.lo \
        core/internal/atomic.lo core/internal/attributes.lo \
-       core/internal/container/array.lo \
+       core/internal/cast_.lo core/internal/container/array.lo \
        core/internal/container/common.lo \
        core/internal/container/hashtab.lo \
        core/internal/container/treap.lo core/internal/convert.lo \
@@ -248,12 +248,11 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \
        gcc/sections/pecoff.lo gcc/simd.lo gcc/unwind/arm.lo \
        gcc/unwind/arm_common.lo gcc/unwind/c6x.lo \
        gcc/unwind/generic.lo gcc/unwind/package.lo gcc/unwind/pe.lo \
-       object.lo rt/aApply.lo rt/aApplyR.lo rt/aaA.lo rt/adi.lo \
-       rt/arraycat.lo rt/cast_.lo rt/config.lo rt/critical_.lo \
-       rt/deh.lo rt/dmain2.lo rt/ehalloc.lo rt/invariant_.lo \
-       rt/lifetime.lo rt/memory.lo rt/minfo.lo rt/monitor_.lo \
-       rt/profilegc.lo rt/sections.lo rt/tlsgc.lo rt/util/typeinfo.lo \
-       rt/util/utility.lo
+       object.lo rt/aApply.lo rt/aApplyR.lo rt/arraycat.lo \
+       rt/config.lo rt/critical_.lo rt/deh.lo rt/dmain2.lo \
+       rt/ehalloc.lo rt/invariant_.lo rt/lifetime.lo rt/memory.lo \
+       rt/minfo.lo rt/monitor_.lo rt/profilegc.lo rt/sections.lo \
+       rt/tlsgc.lo rt/util/typeinfo.lo rt/util/utility.lo
 am__objects_2 = core/stdc/libgdruntime_la-errno_.lo \
        etc/valgrind/libgdruntime_la-valgrind_.lo
 am__objects_3 = core/sys/elf/package.lo
@@ -265,31 +264,32 @@ am__objects_4 = core/stdcpp/allocator.lo core/stdcpp/array.lo \
        core/stdcpp/vector.lo core/stdcpp/xutility.lo
 am__objects_5 = core/sys/posix/aio.lo core/sys/posix/arpa/inet.lo \
        core/sys/posix/config.lo core/sys/posix/dirent.lo \
-       core/sys/posix/dlfcn.lo core/sys/posix/fcntl.lo \
-       core/sys/posix/grp.lo core/sys/posix/iconv.lo \
-       core/sys/posix/inttypes.lo core/sys/posix/libgen.lo \
-       core/sys/posix/locale.lo core/sys/posix/mqueue.lo \
-       core/sys/posix/net/if_.lo core/sys/posix/netdb.lo \
-       core/sys/posix/netinet/in_.lo core/sys/posix/netinet/tcp.lo \
-       core/sys/posix/poll.lo core/sys/posix/pthread.lo \
-       core/sys/posix/pwd.lo core/sys/posix/sched.lo \
-       core/sys/posix/semaphore.lo core/sys/posix/setjmp.lo \
-       core/sys/posix/signal.lo core/sys/posix/spawn.lo \
-       core/sys/posix/stdc/time.lo core/sys/posix/stdio.lo \
-       core/sys/posix/stdlib.lo core/sys/posix/string.lo \
-       core/sys/posix/strings.lo core/sys/posix/sys/filio.lo \
-       core/sys/posix/sys/ioccom.lo core/sys/posix/sys/ioctl.lo \
-       core/sys/posix/sys/ipc.lo core/sys/posix/sys/mman.lo \
-       core/sys/posix/sys/msg.lo core/sys/posix/sys/resource.lo \
-       core/sys/posix/sys/select.lo core/sys/posix/sys/shm.lo \
-       core/sys/posix/sys/socket.lo core/sys/posix/sys/stat.lo \
-       core/sys/posix/sys/statvfs.lo core/sys/posix/sys/time.lo \
-       core/sys/posix/sys/ttycom.lo core/sys/posix/sys/types.lo \
-       core/sys/posix/sys/uio.lo core/sys/posix/sys/un.lo \
-       core/sys/posix/sys/utsname.lo core/sys/posix/sys/wait.lo \
-       core/sys/posix/syslog.lo core/sys/posix/termios.lo \
-       core/sys/posix/time.lo core/sys/posix/ucontext.lo \
-       core/sys/posix/unistd.lo core/sys/posix/utime.lo
+       core/sys/posix/dlfcn.lo core/sys/posix/endian.lo \
+       core/sys/posix/fcntl.lo core/sys/posix/grp.lo \
+       core/sys/posix/iconv.lo core/sys/posix/inttypes.lo \
+       core/sys/posix/libgen.lo core/sys/posix/locale.lo \
+       core/sys/posix/mqueue.lo core/sys/posix/net/if_.lo \
+       core/sys/posix/netdb.lo core/sys/posix/netinet/in_.lo \
+       core/sys/posix/netinet/tcp.lo core/sys/posix/poll.lo \
+       core/sys/posix/pthread.lo core/sys/posix/pwd.lo \
+       core/sys/posix/sched.lo core/sys/posix/semaphore.lo \
+       core/sys/posix/setjmp.lo core/sys/posix/signal.lo \
+       core/sys/posix/spawn.lo core/sys/posix/stdc/time.lo \
+       core/sys/posix/stdio.lo core/sys/posix/stdlib.lo \
+       core/sys/posix/string.lo core/sys/posix/strings.lo \
+       core/sys/posix/sys/filio.lo core/sys/posix/sys/ioccom.lo \
+       core/sys/posix/sys/ioctl.lo core/sys/posix/sys/ipc.lo \
+       core/sys/posix/sys/mman.lo core/sys/posix/sys/msg.lo \
+       core/sys/posix/sys/resource.lo core/sys/posix/sys/select.lo \
+       core/sys/posix/sys/shm.lo core/sys/posix/sys/socket.lo \
+       core/sys/posix/sys/stat.lo core/sys/posix/sys/statvfs.lo \
+       core/sys/posix/sys/time.lo core/sys/posix/sys/ttycom.lo \
+       core/sys/posix/sys/types.lo core/sys/posix/sys/uio.lo \
+       core/sys/posix/sys/un.lo core/sys/posix/sys/utsname.lo \
+       core/sys/posix/sys/wait.lo core/sys/posix/syslog.lo \
+       core/sys/posix/termios.lo core/sys/posix/time.lo \
+       core/sys/posix/ucontext.lo core/sys/posix/unistd.lo \
+       core/sys/posix/utime.lo
 @DRUNTIME_OS_POSIX_TRUE@am__objects_6 = $(am__objects_5)
 am__objects_7 = core/sys/darwin/config.lo \
        core/sys/darwin/crt_externs.lo core/sys/darwin/dlfcn.lo \
@@ -870,13 +870,13 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
        core/internal/array/construction.d core/internal/array/duplication.d \
        core/internal/array/equality.d core/internal/array/operations.d \
        core/internal/array/utils.d core/internal/atomic.d \
-       core/internal/attributes.d core/internal/container/array.d \
-       core/internal/container/common.d core/internal/container/hashtab.d \
-       core/internal/container/treap.d core/internal/convert.d \
-       core/internal/dassert.d core/internal/destruction.d \
-       core/internal/entrypoint.d core/internal/gc/bits.d \
-       core/internal/gc/blkcache.d core/internal/gc/blockmeta.d \
-       core/internal/gc/impl/conservative/gc.d \
+       core/internal/attributes.d core/internal/cast_.d \
+       core/internal/container/array.d core/internal/container/common.d \
+       core/internal/container/hashtab.d core/internal/container/treap.d \
+       core/internal/convert.d core/internal/dassert.d \
+       core/internal/destruction.d core/internal/entrypoint.d \
+       core/internal/gc/bits.d core/internal/gc/blkcache.d \
+       core/internal/gc/blockmeta.d core/internal/gc/impl/conservative/gc.d \
        core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \
        core/internal/gc/os.d core/internal/gc/pooltable.d \
        core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \
@@ -907,11 +907,10 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \
        gcc/sections/macho.d gcc/sections/package.d gcc/sections/pecoff.d \
        gcc/simd.d gcc/unwind/arm.d gcc/unwind/arm_common.d gcc/unwind/c6x.d \
        gcc/unwind/generic.d gcc/unwind/package.d gcc/unwind/pe.d object.d \
-       rt/aApply.d rt/aApplyR.d rt/aaA.d rt/adi.d rt/arraycat.d rt/cast_.d \
-       rt/config.d rt/critical_.d rt/deh.d rt/dmain2.d rt/ehalloc.d \
-       rt/invariant_.d rt/lifetime.d rt/memory.d rt/minfo.d rt/monitor_.d \
-       rt/profilegc.d rt/sections.d rt/tlsgc.d rt/util/typeinfo.d \
-       rt/util/utility.d
+       rt/aApply.d rt/aApplyR.d rt/arraycat.d rt/config.d rt/critical_.d \
+       rt/deh.d rt/dmain2.d rt/ehalloc.d rt/invariant_.d rt/lifetime.d \
+       rt/memory.d rt/minfo.d rt/monitor_.d rt/profilegc.d rt/sections.d \
+       rt/tlsgc.d rt/util/typeinfo.d rt/util/utility.d
 
 DRUNTIME_DSOURCES_STDCXX = core/stdcpp/allocator.d core/stdcpp/array.d \
        core/stdcpp/exception.d core/stdcpp/memory.d core/stdcpp/new_.d \
@@ -1009,15 +1008,15 @@ DRUNTIME_DSOURCES_OPENBSD = core/sys/openbsd/dlfcn.d \
 
 DRUNTIME_DSOURCES_POSIX = core/sys/posix/aio.d \
        core/sys/posix/arpa/inet.d core/sys/posix/config.d \
-       core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/fcntl.d \
-       core/sys/posix/grp.d core/sys/posix/iconv.d core/sys/posix/inttypes.d \
-       core/sys/posix/libgen.d core/sys/posix/locale.d \
-       core/sys/posix/mqueue.d core/sys/posix/net/if_.d \
-       core/sys/posix/netdb.d core/sys/posix/netinet/in_.d \
-       core/sys/posix/netinet/tcp.d core/sys/posix/poll.d \
-       core/sys/posix/pthread.d core/sys/posix/pwd.d core/sys/posix/sched.d \
-       core/sys/posix/semaphore.d core/sys/posix/setjmp.d \
-       core/sys/posix/signal.d core/sys/posix/spawn.d \
+       core/sys/posix/dirent.d core/sys/posix/dlfcn.d core/sys/posix/endian.d \
+       core/sys/posix/fcntl.d core/sys/posix/grp.d core/sys/posix/iconv.d \
+       core/sys/posix/inttypes.d core/sys/posix/libgen.d \
+       core/sys/posix/locale.d core/sys/posix/mqueue.d \
+       core/sys/posix/net/if_.d core/sys/posix/netdb.d \
+       core/sys/posix/netinet/in_.d core/sys/posix/netinet/tcp.d \
+       core/sys/posix/poll.d core/sys/posix/pthread.d core/sys/posix/pwd.d \
+       core/sys/posix/sched.d core/sys/posix/semaphore.d \
+       core/sys/posix/setjmp.d core/sys/posix/signal.d core/sys/posix/spawn.d \
        core/sys/posix/stdc/time.d core/sys/posix/stdio.d \
        core/sys/posix/stdlib.d core/sys/posix/string.d \
        core/sys/posix/strings.d core/sys/posix/sys/filio.d \
@@ -1257,6 +1256,7 @@ core/internal/array/operations.lo:  \
 core/internal/array/utils.lo: core/internal/array/$(am__dirstamp)
 core/internal/atomic.lo: core/internal/$(am__dirstamp)
 core/internal/attributes.lo: core/internal/$(am__dirstamp)
+core/internal/cast_.lo: core/internal/$(am__dirstamp)
 core/internal/container/$(am__dirstamp):
        @$(MKDIR_P) core/internal/container
        @: > core/internal/container/$(am__dirstamp)
@@ -1411,10 +1411,7 @@ rt/$(am__dirstamp):
        @: > rt/$(am__dirstamp)
 rt/aApply.lo: rt/$(am__dirstamp)
 rt/aApplyR.lo: rt/$(am__dirstamp)
-rt/aaA.lo: rt/$(am__dirstamp)
-rt/adi.lo: rt/$(am__dirstamp)
 rt/arraycat.lo: rt/$(am__dirstamp)
-rt/cast_.lo: rt/$(am__dirstamp)
 rt/config.lo: rt/$(am__dirstamp)
 rt/critical_.lo: rt/$(am__dirstamp)
 rt/deh.lo: rt/$(am__dirstamp)
@@ -1466,6 +1463,7 @@ core/sys/posix/arpa/inet.lo: core/sys/posix/arpa/$(am__dirstamp)
 core/sys/posix/config.lo: core/sys/posix/$(am__dirstamp)
 core/sys/posix/dirent.lo: core/sys/posix/$(am__dirstamp)
 core/sys/posix/dlfcn.lo: core/sys/posix/$(am__dirstamp)
+core/sys/posix/endian.lo: core/sys/posix/$(am__dirstamp)
 core/sys/posix/fcntl.lo: core/sys/posix/$(am__dirstamp)
 core/sys/posix/grp.lo: core/sys/posix/$(am__dirstamp)
 core/sys/posix/iconv.lo: core/sys/posix/$(am__dirstamp)
index 899e0b054ba1ba8ba8cf0d9977271120b3d1817c..7e97b9d53facb39d44666996fe35a5cd78965ea7 100644 (file)
@@ -154,7 +154,7 @@ void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, auto
 {
     static assert (is (V : T), "Can't assign `newval` of type `shared " ~ V.stringof ~ "` to `shared " ~ T.stringof ~ "`.");
 
-    core.internal.atomic.atomicStore!ms(cast(T*)&val, cast(V)newval);
+    core.internal.atomic.atomicStore!ms(cast(T*)&val, *cast(V*)&newval);
 }
 
 /**
index 4c40b9957a994096109537bf5d88b02072bb5814..5b305c759a2c722ad521045ae809bf01130673b5 100644 (file)
@@ -757,9 +757,31 @@ unittest
  * Returns:
  *      the product
  */
-pragma(inline, true)
+// pragma(inline, true)
 uint mulu()(uint x, uint y, ref bool overflow)
 {
+    version (D_InlineAsm_X86)         enum useAsm = true;
+    else version (D_InlineAsm_X86_64) enum useAsm = true;
+    else                              enum useAsm = false;
+
+    static if (useAsm)
+    {
+        if (!__ctfe)
+        {
+            uint r;
+            bool o;
+            asm pure nothrow @nogc @trusted
+            {
+                mov EAX, x;
+                mul y;        // EDX:EAX = EAX * y
+                mov r, EAX;
+                setc o;
+            }
+            overflow |= o;
+            return r;
+        }
+    }
+
     immutable ulong r = ulong(x) * ulong(y);
     if (r >> 32)
         overflow = true;
@@ -791,6 +813,12 @@ unittest
 pragma(inline, true)
 ulong mulu()(ulong x, uint y, ref bool overflow)
 {
+    version (D_InlineAsm_X86_64)
+    {
+        if (!__ctfe)
+            return mulu(x, ulong(y), overflow);
+    }
+
     ulong r = x * y;
     if (x >> 32 &&
             r / x != y)
@@ -799,9 +827,27 @@ ulong mulu()(ulong x, uint y, ref bool overflow)
 }
 
 /// ditto
-pragma(inline, true)
+// pragma(inline, true)
 ulong mulu()(ulong x, ulong y, ref bool overflow)
 {
+    version (D_InlineAsm_X86_64)
+    {
+        if (!__ctfe)
+        {
+            ulong r;
+            bool o;
+            asm pure nothrow @nogc @trusted
+            {
+                mov RAX, x;
+                mul y;        // RDX:RAX = RAX * y
+                mov r, RAX;
+                setc o;
+            }
+            overflow |= o;
+            return r;
+        }
+    }
+
     immutable ulong r = x * y;
     if ((x | y) >> 32 &&
             x &&
index af077be77bcb4f64a1816fb52471d14e97457a3b..32de5ab14095231148a73adc8c2eed182b1cedad 100644 (file)
@@ -12,6 +12,8 @@ import core.stdc.stdio : printf;
 
 __gshared Config config;
 
+private __gshared bool _initialized;
+
 struct Config
 {
     bool disable;            // start disabled
@@ -31,7 +33,9 @@ struct Config
 
     bool initialize()
     {
-        return initConfigOptions(this, "gcopt");
+        if (!_initialized)
+            _initialized = initConfigOptions(this, "gcopt");
+        return _initialized;
     }
 
     void help() @nogc nothrow
index c4e78bae782316154ab225163e1ebca7382907e1..6cab62ef995bfc514bd1bf28856a3f2db37de18b 100644 (file)
@@ -13,6 +13,8 @@
  */
 module core.gc.gcinterface;
 
+import core.thread.threadbase : ThreadBase;
+
 static import core.memory;
 alias BlkAttr = core.memory.GC.BlkAttr;
 alias BlkInfo = core.memory.GC.BlkInfo;
@@ -270,4 +272,32 @@ interface GC
      * Returns: true if successful.
      */
     bool shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic = false) nothrow;
+
+    /**
+     * Prepare a thread for use with the GC after the GC is initialized. Note
+     * that you can register an initThread function to call before the GC is
+     * initialized, see core.gc.registry.
+     *
+     * This function is always called *from* the thread as it is being started,
+     * before any static initializers. The thread object parameter may not yet
+     * be registered as `ThreadBase.getThis()`, as this is called before that
+     * happens.
+     */
+    void initThread(ThreadBase thread) nothrow @nogc;
+
+    /**
+     * Clean up any GC related data from the thread before it exits. There is
+     * no equivalent of this function as a hook before the GC is initialized.
+     * That is, the thread init function called before the GC is initialized
+     * (from the registry hook) should assume this function may not be called
+     * on termination if the GC is never initialized.
+     *
+     * Most times, this is called from the thread that is the given thread
+     * reference, but it's possible the `thread` parameter is not the same as
+     * the current thread.
+     *
+     * There is no guarantee the thread is still registered as
+     * `ThreadBase.getThis()`.
+     */
+    void cleanupThread(ThreadBase thread) nothrow @nogc;
 }
index da2dcff1e7d22137c3d9207fbc553bb47360d2ef..2e6edf09c3e74f25ffa41a2826a84c0cdc76272b 100644 (file)
@@ -8,6 +8,7 @@
 module core.gc.registry;
 
 import core.gc.gcinterface : GC;
+import core.thread.threadbase : ThreadBase;
 
 /*@nogc nothrow:*/
 
@@ -21,6 +22,13 @@ import core.gc.gcinterface : GC;
  */
 alias GCFactory = GC function();
 
+/**
+ * A function that will initialize a thread before the GC has been initialized.
+ * Once the GC is initialized, the interface method GC.initThread for each new
+ * thread.
+ */
+alias GCThreadInitFunction = void function(ThreadBase base) nothrow @nogc;
+
 /**
  * Register a GC factory under the given `name`.  This function must be called
  * from a C constructor before druntime is initialized.
@@ -28,21 +36,35 @@ alias GCFactory = GC function();
  * To use the registered GC, it's name must be specified gcopt runtime option,
  * e.g. by passing $(TT, --DRT-gcopt=gc:my_gc_name) as application argument.
  *
+ * The thread init function will be called *only before* the GC has been
+ * initialized to the registered GC. It is called as the first step in starting
+ * the new thread before the thread is registered with the runtime as a running
+ * thread. This allows any specific thread data that is needed for running the
+ * GC to be registered with the thread object.
+ *
+ * After the GC is initialized, the GC interface function `initThread` is
+ * called instead. This function should expect the possibility that the
+ * reciprocal `cleanupThread` method may not be called if the GC is never
+ * initialized.
+ *
  * Params:
  *   name = name of the GC implementation; should be unique
  *   factory = function to instantiate the implementation
+ *   threadInit = function to call from a new thread *before* registration in
+ *       the list of running threads, to set up any GC-specific data.
  * Note: The registry does not perform synchronization, as registration is
  *   assumed to be executed serially, as is the case for C constructors.
  * See_Also:
  *   $(LINK2 https://dlang.org/spec/garbage.html#gc_config, Configuring the Garbage Collector)
  */
-void registerGCFactory(string name, GCFactory factory) nothrow @nogc
+void registerGCFactory(string name, GCFactory factory,
+        GCThreadInitFunction threadInit = null) nothrow @nogc
 {
     import core.stdc.stdlib : realloc;
 
     auto ptr = cast(Entry*)realloc(entries.ptr, (entries.length + 1) * Entry.sizeof);
     entries = ptr[0 .. entries.length + 1];
-    entries[$ - 1] = Entry(name, factory);
+    entries[$ - 1] = Entry(name, factory, threadInit);
 }
 
 /**
@@ -70,6 +92,22 @@ GC createGCInstance(string name)
     return null;
 }
 
+/**
+ * Get the thread init function used for the selected GC.
+ *
+ * Note, this must be called before `createGCInstance`. It is typically called by
+ * `rt_init`.
+ */
+GCThreadInitFunction threadInit(string name) nothrow @nogc
+{
+    foreach (entry; entries)
+    {
+        if (entry.name == name)
+            return entry.threadInit;
+    }
+    return null;
+}
+
 // list of all registerd GCs
 const(Entry[]) registeredGCFactories(scope int dummy=0) nothrow @nogc
 {
@@ -82,6 +120,7 @@ struct Entry
 {
     string name;
     GCFactory factory;
+    GCThreadInitFunction threadInit;
 }
 
 __gshared Entry[] entries;
index 1b2b78ea5707c9db16e8e25c2202f243ae3dd22b..e5c57e67da47c40c1c0d78df7e2adeda36b86851 100644 (file)
@@ -9,8 +9,11 @@
 */
 module core.internal.array.appending;
 
-/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX)
-private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref return scope byte[] px, size_t n) @trusted pure nothrow;
+private extern (C)
+{
+    bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) pure nothrow;
+    bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) pure nothrow;
+}
 
 private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; });
 
@@ -25,22 +28,83 @@ private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lh
  * Bugs:
  *  This function template was ported from a much older runtime hook that bypassed safety,
  *  purity, and throwabilty checks. To prevent breaking existing code, this function template
- *  is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
+ *  is temporarily declared `@trusted` until the implementation can be brought up to modern D expectations.
  */
 ref Tarr _d_arrayappendcTX(Tarr : T[], T)(return ref scope Tarr px, size_t n) @trusted
 {
-    // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718
+    import core.internal.traits: Unqual;
+
+    alias Unqual_T = Unqual!T;
+    alias Unqual_Tarr = Unqual_T[];
+    enum isshared = is(T == shared);
+    auto unqual_px = cast(Unqual_Tarr) px;
+
+    // Ignoring additional attributes allows reusing the same generated code
+    px = cast(Tarr)_d_arrayappendcTX_(unqual_px, n, isshared);
+    return px;
+}
+
+private ref Tarr _d_arrayappendcTX_(Tarr : T[], T)(return ref scope Tarr px, size_t n, bool isshared) @trusted
+{
     version (DigitalMars) pragma(inline, false);
     version (D_TypeInfo)
     {
-        auto ti = typeid(Tarr);
+        // Short circuit if no data is being appended.
+        if (n == 0)
+            return px;
+
+        import core.stdc.string : memcpy, memset;
+        import core.internal.lifetime : __doPostblit;
+        import core.internal.array.utils: __arrayAlloc, newCapacity, __typeAttrs;
+        import core.internal.gc.blockmeta : PAGESIZE;
+        import core.exception: onOutOfMemoryError;
+        import core.memory: GC;
+
+        alias BlkAttr = GC.BlkAttr;
+
+        enum sizeelem = T.sizeof;
+        auto length = px.length;
+        auto newlength = length + n;
+        auto newsize = newlength * sizeelem;
+        auto size = length * sizeelem;
+
+        if (!gc_expandArrayUsed(px, newsize, isshared))
+        {
+            // could not set the size, we must reallocate.
+            auto newcap = newCapacity(newlength, sizeelem);
+            auto attrs = __typeAttrs!T(cast(void*)px.ptr) | BlkAttr.APPENDABLE;
+
+            T* ptr = cast(T*)GC.malloc(newcap, attrs, typeid(T));
+            if (ptr is null)
+            {
+                onOutOfMemoryError();
+                assert(0);
+            }
+
+            if (newsize != newcap)
+            {
+                // For small blocks that are always fully scanned, if we allocated more
+                // capacity than was requested, we are responsible for zeroing that
+                // memory.
+                // TODO: should let the GC figure this out, as this property may
+                // not always hold.
+                if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE)
+                    memset(ptr + newlength, 0, newcap - newsize);
+
+                gc_shrinkArrayUsed(ptr[0 .. newlength], newcap, isshared);
+            }
+
+            memcpy(ptr, px.ptr, size);
 
-        // _d_arrayappendcTX takes the `px` as a ref byte[], but its length
-        // should still be the original length
-        auto pxx = (cast(byte*)px.ptr)[0 .. px.length];
-        ._d_arrayappendcTX(ti, pxx, n);
-        px = (cast(T*)pxx.ptr)[0 .. pxx.length];
+            // do potsblit processing.
+            __doPostblit!T(ptr[0 .. length]);
 
+            px = ptr[0 .. newlength];
+            return px;
+        }
+
+        // we were able to expand in place, just update the length
+        px = px.ptr[0 .. newlength];
         return px;
     }
     else
@@ -50,9 +114,10 @@ ref Tarr _d_arrayappendcTX(Tarr : T[], T)(return ref scope Tarr px, size_t n) @t
 version (D_ProfileGC)
 {
     /**
-     * TraceGC wrapper around $(REF _d_arrayappendT, core,internal,array,appending).
+     * TraceGC wrapper around _d_arrayappendcTX.
      */
-    ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
+    ref Tarr _d_arrayappendcTXTrace(Tarr : T[], T)(return ref scope Tarr px, size_t n,
+        string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted
     {
         version (D_TypeInfo)
         {
@@ -72,39 +137,61 @@ ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @
     version (DigitalMars) pragma(inline, false);
 
     import core.stdc.string : memcpy;
-    import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+    import core.internal.traits : Unqual;
 
-    enum hasPostblit = __traits(hasPostblit, T);
     auto length = x.length;
 
     _d_arrayappendcTX(x, y.length);
 
     // Only call `copyEmplace` if `T` has a copy ctor and no postblit.
-    static if (hasElaborateCopyConstructor!T && !hasPostblit)
+    static if (__traits(hasCopyConstructor, T))
     {
         import core.lifetime : copyEmplace;
 
-        foreach (i, ref elem; y)
-            copyEmplace(elem, x[length + i]);
+        size_t i;
+        try
+        {
+            for (i = 0; i < y.length; ++i)
+                copyEmplace(y[i], x[length + i]);
+        }
+        catch (Exception o)
+        {
+            /* Destroy, in reverse order, what we've constructed so far
+            */
+            while (i--)
+            {
+                auto elem = cast(Unqual!T*) &x[length + i];
+                destroy(*elem);
+            }
+
+            throw o;
+        }
     }
     else
     {
         if (y.length)
         {
             // blit all elements at once
-            auto xptr = cast(Unqual!T *)&x[length];
-            immutable size = T.sizeof;
-
-            memcpy(xptr, cast(Unqual!T *)&y[0], y.length * size);
+            memcpy(cast(void*)&x[length], cast(void*)&y[0], y.length * T.sizeof);
 
             // call postblits if they exist
-            static if (hasPostblit)
+            static if (__traits(hasPostblit, T))
             {
-                auto eptr = xptr + y.length;
-                for (auto ptr = xptr; ptr < eptr; ptr++)
-                    ptr.__xpostblit();
-            }
-        }
+                import core.internal.lifetime : __doPostblit;
+                size_t i = 0;
+                try __doPostblit(x[length .. $], i);
+                catch (Exception o)
+                {
+                    // Destroy, in reverse order, what we've constructed so far
+                    while (i--)
+                    {
+                        auto elem = cast(Unqual!T*) &x[length + i];
+                        destroy(*elem);
+                    }
+
+                    throw o;
+                }
+            }}
     }
 
     return x;
@@ -205,3 +292,66 @@ version (D_ProfileGC)
     _d_arrayappendT(str, "c");
     assert(str == "abc");
 }
+
+@safe nothrow unittest
+{
+    static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } }
+    static size_t inner_postblit_cnt = 0;
+    static size_t inner_dtor_cnt = 0;
+    static size_t outer_postblit_cnt = 0;
+    static size_t outer_dtor_cnt = 0;
+    static struct Inner
+    {
+        char id;
+
+        @safe:
+        this(this)
+        {
+            ++inner_postblit_cnt;
+            if (id == '2')
+                throw new FailedPostblitException();
+        }
+
+        ~this() nothrow
+        {
+            ++inner_dtor_cnt;
+        }
+    }
+
+    static struct Outer
+    {
+        Inner inner1, inner2, inner3;
+
+        nothrow @safe:
+        this(char first, char second, char third)
+        {
+            inner1 = Inner(first);
+            inner2 = Inner(second);
+            inner3 = Inner(third);
+        }
+
+        this(this)
+        {
+            ++outer_postblit_cnt;
+        }
+
+        ~this()
+        {
+            ++outer_dtor_cnt;
+        }
+    }
+
+    Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')];
+
+    try {
+        Outer[] arrApp;
+        arrApp ~= arr;
+    }
+    catch (FailedPostblitException) {}
+    catch (Exception) assert(false);
+
+    assert(inner_postblit_cnt == 5);
+    assert(inner_dtor_cnt == 4);
+    assert(outer_postblit_cnt == 1);
+    assert(outer_dtor_cnt == 1);
+}
index 11bd357d7c27dbc71cf29fb36d9d3d1442d8cd94..42e6d44ec32da93b928955ffc0f55f34b8eb577e 100644 (file)
@@ -17,7 +17,65 @@ extern(C) {
     bool gc_expandArrayUsed(void[] slice, size_t newUsed, bool atomic) nothrow pure;
     size_t gc_reserveArrayCapacity(void[] slice, size_t request, bool atomic) nothrow pure;
     bool gc_shrinkArrayUsed(void[] slice, size_t existingUsed, bool atomic) nothrow pure;
+    void[] gc_getArrayUsed(void *ptr, bool atomic) nothrow pure;
 }
+
+/**
+Shrink the "allocated" length of an array to be the exact size of the array.
+
+It doesn't matter what the current allocated length of the array is, the
+user is telling the runtime that he knows what he is doing.
+
+Params:
+    T = the type of the elements in the array (this should be unqualified)
+    arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type
+    isshared = true if the underlying data is shared
+*/
+void _d_arrayshrinkfit(Tarr: T[], T)(Tarr arr, bool isshared) @trusted
+{
+    import core.exception : onFinalizeError;
+    import core.internal.traits: hasElaborateDestructor;
+
+    debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", T.sizeof, arr.ptr, arr.length);
+    auto reqlen = arr.length;
+
+    auto curArr = cast(Tarr)gc_getArrayUsed(arr.ptr, isshared);
+    if (curArr.ptr is null)
+        // not a valid GC pointer
+        return;
+
+    // align the array.
+    auto offset = arr.ptr - curArr.ptr;
+    auto curlen = curArr.length - offset;
+    if (curlen <= reqlen)
+        // invalid situation, or no change.
+        return;
+
+    // if the type has a destructor, destroy elements we are about to remove.
+    static if(is(T == struct) && hasElaborateDestructor!T)
+    {
+        try
+        {
+            // Finalize the elements that are being removed
+
+            // Due to the fact that the delete operator calls destructors
+            // for arrays from the last element to the first, we maintain
+            // compatibility here by doing the same.
+            for (auto curP = arr.ptr + curlen - 1; curP >= arr.ptr + reqlen; curP--)
+            {
+                // call destructor
+                curP.__xdtor();
+            }
+        }
+        catch (Exception e)
+        {
+            onFinalizeError(typeid(T), e);
+        }
+    }
+
+    gc_shrinkArrayUsed(arr[0 .. reqlen], curlen * T.sizeof, isshared);
+}
+
 /**
 Set the array capacity.
 
@@ -60,6 +118,7 @@ do
     alias BlkAttr = GC.BlkAttr;
 
     auto size = T.sizeof;
+
     bool overflow = false;
     const reqsize = mulu(size, newcapacity, overflow);
     if (overflow)
index 76811ef3ffd0cb6364e6ea0a66573416ee6efc94..b4012d2f1fa14abfec507b2c6477d3f70374dd77 100644 (file)
@@ -65,7 +65,7 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma
 
     enforceRawArraysConformable("initialization", T.sizeof, vFrom, vTo);
 
-    static if (hasElaborateCopyConstructor!T)
+    static if (__traits(hasCopyConstructor, T))
     {
         size_t i;
         try
@@ -88,8 +88,30 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma
     }
     else
     {
-        // blit all elements at once
-        memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
+        if (to.length)
+        {
+            // blit all elements at once
+            memcpy(cast(void*) to.ptr, from.ptr, to.length * T.sizeof);
+
+            // call postblits if they exist
+            static if (__traits(hasPostblit, T))
+            {
+                import core.internal.lifetime : __doPostblit;
+                size_t i = 0;
+                try __doPostblit(to, i);
+                catch (Exception o)
+                {
+                    // Destroy, in reverse order, what we've constructed so far
+                    while (i--)
+                    {
+                        auto elem = cast(Unqual!T*) &to[i];
+                        destroy(*elem);
+                    }
+
+                    throw o;
+                }
+            }
+        }
     }
 
     return to;
@@ -346,6 +368,7 @@ T[] _d_newarrayUPureNothrow(T)(size_t length, bool isShared=false) pure nothrow
 
 T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted
 {
+    import core.checkedint : mulu;
     import core.exception : onOutOfMemoryError;
     import core.internal.traits : Unqual;
     import core.internal.array.utils : __arrayAlloc;
@@ -353,52 +376,24 @@ T[] _d_newarrayU(T)(size_t length, bool isShared=false) @trusted
     alias UnqT = Unqual!T;
 
     size_t elemSize = T.sizeof;
-    size_t arraySize;
 
     debug(PRINTF) printf("_d_newarrayU(length = x%zu, size = %zu)\n", length, elemSize);
     if (length == 0 || elemSize == 0)
         return null;
 
-    version (D_InlineAsm_X86)
+    bool overflow = false;
+    const arraySize = mulu(elemSize, length, overflow);
+    if (!overflow)
     {
-        asm pure nothrow @nogc
+        if (auto arr = __arrayAlloc!UnqT(arraySize))
         {
-            mov     EAX, elemSize       ;
-            mul     EAX, length         ;
-            mov     arraySize, EAX      ;
-            jnc     Lcontinue           ;
+            debug(PRINTF) printf("p = %p\n", arr.ptr);
+            return (cast(T*) arr.ptr)[0 .. length];
         }
     }
-    else version (D_InlineAsm_X86_64)
-    {
-        asm pure nothrow @nogc
-        {
-            mov     RAX, elemSize       ;
-            mul     RAX, length         ;
-            mov     arraySize, RAX      ;
-            jnc     Lcontinue           ;
-        }
-    }
-    else
-    {
-        import core.checkedint : mulu;
 
-        bool overflow = false;
-        arraySize = mulu(elemSize, length, overflow);
-        if (!overflow)
-            goto Lcontinue;
-    }
-
-Loverflow:
     onOutOfMemoryError();
     assert(0);
-
-Lcontinue:
-    auto arr = __arrayAlloc!UnqT(arraySize);
-    if (!arr.ptr)
-        goto Loverflow;
-    debug(PRINTF) printf("p = %p\n", arr.ptr);
-    return (cast(T*) arr.ptr)[0 .. length];
 }
 
 /// ditto
@@ -615,3 +610,66 @@ version (D_ProfileGC)
             assert(0, "Cannot create new multi-dimensional array if compiling without support for runtime type information!");
     }
 }
+
+
+/**
+Allocate an array literal
+
+Rely on the caller to do the initialization of the array.
+
+---
+int[] getArr()
+{
+    return [10, 20];
+    // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2);
+    // res[0] = 10;
+    // res[1] = 20;
+    // return res[0..2];
+}
+---
+
+Params:
+    T = unqualified type of array elements
+    length = `.length` of array literal
+
+Returns: pointer to allocated array
+*/
+void* _d_arrayliteralTX(T)(size_t length) @trusted pure nothrow
+{
+    const allocsize = length * T.sizeof;
+
+    if (allocsize == 0)
+        return null;
+    else
+    {
+        import core.memory : GC;
+        import core.internal.traits : hasIndirections, hasElaborateDestructor;
+        alias BlkAttr = GC.BlkAttr;
+
+        /* Same as in core.internal.array.utils.__typeAttrs!T,
+        *  but don't use a nested template function call here to avoid
+        *  possible linking errors.
+        */
+        uint attrs = BlkAttr.APPENDABLE;
+        static if (!hasIndirections!T)
+            attrs |= BlkAttr.NO_SCAN;
+        static if (is(T == struct) && hasElaborateDestructor!T)
+            attrs |= BlkAttr.FINALIZE;
+
+        return GC.malloc(allocsize, attrs, typeid(T));
+    }
+}
+
+version (D_ProfileGC)
+void* _d_arrayliteralTXTrace(T)(size_t length, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted pure nothrow
+{
+    version (D_TypeInfo)
+    {
+        import core.internal.array.utils : TraceHook, gcStatsPure, accumulatePure;
+        mixin(TraceHook!((T[]).stringof, "_d_arrayliteralTX"));
+
+        return _d_arrayliteralTX!T(length);
+    }
+    else
+        assert(0);
+}
index b5222b3b1751f318222be9324ba47634d0dbc99d..a9b599cfc35da8468b3595b2791c63d54e4f740d 100644 (file)
@@ -362,7 +362,7 @@ U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
 // https://issues.dlang.org/show_bug.cgi?id=24453
 @safe unittest
 {
-    static inout(char)[] foo(ref scope return inout(char)[] s)
+    static inout(char)[] foo(return ref scope inout(char)[] s)
     {
         auto bla = s.idup;
         return s;
index c110d64069f2122f50b33aedf5888d2a13055752..e0a811bf07225d9a6575b0eec21c4e7af345c66c 100644 (file)
@@ -14,41 +14,7 @@ module core.internal.array.equality;
 // * dynamic arrays,
 // * (most) arrays of different (unqualified) element types, and
 // * arrays of structs with custom opEquals.
-
- // The scalar-only overload takes advantage of known properties of scalars to
- // reduce template instantiation. This is expected to be the most common case.
-bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
-@nogc nothrow pure @trusted
-if (__traits(isScalar, T1) && __traits(isScalar, T2))
-{
-    const length = lhs.length;
-
-    static if (T1.sizeof == T2.sizeof
-        // Signedness needs to match for types that promote to int.
-        // (Actually it would be okay to memcmp bool[] and byte[] but that is
-        // probably too uncommon to be worth checking for.)
-        && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2))
-        && !__traits(isFloating, T1) && !__traits(isFloating, T2))
-    {
-        if (__ctfe)
-            return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length);
-        else
-        {
-            // This would improperly allow equality of integers and pointers
-            // but the CTFE branch will stop this function from compiling then.
-            import core.stdc.string : memcmp;
-            return length == rhs.length &&
-                (!length || 0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, length * T1.sizeof));
-        }
-    }
-    else
-    {
-        return length == rhs.length && isEqual(lhs.ptr, rhs.ptr, length);
-    }
-}
-
-bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs)
-if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
+bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs) @trusted
 {
     if (lhs.length != rhs.length)
         return false;
@@ -56,37 +22,9 @@ if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
     if (lhs.length == 0)
         return true;
 
-    static if (useMemcmp!(T1, T2))
-    {
-        if (!__ctfe)
-        {
-            static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure
-            {
-                pragma(inline, true);
-                import core.stdc.string : memcmp;
-                return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0;
-            }
-            return trustedMemcmp(lhs, rhs);
-        }
-        else
-        {
-            foreach (const i; 0 .. lhs.length)
-            {
-                if (at(lhs, i) != at(rhs, i))
-                    return false;
-            }
-            return true;
-        }
-    }
-    else
-    {
-        foreach (const i; 0 .. lhs.length)
-        {
-            if (at(lhs, i) != at(rhs, i))
-                return false;
-        }
-        return true;
-    }
+    alias PureType = bool function(scope T1[], scope T2[], size_t) @safe pure nothrow @nogc;
+
+    return (cast(PureType)&isEqual!(T1,T2))(lhs, rhs, lhs.length);
 }
 
 /******************************
@@ -94,11 +32,26 @@ if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
  * Outlined to enable __equals() to be inlined, as dmd cannot inline loops.
  */
 private
-bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length)
+bool isEqual(T1, T2)(scope T1[] lhs, scope T2[] rhs, size_t length)
 {
+    // Returns a reference to an array element, eliding bounds check and
+    // casting void to ubyte.
+    pragma(inline, true)
+    static ref at(T)(scope T[] r, size_t i) @trusted
+        // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
+        if (!(is(T == struct) && !is(typeof(T.sizeof))))
+    {
+        static if (is(T == void))
+            return (cast(ubyte[]) r)[i];
+        else
+            return r[i];
+    }
+
     foreach (const i; 0 .. length)
-        if (t1[i] != t2[i])
+    {
+        if (at(lhs, i) != at(rhs, i))
             return false;
+    }
     return true;
 }
 
@@ -173,69 +126,6 @@ bool isEqual(T1, T2)(scope const T1* t1, scope const T2* t2, size_t length)
     assert(a1 != a2);
 }
 
-
-private:
-
-// - Recursively folds static array types to their element type,
-// - maps void to ubyte, and
-// - pointers to size_t.
-template BaseType(T)
-{
-    static if (__traits(isStaticArray, T))
-        alias BaseType = BaseType!(typeof(T.init[0]));
-    else static if (is(immutable T == immutable void))
-        alias BaseType = ubyte;
-    else static if (is(T == E*, E))
-        alias BaseType = size_t;
-    else
-        alias BaseType = T;
-}
-
-// Use memcmp if the element sizes match and both base element types are integral.
-// Due to int promotion, disallow small integers of diverging signed-ness though.
-template useMemcmp(T1, T2)
-{
-    static if (T1.sizeof != T2.sizeof)
-        enum useMemcmp = false;
-    else
-    {
-        alias B1 = BaseType!T1;
-        alias B2 = BaseType!T2;
-        enum useMemcmp = __traits(isIntegral, B1) && __traits(isIntegral, B2)
-           && !( (B1.sizeof < 4 || B2.sizeof < 4) && __traits(isUnsigned, B1) != __traits(isUnsigned, B2) );
-    }
-}
-
-unittest
-{
-    enum E { foo, bar }
-
-    static assert(useMemcmp!(byte, byte));
-    static assert(useMemcmp!(ubyte, ubyte));
-    static assert(useMemcmp!(void, const void));
-    static assert(useMemcmp!(void, immutable bool));
-    static assert(useMemcmp!(void, inout char));
-    static assert(useMemcmp!(void, shared ubyte));
-    static assert(!useMemcmp!(void, byte));       // differing signed-ness
-    static assert(!useMemcmp!(char[8], byte[8])); // ditto
-
-    static assert(useMemcmp!(short, short));
-    static assert(useMemcmp!(wchar, ushort));
-    static assert(!useMemcmp!(wchar, short)); // differing signed-ness
-
-    static assert(useMemcmp!(int, uint)); // no promotion, ignoring signed-ness
-    static assert(useMemcmp!(dchar, E));
-
-    static assert(useMemcmp!(immutable void*, size_t));
-    static assert(useMemcmp!(double*, ptrdiff_t));
-    static assert(useMemcmp!(long[2][3], const(ulong)[2][3]));
-
-    static assert(!useMemcmp!(float, float));
-    static assert(!useMemcmp!(double[2], double[2]));
-    static assert(!useMemcmp!(Object, Object));
-    static assert(!useMemcmp!(int[], int[]));
-}
-
 // https://issues.dlang.org/show_bug.cgi?id=21094
 unittest
 {
@@ -262,16 +152,3 @@ unittest
         return lhs == rhs;
     }
 }
-
-// Returns a reference to an array element, eliding bounds check and
-// casting void to ubyte.
-pragma(inline, true)
-ref at(T)(T[] r, size_t i) @trusted
-    // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
-    if (!(is(T == struct) && !is(typeof(T.sizeof))))
-{
-    static if (is(immutable T == immutable void))
-        return (cast(ubyte*) r.ptr)[i];
-    else
-        return r.ptr[i];
-}
index 1d105e6ebf19a349d166e6578319d8a677c01bfb..3d85e6277c90ccebd4864e1f694382152f1542d9 100644 (file)
@@ -157,6 +157,72 @@ void[] __arrayAlloc(T)(size_t arrSize) @trusted
     return null;
 }
 
+/**
+Given an array of length `size` that needs to be expanded to `newlength`,
+compute a new capacity.
+
+Better version by Dave Fladebo, enhanced by Steven Schveighoffer:
+This uses an inverse logorithmic algorithm to pre-allocate a bit more
+space for larger arrays.
+- The maximum "extra" space is about 80% of the requested space. This is for
+PAGE size and smaller.
+- As the arrays grow, the relative pre-allocated space shrinks.
+- Perhaps most importantly, overall memory usage and stress on the GC
+is decreased significantly for demanding environments.
+- The algorithm is tuned to avoid any division at runtime.
+
+Params:
+    newlength = new `.length`
+    elemsize = size of the element in the new array
+Returns: new capacity for array
+*/
+size_t newCapacity(size_t newlength, size_t elemsize) pure nothrow
+{
+    size_t newcap = newlength * elemsize;
+
+    /*
+     * Max growth factor numerator is 234, so allow for multiplying by 256.
+     * But also, the resulting size cannot be more than 2x, so prevent
+     * growing if 2x would fill up the address space (for 32-bit)
+     */
+    enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1);
+    if (!newcap || (newcap & ~largestAllowed))
+        return newcap;
+
+    /*
+     * The calculation for "extra" space depends on the requested capacity.
+     * We use an inverse logarithm of the new capacity to add an extra 15%
+     * to 83% capacity. Note that normally we humans think in terms of
+     * percent, but using 128 instead of 100 for the denominator means we
+     * can avoid all division by simply bit-shifthing. Since there are only
+     * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using
+     * a lookup table allows us to precalculate the multiplier based on the
+     * inverse logarithm. The formula rougly is:
+     *
+     * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1)))
+     */
+    import core.bitop;
+    static immutable multTable = (){
+        assert(__ctfe);
+        ulong[size_t.sizeof * 8] result;
+        foreach (i; 0 .. result.length)
+        {
+            auto factor = 128 + 1280 / (i + 1);
+            result[i] = factor > 234 ? 234 : factor;
+        }
+        return result;
+    }();
+
+    auto mult = multTable[bsr(newcap)];
+
+    // if this were per cent, then the code would look like:
+    // ((newlength * mult + 99) / 100) * elemsize
+    newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize;
+    debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize);
+    debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize);
+    return newcap;
+}
+
 uint __typeAttrs(T)(void *copyAttrsFrom = null)
 {
     import core.internal.traits : hasIndirections, hasElaborateDestructor;
diff --git a/libphobos/libdruntime/core/internal/cast_.d b/libphobos/libdruntime/core/internal/cast_.d
new file mode 100644 (file)
index 0000000..c4de76e
--- /dev/null
@@ -0,0 +1,311 @@
+module core.internal.cast_;
+
+// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
+// but we are trying to implement dynamic cast.
+bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) pure nothrow @safe @nogc
+{
+    // same class if signatures match, works with potential duplicates across binaries
+    if (a is b)
+        return true;
+
+    // new fast way
+    if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig)
+        return a.nameSig[0] == b.nameSig[0]
+            && a.nameSig[1] == b.nameSig[1]
+            && a.nameSig[2] == b.nameSig[2]
+            && a.nameSig[3] == b.nameSig[3];
+
+    // old slow way for temporary binary compatibility
+    return a.name == b.name;
+}
+
+
+/*****
+ * Dynamic cast from a class object `o` to class or interface `To`, where `To` is a subtype of `o`.
+ * Params:
+ *      o = instance of class
+ *      To = class or interface that is a subtype of `o`
+ * Returns:
+ *      null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`.
+ */
+private void* _d_dynamic_cast(To)(const return scope Object o) @trusted
+{
+    void* res = null;
+    size_t offset = 0;
+
+    if (o && _d_isbaseof2!To(typeid(o), offset))
+    {
+        res = cast(void*) o + offset;
+    }
+    return res;
+}
+
+/**
+ * Dynamic cast `o` to final class `To` only one level down
+ * Params:
+ *      o = object that is instance of a class
+ *      To = final class that is a subclass type of `o`
+ * Returns:
+ *      o if it succeeds, null if it fails
+ */
+private void* _d_paint_cast(To)(const return scope Object o)
+{
+    /* If o is really an instance of c, just do a paint
+     */
+    auto p = o && cast(void*)(areClassInfosEqual(typeid(o), typeid(To).info)) ? o : null;
+    debug assert(cast(void*)p is cast(void*)_d_dynamic_cast!To(o));
+    return cast(void*)p;
+}
+
+private void* _d_class_cast_impl(const return scope Object o, const ClassInfo c) pure nothrow @safe @nogc
+{
+    if (!o)
+        return null;
+
+    ClassInfo oc = typeid(o);
+    int delta = oc.depth;
+
+    if (delta && c.depth)
+    {
+        delta -= c.depth;
+        if (delta < 0)
+            return null;
+
+        while (delta--)
+            oc = oc.base;
+        if (areClassInfosEqual(oc, c))
+            return cast(void*)o;
+        return null;
+    }
+
+    // no depth data - support the old way
+    do
+    {
+        if (areClassInfosEqual(oc, c))
+            return cast(void*)o;
+        oc = oc.base;
+    } while (oc);
+    return null;
+}
+
+/*****
+ * Dynamic cast from a class object o to class type `To`, where `To` is a subclass type of `o`.
+ * Params:
+ *      o = instance of class
+ *      To = a subclass type of o
+ * Returns:
+ *      null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`.
+ */
+private void* _d_class_cast(To)(const return scope Object o)
+{
+    return _d_class_cast_impl(o, typeid(To));
+}
+
+/*************************************
+ * Attempts to cast interface Object o to class type `To`.
+ * Returns o if successful, null if not.
+ */
+private void* _d_interface_cast(To)(void* p) @trusted
+{
+    if (!p)
+        return null;
+
+    Interface* pi = **cast(Interface***) p;
+
+    Object o2 = cast(Object)(p - pi.offset);
+    void* res = null;
+    size_t offset = 0;
+    if (o2 && _d_isbaseof2!To(typeid(o2), offset))
+    {
+        res = cast(void*) o2 + offset;
+    }
+    return res;
+}
+
+/**
+* Hook that detects the type of cast performed and calls the appropriate function.
+* Params:
+*      o = object that is being casted
+*      To = type to which the object is being casted
+* Returns:
+*      null if the cast fails, otherwise returns the object casted to the type `To`.
+*/
+void* _d_cast(To, From)(From o) @trusted
+{
+    static if (is(From == class) && is(To == interface))
+    {
+        return _d_dynamic_cast!To(o);
+    }
+
+    static if (is(From == class) && is(To == class))
+    {
+        static if (is(From FromSupers == super) && is(To ToSupers == super))
+        {
+            /* Check for:
+            *  class A { }
+            *  final class B : A { }
+            *  ... cast(B) A ...
+            */
+            // Multiple inheritance is not allowed, so we can safely assume
+            // that the second super can only be an interface.
+            static if (__traits(isFinalClass, To) && is(ToSupers[0] == From) &&
+                       ToSupers.length == 1 && FromSupers.length <= 1)
+            {
+                return _d_paint_cast!To(o);
+            }
+        }
+
+        static if (is (To : From))
+        {
+            static if (is (To == From))
+            {
+                return cast(void*)o;
+            }
+            else
+            {
+                return _d_class_cast!To(o);
+            }
+        }
+
+        return null;
+    }
+
+    static if (is(From == interface))
+    {
+        static if (is(From == To))
+        {
+            return cast(void*)o;
+        }
+        else
+        {
+            return _d_interface_cast!To(cast(void*)o);
+        }
+    }
+    else
+    {
+        return null;
+    }
+}
+
+private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset)
+{
+    auto c = typeid(To).info;
+
+    if (areClassInfosEqual(oc, c))
+        return true;
+
+    do
+    {
+        if (oc.base && areClassInfosEqual(oc.base, c))
+            return true;
+
+        // Bugzilla 2013: Use depth-first search to calculate offset
+        // from the derived (oc) to the base (c).
+        foreach (iface; oc.interfaces)
+        {
+            if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2!To(iface.classinfo, offset))
+            {
+                offset += iface.offset;
+                return true;
+            }
+        }
+
+        oc = oc.base;
+    } while (oc);
+
+    return false;
+}
+
+@safe pure unittest
+{
+    interface I {}
+
+    class A {}
+    class B : A {}
+    class C : B, I{}
+
+    A ac = new C();
+    assert(_d_cast!I(ac) !is null); // A(c) to I
+    assert(_d_dynamic_cast!I(ac) !is null);
+
+    A ab = new B();
+    assert(_d_cast!I(ab) is null); // A(b) to I
+    assert(_d_dynamic_cast!I(ab) is null);
+}
+
+@safe pure unittest
+{
+    class A {}
+    class B : A {}
+    class C : B {}
+    final class D : C {}
+
+    C cd = new D();
+    assert(_d_cast!D(cd) !is null); // C(d) to D
+    assert(_d_paint_cast!D(cd) !is null);
+
+    class G {}
+    final class J {}
+    A a = new A();
+    assert(_d_cast!G(a) is null); // A(a) to G
+    assert(_d_paint_cast!G(a) is null);
+
+    assert(_d_cast!J(a) is null); // A(a) to J
+    assert(_d_paint_cast!J(a) is null);
+}
+
+@safe pure unittest
+{
+    class A {}
+    class B : A {}
+    class C : B {}
+    class D {}
+
+    A ac = new C();
+    assert(_d_cast!C(ac) !is null); // A(c) to C
+    assert(_d_class_cast!C(ac) !is null);
+
+    assert(_d_cast!B(ac) !is null); // A(c) to B
+    assert(_d_class_cast!B(ac) !is null);
+
+    A ab = new B();
+    assert(_d_cast!C(ab) is null); // A(b) to C
+    assert(_d_class_cast!C(ab) is null);
+
+    A a = new A();
+    assert(_d_cast!D(a) is null); // A(a) to D
+    assert(_d_class_cast!D(a) is null);
+}
+
+@safe pure unittest
+{
+    interface I1 {}
+    interface I2 {}
+    interface I3 {}
+    class A {}
+    class B : A, I1, I2 {}
+    class C : B, I3 {}
+
+    I1 bi = new B();
+    assert(_d_cast!I2(bi) !is null); // I1(b) to I2
+    assert(_d_interface_cast!I2(cast(void*)bi) !is null);
+
+    assert(_d_cast!A(bi) !is null); // I1(b) to A
+    assert(_d_interface_cast!A(cast(void*)bi) !is null);
+
+    assert(_d_cast!B(bi) !is null); // I1(b) to B
+    assert(_d_interface_cast!B(cast(void*)bi) !is null);
+
+    assert(_d_cast!I3(bi) is null); // I1(b) to I3
+    assert(_d_interface_cast!I3(cast(void*)bi) is null);
+
+    assert(_d_cast!C(bi) is null); // I1(b) to C
+    assert(_d_interface_cast!C(cast(void*)bi) is null);
+
+    assert(_d_cast!I1(bi) !is null); // I1(b) to I1
+    assert(_d_interface_cast!I1(cast(void*)bi) !is null);
+
+    I3 ci = new C();
+    assert(_d_cast!I1(ci) !is null); // I3(c) to I1
+    assert(_d_interface_cast!I1(cast(void*)ci) !is null);
+}
index 7bac8359a77a6b7c73c629bff97a741926452a0e..120fbdfcfe62bf6d4c884470b1e51bee41dde630 100644 (file)
@@ -647,6 +647,7 @@ package template floatSize(T) if (is(T:real) || is(T:ireal))
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1)
 {
+    pragma(inline, true);
     return cast(const(ubyte)[])arr;
 }
 
@@ -687,6 +688,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) &&
 {
     static if (T.sizeof == 1)
     {
+        pragma(inline, true);
         if (__ctfe)
         {
             ubyte[] result = ctfe_alloc(1);
@@ -722,6 +724,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) &&
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
 {
+    pragma(inline, true);
     if (!__ctfe)
         return (cast(const ubyte*) &val)[0 .. T.sizeof];
     else static if (is(typeof(val[0]) : void))
@@ -744,6 +747,7 @@ const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector))
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum))
 {
+    pragma(inline, true);
     if (__ctfe)
     {
         static if (is(T V == enum)){}
@@ -767,6 +771,7 @@ nothrow pure @safe unittest
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0)
 {
+    pragma(inline, true);
     if (__ctfe)
     {
         if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time");
@@ -781,6 +786,7 @@ const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V
 @trusted pure nothrow @nogc
 const(ubyte)[] toUbyte(T)(const return ref scope T val) if (is(T == struct) || is(T == union))
 {
+    pragma(inline, true);
     if (__ctfe)
     {
         ubyte[] bytes = ctfe_alloc(T.sizeof);
index 07027f56eb6cdd850675e74623595e9f21e544b5..65de25d2f27b82a414f857b81b44abf3047cc243 100644 (file)
@@ -63,17 +63,16 @@ else
 }
 
 // free the allocation on thread exit.
-@standalone static ~this()
+void cleanupBlkCache(void* storage) nothrow @nogc
 {
-    if (__blkcache_storage)
+    if (storage)
     {
+        // check if this is the same thread as the current running thread, and
+        // if so, make sure we don't leave a dangling pointer.
+        if (__blkcache_storage is storage)
+            __blkcache_storage = null;
         import core.stdc.stdlib : free;
-        import core.thread.threadbase;
-        auto tBase = ThreadBase.getThis();
-        if (tBase !is null)
-            tBase.tlsGCData = null;
-        free(__blkcache_storage);
-        __blkcache_storage = null;
+        free(storage);
     }
 }
 
index 64b5bed43b18a6164b1fdd21fed3ca7f2161a00a..8b81de30ab1a956dd022059ff147fd3dea65b65d 100644 (file)
@@ -1532,7 +1532,7 @@ class ConservativeGC : GC
             return false;
 
         // try extending the block into subsequent pages.
-        immutable requiredExtension = newUsed - info.size - LARGEPAD;
+        immutable requiredExtension = newUsed - (info.size - LARGEPAD);
         auto extendedSize = extend(info.base, requiredExtension, requiredExtension, null);
         if (extendedSize == 0)
             // could not extend, can't satisfy the request
@@ -1637,6 +1637,14 @@ class ConservativeGC : GC
 
         return existingCapacity - offset;
     }
+
+    void initThread(ThreadBase t) nothrow @nogc { }
+
+    void cleanupThread(ThreadBase t) nothrow @nogc
+    {
+        cleanupBlkCache(t.tlsGCData);
+        t.tlsGCData = null;
+    }
 }
 
 
@@ -1862,8 +1870,7 @@ struct Gcx
 
         roots.removeAll();
         ranges.removeAll();
-        toscanConservative.reset();
-        toscanPrecise.reset();
+        scanStackConservative.reset(); // scanStackPrecise overlaps with scanStackConservative
     }
 
 
@@ -2427,14 +2434,13 @@ struct Gcx
     {
     nothrow:
         @disable this(this);
-        auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
 
         void reset()
         {
             _length = 0;
             if (_p)
             {
-                os_mem_unmap(_p, _cap * RANGE.sizeof);
+                os_mem_unmap(_p, _cap);
                 _p = null;
             }
             _cap = 0;
@@ -2446,39 +2452,31 @@ struct Gcx
 
         void push(RANGE rng)
         {
-            if (_length == _cap) grow();
-            _p[_length++] = rng;
+            if ((_length + 1) * RANGE.sizeof > _cap) grow();
+            _p[_length] = rng;
+            _length++;
         }
 
-        RANGE pop()
-        in { assert(!empty); }
-        do
+        void pushReverse(RANGE[] ranges)
         {
-            return _p[--_length];
-        }
-
-        bool popLocked(ref RANGE rng)
-        {
-            if (_length == 0)
-                return false;
+            while ((_length + ranges.length) * RANGE.sizeof > _cap)
+                grow();
 
-            stackLock.lock();
-            scope(exit) stackLock.unlock();
-            if (_length == 0)
-                return false;
-            rng = _p[--_length];
-            return true;
+            // reverse order for depth-first-order traversal
+            foreach_reverse (ref range; ranges)
+                _p[_length++] = range;
         }
 
-        ref inout(RANGE) opIndex(size_t idx) inout
-        in { assert(idx < _length); }
+        void pop(ref RANGE rng)
+        in { assert(_length > 0); }
         do
         {
-            return _p[idx];
+            rng = _p[--_length];
         }
 
-        @property size_t length() const { return _length; }
-        @property bool empty() const { return !length; }
+        @property bool empty() const { return !_length; }
+        size_t length() const { return _length; }
+        RANGE* ptr() { return _p; }
 
     private:
         void grow()
@@ -2486,14 +2484,14 @@ struct Gcx
             pragma(inline, false);
 
             enum initSize = 64 * 1024; // Windows VirtualAlloc granularity
-            immutable ncap = _cap ? 2 * _cap : initSize / RANGE.sizeof;
-            auto p = cast(RANGE*)os_mem_map(ncap * RANGE.sizeof);
+            immutable ncap = _cap ? 2 * _cap : initSize;
+            auto p = cast(RANGE*)os_mem_map(ncap);
             if (p is null) onOutOfMemoryError();
             debug (VALGRIND) makeMemUndefined(p[0..ncap]);
             if (_p !is null)
             {
                 p[0 .. _length] = _p[0 .. _length];
-                os_mem_unmap(_p, _cap * RANGE.sizeof);
+                os_mem_unmap(_p, _cap);
             }
             _p = p;
             _cap = ncap;
@@ -2501,18 +2499,20 @@ struct Gcx
 
         size_t _length;
         RANGE* _p;
-        size_t _cap;
+        size_t _cap; // in bytes
     }
 
-    ToScanStack!(ScanRange!false) toscanConservative;
-    ToScanStack!(ScanRange!true) toscanPrecise;
-
+    union
+    {
+        ToScanStack!(ScanRange!false) scanStackConservative;
+        ToScanStack!(ScanRange!true) scanStackPrecise;
+    }
     template scanStack(bool precise)
     {
         static if (precise)
-            alias scanStack = toscanPrecise;
+            alias scanStack = scanStackPrecise;
         else
-            alias scanStack = toscanConservative;
+            alias scanStack = scanStackConservative;
     }
 
     /**
@@ -2520,12 +2520,10 @@ struct Gcx
      */
     private void mark(bool precise, bool parallel, bool shared_mem)(ScanRange!precise rng) scope nothrow
     {
-        alias toscan = scanStack!precise;
-
         debug(MARK_PRINTF)
             printf("marking range: [%p..%p] (%#llx)\n", rng.pbot, rng.ptop, cast(long)(rng.ptop - rng.pbot));
 
-        // limit the amount of ranges added to the toscan stack
+        // limit the amount of ranges added to the scan stack
         enum FANOUT_LIMIT = 32;
         size_t stackPos;
         ScanRange!precise[FANOUT_LIMIT] stack = void;
@@ -2706,16 +2704,16 @@ struct Gcx
             {
                 static if (parallel)
                 {
-                    if (!toscan.popLocked(rng))
+                    if (!scanStackPopLocked(rng))
                         break; // nothing more to do
                 }
                 else
                 {
-                    if (toscan.empty)
+                    if (scanStack!precise.empty)
                         break; // nothing more to do
 
                     // pop range from global stack and recurse
-                    rng = toscan.pop();
+                    scanStack!precise.pop(rng);
                 }
             }
             // printf("  pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
@@ -2725,22 +2723,21 @@ struct Gcx
             rng.pbot += (void*).sizeof;
             if (rng.pbot < rng.ptop)
             {
-                if (stackPos < stack.length)
-                {
-                    stack[stackPos] = tgt;
-                    stackPos++;
-                    continue;
-                }
-                static if (parallel)
+                if (stackPos >= stack.length)
                 {
-                    toscan.stackLock.lock();
-                    scope(exit) toscan.stackLock.unlock();
+                    static if (parallel)
+                    {
+                        scanStackPushLocked!precise(stack);
+                    }
+                    else
+                    {
+                        scanStack!precise.pushReverse(stack);
+                    }
+                    stackPos = 0;
                 }
-                toscan.push(rng);
-                // reverse order for depth-first-order traversal
-                foreach_reverse (ref range; stack)
-                    toscan.push(range);
-                stackPos = 0;
+                stack[stackPos] = tgt;
+                stackPos++;
+                continue;
             }
         LendOfRange:
             // continue with last found range
@@ -3526,8 +3523,9 @@ Lmark:
                         Gcx.instance.numScanThreads = 0;
                         Gcx.instance.scanThreadData = null;
                         Gcx.instance.busyThreads = 0;
+                        Gcx.instance.stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
 
-                        memset(&Gcx.instance.evStart, 0, Gcx.instance.evStart.sizeof);
+                        memset(&Gcx.instance.evStackFilled, 0, Gcx.instance.evStackFilled.sizeof);
                         memset(&Gcx.instance.evDone, 0, Gcx.instance.evDone.sizeof);
                     }
                 }
@@ -3551,12 +3549,12 @@ Lmark:
     uint numScanThreads;
     ScanThreadData* scanThreadData;
 
-    Event evStart;
+    Event evStackFilled;
     Event evDone;
 
     shared uint busyThreads;
     shared uint stoppedThreads;
-    bool stopGC;
+    shared bool stopGC;
 
     void markParallel() nothrow
     {
@@ -3565,25 +3563,24 @@ Lmark:
         if (toscanRoots.empty)
             return;
 
-        void** pbot = toscanRoots._p;
-        void** ptop = toscanRoots._p + toscanRoots._length;
+        auto pbot = toscanRoots.ptr;
+        auto ptop = pbot + toscanRoots.length;
 
         debug(PARALLEL_PRINTF) printf("markParallel\n");
 
-        size_t pointersPerThread = toscanRoots._length / (numScanThreads + 1);
+        size_t pointersPerThread = toscanRoots.length / (numScanThreads + 1);
         if (pointersPerThread > 0)
         {
             void pushRanges(bool precise)()
             {
-                alias toscan = scanStack!precise;
-                toscan.stackLock.lock();
+                stackLock.lock();
 
                 for (int idx = 0; idx < numScanThreads; idx++)
                 {
-                    toscan.push(ScanRange!precise(pbot, pbot + pointersPerThread));
+                    scanStack!precise.push(ScanRange!precise(pbot, pbot + pointersPerThread));
                     pbot += pointersPerThread;
                 }
-                toscan.stackLock.unlock();
+                stackLock.unlock();
             }
             if (ConservativeGC.isPrecise)
                 pushRanges!true();
@@ -3592,21 +3589,22 @@ Lmark:
         }
         assert(pbot < ptop);
 
-        busyThreads.atomicOp!"+="(1); // main thread is busy
-
-        evStart.setIfInitialized();
+        evStackFilled.setIfInitialized(); // background threads start now
 
         debug(PARALLEL_PRINTF) printf("mark %lld roots\n", cast(ulong)(ptop - pbot));
 
+        void pullLoop(bool precise)()
+        {
+            mark!(precise, true, true)(ScanRange!precise(pbot, ptop));
+
+            while (pullFromScanStackImpl!precise())
+                evDone.wait(1.msecs);
+        }
         if (ConservativeGC.isPrecise)
-            mark!(true, true, true)(ScanRange!true(pbot, ptop, null));
+            pullLoop!(true)();
         else
-            mark!(false, true, true)(ScanRange!false(pbot, ptop));
-
-        busyThreads.atomicOp!"-="(1);
+            pullLoop!(false)();
 
-        debug(PARALLEL_PRINTF) printf("waitForScanDone\n");
-        pullFromScanStack();
         debug(PARALLEL_PRINTF) printf("waitForScanDone done\n");
     }
 
@@ -3654,7 +3652,7 @@ Lmark:
         if (!scanThreadData)
             onOutOfMemoryError();
 
-        evStart.initialize(false, false);
+        evStackFilled.initialize(true, false);
         evDone.initialize(false, false);
 
         version (Posix)
@@ -3696,8 +3694,8 @@ Lmark:
         stopGC = true;
         while (atomicLoad(stoppedThreads) < startedThreads && !allThreadsDead)
         {
-            evStart.setIfInitialized();
-            evDone.wait(dur!"msecs"(1));
+            evStackFilled.setIfInitialized();
+            evDone.wait(1.msecs);
         }
 
         for (int idx = 0; idx < numScanThreads; idx++)
@@ -3709,8 +3707,8 @@ Lmark:
             }
         }
 
+        evStackFilled.terminate();
         evDone.terminate();
-        evStart.terminate();
 
         cstdlib.free(scanThreadData);
         // scanThreadData = null; // keep non-null to not start again after shutdown
@@ -3723,26 +3721,24 @@ Lmark:
     {
         while (!stopGC)
         {
-            evStart.wait();
+            evStackFilled.wait();
             pullFromScanStack();
-            evDone.setIfInitialized();
+            evDone.setIfInitialized(); // tell main loop we are done
         }
         stoppedThreads.atomicOp!"+="(1);
+        evDone.setIfInitialized(); // wake up main
     }
 
-    void pullFromScanStack() nothrow
+    bool pullFromScanStack() nothrow
     {
         if (ConservativeGC.isPrecise)
-            pullFromScanStackImpl!true();
+            return pullFromScanStackImpl!true();
         else
-            pullFromScanStackImpl!false();
+            return pullFromScanStackImpl!false();
     }
 
-    void pullFromScanStackImpl(bool precise)() nothrow
+    bool pullFromScanStackImpl(bool precise)() nothrow
     {
-        if (atomicLoad(busyThreads) == 0)
-            return;
-
         version (Posix) debug (PARALLEL_PRINTF)
         {
             import core.sys.posix.pthread : pthread_self, pthread_t;
@@ -3751,29 +3747,60 @@ Lmark:
         }
 
         ScanRange!precise rng;
-        alias toscan = scanStack!precise;
 
-        while (atomicLoad(busyThreads) > 0)
+        stackLock.lock();
+        while (!scanStack!precise.empty())
         {
-            if (toscan.empty)
-            {
-                evDone.wait(dur!"msecs"(1));
-                continue;
-            }
-
             busyThreads.atomicOp!"+="(1);
-            if (toscan.popLocked(rng))
+            scanStack!precise.pop(rng);
+            if (scanStack!precise.empty)
+                evStackFilled.reset();
+            stackLock.unlock();
+
+            version (Posix) debug (PARALLEL_PRINTF)
             {
-                version (Posix) debug (PARALLEL_PRINTF)
-                {
-                    printf("scanBackground thread %d scanning range [%p,%lld] from stack\n",
-                        threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot));
-                }
-                mark!(precise, true, true)(rng);
+                printf("scanBackground thread %d scanning range [%p,%lld] from stack\n",
+                    threadId, rng.pbot, cast(long) (rng.ptop - rng.pbot));
             }
+            mark!(precise, true, true)(rng);
+            // returns here only if an empty scan stack has been seen
+
+            stackLock.lock();
             busyThreads.atomicOp!"-="(1);
         }
+        bool cont = busyThreads > 0;
+        stackLock.unlock();
+
         version (Posix) debug (PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId);
+        return cont;
+    }
+
+    auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
+
+    void scanStackPushLocked(bool precise)(ScanRange!precise[] ranges)
+    {
+        stackLock.lock();
+        scope(exit) stackLock.unlock();
+        bool wasEmpty = scanStack!precise.empty;
+
+        scanStack!precise.pushReverse(ranges);
+
+        if (wasEmpty)
+            evStackFilled.setIfInitialized();
+    }
+
+    bool scanStackPopLocked(bool precise)(ref ScanRange!precise rng)
+    {
+        stackLock.lock();
+        scope(exit) stackLock.unlock();
+        if (scanStack!precise.empty())
+            return false;
+
+        scanStack!precise.pop(rng);
+
+        if (scanStack!precise.empty())
+            evStackFilled.reset();
+        return true;
     }
 }
 
@@ -5366,6 +5393,26 @@ unittest
     }
 }
 
+// https://github.com/dlang/dmd/issues/21615
+debug(SENTINEL) {} else // no additional capacity with SENTINEL
+@safe unittest
+{
+    size_t numReallocations = 0;
+    ubyte[] buffer = new ubyte[4096];
+    auto p = &buffer[0];
+    foreach (i; 0 .. 1000) {
+        buffer.length += 4096;
+        if (p !is &buffer[0])
+        {
+            ++numReallocations;
+            p = &buffer[0];
+        }
+    }
+
+    // pick a decently small number, it's unclear where this memory will start out.
+    assert(numReallocations <= 5);
+}
+
 /* ============================ MEMSTOMP =============================== */
 
 /// Mark the specified memory region as uninitialized -
index 3da92db14e78b0253b33d3b7038a7eb6a6e268ef..5f9c04187ed947dbbbd799a991b3843fe8a0dbfc 100644 (file)
@@ -23,6 +23,8 @@ import core.gc.gcinterface;
 
 import core.internal.container.array;
 
+import core.thread.threadbase : ThreadBase;
+
 import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
 static import core.memory;
 
@@ -287,4 +289,12 @@ class ManualGC : GC
     {
         return false;
     }
+
+    void initThread(ThreadBase t) nothrow @nogc
+    {
+    }
+
+    void cleanupThread(ThreadBase t) nothrow @nogc
+    {
+    }
 }
index cbdcdb8852b5ff783b47022d75bda72ef639b86c..9dd12eab00140884002f321184006dd94654c38d 100644 (file)
@@ -3,6 +3,10 @@ module core.internal.gc.impl.proto.gc;
 
 import core.gc.gcinterface;
 
+import core.gc.registry : GCThreadInitFunction, threadInit;
+
+import core.thread.threadbase : ThreadBase;
+
 import core.internal.container.array;
 
 import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
@@ -28,11 +32,15 @@ private
     extern (C) void gc_addRoot(const void* p ) nothrow @nogc;
 }
 
+
 class ProtoGC : GC
 {
     Array!Root roots;
     Array!Range ranges;
 
+    // stored on first use, which should be called whenever rt_init is called.
+    private GCThreadInitFunction _initThreadFn;
+
     // Call this function when initializing the real GC
     // upon ProtoGC term. This function should be called
     // after the real GC is in place.
@@ -261,4 +269,23 @@ class ProtoGC : GC
     {
         return false;
     }
+
+    void initThread(ThreadBase thread) nothrow
+    {
+        if (_initThreadFn is null)
+        {
+            static void defaultThreadInit(ThreadBase base) nothrow @nogc { }
+            import core.gc.config;
+            config.initialize();
+            _initThreadFn = .threadInit(config.gc);
+            if (_initThreadFn is null)
+                _initThreadFn = &defaultThreadInit;
+        }
+
+        _initThreadFn(thread);
+    }
+
+    void cleanupThread(ThreadBase thread)
+    {
+    }
 }
index d46cb8cd775b888334d2ed6ad6b2fb60880cb7d7..9b83eb5d3fee8e791e0577fa181e185dc75cbf9a 100644 (file)
@@ -266,7 +266,7 @@ extern (C)
         return instance.shrinkArrayUsed( slice, existingUsed, atomic );
     }
 
-    GC gc_getProxy() nothrow
+    GC gc_getProxy() nothrow @nogc
     {
         return instance;
     }
index 80fdc32003276fe54353b133b6231cecd27a5c32..e5c6ab1f0f33a6dfa750361c5ded948cb63d1522 100644 (file)
@@ -132,6 +132,7 @@ private template canBitwiseHash(T)
 size_t hashOf(T)(auto ref T val, size_t seed = 0)
 if (is(T == enum) && !__traits(isScalar, T))
 {
+    pragma(inline, true);
     static if (is(T EType == enum)) {} //for EType
     return hashOf(cast(EType) val, seed);
 }
@@ -140,6 +141,7 @@ if (is(T == enum) && !__traits(isScalar, T))
 size_t hashOf(T)(scope const auto ref T val, size_t seed = 0)
 if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T)
 {
+    pragma(inline, true);
     import core.internal.convert : toUbyte;
     // FIXME:
     // We would like to to do this:
@@ -173,6 +175,7 @@ if (!is(T == enum) && __traits(isStaticArray, T) && canBitwiseHash!T)
 size_t hashOf(T)(auto ref T val, size_t seed = 0)
 if (!is(T == enum) && __traits(isStaticArray, T) && !canBitwiseHash!T)
 {
+    pragma(inline, true);
     // FIXME:
     // We would like to to do this:
     //
@@ -207,10 +210,12 @@ if (is(T == S[], S) && (__traits(isScalar, S) || canBitwiseHash!S)) // excludes
     else static if (is(typeof(toUbyte(val)) == const(ubyte)[]))
     //ubyteble array (arithmetic types and structs without toHash) CTFE ready for arithmetic types and structs without reference fields
     {
+        pragma(inline, true);
         return bytesHashAlignedBy!ElementType(toUbyte(val), seed);
     }
     else //Other types. CTFE unsupported
     {
+        pragma(inline, true);
         assert(!__ctfe, "unable to compute hash of "~T.stringof~" at compile time");
         return bytesHashAlignedBy!ElementType((cast(const(ubyte)*) val.ptr)[0 .. ElementType.sizeof*val.length], seed);
     }
@@ -258,6 +263,7 @@ size_t hashOf(T)(scope const T val) if (__traits(isScalar, T) && !is(T == __vect
     else static if (is(T EType == enum) && is(typeof(val[0])))
     {
         // Enum type whose base type is vector.
+        pragma(inline, true);
         return hashOf(cast(EType) val);
     }
     else static if (__traits(isIntegral, T))
@@ -293,6 +299,7 @@ size_t hashOf(T)(scope const T val, size_t seed) if (__traits(isScalar, T) && !i
 {
     static if (is(T V : V*))
     {
+        pragma(inline, true);
         if (__ctfe)
         {
             if (val is null) return hashOf(size_t(0), seed);
@@ -303,6 +310,7 @@ size_t hashOf(T)(scope const T val, size_t seed) if (__traits(isScalar, T) && !i
     else static if (is(T EType == enum) && is(typeof(val[0])))
     {
         // Enum type whose base type is vector.
+        pragma(inline, true);
         return hashOf(cast(EType) val, seed);
     }
     else static if (__traits(isIntegral, val) && T.sizeof <= size_t.sizeof)
@@ -379,6 +387,7 @@ if (is(T == __vector)) // excludes enum types
     }
     else
     {
+        pragma(inline, true);
         import core.internal.convert : toUbyte;
         return bytesHashAlignedBy!T(toUbyte(val), seed);
     }
@@ -388,6 +397,7 @@ if (is(T == __vector)) // excludes enum types
 @trusted @nogc nothrow pure
 size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null)))
 {
+    pragma(inline, true);
     return 0;
 }
 
@@ -395,6 +405,7 @@ size_t hashOf(T)(scope const T val) if (!is(T == enum) && is(T : typeof(null)))
 @trusted @nogc nothrow pure
 size_t hashOf(T)(scope const T val, size_t seed) if (!is(T == enum) && is(T : typeof(null)))
 {
+    pragma(inline, true);
     return hashOf(size_t(0), seed);
 }
 
@@ -572,6 +583,7 @@ if (!is(T == enum) && (is(T == struct) || is(T == union))
 @trusted @nogc nothrow pure
 size_t hashOf(T)(scope const T val, size_t seed = 0) if (!is(T == enum) && is(T == delegate))
 {
+    pragma(inline, true);
     if (__ctfe)
     {
         if (val is null) return hashOf(size_t(0), hashOf(size_t(0), seed));
@@ -586,6 +598,7 @@ size_t hashOf(T)(scope const T val)
 if (!is(T == enum) && (is(T == interface) || is(T == class))
     && canBitwiseHash!T)
 {
+    pragma(inline, true);
     if (__ctfe) if (val is null) return 0;
     return hashOf(cast(const void*) val);
 }
@@ -596,6 +609,7 @@ size_t hashOf(T)(scope const T val, size_t seed)
 if (!is(T == enum) && (is(T == interface) || is(T == class))
     && canBitwiseHash!T)
 {
+    pragma(inline, true);
     if (__ctfe) if (val is null) return hashOf(size_t(0), seed);
     return hashOf(cast(const void*) val, seed);
 }
@@ -664,6 +678,7 @@ size_t hashOf(T)(T aa, size_t seed) if (!is(T == enum) && __traits(isAssociative
 @system pure nothrow @nogc
 size_t bytesHash()(scope const(void)* buf, size_t len, size_t seed)
 {
+    pragma(inline, true);
     return bytesHashAlignedBy!ubyte((cast(const(ubyte)*) buf)[0 .. len], seed);
 }
 
@@ -702,6 +717,7 @@ private alias smallBytesHash = fnv;
 // handle aligned reads, do the conversion here
 private uint get32bits()(scope const(ubyte)* x) @nogc nothrow pure @system
 {
+    pragma(inline, true);
     version (BigEndian)
     {
         return ((cast(uint) x[0]) << 24) | ((cast(uint) x[1]) << 16) | ((cast(uint) x[2]) << 8) | (cast(uint) x[3]);
@@ -717,7 +733,7 @@ Params:
     dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
 +/
 @nogc nothrow pure @trusted
-private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
+private size_t _bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
 {
     auto len = bytes.length;
     auto data = bytes.ptr;
@@ -771,6 +787,34 @@ private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes,
     return h1;
 }
 
+// precompile bytesHash into the runtime to also get optimized versions in debug builds
+@nogc nothrow pure @trusted
+private size_t _bytesHashAligned(scope const(ubyte)[] bytes, size_t seed)
+{
+    pragma(inline, true);
+    return _bytesHash!true(bytes, seed);
+}
+@nogc nothrow pure @trusted
+private size_t _bytesHashUnaligned(scope const(ubyte)[] bytes, size_t seed)
+{
+    pragma(inline, true);
+    return _bytesHash!false(bytes, seed);
+}
+
+/+
+Params:
+dataKnownToBeAligned = whether the data is known at compile time to be uint-aligned.
++/
+@nogc nothrow pure @trusted
+private size_t bytesHash(bool dataKnownToBeAligned)(scope const(ubyte)[] bytes, size_t seed)
+{
+    pragma(inline, true);
+    static if (dataKnownToBeAligned)
+        return _bytesHashAligned(bytes, seed);
+    else
+        return _bytesHashUnaligned(bytes, seed);
+}
+
 //  Check that bytesHash works with CTFE
 pure nothrow @system @nogc unittest
 {
index fd78de221d11a8a84f00a02c84204c9f04a8ad1d..053b3edc69d2382340f3d1bd3ff5a58c24503aca 100644 (file)
@@ -210,14 +210,29 @@ void __doPostblit(T)(T[] arr)
     {
         static if (__traits(isStaticArray, T) && is(T : E[], E))
             __doPostblit(cast(E[]) arr);
-        else static if (!__traits(compiles, arr[0].__xpostblit))
+        else
         {
-            alias Unqual_T = Unqual!T;
-            foreach (ref elem; (() @trusted => cast(Unqual_T[]) arr)())
+            import core.internal.traits : Unqual;
+            foreach (ref elem; (() @trusted => cast(Unqual!T[]) arr)())
                 elem.__xpostblit();
         }
+    }
+}
+
+// ditto, but with an index to keep track of how many elements have been postblitted
+void __doPostblit(T)(T[] arr, ref size_t i)
+{
+    // infer static postblit type, run postblit if any
+    static if (__traits(hasPostblit, T))
+    {
+        static if (__traits(isStaticArray, T) && is(T : E[], E))
+            __doPostblit(cast(E[]) arr, i);
         else
-            foreach (ref elem; arr)
-                elem.__xpostblit();
+        {
+            i = 0;
+            import core.internal.traits : Unqual;
+            for(auto eptr = cast(Unqual!T*)&arr[0]; i < arr.length; ++i, ++eptr)
+                eptr.__xpostblit();
+        }
     }
 }
index 47283f28030191a8faff97683c9e1675532e87e7..764d1e7b5868e0459556f2e305d68e7360c0d934 100644 (file)
@@ -1,17 +1,21 @@
 /**
  Turn an Associative Array into a binary compatible struct for static initialization.
-
-   This does not implement all the pieces of
-   the associative array type in druntime, just enough to create an AA from an
-   existing range of key/value pairs.
-
-   Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022.
-   License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
-   Authors:   Martin Nowak, Steven Schveighoffer
-*/
* template implementation of associative arrays.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2015, Steven Schveighoffer 2022.
+ * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:   Martin Nowak, Steven Schveighoffer, Rainer Schuetze
+ *
+ * Source: $(DRUNTIMESRC core/internal/_newaa.d)
+ *
+ * derived from rt/aaA.d
+ */
 module core.internal.newaa;
 
-import core.memory;
+/// AA version for debuggers, bump whenever changing the layout
+immutable int _aaVersion = 1;
+
+import core.internal.util.math : min, max;
+import core.internal.traits : substInout;
 
 // grow threshold
 private enum GROW_NUM = 4;
@@ -31,26 +35,225 @@ private enum INIT_DEN = SHRINK_DEN * GROW_DEN;
 private enum INIT_NUM_BUCKETS = 8;
 // magic hash constants to distinguish empty, deleted, and filled buckets
 private enum HASH_EMPTY = 0;
+private enum HASH_DELETED = 0x1;
 private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
 
-private struct Bucket
+/// AA wrapper
+struct AA(K, V)
 {
-    size_t hash;
-    void *entry;
+    Impl!(K,V)* impl;
+    alias impl this;
+
+    @property bool empty() const pure nothrow @nogc @safe
+    {
+        pragma(inline, true);
+        return impl is null || !impl.length;
+    }
+    @property size_t length() const pure nothrow @nogc @safe
+    {
+        pragma(inline, true);
+        return impl is null ? 0 : impl.length;
+    }
+}
+
+/// like core.internal.traits.Unconst, but stripping inout, too
+private template Unconstify(T : const U, U)
+{
+    static if (is(U == inout V, V))
+        alias Unconstify = V;
+    else
+        alias Unconstify = U;
+}
+
+ref _refAA(K, V)(ref V[K] aa) @trusted
+{
+    pragma(inline, true);
+    return *(cast(AA!(substInout!K, substInout!V)*)&aa);
+}
+
+auto _toAA(K, V)(const V[K] aa) @trusted
+{
+    pragma(inline, true);
+    return *(cast(const(AA!(K, V))*)&aa);
+}
+
+auto _toAA(K, V)(inout V[K] aa) @trusted
+{
+    pragma(inline, true);
+    return *(cast(inout(AA!(K, V))*)&aa);
 }
 
-struct Impl
+// for backward compatibility, but should be deprecated
+auto _toAA(K, V)(shared const V[K] aa) @trusted
 {
+    pragma(inline, true);
+    return *(cast(AA!(K, V)*)&aa);
+}
+
+// for backward compatibility, but should be deprecated
+auto _toAA(K, V)(shared V[K] aa) @trusted
+{
+    pragma(inline, true);
+    return *(cast(AA!(K, V)*)&aa);
+}
+
+// resolve ambiguity for immutable converting to const and shared const
+auto _toAA(K, V)(immutable V[K] aa) @trusted
+{
+    pragma(inline, true);
+    return *(cast(AA!(K, V)*)&aa);
+}
+
+static struct Entry(K, V)
+{
+    K key;
+    V value;
+}
+
+// backward compatibility conversions
+private ref compat_key(K, K2)(ref K2 key)
+{
+    pragma(inline, true);
+    static if (is(K2 == const(char)[]) && is(K == string))
+        return (ref (ref return K2 k2) @trusted => *cast(string*)&k2)(key);
+    else
+        return key;
+}
+
+private void _aaMove(V)(ref V src, ref V dst) @trusted
+{
+    import core.stdc.string : memcpy, memset;
+    // move without postblit!?
+    memcpy(&dst, &src, V.sizeof);
+    static if (__traits(isZeroInit, V))
+        memset(&src, 0, V.sizeof);
+    else
+        memcpy(&src, &V.init, V.sizeof);
+}
+
+// mimick behaviour of rt.aaA for initialization
+Entry!(K, V)* _newEntry(K, V)(ref K key, ref V value)
+{
+    static if (__traits(compiles, new Entry!(K, V)(key, value)))
+    {
+        auto entry = new Entry!(K, V)(key, value);
+    }
+    else static if (__traits(compiles, { K k; new Entry!(K, V)(k); }))
+    {
+        auto entry = new Entry!(K, V)(key);
+        _aaMove(value, entry.value);
+    }
+    else
+    {
+        auto entry = new Entry!(K, V);
+        _aaMove(key, entry.key);
+        _aaMove(value, entry.value);
+    }
+    return entry;
+}
+
+// mimick behaviour of rt.aaA for initialization
+Entry!(K, V)* _newEntry(K, V, K2)(ref K2 key)
+{
+    static if (__traits(compiles, new Entry!(K, V)(key)) &&
+               !(is(V == struct) && __traits(isNested, V))) // not detected by "compiles"
+    {
+        auto entry = new Entry!(K, V)(key);
+    }
+    else static if (__traits(compiles, { K2 k; new Entry!(K, V)(k, V.init); }))
+    {
+        // with disabled ctor for V
+        auto entry = new Entry!(K, V)(key, V.init);
+    }
+    else
+    {
+        // with disabled ctor for K and V
+        auto entry = new Entry!(K, V);
+        entry.key = key;
+    }
+    static if (!__traits(isZeroInit, V))
+    {
+        () @trusted { (cast(ubyte*)&entry.value)[0..V.sizeof] = 0; }();
+    }
+    return entry;
+}
+
+template pure_hashOf(K)
+{
+    static if (__traits(compiles, function hash_t(scope const ref K key) pure nothrow @nogc @trusted { return hashOf(cast()key); }))
+    {
+        // avoid wrapper call in debug builds if pure nothrow @nogc is inferred
+        pragma(inline, true)
+        hash_t pure_hashOf(scope const ref K key) @trusted { return hashOf(cast()key); }
+    }
+    else
+    {
+        // for backward compatibility, do not require const in hashOf()
+        hash_t wrap_hashOf(K)(scope const ref K key) @trusted { return hashOf(cast()key); }
+        enum pure_hashOf = cast(hash_t function(scope ref const K key) pure nothrow @nogc @safe) &wrap_hashOf!K;
+    }
+}
+
+// for backward compatibilty pretend the comparison is @safe, pure, etc
+// this also breaks cyclic inference on recursive data types
+template pure_keyEqual(K1, K2 = K1)
+{
+    static if (__traits(compiles, function bool(ref const K1 k1, ref const K2 k2) pure nothrow @nogc @trusted { return cast()k1 == cast()k2; }))
+    {
+        // avoid wrapper call in debug builds if pure nothrow @nogc is inferred
+        pragma(inline, true)
+        bool pure_keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; }
+    }
+    else
+    {
+        bool keyEqual(ref const K1 k1, ref const K2 k2) @trusted { return cast()k1 == cast()k2; }
+        enum pure_keyEqual = cast(bool function(ref const K1, ref const K2) pure nothrow @nogc @safe) &keyEqual;
+    }
+}
+
+private struct Impl(K, V)
+{
+private:
+    alias Bucket = .Bucket!(K, V);
+
+    this(size_t sz /* = INIT_NUM_BUCKETS */) nothrow
+    {
+        buckets = allocBuckets(sz);
+        firstUsed = cast(uint) buckets.length;
+
+        // only for binary compatibility
+        version(D_TypeInfo)
+            entryTI = typeid(Entry!(K, V));
+        hashFn = delegate size_t (scope ref const K key) nothrow pure @nogc @safe {
+            return pure_hashOf!K(key);
+        };
+
+        keysz = cast(uint) K.sizeof;
+        valsz = cast(uint) V.sizeof;
+        valoff = cast(uint) talign(keysz, V.alignof);
+
+        enum ctflags = () {
+            import core.internal.traits;
+            Impl.Flags flags;
+            static if (__traits(hasPostblit, K))
+                flags |= flags.keyHasPostblit;
+            static if (hasIndirections!K || hasIndirections!V)
+                flags |= flags.hasPointers;
+            return flags;
+        } ();
+        flags = ctflags;
+    }
+
     Bucket[] buckets;
     uint used;
     uint deleted;
-    const(TypeInfo) entryTI;
+    const(TypeInfo) entryTI; // only for binary compatibility
     uint firstUsed;
-    immutable uint keysz;
-    immutable uint valsz;
-    immutable uint valoff;
-    Flags flags;
-    size_t delegate(scope const void*) nothrow hashFn;
+    immutable uint keysz;    // only for binary compatibility
+    immutable uint valsz;    // only for binary compatibility
+    immutable uint valoff;   // only for binary compatibility
+    Flags flags;             // only for binary compatibility
+    size_t delegate(scope ref const K) nothrow pure @nogc @safe hashFn;
 
     enum Flags : ubyte
     {
@@ -58,13 +261,150 @@ struct Impl
         keyHasPostblit = 0x1,
         hasPointers = 0x2,
     }
+
+    @property size_t length() const pure nothrow @nogc @safe
+    {
+        pragma(inline, true);
+        assert(used >= deleted);
+        return used - deleted;
+    }
+
+    @property size_t dim() const pure nothrow @nogc @safe
+    {
+        pragma(inline, true);
+        return buckets.length;
+    }
+
+    @property size_t mask() const pure nothrow @nogc @safe
+    {
+        pragma(inline, true);
+        return dim - 1;
+    }
+
+    // find the first slot to insert a value with hash
+    size_t findSlotInsert(size_t hash) const pure nothrow @nogc @safe
+    {
+        for (size_t i = hash & mask, j = 1;; ++j)
+        {
+            if (!buckets[i].filled)
+                return i;
+            i = (i + j) & mask;
+        }
+    }
+
+    // lookup a key
+    inout(Bucket)* findSlotLookup(K2)(size_t hash, scope ref const K2 key) inout pure @safe nothrow
+    {
+        for (size_t i = hash & mask, j = 1;; ++j)
+        {
+            auto b = &buckets[i]; // avoid multiple bounds checks
+            if (b.hash == hash && b.entry)
+                if (pure_keyEqual!(K2, K)(key, b.entry.key))
+                    return b;
+            if (b.empty)
+                return null;
+            i = (i + j) & mask;
+        }
+    }
+
+    void grow() pure nothrow @safe
+    {
+        // If there are so many deleted entries, that growing would push us
+        // below the shrink threshold, we just purge deleted entries instead.
+        if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
+            resize(dim);
+        else
+            resize(GROW_FAC * dim);
+    }
+
+    void shrink() pure nothrow @safe
+    {
+        if (dim > INIT_NUM_BUCKETS)
+            resize(dim / GROW_FAC);
+    }
+
+    void resize(size_t ndim) pure nothrow @safe
+    {
+        auto obuckets = buckets;
+        buckets = allocBuckets(ndim);
+
+        foreach (ref b; obuckets[firstUsed .. $])
+            if (b.filled)
+                buckets[findSlotInsert(b.hash)] = b;
+
+        firstUsed = 0;
+        used -= deleted;
+        deleted = 0;
+        obuckets.length = 0; // safe to free b/c impossible to reference, but doesn't really free
+    }
+
+    void clear() pure nothrow
+    {
+        // clear all data, but don't change bucket array length
+        buckets[firstUsed .. $] = Bucket.init;
+        deleted = used = 0;
+        firstUsed = cast(uint) dim;
+    }
+
+    size_t calcHash(K2)(ref K2 key) const nothrow pure @nogc @safe
+    {
+        static if(is(K2* : K*)) // ref compatible?
+            hash_t hash = pure_hashOf!K(key);
+        else
+            hash_t hash = pure_hashOf!K2(key);
+        // highest bit is set to distinguish empty/deleted from filled buckets
+        return mix(hash) | HASH_FILLED_MARK;
+    }
+
+    static Bucket[] allocBuckets(size_t dim) pure nothrow @safe
+    {
+        // could allocate with BlkAttr.NO_INTERIOR, but that does not combine
+        //  well with arrays and type info for precise scanning
+        return new Bucket[dim];
+    }
 }
 
-private struct AAShell
+//==============================================================================
+// Bucket
+//------------------------------------------------------------------------------
+
+private struct Bucket(K, V)
+{
+private pure nothrow @nogc:
+    size_t hash;
+    Entry!(K, V)* entry;
+
+    @property bool empty() const
+    {
+        pragma(inline, true);
+        return hash == HASH_EMPTY;
+    }
+
+    @property bool deleted() const
+    {
+        pragma(inline, true);
+        return hash == HASH_DELETED;
+    }
+
+    @property bool filled() const @safe
+    {
+        pragma(inline, true);
+        return cast(ptrdiff_t) hash < 0;
+    }
+}
+
+//==============================================================================
+// Helper functions
+//------------------------------------------------------------------------------
+
+private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
 {
-    Impl *impl;
+    immutable mask = algn - 1;
+    assert(!(mask & algn));
+    return (tsize + mask) & ~mask;
 }
 
+// mix hash to "fix" bad hash functions
 private size_t mix(size_t h) @safe pure nothrow @nogc
 {
     // final mix function of MurmurHash2
@@ -75,74 +415,612 @@ private size_t mix(size_t h) @safe pure nothrow @nogc
     return h;
 }
 
-// create a binary-compatible AA structure that can be used directly as an
-// associative array.
-// NOTE: this must only be called during CTFE
-AAShell makeAA(K, V)(V[K] src) @trusted
+private size_t nextpow2(const size_t n) pure nothrow @nogc @safe
 {
-    assert(__ctfe, "makeAA Must only be called at compile time");
-    immutable srclen = src.length;
-    assert(srclen <= uint.max);
-    alias E = TypeInfo_AssociativeArray.Entry!(K, V);
-    if (srclen == 0)
-        return AAShell.init;
-    // first, determine the size that would be used if we grew the bucket list
-    // one element at a time using the standard AA algorithm.
-    size_t dim = INIT_NUM_BUCKETS;
-    while (srclen * GROW_DEN > dim * GROW_NUM)
-        dim = dim * GROW_FAC;
-
-    // used during runtime.
-    typeof(Impl.hashFn) hashFn = (scope const void* val) {
-        auto x = cast(K*)val;
-        return hashOf(*x);
-    };
+    import core.bitop : bsr;
 
-    Bucket[] buckets;
-    // Allocate and fill the buckets
-    if (__ctfe)
-        buckets = new Bucket[dim];
+    if (!n)
+        return 1;
+
+    const isPowerOf2 = !((n - 1) & n);
+    return 1 << (bsr(n) + !isPowerOf2);
+}
+
+pure nothrow @nogc unittest
+{
+    //                            0, 1, 2, 3, 4, 5, 6, 7, 8,  9
+    foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16])
+        assert(nextpow2(n) == pow2);
+}
+
+//==============================================================================
+// API Implementation
+//------------------------------------------------------------------------------
+
+/** Allocate associative array data.
+ * Called for `new SomeAA` expression.
+ * Returns:
+ *      A new associative array.
+ * Note:
+ *  not supported in CTFE
+ */
+V[K] _d_aaNew(K, V)()
+{
+    AA!(K, V) aa;
+    aa.impl = new Impl!(K,V)(INIT_NUM_BUCKETS);
+    return *cast(V[K]*)&aa;
+}
+
+/// Determine number of entries in associative array.
+/// Note:
+///  emulated by the compiler during CTFE
+size_t _d_aaLen(K, V)(inout V[K] a)
+{
+    auto aa = _toAA!(K, V)(a);
+    return aa ? aa.length : 0;
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of (aa[key]) expressions when value is mutable.
+ * Params:
+ *      aa = associative array
+ *      key = reference to the key value
+ *      found = returns whether the key was found or a new entry was added
+ * Returns:
+ *      if key was in the aa, a mutable pointer to the existing value.
+ *      If key was not in the aa, a mutable pointer to newly inserted value which
+ *      is set to zero
+ */
+V* _d_aaGetY(K, V, T : V1[K1], K1, V1, K2)(auto ref scope T aa, auto ref K2 key, out bool found)
+{
+    ref aax = cast(V[K])cast(V1[K1])aa; // remove outer const from T
+    return _aaGetX!(K, V)(aax, key, found);
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of require, update and _d_aaGetY
+ * Params:
+ *      a = associative array
+ *      key = reference to the key value
+ *      found = true if the value was found
+ * Returns:
+ *      if key was in the aa, a mutable pointer to the existing value.
+ *      If key was not in the aa, a mutable pointer to newly inserted value which
+ *      is set to V.init
+ */
+V* _aaGetX(K, V, K2)(auto ref scope V[K] a, auto ref K2 key, out bool found)
+{
+    ref aa = _refAA!(K, V)(a);
+
+    // lazily alloc implementation
+    if (aa is null)
+    {
+        aa.impl = new Impl!(K, V)(INIT_NUM_BUCKETS);
+    }
+
+    ref key2 = compat_key!(K)(key);
+
+    // get hash and bucket for key
+    immutable hash = aa.calcHash(key2);
+
+    // found a value => return it
+    if (auto p = aa.findSlotLookup(hash, key2))
+    {
+        found = true;
+        return &p.entry.value;
+    }
+
+    auto pi = aa.findSlotInsert(hash);
+    if (aa.buckets[pi].deleted)
+        --aa.deleted;
+    // check load factor and possibly grow
+    else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
+    {
+        aa.grow();
+        pi = aa.findSlotInsert(hash);
+        assert(aa.buckets[pi].empty);
+    }
+
+    // update search cache and allocate entry
+    aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
+    ref p = aa.buckets[pi];
+    p.hash = hash;
+    p.entry = _newEntry!(K, V)(key2);
+    return &p.entry.value;
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of (aa[key]) expressions when value is not mutable.
+ * Params:
+ *      aa = associative array
+ *      key = key value
+ * Returns:
+ *      pointer to value if present, null otherwise
+ */
+auto _d_aaGetRvalueX(K, V, K2)(inout V[K] aa, auto ref scope K2 key)
+{
+    return _d_aaIn(aa, key);
+}
+
+/// ditto
+auto _d_aaGetRvalueX(K, V, K2)(shared(V[K]) aa, auto ref scope K2 key)
+{
+    // accept shared for backward compatibility, should be deprecated
+    return cast(shared(V)*)_d_aaIn(cast(V[K]) aa, key);
+}
+
+/// ditto
+auto _d_aaGetRvalueX(K, V, K2)(shared const(V[K]) aa, auto ref scope K2 key)
+{
+    // accept shared for backward compatibility, should be deprecated
+    return cast(const shared(V)*)_d_aaIn(cast(V[K]) aa, key);
+}
+
+/// ditto
+auto _d_aaGetRvalueX(K, V, K2)(immutable(V[K]) aa, auto ref scope K2 key)
+{
+    // resolve ambiguity for immutable converting to const and shared const
+    return _d_aaIn((() @trusted => cast(V[K]) aa) (), key);
+}
+
+/***********************************
+ * Creates a new associative array of the same size and copies the contents of
+ * the associative array into it.
+ * Params:
+ *      a =     The associative array.
+ */
+auto _aaDup(T : V[K], K, V)(T a)
+{
+    auto aa = _toAA!(K, V)(a);
+    immutable len = aa.length;
+    if (len == 0)
+        return null;
+
+    auto impl = new Impl!(K, V)(aa.dim);
+    // copy the entries
+    bool sameHash = aa.hashFn == impl.hashFn; // can be different if coming from template/rt
+    foreach (b; aa.buckets[aa.firstUsed .. $])
+    {
+        if (!b.filled)
+            continue;
+        hash_t hash = sameHash ? b.hash : impl.calcHash(b.entry.key);
+        auto pi = impl.findSlotInsert(hash);
+        auto p = &impl.buckets[pi];
+        p.hash = hash;
+        p.entry = new Entry!(K, V)(b.entry.key, b.entry.value);
+        impl.firstUsed = min(impl.firstUsed, cast(uint)pi);
+    }
+    impl.used = cast(uint) len;
+    return () @trusted { return *cast(Unconstify!V[K]*)&impl; }();
+}
+
+/******************************
+ * Lookup key in aa.
+ * Called only from implementation of (key in aa) expressions.
+ * Params:
+ *      a = associative array opaque pointer
+ *      key = reference to the key value
+ * Returns:
+ *      pointer to value if present, null otherwise
+ */
+auto _d_aaIn(T : V[K], K, V, K2)(inout T a, auto ref scope K2 key)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (aa.empty)
+        return null;
+
+    ref key2 = compat_key!(K)(key);
+
+    immutable hash = aa.calcHash(key2);
+    if (auto p = aa.findSlotLookup(hash, key2))
+        return &p.entry.value;
+    return null;
+}
+
+// fake purity for backward compatibility with runtime hooks
+private extern(C) bool gc_inFinalizer() pure nothrow @safe;
+
+/// Delete entry scope const AA, return true if it was present
+auto _d_aaDel(T : V[K], K, V, K2)(T a, auto ref K2 key)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (aa.empty)
+        return false;
+
+    ref key2 = compat_key!(K)(key);
+
+    immutable hash = aa.calcHash(key2);
+    if (auto p = aa.findSlotLookup(hash, key2))
+    {
+        // clear entry
+        p.hash = HASH_DELETED;
+        p.entry = null;
+
+        ++aa.deleted;
+        // `shrink` reallocates, and allocating from a finalizer leads to
+        // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
+        if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !__ctfe && !gc_inFinalizer())
+            aa.shrink();
+
+        return true;
+    }
+    return false;
+}
+
+/// Remove all elements from AA.
+void _aaClear(K, V)(V[K] a)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (!aa.empty)
+    {
+        aa.clear();
+    }
+}
+
+/// Rehash AA
+V[K] _aaRehash(K, V)(V[K] a)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (!aa.empty)
+        aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
+    return a;
+}
+
+/// Return a GC allocated array of all values
+auto _aaValues(K, V)(inout V[K] a)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (aa.empty)
+        return null;
+
+    static if (__traits(compiles, { V val = aa.buckets[0].entry.value; } ))
+        V[] res; // if value has no const indirections
+    else
+        typeof([aa.buckets[0].entry.value]) res; // as mutable as it can get
+    res = new typeof(res[0])[aa.length];
+
+    if (false) // never execute, but infer function attributes from this operation
+        res ~= aa.buckets[0].entry.value;
+
+    size_t i = 0;
+    foreach (b; aa.buckets[aa.firstUsed .. $])
+    {
+        if (!b.filled)
+            continue;
+        import core.lifetime;
+        () @trusted { copyEmplace(b.entry.value, res[i++]); }();
+    }
+    return res;
+}
+
+/// Return a GC allocated array of all keys
+auto _aaKeys(K, V)(inout V[K] a)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (aa.empty)
+        return null;
+
+    static if (__traits(compiles, { K key = aa.buckets[0].entry.key; } ))
+        K[] res; // if key has no const indirections
     else
-        assert(0);
+        typeof([aa.buckets[0].entry.key]) res; // as mutable as it can get
+    res = new typeof(res[0])[aa.length];
 
-    assert(buckets.length >= dim);
+    if (false) // never execute, but infer function attributes from this operation
+        res ~= aa.buckets[0].entry.key;
 
-    immutable mask = dim - 1;
-    assert((dim & mask) == 0); // must be a power of 2
+    size_t i = 0;
+    foreach (b; aa.buckets[aa.firstUsed .. $])
+    {
+        if (!b.filled)
+            continue;
+        // res ~= b.entry.key;
+        import core.lifetime;
+        () @trusted { copyEmplace(b.entry.key, res[i++]); }();
+    }
+    return res;
+}
 
-    Bucket* findSlotInsert(immutable size_t hash)
+/// foreach opApply over all values
+/// Note:
+///  emulated by the compiler during CTFE
+int _d_aaApply(K, V, DG)(inout V[K] a, DG dg)
+{
+    auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }();
+    if (aa.empty)
+        return 0;
+
+    foreach (b; aa.buckets)
     {
-        for (size_t i = hash & mask, j = 1;; ++j)
+        if (!b.filled)
+            continue;
+        if (auto res = dg(b.entry.value))
+            return res;
+    }
+    return 0;
+}
+
+int _d_aaApply(K, V, DG)(shared V[K] a, DG dg)
+{
+    return _d_aaApply!(K, V, DG)(cast(V[K]) a, dg);
+}
+
+int _d_aaApply(K, V, DG)(shared const V[K] a, DG dg)
+{
+    return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+int _d_aaApply(K, V, DG)(immutable V[K] a, DG dg)
+{
+    return _d_aaApply!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+/// foreach opApply over all key/value pairs
+/// Note:
+///  emulated by the compiler during CTFE
+int _d_aaApply2(K, V, DG)(inout V[K] a, DG dg)
+{
+    auto aa = () @trusted { return cast(AA!(K, V))_toAA!(K, V)(a); }();
+    if (aa.empty)
+        return 0;
+
+    foreach (b; aa.buckets)
+    {
+        if (!b.filled)
+            continue;
+        if (auto res = dg(b.entry.key, b.entry.value))
+            return res;
+    }
+    return 0;
+}
+
+int _d_aaApply2(K, V, DG)(shared V[K] a, DG dg)
+{
+    return _d_aaApply2!(K, V, DG)(cast(V[K]) a, dg);
+}
+
+int _d_aaApply2(K, V, DG)(shared const V[K] a, DG dg)
+{
+    return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+int _d_aaApply2(K, V, DG)(immutable V[K] a, DG dg)
+{
+    return _d_aaApply2!(K, V, DG)(cast(const V[K]) a, dg);
+}
+
+/** Construct an associative array of type ti from corresponding keys and values.
+ * Called for an AA literal `[k1:v1, k2:v2]`.
+ * Params:
+ *      keys = array of keys
+ *      vals = array of values
+ * Returns:
+ *      A new associative array opaque pointer, or null if `keys` is empty.
+ */
+Impl!(K, V)* _d_assocarrayliteralTX(K, V)(K[] keys, V[] vals)
+{
+    assert(keys.length == vals.length);
+
+    immutable length = keys.length;
+
+    if (!length)
+        return null;
+
+    auto aa = new Impl!(K, V)(nextpow2(INIT_DEN * length / INIT_NUM));
+    size_t duplicates = 0;
+    foreach (i; 0 .. length)
+    {
+        immutable hash = aa.calcHash(keys[i]);
+
+        auto p = aa.findSlotLookup!K(hash, keys[i]);
+        if (p)
         {
-            if (buckets[i].hash == HASH_EMPTY)
-                return &buckets[i];
-            i = (i + j) & mask;
+            static if (__traits(compiles, p.entry.value = vals[i])) // immutable?
+                p.entry.value = vals[i];
+            else
+                p.entry = _newEntry!(K, V)(keys[i], vals[i]);
+            duplicates++;
+            continue;
+        }
+        auto pi = aa.findSlotInsert(hash);
+        p = &aa.buckets[pi];
+        p.hash = hash;
+        p.entry = _newEntry!(K, V)(keys[i], vals[i]); // todo: move key and value?
+        aa.firstUsed = min(aa.firstUsed, cast(uint)pi);
+    }
+    aa.used = cast(uint) (length - duplicates);
+    return aa;
+}
+
+/// compares 2 AAs for equality
+bool _aaEqual(T : AA!(K, V), K, V)(scope T aa1, scope T aa2)
+{
+    if (aa1 is aa2)
+        return true;
+
+    immutable len = aa1.length;
+    if (len != aa2.length)
+        return false;
+
+    if (!len) // both empty
+        return true;
+
+    bool sameHash = aa1.hashFn == aa2.hashFn; // can be different if coming from template/rt
+    // compare the entries
+    foreach (b1; aa1.buckets[aa1.firstUsed .. $])
+    {
+        if (!b1.filled)
+            continue;
+        hash_t hash = sameHash ? b1.hash : aa2.calcHash(b1.entry.key);
+        auto pb2 = aa2.findSlotLookup!K(hash, b1.entry.key);
+        if (pb2 is null || !pure_keyEqual!(V, V)(b1.entry.value, pb2.entry.value)) // rarely, inference on opEqual breaks builds here
+            return false;
+    }
+    return true;
+}
+
+/// compares 2 AAs for equality (compiler hook)
+bool _d_aaEqual(K, V)(scope const V[K] a1, scope const V[K] a2)
+{
+    scope aa1 = _toAA!(K, V)(a1);
+    scope aa2 = _toAA!(K, V)(a2);
+    return _aaEqual(aa1, aa2);
+}
+
+/// callback from TypeInfo_AssociativeArray.equals (ignore const for now)
+bool _aaOpEqual(K, V)(scope /* const */ AA!(K, V)* aa1, scope /* const */ AA!(K, V)* aa2)
+{
+    return _aaEqual(*aa1, *aa2);
+}
+
+/// compute a hash callback from TypeInfo_AssociativeArray.xtoHash (ignore scope const for now)
+hash_t _aaGetHash(K, V)(/* scope const */ AA!(K, V)* paa)
+{
+    const aa = *paa;
+
+    if (aa.empty)
+        return 0;
+
+    size_t h;
+    foreach (b; aa.buckets)
+    {
+        // use addition here, so that hash is independent of element order
+        if (b.filled)
+            h += hashOf(pure_hashOf!V(b.entry.value), pure_hashOf!K(b.entry.key));
+    }
+
+    return h;
+}
+
+/**
+ * _aaRange implements a ForwardRange
+ */
+struct AARange(K, V)
+{
+    alias Key = substInout!K;
+    alias Value = substInout!V;
+
+    Impl!(Key, Value)* impl;
+    size_t idx;
+    alias impl this;
+}
+
+AARange!(K, V) _aaRange(K, V)(V[K] a)
+{
+    auto aa = _toAA!(K, V)(a);
+    if (!aa)
+        return AARange!(K, V)();
+
+    foreach (i; aa.firstUsed .. aa.dim)
+    {
+        if (aa.buckets[i].filled)
+            return AARange!(K, V)(aa, i);
+    }
+    return AARange!(K, V)(aa, aa.dim);
+}
+
+bool _aaRangeEmpty(K, V)(AARange!(K, V) r)
+{
+    return r.impl is null || r.idx >= r.dim;
+}
+
+K* _aaRangeFrontKey(K, V)(AARange!(K, V) r)
+{
+    assert(!_aaRangeEmpty(r));
+    if (r.idx >= r.dim)
+        return null;
+    auto entry = r.buckets[r.idx].entry;
+    return entry is null ? null : &r.buckets[r.idx].entry.key;
+}
+
+V* _aaRangeFrontValue(K, V)(AARange!(K, V) r)
+{
+    assert(!_aaRangeEmpty(r));
+    if (r.idx >= r.dim)
+        return null;
+
+    auto entry = r.buckets[r.idx].entry;
+    return entry is null ? null : &r.buckets[r.idx].entry.value;
+}
+
+void _aaRangePopFront(K, V)(ref AARange!(K, V) r)
+{
+    if (r.idx >= r.dim) return;
+    for (++r.idx; r.idx < r.dim; ++r.idx)
+    {
+        if (r.buckets[r.idx].filled)
+            break;
+    }
+}
+
+// test postblit for AA literals
+unittest
+{
+    import core.memory;
+
+    static struct T
+    {
+        ubyte field;
+        static size_t postblit, dtor;
+        this(this)
+        {
+            ++postblit;
+        }
+
+        ~this()
+        {
+            ++dtor;
         }
     }
 
-    uint firstUsed = cast(uint) buckets.length;
-    foreach (k, v; src)
-    {
-        immutable h = hashOf(k).mix | HASH_FILLED_MARK;
-        auto location = findSlotInsert(h);
-        immutable nfu = cast(uint) (location - &buckets[0]);
-        if (nfu < firstUsed)
-            firstUsed = nfu;
-        *location = Bucket(h, new E(k, v));
-    }
-
-    enum flags = () {
-        import core.internal.traits;
-        Impl.Flags flags;
-        static if (__traits(hasPostblit, K))
-            flags |= flags.keyHasPostblit;
-        static if (hasIndirections!E)
-            flags |= flags.hasPointers;
-        return flags;
-    } ();
-    // return the new implementation
-    return AAShell(new Impl(buckets, cast(uint)srclen, 0, typeid(E), firstUsed,
-            K.sizeof, V.sizeof, E.value.offsetof, flags, hashFn));
+    T t;
+    auto aa1 = [0 : t, 1 : t];
+    assert(T.dtor == 2 && T.postblit == 4);
+    aa1[0] = t;
+    assert(T.dtor == 3 && T.postblit == 5);
+
+    T.dtor = 0;
+    T.postblit = 0;
+
+    auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten
+    assert(T.dtor == 4 && T.postblit == 6);
+
+    T.dtor = 0;
+    T.postblit = 0;
+
+    auto aa3 = [t : 0];
+    assert(T.dtor == 1 && T.postblit == 2);
+    aa3[t] = 1;
+    assert(T.dtor == 1 && T.postblit == 2);
+    aa3.remove(t);
+    assert(T.dtor == 1 && T.postblit == 2);
+    aa3[t] = 2;
+    assert(T.dtor == 1 && T.postblit == 3);
+
+    // dtor will be called by GC finalizers
+    aa1 = null;
+    aa2 = null;
+    aa3 = null;
+    auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor;
+    GC.runFinalizers((cast(char*)dtor1)[0 .. 1]);
+    auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor;
+    GC.runFinalizers((cast(char*)dtor2)[0 .. 1]);
+    assert(T.dtor == 7 && T.postblit == 3);
+}
+
+// create a binary-compatible AA structure that can be used directly as an
+// associative array.
+// NOTE: this must only be called during CTFE
+AA!(K, V) makeAA(K, V)(V[K] src) @trusted
+{
+    assert(__ctfe, "makeAA Must only be called at compile time");
+    // keys and values are cheap operations in CTFE, so just reuse _d_assocarrayliteralTX
+    auto impl = _d_assocarrayliteralTX!(K, V)(cast(K[])src.keys, cast(V[])src.values);
+    auto aa = AA!(K, V)(impl);
+    return aa;
 }
 
 unittest
index 2475559878c15e28e753e0c75ca760ac4378d35a..52e3307256bda7f341e0c9606232570cccd878c2 100644 (file)
@@ -107,11 +107,38 @@ private template substInoutForm(T)
     {
         alias substInoutForm = T;   // prevent matching to the form of alias-this-ed type
     }
-    else static if (is(T : V[K], K, V))        alias substInoutForm = substInout!V[substInout!K];
-    else static if (is(T : U[n], U, size_t n)) alias substInoutForm = substInout!U[n];
-    else static if (is(T : U[], U))            alias substInoutForm = substInout!U[];
-    else static if (is(T : U*, U))             alias substInoutForm = substInout!U*;
-    else                                       alias substInoutForm = T;
+    else static if (is(T == V[K], K, V))        alias substInoutForm = substInout!V[substInout!K];
+    else static if (is(T == U[n], U, size_t n)) alias substInoutForm = substInout!U[n];
+    else static if (is(T == U[], U))            alias substInoutForm = substInout!U[];
+    else static if (is(T == U*, U))             alias substInoutForm = substInout!U*;
+    else                                        alias substInoutForm = T;
+}
+
+unittest
+{
+    // https://github.com/dlang/dmd/issues/21452
+    struct S { int x; }
+    struct T { int x; alias x this; }
+
+    enum EnumInt { a = 123 }
+    enum EnumUInt : uint { a = 123 }
+    enum EnumFloat : float { a = 123 }
+    enum EnumString : string { a = "123" }
+    enum EnumStringW : wstring { a = "123" }
+    enum EnumStruct : S { a = S(7) }
+    enum EnumAliasThis : T { a = T(7) }
+    enum EnumDArray : int[] { a = [1] }
+    enum EnumAArray : int[int] { a = [0 : 1] }
+
+    static assert(substInout!(EnumInt).stringof                  == "EnumInt");
+    static assert(substInout!(inout(EnumUInt)).stringof          == "const(EnumUInt)");
+    static assert(substInout!(EnumFloat).stringof                == "EnumFloat");
+    static assert(substInout!(EnumString).stringof               == "EnumString");
+    static assert(substInout!(inout(EnumStringW)).stringof       == "const(EnumStringW)");
+    static assert(substInout!(EnumStruct).stringof               == "EnumStruct");
+    static assert(substInout!(EnumAliasThis).stringof            == "EnumAliasThis");
+    static assert(substInout!(EnumDArray).stringof               == "EnumDArray");
+    static assert(substInout!(inout(EnumAArray)[int]).stringof   == "const(EnumAArray)[int]");
 }
 
 /// used to declare an extern(D) function that is defined in a different module
index 34f78e03e4a778123733a483bea43d72c76e6e8d..952ceacb217b4cd4a6440d44acfe6af431f8367f 100644 (file)
@@ -260,6 +260,23 @@ else version (DigitalMars)
         else version (Darwin)
             alias real c_long_double;
     }
+    else version (AArch64)
+    {
+        version (linux)
+            alias real c_long_double;
+        else version (FreeBSD)
+            alias real c_long_double;
+        else version (OpenBSD)
+            alias real c_long_double;
+        else version (NetBSD)
+            alias real c_long_double;
+        else version (DragonFlyBSD)
+            alias real c_long_double;
+        else version (Solaris)
+            alias real c_long_double;
+        else version (Darwin)
+            alias real c_long_double;
+    }
 }
 
 static assert(is(c_long_double), "c_long_double needs to be declared for this platform/architecture.");
index c694dfb3ac3f1f10de56f3cc26c57e5218aa9a40..220b27c4cf8e69bb0a20f5c782d5c8dc0093c911 100644 (file)
@@ -130,6 +130,8 @@ else version (AAPCS32)
 else version (AAPCS64)
 {
     alias va_list = core.internal.vararg.aarch64.va_list;
+    version (DigitalMars)
+        public import core.internal.vararg.aarch64 : __va_list, __va_list_tag;
 }
 else version (RISCV_Any)
 {
index 0315867da60c8228b58ea789216003f30887d5fa..3c974177fa33787e3575d4aff12db20b103655a3 100644 (file)
@@ -343,7 +343,7 @@ extern(D):
         ///
         inout(T)* data() inout @safe                                        { return _Get_data()._Myptr; }
         ///
-        inout(T)[] as_array() scope return inout nothrow @trusted           { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
+        inout(T)[] as_array() return ref scope inout nothrow @trusted       { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
         ///
         ref inout(T) at(size_type i) inout nothrow @trusted                 { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
 
@@ -1920,7 +1920,7 @@ extern(D):
         ///
         inout(T)* data() inout @trusted                                     { return __get_pointer(); }
         ///
-        inout(T)[] as_array() scope return inout nothrow @trusted           { return __get_pointer()[0 .. size()]; }
+        inout(T)[] as_array() return ref scope inout nothrow @trusted       { return __get_pointer()[0 .. size()]; }
         ///
         ref inout(T) at(size_type i) inout nothrow @trusted                 { return __get_pointer()[0 .. size()][i]; }
 
index 0a940db65ed4d64e0bf834883e59b469bd57fffe..97a1478d76ab88624e6726df8b09f90dd71cb6d3 100644 (file)
@@ -151,6 +151,10 @@ version (CRuntime_Glibc)
     /* Determine CPU on which the calling thread is running */
     int sched_getcpu();
 }
+else version (CRuntime_Musl)
+{
+    int sched_getcpu();
+}
 
 /* Reassociate the calling thread with namespace referred to by fd */
 int setns(int fd, int nstype);
index cb76573a95b5391a6e73e027acdb31de502ddab5..0e822f556c70a690d576c0586ac276b910b68e71 100644 (file)
@@ -394,16 +394,14 @@ else version (CRuntime_Musl)
 
     struct DIR
     {
+        // Managed by OS
     }
 
-    static if ( __USE_FILE_OFFSET64 )
-    {
-        dirent* readdir64(DIR*);
-        alias   readdir64 readdir;
-    }
-    else
+    dirent* readdir(DIR*);
+
+    static if (__USE_LARGEFILE64)
     {
-        dirent* readdir(DIR*);
+        alias readdir64 = readdir;
     }
 }
 else version (CRuntime_UClibc)
index 76542c64c8a3edd82db8cee21110d227ae313d57..ff590b30f7ac1a52eb5ccdaf821c350707123b8f 100644 (file)
@@ -189,8 +189,7 @@ version (CRuntime_Glibc)
         void* dli_saddr;
     }
 }
-else
-version (CRuntime_Musl)
+else version (CRuntime_Musl)
 {
     enum RTLD_LAZY   = 1;
     enum RTLD_NOW    = 2;
@@ -402,30 +401,6 @@ else version (CRuntime_Bionic)
         void*        dli_saddr;
     }
 }
-else version (CRuntime_Musl)
-{
-    enum {
-        RTLD_LAZY     = 1,
-        RTLD_NOW      = 2,
-        RTLD_NOLOAD   = 4,
-        RTLD_NODELETE = 4096,
-        RTLD_GLOBAL   = 256,
-        RTLD_LOCAL    = 0,
-    }
-    int          dlclose(void*);
-    const(char)* dlerror();
-    void*        dlopen(const scope char*, int);
-    void*        dlsym(void*, const scope char*);
-
-    int dladdr(scope const void *addr, Dl_info *info);
-    struct Dl_info
-    {
-        const(char)* dli_fname;
-        void*        dli_fbase;
-        const(char)* dli_sname;
-        void*        dli_saddr;
-    }
-}
 else version (CRuntime_UClibc)
 {
     version (X86_Any)
diff --git a/libphobos/libdruntime/core/sys/posix/endian.d b/libphobos/libdruntime/core/sys/posix/endian.d
new file mode 100644 (file)
index 0000000..3cfd8e4
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * D header file for POSIX.
+ *
+ * $(LINK2 https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html posix endian.h)
+ *
+ * Standards: The Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
+ */
+module core.sys.posix.endian;
+
+version (Posix):
+nothrow:
+@safe:
+@nogc:
+
+import core.bitop;
+
+enum LITTLE_ENDIAN = 1234;
+enum BIG_ENDIAN = 4321;
+enum PDP_ENDIAN = 3412;
+
+version (LittleEndian)
+{
+    enum BYTE_ORDER = LITTLE_ENDIAN;
+
+    pragma(inline, true):
+    ushort htobe16(ushort x) => core.bitop.byteswap(x);
+    ushort htole16(ushort x) => x;
+    ushort be16toh(ushort x) => core.bitop.byteswap(x);
+    ushort le16toh(ushort x) => x;
+
+    uint htobe32(uint x) => core.bitop.bswap(x);
+    uint htole32(uint x) => x;
+    uint be32toh(uint x) => core.bitop.bswap(x);
+    uint le32toh(uint x) => x;
+
+    ulong htobe64(ulong x) => core.bitop.bswap(x);
+    ulong htole64(ulong x) => x;
+    ulong be64toh(ulong x) => core.bitop.bswap(x);
+    ulong le64toh(ulong x) => x;
+}
+else
+{
+    enum BYTE_ORDER = BIG_ENDIAN;
+
+    pragma(inline, true):
+    ushort htobe16(ushort x) => x;
+    ushort htole16(ushort x) => core.bitop.byteswap(x);
+    ushort be16toh(ushort x) => x;
+    ushort le16toh(ushort x) => core.bitop.byteswap(x);
+
+    uint htobe32(uint x) => x;
+    uint htole32(uint x) => core.bitop.bswap(x);
+    uint be32toh(uint x) => x;
+    uint le32toh(uint x) => core.bitop.bswap(x);
+
+    ulong htobe64(ulong x) => x;
+    ulong htole64(ulong x) => core.bitop.bswap(x);
+    ulong be64toh(ulong x) => x;
+    ulong le64toh(ulong x) => core.bitop.bswap(x);
+}
index ba7ab8984a9c2a6a01d640594b9dbda89f3db1a0..d5798358415851c855242e8889852e7aaf219759 100644 (file)
@@ -66,9 +66,16 @@ version (linux)
             int sched_priority;
             int __reserved1;
             static if (muslRedirTime64)
-                c_long[2] __reserved2;
+                c_long[4] __reserved2;
             else
-                timespec[2] __reserved2;
+            {
+                struct __timespec32
+                {
+                    time_t __reserved_1;
+                    c_long __reserved_2;
+                }
+                __timespec32[2] __reserved2;
+            }
             int __reserved3;
         }
     }
index d3799890261a06eaced46e41ab884aabab655b89..e2ee0b6085f32c73e3854d0018e072a7c08cb8ee 100644 (file)
@@ -182,33 +182,20 @@ else version (CRuntime_UClibc)
 }
 else version (CRuntime_Musl)
 {
-    static if ( __USE_FILE_OFFSET64 )
-    {
-        int   fgetpos64(FILE*, fpos_t *);
-        alias fgetpos64 fgetpos;
-
-        FILE* fopen64(const scope char*, const scope char*);
-        alias fopen64 fopen;
-
-        FILE* freopen64(const scope char*, const scope char*, FILE*);
-        alias freopen64 freopen;
-
-        int   fseek(FILE*, c_long, int);
-
-        int   fsetpos64(FILE*, const scope fpos_t*);
-        alias fsetpos64 fsetpos;
+    int   fgetpos(FILE*, fpos_t *);
+    FILE* fopen(const scope char*, const scope char*);
+    FILE* freopen(const scope char*, const scope char*, FILE*);
+    int   fseek(FILE*, c_long, int);
+    int   fsetpos(FILE*, const scope fpos_t*);
+    FILE* tmpfile();
 
-        FILE* tmpfile64();
-        alias tmpfile64 tmpfile;
-    }
-    else
+    static if (__USE_LARGEFILE64)
     {
-        int   fgetpos(FILE*, fpos_t *);
-        FILE* fopen(const scope char*, const scope char*);
-        FILE* freopen(const scope char*, const scope char*, FILE*);
-        int   fseek(FILE*, c_long, int);
-        int   fsetpos(FILE*, const scope fpos_t*);
-        FILE* tmpfile();
+        alias fgetpos64 = fgetpos;
+        alias fopen64 = fopen;
+        alias freopen64 = freopen;
+        alias fsetpos64 = fsetpos;
+        alias tmpfile64 = tmpfile;
     }
 }
 else version (Solaris)
@@ -320,24 +307,13 @@ else version (CRuntime_Musl)
 {
     enum L_ctermid = 20;
 
-    static if ( __USE_FILE_OFFSET64 )
-    {
-        int   fseeko64(FILE*, off_t, int);
-        alias fseeko64 fseeko;
-    }
-    else
-    {
-        int   fseeko(FILE*, off_t, int);
-    }
+    int   fseeko(FILE*, off_t, int);
+    off_t ftello(FILE*);
 
-    static if ( __USE_FILE_OFFSET64 )
+    static if (__USE_LARGEFILE64)
     {
-        off_t ftello64(FILE*);
-        alias ftello64 ftello;
-    }
-    else
-    {
-        off_t ftello(FILE*);
+        alias fseeko64 = fseeko;
+        alias ftello64 = ftello;
     }
 
     ssize_t getdelim(char**, size_t*, int, FILE*);
index 8dd7b68e869db3eb6e8be0bd923e46487cefa43a..ea7048ec384202dcb7fa341f2baa8772de8a268a 100644 (file)
@@ -603,17 +603,12 @@ else version (CRuntime_Musl)
     void   srand48(c_long);
     void   srandom(uint);
     int    unlockpt(int);
-
-  static if ( __USE_LARGEFILE64 )
-  {
-    int    mkstemp64(char*);
-    alias  mkstemp64 mkstemp;
-  }
-  else
-  {
     int    mkstemp(char*);
-  }
 
+    static if (__USE_LARGEFILE64)
+    {
+        alias mkstemp64 = mkstemp;
+    }
 }
 else version (Solaris)
 {
index b1eb9fa81073c1d00a9b9a01c0b4f8c34ea77519..42da553c40428afebd277428c135959a20fe672d 100644 (file)
@@ -293,12 +293,13 @@ else version (CRuntime_Bionic)
 }
 else version (CRuntime_Musl)
 {
-    static if (__USE_LARGEFILE64) void* mmap64(void*, size_t, int, int, int, off_t);
-    static if (__USE_FILE_OFFSET64)
-        alias mmap = mmap64;
-    else
-        void* mmap(void*, size_t, int, int, int, off_t);
+    void* mmap(void*, size_t, int, int, int, off_t);
     int munmap(void*, size_t);
+
+    static if (__USE_LARGEFILE64)
+    {
+        alias mmap64 = mmap;
+    }
 }
 else version (CRuntime_UClibc)
 {
index ab79a4067509d209781ef997154b81872e95d6e4..0dbf4ebb7b6b87403da68447034e036f04bb9ced 100644 (file)
@@ -263,8 +263,16 @@ version (linux)
     {
         struct stat_t
         {
-            c_ulong     st_dev;
-            c_long[3]   st_pad1;
+            version (CRuntime_Musl)
+            {
+                dev_t       st_dev;
+                c_long[2]   st_pad1;
+            }
+            else
+            {
+                c_ulong     st_dev;
+                c_long[3]   st_pad1;
+            }
             ino_t       st_ino;
             mode_t      st_mode;
             nlink_t     st_nlink;
index 9405a6d5387ebdfd6ef9619b85040a92ebaed193..ede7866bae75f664ea55eafa0b803ab35af6f2f6 100644 (file)
@@ -97,7 +97,7 @@ else version (CRuntime_Musl)
         fsfilcnt_t f_files;
         fsfilcnt_t f_ffree;
         fsfilcnt_t f_favail;
-        static if (true /+__BYTE_ORDER == __LITTLE_ENDIAN+/)
+        version (LittleEndian)
         {
             c_ulong f_fsid;
             byte[2*int.sizeof-c_long.sizeof] __padding;
@@ -133,8 +133,8 @@ else version (CRuntime_Musl)
     int statvfs (const char * file, statvfs_t* buf);
     int fstatvfs (int fildes, statvfs_t *buf);
 
-    alias statvfs statvfs64;
-    alias fstatvfs fstatvfs64;
+    alias statvfs64 = statvfs;
+    alias fstatvfs64 = fstatvfs;
 }
 else version (NetBSD)
 {
index 24bcc822e9b82a7b14928f77a78421c3f930251e..4f35cc048d82834a5b231bda78172abb44f402c9 100644 (file)
@@ -136,6 +136,10 @@ class ThreadBase
     package void tlsRTdataInit() nothrow @nogc
     {
         m_tlsrtdata = rt_tlsgc_init();
+
+        // Let the selected GC initialize anything it needs.
+        import core.internal.gc.proxy : gc_getProxy;
+        gc_getProxy().initThread(this);
     }
 
     package void initDataStorage() nothrow
@@ -149,6 +153,10 @@ class ThreadBase
 
     package void destroyDataStorage() nothrow @nogc
     {
+        // allow the GC to clean up any resources it allocated for this thread.
+        import core.internal.gc.proxy : gc_getProxy;
+        gc_getProxy().cleanupThread(this);
+
         rt_tlsgc_destroy(m_tlsrtdata);
         m_tlsrtdata = null;
     }
index e9838db4f383bc7395aa15416202efccec01d598..80629ed760acb4a13210802d4c9bd44a25dafda5 100644 (file)
@@ -1301,12 +1301,12 @@ class TypeInfo_AssociativeArray : TypeInfo
 
     override bool equals(in void* p1, in void* p2) @trusted const
     {
-        return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2);
+        return xopEquals(p1, p2);
     }
 
     override hash_t getHash(scope const void* p) nothrow @trusted const
     {
-        return _aaGetHash(cast(AA*)p, this);
+        return xtoHash(p);
     }
 
     // BUG: need to add the rest of the functions
@@ -1325,16 +1325,19 @@ class TypeInfo_AssociativeArray : TypeInfo
     override @property uint flags() nothrow pure const { return 1; }
 
     // TypeInfo entry is generated from the type of this template to help rt/aaA.d
-    static struct Entry(K, V)
-    {
-        K key;
-        V value;
-    }
+    private static import core.internal.newaa;
+    alias Entry(K, V) = core.internal.newaa.Entry!(K, V);
 
     TypeInfo value;
     TypeInfo key;
     TypeInfo entry;
 
+    bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals;
+    hash_t function(scope const void*) nothrow @safe xtoHash;
+
+    alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V);
+    alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V);
+
     override @property size_t talign() nothrow pure const
     {
         return (char[int]).alignof;
@@ -1572,8 +1575,57 @@ class TypeInfo_Delegate : TypeInfo
 }
 
 private extern (C) Object _d_newclass(const TypeInfo_Class ci);
-private extern (C) int _d_isbaseof(scope TypeInfo_Class child,
-    scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_
+
+extern(C) int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @nogc nothrow pure @safe
+{
+    import core.internal.cast_ : areClassInfosEqual;
+
+    if (areClassInfosEqual(oc, c))
+        return true;
+
+    do
+    {
+        if (oc.base && areClassInfosEqual(oc.base, c))
+            return true;
+
+        // Bugzilla 2013: Use depth-first search to calculate offset
+        // from the derived (oc) to the base (c).
+        foreach (iface; oc.interfaces)
+        {
+            if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
+                return true;
+        }
+
+        oc = oc.base;
+    } while (oc);
+
+    return false;
+}
+
+/******************************************
+ * Given a pointer:
+ *      If it is an Object, return that Object.
+ *      If it is an interface, return the Object implementing the interface.
+ *      If it is null, return null.
+ *      Else, undefined crash
+ */
+extern(C) Object _d_toObject(return scope void* p) @nogc nothrow pure @trusted
+{
+    if (!p)
+        return null;
+
+    Object o = cast(Object) p;
+    Interface* pi = **cast(Interface***) p;
+
+    /* Interface.offset lines up with ClassInfo.name.ptr,
+     * so we rely on pointers never being less than 64K,
+     * and Objects never being greater.
+     */
+    if (pi.offset < 0x10000)
+        return cast(Object)(p - pi.offset);
+
+    return o;
+}
 
 /**
  * Runtime type information about a class.
@@ -2535,8 +2587,27 @@ class Throwable : Object
      * $(D Throwable) is thrown from inside a $(D catch) block. The originally
      * caught $(D Exception) will be chained to the new $(D Throwable) via this
      * field.
+     *
+     * If the 0 bit is set in this, it means the next exception is refcounted,
+     * meaning this object owns that object and should destroy it on
+     * destruction.
+     *
+     * WARNING: This means we are storing an interior pointer to the next in
+     * chain, to signify that the next in the chain is actually reference
+     * counted. We rely on the fact that a pointer to a Throwable is not 1-byte
+     * aligned. It is important to use a local field to indicate reference
+     * counting since we are not allowed to look at GC-allocated references
+     * inside the destructor. It is very important to store this as a void*,
+     * since optimizers can consider an unaligned pointer to not exist.
      */
-    private Throwable   nextInChain;
+    private void*   _nextInChainPtr;
+
+    private @property bool _nextIsRefcounted() @trusted scope pure nothrow @nogc const
+    {
+        if (__ctfe)
+            return false;
+        return (cast(size_t)_nextInChainPtr) & 1;
+    }
 
     private uint _refcount;     // 0 : allocated by GC
                                 // 1 : allocated by _d_newThrowable()
@@ -2549,24 +2620,36 @@ class Throwable : Object
      * caught $(D Exception) will be chained to the new $(D Throwable) via this
      * field.
      */
-    @property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
+    @property inout(Throwable) next() @trusted inout return scope pure nothrow @nogc
+    {
+        if (__ctfe)
+            return cast(inout(Throwable)) _nextInChainPtr;
+
+        return cast(inout(Throwable)) (_nextInChainPtr - _nextIsRefcounted);
+    }
+
 
     /**
      * Replace next in chain with `tail`.
      * Use `chainTogether` instead if at all possible.
      */
-    @property void next(Throwable tail) @safe scope pure nothrow @nogc
+    @property void next(Throwable tail) @trusted scope pure nothrow @nogc
     {
+        void* newTail = cast(void*)tail;
         if (tail && tail._refcount)
+        {
             ++tail._refcount;           // increment the replacement *first*
+            ++newTail;                  // indicate ref counting is used
+        }
 
-        auto n = nextInChain;
-        nextInChain = null;             // sever the tail before deleting it
+        auto n = next;
+        auto nrc = _nextIsRefcounted;
+        _nextInChainPtr = null;          // sever the tail before deleting it
 
-        if (n && n._refcount)
+        if (nrc)
             _d_delThrowable(n);         // now delete the old tail
 
-        nextInChain = tail;             // and set the new tail
+        _nextInChainPtr = newTail;       // and set the new tail
     }
 
     /**
@@ -2585,7 +2668,7 @@ class Throwable : Object
     int opApply(scope int delegate(Throwable) dg)
     {
         int result = 0;
-        for (Throwable t = this; t; t = t.nextInChain)
+        for (Throwable t = this; t; t = t.next)
         {
             result = dg(t);
             if (result)
@@ -2608,14 +2691,12 @@ class Throwable : Object
             return e2;
         if (!e2)
             return e1;
-        if (e2.refcount())
-            ++e2.refcount();
 
-        for (auto e = e1; 1; e = e.nextInChain)
+        for (auto e = e1; 1; e = e.next)
         {
-            if (!e.nextInChain)
+            if (!e.next)
             {
-                e.nextInChain = e2;
+                e.next = e2;
                 break;
             }
         }
@@ -2625,9 +2706,7 @@ class Throwable : Object
     @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
     {
         this.msg = msg;
-        this.nextInChain = nextInChain;
-        if (nextInChain && nextInChain._refcount)
-            ++nextInChain._refcount;
+        this.next = nextInChain;
         //this.info = _d_traceContext();
     }
 
@@ -2641,8 +2720,9 @@ class Throwable : Object
 
     @trusted nothrow ~this()
     {
-        if (nextInChain && nextInChain._refcount)
-            _d_delThrowable(nextInChain);
+        if (_nextIsRefcounted)
+            _d_delThrowable(next);
+
         // handle owned traceinfo
         if (infoDeallocator !is null)
         {
@@ -2771,7 +2851,7 @@ class Exception : Throwable
         auto e = new Exception("msg");
         assert(e.file == __FILE__);
         assert(e.line == __LINE__ - 2);
-        assert(e.nextInChain is null);
+        assert(e._nextInChainPtr is null);
         assert(e.msg == "msg");
     }
 
@@ -2779,7 +2859,7 @@ class Exception : Throwable
         auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42);
         assert(e.file == "hello");
         assert(e.line == 42);
-        assert(e.nextInChain !is null);
+        assert(e._nextInChainPtr !is null);
         assert(e.msg == "msg");
     }
 
@@ -2787,7 +2867,7 @@ class Exception : Throwable
         auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!"));
         assert(e.file == "hello");
         assert(e.line == 42);
-        assert(e.nextInChain !is null);
+        assert(e._nextInChainPtr !is null);
         assert(e.msg == "msg");
     }
 
@@ -2854,7 +2934,7 @@ class Error : Throwable
         auto e = new Error("msg");
         assert(e.file is null);
         assert(e.line == 0);
-        assert(e.nextInChain is null);
+        assert(e._nextInChainPtr is null);
         assert(e.msg == "msg");
         assert(e.bypassedException is null);
     }
@@ -2863,7 +2943,7 @@ class Error : Throwable
         auto e = new Error("msg", new Exception("It's an Exception!"));
         assert(e.file is null);
         assert(e.line == 0);
-        assert(e.nextInChain !is null);
+        assert(e._nextInChainPtr !is null);
         assert(e.msg == "msg");
         assert(e.bypassedException is null);
     }
@@ -2872,60 +2952,27 @@ class Error : Throwable
         auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!"));
         assert(e.file == "hello");
         assert(e.line == 42);
-        assert(e.nextInChain !is null);
+        assert(e._nextInChainPtr !is null);
         assert(e.msg == "msg");
         assert(e.bypassedException is null);
     }
 }
 
-extern (C)
-{
-    // from druntime/src/rt/aaA.d
-
-    private struct AA { void* impl; }
-    // size_t _aaLen(in AA aa) pure nothrow @nogc;
-    private void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
-    private void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) pure nothrow;
-    // inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
-    inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow;
-    inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
-    void* _aaRehash(AA* paa, const scope TypeInfo keyti) pure nothrow;
-    void _aaClear(AA aa) pure nothrow;
-
-    // alias _dg_t = extern(D) int delegate(void*);
-    // int _aaApply(AA aa, size_t keysize, _dg_t dg);
+public import core.internal.newaa : _d_aaIn, _d_aaDel, _d_aaNew, _d_aaEqual, _d_assocarrayliteralTX;
+public import core.internal.newaa : _d_aaLen, _d_aaGetY, _d_aaGetRvalueX, _d_aaApply, _d_aaApply2;
+// public import core.exception : onRangeError; // causes extra messages with -transition=fields
+extern (C) noreturn _d_arraybounds(string file, uint line) @trusted pure nothrow @nogc;
 
-    // alias _dg2_t = extern(D) int delegate(void*, void*);
-    // int _aaApply2(AA aa, size_t keysize, _dg2_t dg);
+private import core.internal.newaa;
 
-    private struct AARange { AA impl; size_t idx; }
-    AARange _aaRange(AA aa) pure nothrow @nogc @safe;
-    bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe;
-    void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe;
-    void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
-    void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
-
-    int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
-    hash_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
-
-    /*
-        _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
-        This is a typesystem hole, however this is existing hole.
-        Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus
-        copiler allowed to create AA literal with keys, which have impure unsafe toHash methods.
-    */
-    void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure;
-}
-
-void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure
+void* aaLiteral(Key, Value)(Key[] keys, Value[] values)
 {
-    return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values);
+    return _d_assocarrayliteralTX(keys, values);
 }
 
 // Lower an Associative Array to a newaa struct for static initialization.
 auto _aaAsStruct(K, V)(V[K] aa) @safe
 {
-    import core.internal.newaa : makeAA;
     assert(__ctfe);
     return makeAA!(K, V)(aa);
 }
@@ -2939,13 +2986,13 @@ alias AssociativeArray(Key, Value) = Value[Key];
  */
 void clear(Value, Key)(Value[Key] aa) @trusted
 {
-    _aaClear(*cast(AA *) &aa);
+    _aaClear(aa);
 }
 
 /** ditto */
 void clear(Value, Key)(Value[Key]* aa) @trusted
 {
-    _aaClear(*cast(AA *) aa);
+    (*aa).clear();
 }
 
 ///
@@ -2982,32 +3029,30 @@ void clear(Value, Key)(Value[Key]* aa) @trusted
  *      aa =     The associative array.
  * Returns:
  *      The rehashed associative array.
+ * Note:
+ *  emulated by the compiler during CTFE
  */
-T rehash(T : Value[Key], Value, Key)(T aa)
+Value[Key] rehash(Value, Key)(Value[Key] aa)
 {
-    _aaRehash(cast(AA*)&aa, typeid(Value[Key]));
-    return aa;
+    return _aaRehash(aa);
 }
 
 /** ditto */
-T rehash(T : Value[Key], Value, Key)(T* aa)
+Value[Key] rehash(T : Value[Key], Value, Key)(T* aa)
 {
-    _aaRehash(cast(AA*)aa, typeid(Value[Key]));
-    return *aa;
+    return (*aa).rehash(); // CTFE only intercepts the non-pointer overload
 }
 
 /** ditto */
-T rehash(T : shared Value[Key], Value, Key)(T aa)
+Value[Key] rehash(T : shared Value[Key], Value, Key)(auto ref T aa)
 {
-    _aaRehash(cast(AA*)&aa, typeid(Value[Key]));
-    return aa;
+    return (cast(Value[Key])aa).rehash();  // CTFE only intercepts the V[K] overload
 }
 
 /** ditto */
-T rehash(T : shared Value[Key], Value, Key)(T* aa)
+Value[Key] rehash(T : shared Value[Key], Value, Key)(T* aa)
 {
-    _aaRehash(cast(AA*)aa, typeid(Value[Key]));
-    return *aa;
+    return (cast(Value[Key])*aa).rehash(); // CTFE only intercepts the non-pointer overload
 }
 
 /***********************************
@@ -3015,46 +3060,20 @@ T rehash(T : shared Value[Key], Value, Key)(T* aa)
  * the associative array into it.
  * Params:
  *      aa =     The associative array.
+ * Note:
+ *  emulated by the compiler during CTFE
  */
-V[K] dup(T : V[K], K, V)(T aa)
+auto dup(T : V[K], K, V)(T aa)
 {
-    //pragma(msg, "K = ", K, ", V = ", V);
-
     // Bug10720 - check whether V is copyable
     static assert(is(typeof({ V v = aa[K.init]; })),
         "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable");
 
-    V[K] result;
-
-    //foreach (k, ref v; aa)
-    //    result[k] = v;  // Bug13701 - won't work if V is not mutable
-
-    ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow
-    {
-        import core.stdc.string : memcpy;
-
-        void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k);
-        memcpy(pv, &v, V.sizeof);
-        return *cast(V*)pv;
-    }
-
-    foreach (k, ref v; aa)
-    {
-        static if (!__traits(hasPostblit, V))
-            duplicateElem(k, v);
-        else static if (__traits(isStaticArray, V))
-            _doPostblit(duplicateElem(k, v)[]);
-        else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
-            (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
-        else
-            duplicateElem(k, v).__xpostblit();
-    }
-
-    return result;
+    return _aaDup(aa);
 }
 
 /** ditto */
-V[K] dup(T : V[K], K, V)(T* aa)
+auto dup(T : V[K], K, V)(T* aa)
 {
     return (*aa).dup;
 }
@@ -3069,14 +3088,14 @@ V[K] dup(T : V[K], K, V)(T* aa)
 }
 
 // this should never be made public.
-private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
+private auto _aaToRange(K, V)(auto ref inout V[K] aa) @trusted
 {
-    // ensure we are dealing with a genuine AA.
-    static if (is(const(V[K]) == const(T)))
-        alias realAA = aa;
-    else
-        const(V[K]) realAA = aa;
-    return _aaRange(() @trusted { return *cast(AA*)&realAA; } ());
+    import core.internal.traits : substInout;
+
+    alias K2 = substInout!K;
+    alias V2 = substInout!V;
+    auto aa2 = cast(V2[K2])aa;
+    return _aaRange(aa2);
 }
 
 /***********************************
@@ -3106,25 +3125,27 @@ auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
 {
     import core.internal.traits : substInout;
 
+    const(V[K]) aa2 = aa;
+
     static struct Result
     {
-        AARange r;
+        typeof(_aaToRange(aa2)) r;
 
     pure nothrow @nogc:
         @property bool empty()  @safe { return _aaRangeEmpty(r); }
         @property ref front() @trusted
         {
-            return *cast(substInout!K*) _aaRangeFrontKey(r);
+            return *cast(substInout!K*)_aaRangeFrontKey(r);
         }
         void popFront() @safe { _aaRangePopFront(r); }
         @property Result save() { return this; }
     }
 
-    return Result(_aaToRange(aa));
+    return Result(_aaToRange(aa2));
 }
 
 /** ditto */
-auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
+auto byKey(K, V)(V[K]* aa) pure nothrow @nogc
 {
     return (*aa).byKey();
 }
@@ -3167,25 +3188,27 @@ auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
 {
     import core.internal.traits : substInout;
 
+    const(V[K]) aa2 = aa;
+
     static struct Result
     {
-        AARange r;
+        typeof(_aaToRange(aa2)) r;
 
     pure nothrow @nogc:
         @property bool empty() @safe { return _aaRangeEmpty(r); }
         @property ref front() @trusted
         {
-            return *cast(substInout!V*) _aaRangeFrontValue(r);
+            return *cast(substInout!V*)_aaRangeFrontValue(r);
         }
         void popFront() @safe { _aaRangePopFront(r); }
         @property Result save() { return this; }
     }
 
-    return Result(_aaToRange(aa));
+    return Result(_aaToRange(aa2));
 }
 
 /** ditto */
-auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
+auto byValue(K, V)(V[K]* aa) pure nothrow @nogc
 {
     return (*aa).byValue();
 }
@@ -3240,9 +3263,11 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
 {
     import core.internal.traits : substInout;
 
+    const(V[K]) aa2 = aa;
+
     static struct Result
     {
-        AARange r;
+        typeof(_aaToRange(aa2)) r;
 
     pure nothrow @nogc:
         @property bool empty() @safe { return _aaRangeEmpty(r); }
@@ -3252,16 +3277,16 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
             {
                 // We save the pointers here so that the Pair we return
                 // won't mutate when Result.popFront is called afterwards.
-                private void* keyp;
-                private void* valp;
+                private const(substInout!K)* keyp;
+                private const(substInout!V)* valp;
 
                 @property ref key() inout @trusted
                 {
-                    return *cast(substInout!K*) keyp;
+                    return *cast(K*)keyp;
                 }
                 @property ref value() inout @trusted
                 {
-                    return *cast(substInout!V*) valp;
+                    return *cast(V*)valp;
                 }
             }
             return Pair(_aaRangeFrontKey(r),
@@ -3271,7 +3296,7 @@ auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
         @property Result save() { return this; }
     }
 
-    return Result(_aaToRange(aa));
+    return Result(_aaToRange(aa2));
 }
 
 /** ditto */
@@ -3304,27 +3329,18 @@ auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
  *      aa =     The associative array.
  * Returns:
  *      A dynamic array containing a copy of the keys.
+ * Note:
+ *  emulated by the compiler during CTFE
  */
-Key[] keys(T : Value[Key], Value, Key)(T aa) @property
+auto keys(Value, Key)(inout Value[Key] aa) @property
 {
-    // ensure we are dealing with a genuine AA.
-    static if (is(const(Value[Key]) == const(T)))
-        alias realAA = aa;
-    else
-        const(Value[Key]) realAA = aa;
-    auto res = () @trusted {
-        auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
-        return *cast(Key[]*)&a;
-    }();
-    static if (__traits(hasPostblit, Key))
-        _doPostblit(res);
-    return res;
+    return _aaKeys!(Key, Value)(aa);
 }
 
 /** ditto */
-Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
+auto keys(T : Value[Key], Value, Key)(T *aa) @property
 {
-    return (*aa).keys;
+    return (*aa).keys; // CTFE only intercepts the non-pointer overload
 }
 
 ///
@@ -3388,27 +3404,18 @@ Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
  *      aa =     The associative array.
  * Returns:
  *      A dynamic array containing a copy of the values.
+ * Note:
+ *  emulated by the compiler during CTFE
  */
-Value[] values(T : Value[Key], Value, Key)(T aa) @property
+auto values(Value, Key)(inout Value[Key] aa) @property
 {
-    // ensure we are dealing with a genuine AA.
-    static if (is(const(Value[Key]) == const(T)))
-        alias realAA = aa;
-    else
-        const(Value[Key]) realAA = aa;
-    auto res = () @trusted {
-        auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
-        return *cast(Value[]*)&a;
-    }();
-    static if (__traits(hasPostblit, Value))
-        _doPostblit(res);
-    return res;
+    return _aaValues!(Key, Value)(aa);
 }
 
 /** ditto */
-Value[] values(T : Value[Key], Value, Key)(T *aa) @property
+auto values(T : Value[Key], Value, Key)(T *aa) @property
 {
-    return (*aa).values;
+    return (*aa).values; // CTFE only intercepts the non-pointer overload
 }
 
 ///
@@ -3508,25 +3515,11 @@ inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
 ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
 {
     bool found;
-    // if key is @safe-ly copyable, `require` can infer @safe
-    static if (isSafeCopyable!K)
-    {
-        auto p = () @trusted
-        {
-            return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
-        } ();
-    }
-    else
-    {
-        auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
-    }
+    auto p = _aaGetX(aa, key, found);
     if (found)
         return *p;
-    else
-    {
-        *p = value; // Not `return (*p = value)` since if `=` is overloaded
-        return *p;  // this might not return a ref to the left-hand side.
-    }
+    *p = value; // Not `return (*p = value)` since if `=` is overloaded
+    return *p;  // this might not return a ref to the left-hand side.
 }
 
 ///
@@ -3545,7 +3538,7 @@ private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x
  * Calls `create` if `key` doesn't exist in the associative array,
  * otherwise calls `update`.
  * `create` returns a corresponding value for `key`.
- * `update` accepts a key parameter. If it returns a value, the value is
+ * `update` accepts a value parameter. If it returns a value, the value is
  * set for `key`.
  * Params:
  *      aa =     The associative array.
@@ -3553,24 +3546,13 @@ private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x
  *      create = The callable to create a value for `key`.
  *               Must return V.
  *      update = The callable to call if `key` exists.
- *               Takes a K argument, returns a V or void.
+ *               Takes a V argument, returns a V or void.
  */
 void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
 if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
 {
     bool found;
-    // if key is @safe-ly copyable, `update` may infer @safe
-    static if (isSafeCopyable!K)
-    {
-        auto p = () @trusted
-        {
-            return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
-        } ();
-    }
-    else
-    {
-        auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
-    }
+    auto p = _aaGetX(aa, key, found);
     if (!found)
         *p = create();
     else
@@ -4010,10 +3992,6 @@ size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted
     a.reserve(10);
 }
 
-// HACK:  This is a lie.  `_d_arrayshrinkfit` is not `nothrow`, but this lie is necessary
-// for now to prevent breaking code.
-private extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow;
-
 /**
 Assume that it is safe to append to this array. Appends made to this array
 after calling this function may append in place, even if the array was a
@@ -4031,7 +4009,13 @@ Returns:
 */
 auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system
 {
-    _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr));
+    import core.internal.array.capacity : _d_arrayshrinkfit;
+    import core.internal.traits : Unqual;
+
+    alias Unqual_Tarr = Unqual!T[];
+    enum isshared = is(T == shared);
+    _d_arrayshrinkfit(cast(Unqual_Tarr)arr, isshared);
+
     return arr;
 }
 
@@ -4703,6 +4687,7 @@ version (D_ProfileGC)
     public import core.internal.array.construction : _d_newarrayTTrace;
     public import core.internal.array.construction : _d_newarraymTXTrace;
     public import core.internal.array.capacity: _d_arraysetlengthTTrace;
+    public import core.internal.array.construction : _d_arrayliteralTXTrace;
 }
 public import core.internal.array.appending : _d_arrayappendcTX;
 public import core.internal.array.comparison : __cmp;
@@ -4713,10 +4698,12 @@ public import core.internal.array.construction : _d_arrayctor;
 public import core.internal.array.construction : _d_arraysetctor;
 public import core.internal.array.construction : _d_newarrayT;
 public import core.internal.array.construction : _d_newarraymTX;
+public import core.internal.array.construction : _d_arrayliteralTX;
 public import core.internal.array.arrayassign : _d_arrayassign_l;
 public import core.internal.array.arrayassign : _d_arrayassign_r;
 public import core.internal.array.arrayassign : _d_arraysetassign;
 public import core.internal.array.capacity : _d_arraysetlengthT;
+public import core.internal.cast_: _d_cast;
 
 public import core.internal.dassert: _d_assert_fail;
 
diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d
deleted file mode 100644 (file)
index 3b4bee0..0000000
+++ /dev/null
@@ -1,867 +0,0 @@
-/**
- * Implementation of associative arrays.
- *
- * Copyright: Copyright Digital Mars 2000 - 2015.
- * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors:   Martin Nowak
- * Source: $(DRUNTIMESRC rt/_aaA.d)
- */
-module rt.aaA;
-
-/// AA version for debuggers, bump whenever changing the layout
-extern (C) immutable int _aaVersion = 1;
-
-import core.memory : GC;
-import core.internal.util.math : min, max;
-
-// grow threshold
-private enum GROW_NUM = 4;
-private enum GROW_DEN = 5;
-// shrink threshold
-private enum SHRINK_NUM = 1;
-private enum SHRINK_DEN = 8;
-// grow factor
-private enum GROW_FAC = 4;
-// growing the AA doubles it's size, so the shrink threshold must be
-// smaller than half the grow threshold to have a hysteresis
-static assert(GROW_FAC * SHRINK_NUM * GROW_DEN < GROW_NUM * SHRINK_DEN);
-// initial load factor (for literals), mean of both thresholds
-private enum INIT_NUM = (GROW_DEN * SHRINK_NUM + GROW_NUM * SHRINK_DEN) / 2;
-private enum INIT_DEN = SHRINK_DEN * GROW_DEN;
-
-private enum INIT_NUM_BUCKETS = 8;
-// magic hash constants to distinguish empty, deleted, and filled buckets
-private enum HASH_EMPTY = 0;
-private enum HASH_DELETED = 0x1;
-private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
-
-/// Opaque AA wrapper
-struct AA
-{
-    Impl* impl;
-    alias impl this;
-
-    private @property bool empty() const pure nothrow @nogc @safe
-    {
-        return impl is null || !impl.length;
-    }
-}
-
-private struct Impl
-{
-private:
-    this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) nothrow
-    {
-        keysz = cast(uint) ti.key.tsize;
-        valsz = cast(uint) ti.value.tsize;
-        buckets = allocBuckets(sz);
-        firstUsed = cast(uint) buckets.length;
-        valoff = cast(uint) talign(keysz, ti.value.talign);
-        hashFn = &ti.key.getHash;
-
-        import rt.lifetime : hasPostblit, unqualify;
-
-        if (hasPostblit(unqualify(ti.key)))
-            flags |= Flags.keyHasPostblit;
-        if ((ti.key.flags | ti.value.flags) & 1)
-            flags |= Flags.hasPointers;
-
-        entryTI = ti.entry;
-    }
-
-    Bucket[] buckets;
-    uint used;
-    uint deleted;
-    const(TypeInfo) entryTI;
-    uint firstUsed;
-    immutable uint keysz;
-    immutable uint valsz;
-    immutable uint valoff;
-    Flags flags;
-
-    // function that calculates hash of a key. Set on creation
-    // the parameter is a pointer to the key.
-    size_t delegate(scope const void*) nothrow hashFn;
-
-    enum Flags : ubyte
-    {
-        none = 0x0,
-        keyHasPostblit = 0x1,
-        hasPointers = 0x2,
-    }
-
-    @property size_t length() const pure nothrow @nogc @safe
-    {
-        assert(used >= deleted);
-        return used - deleted;
-    }
-
-    @property size_t dim() const pure nothrow @nogc @safe
-    {
-        return buckets.length;
-    }
-
-    @property size_t mask() const pure nothrow @nogc
-    {
-        return dim - 1;
-    }
-
-    // find the first slot to insert a value with hash
-    inout(Bucket)* findSlotInsert(size_t hash) inout pure nothrow @nogc
-    {
-        for (size_t i = hash & mask, j = 1;; ++j)
-        {
-            if (!buckets[i].filled)
-                return &buckets[i];
-            i = (i + j) & mask;
-        }
-    }
-
-    // lookup a key
-    inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout
-    {
-        for (size_t i = hash & mask, j = 1;; ++j)
-        {
-            if (buckets[i].hash == hash && keyti.equals(pkey, buckets[i].entry))
-                return &buckets[i];
-            else if (buckets[i].empty)
-                return null;
-            i = (i + j) & mask;
-        }
-    }
-
-    void grow(scope const TypeInfo keyti) pure nothrow
-    {
-        // If there are so many deleted entries, that growing would push us
-        // below the shrink threshold, we just purge deleted entries instead.
-        if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
-            resize(dim);
-        else
-            resize(GROW_FAC * dim);
-    }
-
-    void shrink(scope const TypeInfo keyti) pure nothrow
-    {
-        if (dim > INIT_NUM_BUCKETS)
-            resize(dim / GROW_FAC);
-    }
-
-    void resize(size_t ndim) pure nothrow
-    {
-        auto obuckets = buckets;
-        buckets = allocBuckets(ndim);
-
-        foreach (ref b; obuckets[firstUsed .. $])
-            if (b.filled)
-                *findSlotInsert(b.hash) = b;
-
-        firstUsed = 0;
-        used -= deleted;
-        deleted = 0;
-        GC.free(obuckets.ptr); // safe to free b/c impossible to reference
-    }
-
-    void clear() pure nothrow @trusted
-    {
-        import core.stdc.string : memset;
-        // clear all data, but don't change bucket array length
-        memset(&buckets[firstUsed], 0, (buckets.length - firstUsed) * Bucket.sizeof);
-        deleted = used = 0;
-        firstUsed = cast(uint) dim;
-    }
-}
-
-//==============================================================================
-// Bucket
-//------------------------------------------------------------------------------
-
-private struct Bucket
-{
-private pure nothrow @nogc:
-    size_t hash;
-    void* entry;
-
-    @property bool empty() const
-    {
-        return hash == HASH_EMPTY;
-    }
-
-    @property bool deleted() const
-    {
-        return hash == HASH_DELETED;
-    }
-
-    @property bool filled() const @safe
-    {
-        return cast(ptrdiff_t) hash < 0;
-    }
-}
-
-Bucket[] allocBuckets(size_t dim) @trusted pure nothrow
-{
-    enum attr = GC.BlkAttr.NO_INTERIOR;
-    immutable sz = dim * Bucket.sizeof;
-    return (cast(Bucket*) GC.calloc(sz, attr))[0 .. dim];
-}
-
-//==============================================================================
-// Entry
-//------------------------------------------------------------------------------
-
-private void* allocEntry(scope const Impl* aa, scope const void* pkey)
-{
-    import rt.lifetime : _d_newitemU;
-    import core.stdc.string : memcpy, memset;
-
-    immutable akeysz = aa.valoff;
-    void* res = void;
-    if (aa.entryTI)
-        res = _d_newitemU(aa.entryTI);
-    else
-    {
-        auto flags = (aa.flags & Impl.Flags.hasPointers) ? 0 : GC.BlkAttr.NO_SCAN;
-        res = GC.malloc(akeysz + aa.valsz, flags);
-    }
-
-    memcpy(res, pkey, aa.keysz); // copy key
-    memset(res + akeysz, 0, aa.valsz); // zero value
-
-    return res;
-}
-
-private bool hasDtor(const TypeInfo ti) pure nothrow
-{
-    import rt.lifetime : unqualify;
-
-    if (typeid(ti) is typeid(TypeInfo_Struct))
-        if ((cast(TypeInfo_Struct) cast(void*) ti).xdtor)
-            return true;
-    if (typeid(ti) is typeid(TypeInfo_StaticArray))
-        return hasDtor(unqualify(ti.next));
-
-    return false;
-}
-
-private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow
-{
-    // classes are references
-    const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class);
-    return isNoClass ? ti.rtInfo() : rtinfoHasPointers;
-}
-
-unittest
-{
-    void test(K, V)()
-    {
-        static struct Entry
-        {
-            K key;
-            V val;
-        }
-        auto keyti = typeid(K);
-        auto valti = typeid(V);
-        auto valrti = getRTInfo(valti);
-        auto keyrti = getRTInfo(keyti);
-
-        auto impl = new Impl(typeid(V[K]));
-        if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers)
-        {
-            assert(!(impl.flags & Impl.Flags.hasPointers));
-        }
-        else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers)
-        {
-            assert(impl.flags & Impl.Flags.hasPointers);
-        }
-        else
-        {
-            auto rtInfo  = cast(size_t*) impl.entryTI.rtInfo();
-            auto refInfo = cast(size_t*) typeid(Entry).rtInfo();
-            assert(rtInfo[0] == refInfo[0]); // size
-            enum bytesPerWord = 8 * size_t.sizeof * (void*).sizeof;
-            size_t words = (rtInfo[0] + bytesPerWord - 1) / bytesPerWord;
-            foreach (i; 0 .. words)
-                assert(rtInfo[1 + i] == refInfo[i + 1]);
-        }
-    }
-    test!(long, int)();
-    test!(string, string);
-    test!(ubyte[16], Object);
-
-    static struct Small
-    {
-        ubyte[16] guid;
-        string name;
-    }
-    test!(string, Small);
-
-    static struct Large
-    {
-        ubyte[1024] data;
-        string[412] names;
-        ubyte[1024] moredata;
-    }
-    version (OnlyLowMemUnittests) {} else
-    test!(Large, Large);
-}
-
-//==============================================================================
-// Helper functions
-//------------------------------------------------------------------------------
-
-private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
-{
-    immutable mask = algn - 1;
-    assert(!(mask & algn));
-    return (tsize + mask) & ~mask;
-}
-
-// mix hash to "fix" bad hash functions
-private size_t mix(size_t h) @safe pure nothrow @nogc
-{
-    // final mix function of MurmurHash2
-    enum m = 0x5bd1e995;
-    h ^= h >> 13;
-    h *= m;
-    h ^= h >> 15;
-    return h;
-}
-
-private size_t calcHash(scope const void *pkey, scope const Impl* impl) nothrow
-{
-    immutable hash = impl.hashFn(pkey);
-    // highest bit is set to distinguish empty/deleted from filled buckets
-    return mix(hash) | HASH_FILLED_MARK;
-}
-
-private size_t nextpow2(const size_t n) pure nothrow @nogc
-{
-    import core.bitop : bsr;
-
-    if (!n)
-        return 1;
-
-    const isPowerOf2 = !((n - 1) & n);
-    return 1 << (bsr(n) + !isPowerOf2);
-}
-
-pure nothrow @nogc unittest
-{
-    //                            0, 1, 2, 3, 4, 5, 6, 7, 8,  9
-    foreach (const n, const pow2; [1, 1, 2, 4, 4, 8, 8, 8, 8, 16])
-        assert(nextpow2(n) == pow2);
-}
-
-//==============================================================================
-// API Implementation
-//------------------------------------------------------------------------------
-
-/** Allocate associative array data.
- * Called for `new SomeAA` expression.
- * Params:
- *      ti = TypeInfo for the associative array
- * Returns:
- *      A new associative array.
- */
-extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti)
-{
-    return new Impl(ti);
-}
-
-/// Determine number of entries in associative array.
-extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
-{
-    return aa ? aa.length : 0;
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of (aa[key]) expressions when value is mutable.
- * Params:
- *      paa = associative array opaque pointer
- *      ti = TypeInfo for the associative array
- *      valsz = ignored
- *      pkey = pointer to the key value
- * Returns:
- *      if key was in the aa, a mutable pointer to the existing value.
- *      If key was not in the aa, a mutable pointer to newly inserted value which
- *      is set to all zeros
- */
-extern (C) void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti,
-    const size_t valsz, scope const void* pkey)
-{
-    bool found;
-    return _aaGetX(paa, ti, valsz, pkey, found);
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of require
- * Params:
- *      paa = associative array opaque pointer
- *      ti = TypeInfo for the associative array
- *      valsz = ignored
- *      pkey = pointer to the key value
- *      found = true if the value was found
- * Returns:
- *      if key was in the aa, a mutable pointer to the existing value.
- *      If key was not in the aa, a mutable pointer to newly inserted value which
- *      is set to all zeros
- */
-extern (C) void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti,
-    const size_t valsz, scope const void* pkey, out bool found)
-{
-    // lazily alloc implementation
-    AA aa = *paa;
-    if (aa is null)
-    {
-        aa = new Impl(ti);
-        *paa = aa;
-    }
-
-    // get hash and bucket for key
-    immutable hash = calcHash(pkey, aa);
-
-    // found a value => return it
-    if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
-    {
-        found = true;
-        return p.entry + aa.valoff;
-    }
-
-    auto p = aa.findSlotInsert(hash);
-    if (p.deleted)
-        --aa.deleted;
-    // check load factor and possibly grow
-    else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
-    {
-        aa.grow(ti.key);
-        p = aa.findSlotInsert(hash);
-        assert(p.empty);
-    }
-
-    // update search cache and allocate entry
-    aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
-    p.hash = hash;
-    p.entry = allocEntry(aa, pkey);
-    // postblit for key
-    if (aa.flags & Impl.Flags.keyHasPostblit)
-    {
-        import rt.lifetime : __doPostblit, unqualify;
-
-        __doPostblit(p.entry, aa.keysz, unqualify(ti.key));
-    }
-    // return pointer to value
-    return p.entry + aa.valoff;
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of (aa[key]) expressions when value is not mutable.
- * Params:
- *      aa = associative array opaque pointer
- *      keyti = TypeInfo for the key
- *      valsz = ignored
- *      pkey = pointer to the key value
- * Returns:
- *      pointer to value if present, null otherwise
- */
-extern (C) inout(void)* _aaGetRvalueX(inout AA aa, scope const TypeInfo keyti, const size_t valsz,
-    scope const void* pkey)
-{
-    return _aaInX(aa, keyti, pkey);
-}
-
-/******************************
- * Lookup *pkey in aa.
- * Called only from implementation of (key in aa) expressions.
- * Params:
- *      aa = associative array opaque pointer
- *      keyti = TypeInfo for the key
- *      pkey = pointer to the key value
- * Returns:
- *      pointer to value if present, null otherwise
- */
-extern (C) inout(void)* _aaInX(inout AA aa, scope const TypeInfo keyti, scope const void* pkey)
-{
-    if (aa.empty)
-        return null;
-
-    immutable hash = calcHash(pkey, aa);
-    if (auto p = aa.findSlotLookup(hash, pkey, keyti))
-        return p.entry + aa.valoff;
-    return null;
-}
-
-/// Delete entry scope const AA, return true if it was present
-extern (C) bool _aaDelX(AA aa, scope const TypeInfo keyti, scope const void* pkey)
-{
-    if (aa.empty)
-        return false;
-
-    immutable hash = calcHash(pkey, aa);
-    if (auto p = aa.findSlotLookup(hash, pkey, keyti))
-    {
-        // clear entry
-        p.hash = HASH_DELETED;
-        p.entry = null;
-
-        ++aa.deleted;
-        // `shrink` reallocates, and allocating from a finalizer leads to
-        // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
-        if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !GC.inFinalizer())
-            aa.shrink(keyti);
-
-        return true;
-    }
-    return false;
-}
-
-/// Remove all elements from AA.
-extern (C) void _aaClear(AA aa) pure nothrow @safe
-{
-    if (!aa.empty)
-    {
-        aa.clear();
-    }
-}
-
-/// Rehash AA
-extern (C) void* _aaRehash(AA* paa, scope const TypeInfo keyti) pure nothrow
-{
-    AA aa = *paa;
-    if (!aa.empty)
-        aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
-    return aa;
-}
-
-/// Return a GC allocated array of all values
-extern (C) inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz,
-    const TypeInfo tiValueArray) pure nothrow
-{
-    if (aa.empty)
-        return null;
-
-    import rt.lifetime : _d_newarrayU;
-
-    auto res = _d_newarrayU(tiValueArray, aa.length).ptr;
-    auto pval = res;
-
-    immutable off = aa.valoff;
-    foreach (b; aa.buckets[aa.firstUsed .. $])
-    {
-        if (!b.filled)
-            continue;
-        pval[0 .. valsz] = cast(void[]) b.entry[off .. valsz + off];
-        pval += valsz;
-    }
-    // postblit is done in object.values
-    return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
-}
-
-/// Return a GC allocated array of all keys
-extern (C) inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow
-{
-    if (aa.empty)
-        return null;
-
-    import rt.lifetime : _d_newarrayU;
-
-    auto res = _d_newarrayU(tiKeyArray, aa.length).ptr;
-    auto pkey = res;
-
-    foreach (b; aa.buckets[aa.firstUsed .. $])
-    {
-        if (!b.filled)
-            continue;
-        pkey[0 .. keysz] = cast(void[]) b.entry[0 .. keysz];
-        pkey += keysz;
-    }
-    // postblit is done in object.keys
-    return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
-}
-
-// opApply callbacks are extern(D)
-extern (D) alias dg_t = int delegate(void*);
-extern (D) alias dg2_t = int delegate(void*, void*);
-
-/// foreach opApply over all values
-extern (C) int _aaApply(AA aa, const size_t keysz, dg_t dg)
-{
-    if (aa.empty)
-        return 0;
-
-    immutable off = aa.valoff;
-    foreach (b; aa.buckets)
-    {
-        if (!b.filled)
-            continue;
-        if (auto res = dg(b.entry + off))
-            return res;
-    }
-    return 0;
-}
-
-/// foreach opApply over all key/value pairs
-extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg)
-{
-    if (aa.empty)
-        return 0;
-
-    immutable off = aa.valoff;
-    foreach (b; aa.buckets)
-    {
-        if (!b.filled)
-            continue;
-        if (auto res = dg(b.entry, b.entry + off))
-            return res;
-    }
-    return 0;
-}
-
-/** Construct an associative array of type ti from corresponding keys and values.
- * Called for an AA literal `[k1:v1, k2:v2]`.
- * Params:
- *      ti = TypeInfo for the associative array
- *      keys = array of keys
- *      vals = array of values
- * Returns:
- *      A new associative array opaque pointer, or null if `keys` is empty.
- */
-extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
-    void[] vals)
-{
-    assert(keys.length == vals.length);
-
-    immutable keysz = ti.key.tsize;
-    immutable valsz = ti.value.tsize;
-    immutable length = keys.length;
-
-    if (!length)
-        return null;
-
-    auto aa = new Impl(ti, nextpow2(INIT_DEN * length / INIT_NUM));
-
-    void* pkey = keys.ptr;
-    void* pval = vals.ptr;
-    immutable off = aa.valoff;
-    uint actualLength = 0;
-    foreach (_; 0 .. length)
-    {
-        immutable hash = calcHash(pkey, aa);
-
-        auto p = aa.findSlotLookup(hash, pkey, ti.key);
-        if (p is null)
-        {
-            p = aa.findSlotInsert(hash);
-            p.hash = hash;
-            p.entry = allocEntry(aa, pkey); // move key, no postblit
-            aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
-            actualLength++;
-        }
-        else if (aa.entryTI && hasDtor(ti.value))
-        {
-            // destroy existing value before overwriting it
-            ti.value.destroy(p.entry + off);
-        }
-        // set hash and blit value
-        auto pdst = p.entry + off;
-        pdst[0 .. valsz] = pval[0 .. valsz]; // move value, no postblit
-
-        pkey += keysz;
-        pval += valsz;
-    }
-    aa.used = actualLength;
-    return aa;
-}
-
-/// compares 2 AAs for equality
-extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2)
-{
-    if (aa1 is aa2)
-        return true;
-
-    immutable len = _aaLen(aa1);
-    if (len != _aaLen(aa2))
-        return false;
-
-    if (!len) // both empty
-        return true;
-
-    import rt.lifetime : unqualify;
-
-    auto uti = unqualify(tiRaw);
-    auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
-    // compare the entries
-    immutable off = aa1.valoff;
-    foreach (b1; aa1.buckets)
-    {
-        if (!b1.filled)
-            continue;
-        auto pb2 = aa2.findSlotLookup(b1.hash, b1.entry, ti.key);
-        if (pb2 is null || !ti.value.equals(b1.entry + off, pb2.entry + off))
-            return false;
-    }
-    return true;
-}
-
-/// compute a hash
-extern (C) hash_t _aaGetHash(scope const AA* paa, scope const TypeInfo tiRaw) nothrow
-{
-    const AA aa = *paa;
-
-    if (aa.empty)
-        return 0;
-
-    import rt.lifetime : unqualify;
-
-    auto uti = unqualify(tiRaw);
-    auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
-    immutable off = aa.valoff;
-    auto keyHash = &ti.key.getHash;
-    auto valHash = &ti.value.getHash;
-
-    size_t h;
-    foreach (b; aa.buckets)
-    {
-        // use addition here, so that hash is independent of element order
-        if (b.filled)
-            h += hashOf(valHash(b.entry + off), keyHash(b.entry));
-    }
-
-    return h;
-}
-
-/**
- * _aaRange implements a ForwardRange
- */
-struct Range
-{
-    Impl* impl;
-    size_t idx;
-    alias impl this;
-}
-
-extern (C) pure nothrow @nogc @safe
-{
-    Range _aaRange(return scope AA aa)
-    {
-        if (!aa)
-            return Range();
-
-        foreach (i; aa.firstUsed .. aa.dim)
-        {
-            if (aa.buckets[i].filled)
-                return Range(aa, i);
-        }
-        return Range(aa, aa.dim);
-    }
-
-    bool _aaRangeEmpty(Range r)
-    {
-        return r.impl is null || r.idx >= r.dim;
-    }
-
-    void* _aaRangeFrontKey(Range r)
-    {
-        assert(!_aaRangeEmpty(r));
-        if (r.idx >= r.dim)
-            return null;
-        return r.buckets[r.idx].entry;
-    }
-
-    void* _aaRangeFrontValue(Range r)
-    {
-        assert(!_aaRangeEmpty(r));
-        if (r.idx >= r.dim)
-            return null;
-
-        auto entry = r.buckets[r.idx].entry;
-        return entry is null ?
-            null :
-            (() @trusted { return entry + r.valoff; } ());
-    }
-
-    void _aaRangePopFront(ref Range r)
-    {
-        if (r.idx >= r.dim) return;
-        for (++r.idx; r.idx < r.dim; ++r.idx)
-        {
-            if (r.buckets[r.idx].filled)
-                break;
-        }
-    }
-}
-
-// Most tests are now in test_aa.d
-
-// test postblit for AA literals
-unittest
-{
-    static struct T
-    {
-        ubyte field;
-        static size_t postblit, dtor;
-        this(this)
-        {
-            ++postblit;
-        }
-
-        ~this()
-        {
-            ++dtor;
-        }
-    }
-
-    T t;
-    auto aa1 = [0 : t, 1 : t];
-    assert(T.dtor == 0 && T.postblit == 2);
-    aa1[0] = t;
-    assert(T.dtor == 1 && T.postblit == 3);
-
-    T.dtor = 0;
-    T.postblit = 0;
-
-    auto aa2 = [0 : t, 1 : t, 0 : t]; // literal with duplicate key => value overwritten
-    assert(T.dtor == 1 && T.postblit == 3);
-
-    T.dtor = 0;
-    T.postblit = 0;
-
-    auto aa3 = [t : 0];
-    assert(T.dtor == 0 && T.postblit == 1);
-    aa3[t] = 1;
-    assert(T.dtor == 0 && T.postblit == 1);
-    aa3.remove(t);
-    assert(T.dtor == 0 && T.postblit == 1);
-    aa3[t] = 2;
-    assert(T.dtor == 0 && T.postblit == 2);
-
-    // dtor will be called by GC finalizers
-    aa1 = null;
-    aa2 = null;
-    aa3 = null;
-    auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor;
-    GC.runFinalizers((cast(char*)dtor1)[0 .. 1]);
-    auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor;
-    GC.runFinalizers((cast(char*)dtor2)[0 .. 1]);
-    assert(T.dtor == 6 && T.postblit == 2);
-}
-
-// Ensure the newaa struct layout (used for static initialization) is in sync
-unittest
-{
-    import newaa = core.internal.newaa;
-    static assert(newaa.Impl.sizeof == Impl.sizeof);
-    // ensure compatible types and offsets
-    static foreach (i; 0 .. Impl.tupleof.length)
-    {
-        // for bucket array and Flags, we "compatible" types, not exactly the same types.
-        static if (__traits(identifier, Impl.tupleof[i]) == "buckets"
-            || __traits(identifier, Impl.tupleof[i]) == "flags")
-            static assert(Impl.tupleof[i].sizeof == newaa.Impl.tupleof[i].sizeof);
-        else
-            static assert(is(typeof(Impl.tupleof[i]) == typeof(newaa.Impl.tupleof[i])));
-
-        static assert(Impl.tupleof[i].offsetof == newaa.Impl.tupleof[i].offsetof);
-    }
-}
diff --git a/libphobos/libdruntime/rt/adi.d b/libphobos/libdruntime/rt/adi.d
deleted file mode 100644 (file)
index 02e4d77..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Implementation of dynamic array property support routines.
- *
- * Copyright: Copyright Digital Mars 2000 - 2015.
- * License: Distributed under the
- *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
- *    (See accompanying file LICENSE)
- * Authors:   Walter Bright
- * Source: $(DRUNTIMESRC rt/_adi.d)
- */
-
-module rt.adi;
-
-// debug = adi; // uncomment to turn on debugging printf's
-
-debug (adi) import core.stdc.stdio : printf;
-
-/***************************************
- * Support for array equality test.
- * Returns:
- *      1       equal
- *      0       not equal
- */
-
-extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
-{
-    debug(adi) printf("_adEq2(a1.length = %zd, a2.length = %zd)\n", a1.length, a2.length);
-    if (a1.length != a2.length)
-        return 0;               // not equal
-    if (!ti.equals(&a1, &a2))
-        return 0;
-    return 1;
-}
-
-@safe unittest
-{
-    debug(adi) printf("array.Eq unittest\n");
-
-    struct S(T) { T val; }
-    alias String = S!string;
-    alias Float = S!float;
-
-    String[1] a = [String("hello"c)];
-
-    assert(a != [String("hel")]);
-    assert(a != [String("helloo")]);
-    assert(a != [String("betty")]);
-    assert(a == [String("hello")]);
-    assert(a != [String("hxxxx")]);
-
-    Float[1] fa = [Float(float.nan)];
-    assert(fa != fa);
-}
-
-unittest
-{
-    debug(adi) printf("struct.Eq unittest\n");
-
-    static struct TestStruct
-    {
-        int value;
-
-        bool opEquals(const TestStruct rhs) const
-        {
-            return value == rhs.value;
-        }
-    }
-
-    TestStruct[] b = [TestStruct(5)];
-    TestStruct[] c = [TestStruct(6)];
-    assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&c, typeid(TestStruct[])) == false);
-    assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&b, typeid(TestStruct[])) == true);
-}
diff --git a/libphobos/libdruntime/rt/cast_.d b/libphobos/libdruntime/rt/cast_.d
deleted file mode 100644 (file)
index cd110bf..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/**
- * Implementation of array assignment support routines.
- *
- * Copyright: Copyright Digital Mars 2004 - 2010.
- * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors:   Walter Bright, Sean Kelly
- * Source: $(DRUNTIMESRC rt/_cast_.d)
- */
-
-/*          Copyright Digital Mars 2004 - 2010.
- * Distributed under the Boost Software License, Version 1.0.
- *    (See accompanying file LICENSE or copy at
- *          http://www.boost.org/LICENSE_1_0.txt)
- */
-module rt.cast_;
-
-debug(cast_) import core.stdc.stdio : printf;
-
-extern (C):
-@nogc:
-nothrow:
-pure:
-
-// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
-// but we are trying to implement dynamic cast.
-extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe
-{
-    // same class if signatures match, works with potential duplicates across binaries
-    if (a is b)
-        return true;
-
-    // new fast way
-    if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig)
-        return a.nameSig[0] == b.nameSig[0]
-            && a.nameSig[1] == b.nameSig[1]
-            && a.nameSig[2] == b.nameSig[2]
-            && a.nameSig[3] == b.nameSig[3];
-
-    // old slow way for temporary binary compatibility
-    return a.name == b.name;
-}
-
-/******************************************
- * Given a pointer:
- *      If it is an Object, return that Object.
- *      If it is an interface, return the Object implementing the interface.
- *      If it is null, return null.
- *      Else, undefined crash
- */
-Object _d_toObject(return scope void* p)
-{
-    if (!p)
-        return null;
-
-    Object o = cast(Object) p;
-    ClassInfo oc = typeid(o);
-    Interface* pi = **cast(Interface***) p;
-
-    /* Interface.offset lines up with ClassInfo.name.ptr,
-     * so we rely on pointers never being less than 64K,
-     * and Objects never being greater.
-     */
-    if (pi.offset < 0x10000)
-    {
-        debug(cast_) printf("\tpi.offset = %zd\n", pi.offset);
-        return cast(Object)(p - pi.offset);
-    }
-    return o;
-}
-
-/*************************************
- * Attempts to cast interface Object o to class c.
- * Returns o if successful, null if not.
- */
-void* _d_interface_cast(void* p, ClassInfo c)
-{
-    debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, cast(int) c.name.length, c.name.ptr);
-    if (!p)
-        return null;
-
-    Interface* pi = **cast(Interface***) p;
-
-    debug(cast_) printf("\tpi.offset = %zd\n", pi.offset);
-    Object o2 = cast(Object)(p - pi.offset);
-    void* res = null;
-    size_t offset = 0;
-    if (o2 && _d_isbaseof2(typeid(o2), c, offset))
-    {
-        debug(cast_) printf("\toffset = %zd\n", offset);
-        res = cast(void*) o2 + offset;
-    }
-    debug(cast_) printf("\tresult = %p\n", res);
-    return res;
-}
-
-/*****
- * Dynamic cast from a class object `o` to class or interface `c`, where `c` is a subtype of `o`.
- * Params:
- *      o = instance of class
- *      c = a subclass of o
- * Returns:
- *      null if o is null or c is not a subclass of o. Otherwise, return o.
- */
-void* _d_dynamic_cast(Object o, ClassInfo c)
-{
-    debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr);
-
-    void* res = null;
-    size_t offset = 0;
-    if (o && _d_isbaseof2(typeid(o), c, offset))
-    {
-        debug(cast_) printf("\toffset = %zd\n", offset);
-        res = cast(void*) o + offset;
-    }
-    debug(cast_) printf("\tresult = %p\n", res);
-    return res;
-}
-
-/*****
- * Dynamic cast from a class object o to class c, where c is a subclass of o.
- * Params:
- *      o = instance of class
- *      c = a subclass of o
- * Returns:
- *      null if o is null or c is not a subclass of o. Otherwise, return o.
- */
-void* _d_class_cast(Object o, ClassInfo c)
-{
-    debug(cast_) printf("_d_cast_cast(o = %p, c = '%.*s')\n", o, cast(int) c.name.length, c.name.ptr);
-
-    if (!o)
-        return null;
-
-    ClassInfo oc = typeid(o);
-    int delta = oc.depth;
-
-    if (delta && c.depth)
-    {
-        delta -= c.depth;
-        if (delta < 0)
-            return null;
-
-        while (delta--)
-            oc = oc.base;
-        if (areClassInfosEqual(oc, c))
-            return cast(void*)o;
-        return null;
-    }
-
-    // no depth data - support the old way
-    do
-    {
-        if (areClassInfosEqual(oc, c))
-            return cast(void*)o;
-        oc = oc.base;
-    } while (oc);
-    return null;
-}
-
-/**
- * Dynamic cast `o` to final class `c` only one level down
- * Params:
- *      o = object that is instance of a class
- *      c = class to cast it to
- * Returns:
- *      o if it succeeds, null if it fails
- */
-void* _d_paint_cast(Object o, ClassInfo c)
-{
-    /* If o is really an instance of c, just do a paint
-     */
-    auto p = o && cast(void*)(areClassInfosEqual(typeid(o), c)) ? o : null;
-    debug assert(cast(void*)p is cast(void*)_d_dynamic_cast(o, c));
-    return cast(void*)p;
-}
-
-int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
-{
-    if (areClassInfosEqual(oc, c))
-        return true;
-
-    do
-    {
-        if (oc.base && areClassInfosEqual(oc.base, c))
-            return true;
-
-        // Bugzilla 2013: Use depth-first search to calculate offset
-        // from the derived (oc) to the base (c).
-        foreach (iface; oc.interfaces)
-        {
-            if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset))
-            {
-                offset += iface.offset;
-                return true;
-            }
-        }
-
-        oc = oc.base;
-    } while (oc);
-
-    return false;
-}
-
-int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
-{
-    size_t offset = 0;
-    return _d_isbaseof2(oc, c, offset);
-}
index 3558976bd0846a2cd6f77b8830f91ca06aa9fcb2..159df5956b7f3843648a019972cc4a64d07fc632 100644 (file)
@@ -229,56 +229,6 @@ private uint __typeAttrs(const scope TypeInfo ti, void *copyAttrsFrom = null) pu
     return attrs;
 }
 
-/**
-Shrink the "allocated" length of an array to be the exact size of the array.
-
-It doesn't matter what the current allocated length of the array is, the
-user is telling the runtime that he knows what he is doing.
-
-Params:
-    ti = `TypeInfo` of array type
-    arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type
-*/
-extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow
-{
-    debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %zd, arr.ptr = %p arr.length = %zd\n", ti.next.tsize, arr.ptr, arr.length);
-    auto tinext = unqualify(ti.next);
-    auto size = tinext.tsize;                  // array element size
-    auto reqsize = arr.length * size;
-    auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-
-    auto curArr = gc_getArrayUsed(arr.ptr, isshared);
-    if (curArr.ptr is null)
-        // not a valid GC pointer
-        return;
-
-    // align the array.
-    auto offset = arr.ptr - curArr.ptr;
-    auto cursize = curArr.length - offset;
-    if (cursize <= reqsize)
-        // invalid situation, or no change.
-        return;
-
-    // if the type has a destructor, destroy elements we are about to remove.
-    if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
-    {
-        auto sti = cast(TypeInfo_Struct)cast(void*)tinext;
-        if (sti.xdtor)
-        {
-            try
-            {
-                finalize_array(arr.ptr + reqsize, cursize - reqsize, sti);
-            }
-            catch (Exception e)
-            {
-                onFinalizeError(sti, e);
-            }
-        }
-    }
-
-    gc_shrinkArrayUsed(arr.ptr[0 .. reqsize], cursize, isshared);
-}
-
 package bool hasPostblit(in TypeInfo ti) nothrow pure
 {
     return (&ti.postblit).funcptr !is &TypeInfo.postblit;
@@ -335,43 +285,19 @@ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure noth
     if (length == 0 || size == 0)
         return null;
 
-    version (D_InlineAsm_X86)
+    bool overflow = false;
+    size = mulu(size, length, overflow);
+    if (!overflow)
     {
-        asm pure nothrow @nogc
+        if (auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext))
         {
-            mov     EAX,size        ;
-            mul     EAX,length      ;
-            mov     size,EAX        ;
-            jnc     Lcontinue       ;
+            debug(PRINTF) printf(" p = %p\n", ptr);
+            return ptr[0 .. length];
         }
     }
-    else version (D_InlineAsm_X86_64)
-    {
-        asm pure nothrow @nogc
-        {
-            mov     RAX,size        ;
-            mul     RAX,length      ;
-            mov     size,RAX        ;
-            jnc     Lcontinue       ;
-        }
-    }
-    else
-    {
-        bool overflow = false;
-        size = mulu(size, length, overflow);
-        if (!overflow)
-            goto Lcontinue;
-    }
-Loverflow:
+
     onOutOfMemoryError();
     assert(0);
-Lcontinue:
-
-    auto ptr = GC.malloc(size, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext);
-    if (!ptr)
-        goto Loverflow;
-    debug(PRINTF) printf(" p = %p\n", ptr);
-    return ptr[0 .. length];
 }
 
 /// ditto
@@ -628,143 +554,6 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr, TypeInfo type
 }
 
 
-/**
-Given an array of length `size` that needs to be expanded to `newlength`,
-compute a new capacity.
-
-Better version by Dave Fladebo, enhanced by Steven Schveighoffer:
-This uses an inverse logorithmic algorithm to pre-allocate a bit more
-space for larger arrays.
-- The maximum "extra" space is about 80% of the requested space. This is for
-PAGE size and smaller.
-- As the arrays grow, the relative pre-allocated space shrinks.
-- Perhaps most importantly, overall memory usage and stress on the GC
-is decreased significantly for demanding environments.
-- The algorithm is tuned to avoid any division at runtime.
-
-Params:
-    newlength = new `.length`
-    elemsize = size of the element in the new array
-Returns: new capacity for array
-*/
-size_t newCapacity(size_t newlength, size_t elemsize)
-{
-    size_t newcap = newlength * elemsize;
-
-    /*
-     * Max growth factor numerator is 234, so allow for multiplying by 256.
-     * But also, the resulting size cannot be more than 2x, so prevent
-     * growing if 2x would fill up the address space (for 32-bit)
-     */
-    enum largestAllowed = (ulong.max >> 8) & (size_t.max >> 1);
-    if (!newcap || (newcap & ~largestAllowed))
-        return newcap;
-
-    /*
-     * The calculation for "extra" space depends on the requested capacity.
-     * We use an inverse logarithm of the new capacity to add an extra 15%
-     * to 83% capacity. Note that normally we humans think in terms of
-     * percent, but using 128 instead of 100 for the denominator means we
-     * can avoid all division by simply bit-shifthing. Since there are only
-     * 64 bits in a long, the bsr of a size_t is going to be 0 - 63. Using
-     * a lookup table allows us to precalculate the multiplier based on the
-     * inverse logarithm. The formula rougly is:
-     *
-     * newcap = request * (1.0 + min(0.83, 10.0 / (log(request) + 1)))
-     */
-    import core.bitop;
-    static immutable multTable = (){
-        assert(__ctfe);
-        ulong[size_t.sizeof * 8] result;
-        foreach (i; 0 .. result.length)
-        {
-            auto factor = 128 + 1280 / (i + 1);
-            result[i] = factor > 234 ? 234 : factor;
-        }
-        return result;
-    }();
-
-    auto mult = multTable[bsr(newcap)];
-
-    // if this were per cent, then the code would look like:
-    // ((newlength * mult + 99) / 100) * elemsize
-    newcap = cast(size_t)((newlength * mult + 127) >> 7) * elemsize;
-    debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/128.0,newcap / cast(double)elemsize);
-    debug(PRINTF) printf("newcap = %zd, newlength = %zd, elemsize = %zd\n", newcap, newlength, elemsize);
-    return newcap;
-}
-
-
-/**
-Extend an array by n elements.
-
-Caller must initialize those elements.
-
-Params:
-    ti = type info of array type (not element type)
-    px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated.
-    n = number of elements to append
-Returns: `px` after being appended to
-*/
-extern (C)
-byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
-{
-    // This is a cut&paste job from _d_arrayappendT(). Should be refactored.
-
-    // Short circuit if no data is being appended.
-    if (n == 0)
-        return px;
-
-
-    // only optimize array append where ti is not a shared type
-    auto tinext = unqualify(ti.next);
-    auto sizeelem = tinext.tsize;              // array element size
-    auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
-    auto length = px.length;
-    auto newlength = length + n;
-    auto newsize = newlength * sizeelem;
-    auto size = length * sizeelem;
-
-    if (!gc_expandArrayUsed(px.ptr[0 .. size], newsize, isshared))
-    {
-        // could not set the size, we must reallocate.
-        auto newcap = newCapacity(newlength, sizeelem);
-        auto attrs = __typeAttrs(tinext, px.ptr) | BlkAttr.APPENDABLE;
-        auto ptr = cast(byte*) GC.malloc(newcap, attrs, tinext);
-        if (ptr is null)
-        {
-            onOutOfMemoryError();
-            assert(0);
-        }
-
-        if (newsize != newcap)
-        {
-            // For small blocks that are always fully scanned, if we allocated more
-            // capacity than was requested, we are responsible for zeroing that
-            // memory.
-            // TODO: should let the GC figure this out, as this property may
-            // not always hold.
-            if (!(attrs & BlkAttr.NO_SCAN) && newcap < PAGESIZE)
-                memset(ptr + newsize, 0, newcap - newsize);
-
-            gc_shrinkArrayUsed(ptr[0 .. newsize], newcap, isshared);
-        }
-
-        memcpy(ptr, px.ptr, size);
-
-        // do potsblit processing.
-        __doPostblit(ptr, size, tinext);
-
-        px = ptr[0 .. newlength];
-        return px;
-    }
-
-    // we were able to expand in place, just update the length
-    px = px.ptr[0 .. newlength];
-    return px;
-}
-
-
 /**
 Append `dchar` to `char[]`, converting UTF-32 to UTF-8
 
@@ -906,45 +695,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
     return x;
 }
 
-/**
-Allocate an array literal
-
-Rely on the caller to do the initialization of the array.
-
----
-int[] getArr()
-{
-    return [10, 20];
-    // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2);
-    // res[0] = 10;
-    // res[1] = 20;
-    // return res[0..2];
-}
----
-
-Params:
-    ti = `TypeInfo` of resulting array type
-    length = `.length` of array literal
-
-Returns: pointer to allocated array
-*/
-extern (C)
-void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak
-{
-    auto tinext = unqualify(ti.next);
-    auto sizeelem = tinext.tsize;              // array element size
-    void* result;
-
-    debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %zd, length = %zd)\n", sizeelem, length);
-    if (length == 0 || sizeelem == 0)
-        return null;
-    else
-    {
-        auto allocsize = length * sizeelem;
-        return GC.malloc(allocsize, __typeAttrs(tinext) | BlkAttr.APPENDABLE, tinext);
-    }
-}
-
 
 unittest
 {
index b1bf0c3c930fd3f3c0a6c3d7df19d631288a34ab..4bf563baa9f2494f0433b6e5c8ff1042bb192714 100644 (file)
@@ -1,4 +1,4 @@
-5a142da0af7b72bfed314278d14464d8f1147391
+f87979028bd0e0f3e67dbf5b0cd1335ca3f7ed67
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
index 7529b360eea813ec835d5a008eecf51f5dc2131a..13c35c22ddd23a6beb4479fd6212d10a41c8ca75 100644 (file)
@@ -137,10 +137,11 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/odbc/odbc32.d etc/c/odbc/odbc64.d \
        std/format/internal/write.d std/format/package.d std/format/read.d \
        std/format/spec.d std/format/write.d std/functional.d std/getopt.d \
        std/int128.d std/internal/attributes.d std/internal/cstring.d \
-       std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \
-       std/internal/math/errorfunction.d std/internal/math/gammafunction.d \
-       std/internal/memory.d std/internal/scopebuffer.d \
-       std/internal/test/dummyrange.d std/internal/test/range.d \
+       std/internal/entropy.d std/internal/math/biguintcore.d \
+       std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \
+       std/internal/math/gammafunction.d std/internal/memory.d \
+       std/internal/scopebuffer.d std/internal/test/dummyrange.d \
+       std/internal/test/range.d \
        std/internal/test/sumtype_example_overloads.d std/internal/test/uda.d \
        std/internal/unicode_comp.d std/internal/unicode_decomp.d \
        std/internal/unicode_grapheme.d std/internal/unicode_norm.d \
index 27ed8788a47005a746fbfac0b0b0a88eb9974d93..960ca98cc1d485ebfc3e89786b9c7dde8235d240 100644 (file)
@@ -238,6 +238,7 @@ am__dirstamp = $(am__leading_dot)dirstamp
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/int128.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/attributes.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/cstring.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/entropy.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/biguintcore.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/biguintnoasm.lo \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/errorfunction.lo \
@@ -609,10 +610,11 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/format/internal/write.d std/format/package.d std/format/read.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/format/spec.d std/format/write.d std/functional.d std/getopt.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/int128.d std/internal/attributes.d std/internal/cstring.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/errorfunction.d std/internal/math/gammafunction.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/memory.d std/internal/scopebuffer.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/test/dummyrange.d std/internal/test/range.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/entropy.d std/internal/math/biguintcore.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/biguintnoasm.d std/internal/math/errorfunction.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/math/gammafunction.d std/internal/memory.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/scopebuffer.d std/internal/test/dummyrange.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/test/range.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/test/sumtype_example_overloads.d std/internal/test/uda.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/unicode_comp.d std/internal/unicode_decomp.d \
 @ENABLE_LIBDRUNTIME_ONLY_FALSE@        std/internal/unicode_grapheme.d std/internal/unicode_norm.d \
@@ -879,6 +881,7 @@ std/internal/$(am__dirstamp):
        @: > std/internal/$(am__dirstamp)
 std/internal/attributes.lo: std/internal/$(am__dirstamp)
 std/internal/cstring.lo: std/internal/$(am__dirstamp)
+std/internal/entropy.lo: std/internal/$(am__dirstamp)
 std/internal/math/$(am__dirstamp):
        @$(MKDIR_P) std/internal/math
        @: > std/internal/math/$(am__dirstamp)
index 481222596702c791059fb90b3bdbdbc6793d06f0..ed8e46ba8bbf6f6bf1bcaa623ee225894895f0bb 100644 (file)
@@ -306,6 +306,11 @@ $(BOOKTABLE ,
         $(TDNW $(MREF std,bigint))
         $(TD An arbitrary-precision integer type.)
     )
+    $(TR
+        $(TDNW $(MREF std,int128))
+        $(TDNW $(MREF core,int128))
+        $(TD 128 bit-precision integer type.)
+    )
     $(TR
         $(TDNW $(MREF std,complex))
         $(TD A complex number type.)
index 7923eeee944d3e35608a1e5c48636c478a1f498e..34d787ec2784bb67a47283f11afbd1ee9791247e 100644 (file)
@@ -8161,6 +8161,13 @@ Returns:
     A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
     of elements of which are an $(REF indexed, std,range) view into `r`.
 
+Note:
+    The elements of the resulting range reuse the same internal buffer of
+    permutations, so each element is invalidated by `popFront`.  If copies of
+    intermediate permutations are desired, they need to be individually copied,
+    such as using `.map!(e => e.array)` to save them in individual, independent
+    arrays.
+
 See_Also:
 $(REF nextPermutation, std,algorithm,sorting).
 */
index 8a45ecb81319b7d8c1dc61560e22adfe8a99c861..1dc0e79992b596bb29c7f4b9156fdaea1731842b 100644 (file)
@@ -3120,7 +3120,7 @@ if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
     static struct A
     {
         int x;
-        this(scope ref return const A other)
+        this(scope return ref const A other)
         {
             import std.stdio;
             x = other.x;
index e43aeb533172c5fada49e0b698cf1e69162ad079..30becba4d94dea50010019c92cf8c40abcdf3776 100644 (file)
@@ -807,6 +807,11 @@ if (isInputRange!R && !isInfinite!R)
     - `needle` is the index into `needles` which matched.
     - Both are `-1` if there was no match.
 
+    Warning: Due to $(LINK2 https://tour.dlang.org/tour/en/gems/unicode,
+    auto-decoding), the return value of this function may $(I not) correspond
+    to the array index for strings.  To find the index of an element matching
+    the predicate in a string, use $(REF indexOf, std,string) instead.
+
     See_Also: $(REF indexOf, std,string)
   +/
 auto countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
@@ -1046,6 +1051,11 @@ if (isInputRange!R &&
     $(LREF startsWith)`!pred(haystack)` is `true`.
   - If `startsWith!pred(haystack)` is not `true` for any element in
     `haystack`, then `-1` is returned.
+
+    Warning: Due to $(LINK2 https://tour.dlang.org/tour/en/gems/unicode,
+    auto-decoding), the return value of this function may $(I not) correspond
+    to the array index for strings.  To find the index of an element matching
+    the predicate in a string, use $(REF indexOf, std,string) instead.
   +/
 ptrdiff_t countUntil(alias pred, R)(R haystack)
 if (isInputRange!R &&
index 367c981320a3c36897fbaffd16b8949d290e6a7b..a69b7bf84d9c94c38caa96f80d4fb5349c24256e 100644 (file)
@@ -23,6 +23,7 @@ $(TR $(TD Validation) $(TD
         $(LREF isDigit)
         $(LREF isGraphical)
         $(LREF isHexDigit)
+        $(LREF isLower)
         $(LREF isOctalDigit)
         $(LREF isPrintable)
         $(LREF isPunctuation)
index fe139a56be384f7fa1ceec93d33d858336c49fd4..17e9fc42215306d5ea89c3c1a56937801bb94bec 100644 (file)
@@ -679,7 +679,7 @@ private struct RBRange(N)
     /**
      * Returns the first element in the range
      */
-    @property Elem front()
+    ref @property Elem front()
     {
         return _begin.value;
     }
@@ -687,7 +687,7 @@ private struct RBRange(N)
     /**
      * Returns the last element in the range
      */
-    @property Elem back()
+    ref @property Elem back()
     {
         return _end.prev.value;
     }
@@ -737,13 +737,17 @@ private struct RBRange(N)
  * elements `a` and `b`, $(D less(a, b) == !less(b, a)). $(D less(a, a)) should
  * always equal `false`.
  *
+ * Care should also be taken to not modify elements in the tree (e.g. via `front` /
+ * `back`, which return by `ref`) in a way which would affect the order defined by
+ * the `less` predicate.
+ *
  * If `allowDuplicates` is set to `true`, then inserting the same element more than
  * once continues to add more elements.  If it is `false`, duplicate elements are
  * ignored on insertion.  If duplicates are allowed, then new elements are
  * inserted after all existing duplicate elements.
  */
 final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false)
-if (is(typeof(binaryFun!less(T.init, T.init))))
+if (is(typeof((ref const T a) => binaryFun!less(a, a))))
 {
     import std.meta : allSatisfy;
     import std.range : Take;
@@ -1036,7 +1040,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
      *
      * Complexity: $(BIGOH 1)
      */
-    inout(Elem) front() inout
+    ref inout(Elem) front() inout
     {
         return _begin.value;
     }
@@ -1046,7 +1050,7 @@ if (is(typeof(binaryFun!less(T.init, T.init))))
      *
      * Complexity: $(BIGOH log(n))
      */
-    inout(Elem) back() inout
+    ref inout(Elem) back() inout
     {
         return _end.prev.value;
     }
@@ -2256,3 +2260,14 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init
     t.insert([1, 3, 5, 4, 2]);
     assert(t[].equal([5, 4, 3, 2, 1]));
 }
+
+// should support `less` predicate taking `ref const`
+@safe pure unittest
+{
+    struct S
+    {
+        int* value;
+    }
+
+    cast(void) new RedBlackTree!(S, (ref const S a, ref const S b) => a.value > b.value);
+}
index 0f417b15081e84bf3dc1c494549c7938a2b351f7..ced65c9a9207dbdd1264e8f993e119ddae59c6f9 100644 (file)
@@ -2131,6 +2131,7 @@ public:
         $(BOOKTABLE,
         $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime))
         $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime))
+        $(TR $(TD Duration) $(TD +) $(TD DateTime) $(TD -->) $(TD DateTime))
         )
 
         Params:
@@ -2145,6 +2146,14 @@ public:
         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
     }
 
+    /// ditto
+    DateTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
+    if (op == "+")
+    {
+        return this + duration;
+    }
+
+
     ///
     @safe unittest
     {
@@ -2161,6 +2170,9 @@ public:
 
         assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
                DateTime(2015, 12, 31, 23, 59, 59));
+
+        assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
+               hours(1) + DateTime(2015, 12, 31, 23, 59, 59));
     }
 
     @safe unittest
@@ -6134,13 +6146,14 @@ public:
     import core.time : Duration;
     /++
         Gives the result of adding or subtracting a $(REF Duration, core,time)
-        from
+        from this $(LREF Date).
 
         The legal types of arithmetic for $(LREF Date) using this operator are
 
         $(BOOKTABLE,
         $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date))
         $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date))
+        $(TR $(TD Duration) $(TD +) $(TD Date) $(TD -->) $(TD Date))
         )
 
         Params:
@@ -6155,6 +6168,14 @@ public:
         mixin("return retval._addDays(" ~ op ~ "days);");
     }
 
+
+    /// ditto
+    Date opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
+    if (op == "+")
+    {
+        return this + duration;
+    }
+
     ///
     @safe unittest
     {
@@ -6165,6 +6186,8 @@ public:
 
         assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
         assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
+
+        assert(Date(2004, 2, 26) + days(4) == days(4) + Date(2004, 2, 26));
     }
 
     @safe unittest
@@ -8844,6 +8867,7 @@ public:
         $(BOOKTABLE,
         $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay))
         $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay))
+        $(TR $(TD Duration) $(TD +) $(TD TimeOfDay) $(TD -->) $(TD TimeOfDay))
         )
 
         Params:
@@ -8858,6 +8882,13 @@ public:
         mixin("return retval._addSeconds(" ~ op ~ "seconds);");
     }
 
+    /// ditto
+    TimeOfDay opBinaryRight(string op)(Duration duration) const @safe pure nothrow @nogc
+    if (op == "+")
+    {
+        return this + duration;
+    }
+
     ///
     @safe unittest
     {
@@ -8872,6 +8903,8 @@ public:
         assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
         assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
         assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
+
+        assert(TimeOfDay(12, 12, 12) + seconds(1) == seconds(1) + TimeOfDay(12, 12, 12));
     }
 
     @safe unittest
index fd2a9e192e4278d05b9fa02931c4ee97fb1fa65a..189b95ce5b1b08b4af21bf2264fb778e8a557e12 100644 (file)
@@ -6301,6 +6301,7 @@ public:
         $(BOOKTABLE,
         $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime))
         $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime))
+        $(TR $(TD Duration) $(TD +) $(TD SysTime) $(TD -->) $(TD SysTime))
         )
 
         Params:
@@ -6316,6 +6317,13 @@ public:
         return retval;
     }
 
+    /// ditto
+    SysTime opBinaryRight(string op)(Duration duration) const @safe pure nothrow
+    if (op == "+")
+    {
+        return this + duration;
+    }
+
     ///
     @safe unittest
     {
@@ -6333,6 +6341,9 @@ public:
 
         assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) ==
                SysTime(DateTime(2015, 12, 31, 23, 59, 59)));
+
+        assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) ==
+               seconds(1) + SysTime(DateTime(2015, 12, 31, 23, 59, 59)));
     }
 
     @safe unittest
index 6a1898b0ef50c90b6acda4b8ecd5447dcd3c3abd..7461fcaa33b4a0ebbfee241cccd2c30ba6e8404f 100644 (file)
@@ -291,7 +291,7 @@ public:
                 // be there, but since PosixTimeZone _does_ use leap seconds if
                 // the time zone file does, we'll test that functionality if the
                 // appropriate files exist.
-                if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists)
+                if (chainPath(PosixTimeZone.getDefaultTZDatabaseDir(), "right", tzName).exists)
                 {
                     auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName);
 
@@ -2014,7 +2014,8 @@ public:
             the TZDatabaseDir version to pass an arbitrary path at compile-time,
             rather than hard-coding it here. Android concatenates all time zone
             data into a single file called tzdata and stores it in the directory
-            below.
+            below. If the TZDIR environment variable is set, it is consulted
+            before this constant.
           +/
         enum defaultTZDatabaseDir = "";
     }
@@ -2040,6 +2041,18 @@ public:
         enum defaultTZDatabaseDir = "";
     }
 
+    private static string getDefaultTZDatabaseDir()
+    {
+        import core.stdc.stdlib : getenv;
+        import std.string : fromStringz;
+
+        auto dir = getenv("TZDIR");
+        if (dir)
+            return fromStringz(dir).idup;
+
+        return defaultTZDatabaseDir;
+    }
+
 
     /++
         Returns a $(LREF TimeZone) with the give name per the TZ Database. The
@@ -2067,7 +2080,7 @@ public:
       +/
     // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed
     //      directory.
-    static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted
+    static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = getDefaultTZDatabaseDir()) @trusted
     {
         import std.algorithm.sorting : sort;
         import std.conv : to;
@@ -2418,7 +2431,7 @@ public:
         Throws:
             `FileException` if it fails to read from disk.
       +/
-    static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @safe
+    static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = getDefaultTZDatabaseDir()) @safe
     {
         import std.algorithm.sorting : sort;
         import std.array : appender;
@@ -2510,11 +2523,12 @@ public:
         {}
         else
         {
-            foreach (DirEntry de; dirEntries(defaultTZDatabaseDir, SpanMode.depth))
+            string tzDatabaseDir = getDefaultTZDatabaseDir();
+            foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth))
             {
                 if (de.isFile)
                 {
-                    auto tzName = de.name[defaultTZDatabaseDir.length .. $];
+                    auto tzName = de.name[tzDatabaseDir.length .. $];
 
                     if (!canFind(tzNames, tzName))
                         assertThrown!DateTimeException(testPTZFailure(tzName));
index 167cf1bc6bce32e3e6f791b08b5d86be190a11b4..3d04bb6f0539e6df75bb19e220f3d0eeb08c470d 100644 (file)
@@ -111,11 +111,13 @@ struct KRRegion(ParentAllocator = NullAllocator)
 
         this(this) @disable;
 
+        nothrow @nogc @trusted
         void[] payload() inout
         {
             return (cast(ubyte*) &this)[0 .. size];
         }
 
+        nothrow @nogc @trusted
         bool adjacent(in Node* right) const
         {
             assert(right);
@@ -123,6 +125,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
             return p.ptr < right && right < p.ptr + p.length + Node.sizeof;
         }
 
+        nothrow @nogc @trusted
         bool coalesce(void* memoryEnd = null)
         {
             // Coalesce the last node before the memory end with any possible gap
@@ -139,6 +142,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
             return true;
         }
 
+        nothrow @nogc @safe
         Tuple!(void[], Node*) allocateHere(size_t bytes)
         {
             assert(bytes >= Node.sizeof);
@@ -152,7 +156,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
             if (leftover >= Node.sizeof)
             {
                 // There's room for another node
-                auto newNode = cast(Node*) ((cast(ubyte*) &this) + bytes);
+                auto newNode = (() @trusted => cast(Node*) ((cast(ubyte*) &this) + bytes))();
                 newNode.size = leftover;
                 newNode.next = next == &this ? newNode : next;
                 assert(next);
@@ -174,8 +178,8 @@ struct KRRegion(ParentAllocator = NullAllocator)
     else alias parent = ParentAllocator.instance;
     private void[] payload;
     private Node* root;
-    private bool regionMode() const { return bytesUsedRegionMode != size_t.max; }
-    private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; }
+    nothrow @nogc @safe private bool regionMode() const { return bytesUsedRegionMode != size_t.max; }
+    nothrow @nogc @safe private void cancelRegionMode() { bytesUsedRegionMode = size_t.max; }
     private size_t bytesUsedRegionMode = 0;
 
     auto byNodePtr()
@@ -257,6 +261,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
         }
     }
 
+    nothrow @nogc @safe
     private Node* sortFreelist(Node* root)
     {
         // Find a monotonic run
@@ -274,6 +279,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
         return merge(root, tail);
     }
 
+    nothrow @nogc @safe
     private Node* merge(Node* left, Node* right)
     {
         assert(left != right);
@@ -290,6 +296,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
         return result;
     }
 
+    nothrow @nogc @safe
     private void coalesceAndMakeCircular()
     {
         for (auto n = root;;)
@@ -368,6 +375,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
     Otherwise, sorts the free list accumulated so far and switches strategy for
     future allocations to KR style.
     */
+    nothrow @nogc @safe
     void switchToFreeList()
     {
         if (!regionMode) return;
@@ -396,6 +404,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
 
     Returns: A word-aligned buffer of `n` bytes, or `null`.
     */
+    nothrow @nogc @safe
     void[] allocate(size_t n)
     {
         if (!n || !root) return null;
@@ -413,7 +422,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
                 immutable balance = root.size - actualBytes;
                 if (balance >= Node.sizeof)
                 {
-                    auto newRoot = cast(Node*) (result + actualBytes);
+                    auto newRoot = (() @trusted => cast(Node*) ((cast(ubyte*) result) + actualBytes))();
                     newRoot.next = root.next;
                     newRoot.size = balance;
                     root = newRoot;
@@ -423,7 +432,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
                     root = null;
                     switchToFreeList;
                 }
-                return result[0 .. n];
+                return (() @trusted => result[0 .. n])();
             }
 
             // Not enough memory, switch to freelist mode and fall through
@@ -554,6 +563,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
     at the front of the free list. These blocks get coalesced, whether
     `allocateAll` succeeds or fails due to fragmentation.
     */
+    nothrow @nogc @safe
     void[] allocateAll()
     {
         if (regionMode) switchToFreeList;
index fc5cdac73cec00c4103552f873417464aed23a38..0c5dfb9506cf95ee69c3be1fbee7c946f65ee400 100644 (file)
@@ -542,6 +542,13 @@ private pure Option splitAndGet(string opt) @trusted nothrow
     assert(olongshort.optLong == "--foo");
 }
 
+private string optionValidatorErrorFormat(string msg, size_t idx)
+{
+    import std.conv : to;
+    return "getopt validator: " ~ msg ~ " (at position " ~ to!(string)(idx) ~
+        ")";
+}
+
 /*
 This function verifies that the variadic parameters passed in getOpt
 follow this pattern:
@@ -555,9 +562,6 @@ follow this pattern:
 */
 private template optionValidator(A...)
 {
-    import std.format : format;
-
-    enum fmt = "getopt validator: %s (at position %d)";
     enum isReceiver(T) = is(T == U*, U) || (is(T == function)) || (is(T == delegate));
     enum isOptionStr(T) = isSomeString!T || isSomeChar!T;
 
@@ -568,11 +572,11 @@ private template optionValidator(A...)
         {
             static if (isReceiver!(A[0]))
             {
-                msg = format(fmt, "first argument must be a string or a config", 0);
+                msg = optionValidatorErrorFormat("first argument must be a string or a config", 0);
             }
             else static if (!isOptionStr!(A[0]) && !is(A[0] == config))
             {
-                msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0);
+                msg = optionValidatorErrorFormat("invalid argument type: " ~ A[0].stringof, 0);
             }
             else
             {
@@ -581,25 +585,25 @@ private template optionValidator(A...)
                     static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) &&
                         !(is(A[i] == config)))
                     {
-                        msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i);
+                        msg = optionValidatorErrorFormat("invalid argument type: " ~ A[i].stringof, i);
                         goto end;
                     }
                     else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1]))
                     {
-                        msg = format(fmt, "a receiver can not be preceeded by a receiver", i);
+                        msg = optionValidatorErrorFormat("a receiver can not be preceeded by a receiver", i);
                         goto end;
                     }
                     else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1])
                         && isSomeString!(A[i-2]))
                     {
-                        msg = format(fmt, "a string can not be preceeded by two strings", i);
+                        msg = optionValidatorErrorFormat("a string can not be preceeded by two strings", i);
                         goto end;
                     }
                 }
             }
             static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config))
             {
-                msg = format(fmt, "last argument must be a receiver or a config",
+                msg = optionValidatorErrorFormat("last argument must be a receiver or a config",
                     A.length -1);
             }
         }
@@ -614,16 +618,16 @@ private auto getoptTo(R)(string option, string value,
         size_t idx, string file = __FILE__, size_t line = __LINE__)
 {
     import std.conv : to, ConvException;
-    import std.format : format;
     try
     {
         return to!R(value);
     }
     catch (ConvException e)
     {
-        throw new ConvException(format("Argument '%s' at position '%u' could "
-            ~ "not be converted to type '%s' as required by option '%s'.",
-            value, idx, R.stringof, option), e, file, line);
+        throw new ConvException("Argument '" ~ value ~ "' at position '" ~
+            to!(string)(idx) ~ "' could not be converted to type '" ~
+            R.stringof ~ "' as required by option '" ~ option ~ "'.", e, file,
+            line);
     }
 }
 
diff --git a/libphobos/src/std/internal/entropy.d b/libphobos/src/std/internal/entropy.d
new file mode 100644 (file)
index 0000000..92b3323
--- /dev/null
@@ -0,0 +1,918 @@
+// Written in the D programming language.
+
+/+
+    CSPRNG library prototype.
+
+    This code has not been audited.
+    Do not use for cryptographic purposes.
+
+    The terms $(I entropy) and $(I entropy sources) here do refer to
+    cryptographically-safe random numbers and higher-level generators of such
+    — typically powered by an entropy pool provided by the operating system.
+
+    An example of similar usage of said terminology would be the `getentropy()`
+    function provided by
+    $(LINK2 https://man.freebsd.org/cgi/man.cgi?query=getentropy&apropos=0&sektion=3&manpath=FreeBSD+14.2-RELEASE&arch=default&format=html,
+    FreeBSD).
+
+    This library does not interact with any actual low-level entropy sources
+    by itself. Instead it interfaces with system-provided CSPRNGs that are
+    typically seeded through aforementioned entropy sources by the operating
+    system as needed.
+
+    See_also:
+        $(LINK https://blog.cr.yp.to/20140205-entropy.html),
+        $(LINK https://cr.yp.to/talks/2014.10.18/slides-djb-20141018-a4.pdf)
+
+    License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+    Authors:   Elias Batek
+    Source:    $(PHOBOSSRC std/internal/entropy.d)
+ +/
+module std.internal.entropy;
+
+import std.meta;
+
+version (OSX)
+    version = Darwin;
+else version (iOS)
+    version = Darwin;
+else version (TVOS)
+    version = Darwin;
+else version (WatchOS)
+    version = Darwin;
+
+// Self-test: Detect potentially unsuitable default entropy source.
+@safe unittest
+{
+    auto buffer = new ubyte[](32);
+    forceEntropySource(defaultEntropySource);
+    const result = getEntropy(buffer);
+
+    assert(
+        !result.isUnavailable,
+        "The default entropy source for the target platform"
+        ~ " is unavailable on this machine. Please consider"
+        ~ " patching it to accommodate to your environment."
+    );
+    assert(result.isOK);
+}
+
+// Self-test: Detect faulty implementation.
+@system unittest
+{
+    forceEntropySource(defaultEntropySource);
+
+    bool test() @system
+    {
+        static immutable pattern = 0xDEAD_BEEF_1337_0000;
+        long number = pattern;
+        const result = getEntropy(&number, number.sizeof);
+        assert(result.isOK);
+        return number != pattern;
+    }
+
+    size_t timesFailed = 0;
+    foreach (n; 0 .. 3)
+        if (!test())
+            ++timesFailed;
+
+    assert(
+        timesFailed <= 1,
+        "Suspicious random data: Potential security issue or really unlucky; please retry."
+    );
+}
+
+// Self-test: Detect faulty implementation.
+@safe unittest
+{
+    forceEntropySource(defaultEntropySource);
+
+    bool test() @safe
+    {
+        ubyte[32] data;
+        data[] = 0;
+
+        const result = getEntropy(data[]);
+        assert(result.isOK);
+
+        size_t zeros = 0;
+        foreach (b; data)
+            if (b == 0)
+                ++zeros;
+
+        enum threshold = 24;
+        return zeros < threshold;
+    }
+
+    size_t timesFailed = 0;
+    foreach (n; 0 .. 3)
+        if (!test())
+            ++timesFailed;
+
+    assert(
+        timesFailed <= 1,
+        "Suspicious random data: Potential security issue or really unlucky; please retry."
+    );
+}
+
+@nogc nothrow:
+
+// Flagship function
+/++
+    Retrieves random data from an applicable system CSPRNG.
+
+    Params:
+        buffer = An output buffer to store the retrieved entropy in.
+                 The length of it will determine the amount of random data to
+                 be obtained.
+
+                 This function (and all overloads) always attempt to fill
+                 the entire buffer. Therefore, they can block, spin or report
+                 an error.
+
+    Returns:
+        An `EntropyResult` that either reports success
+        or the type of error that has occurred.
+
+        In case of an error, the data in `buffer` MUST NOT be used.
+        The recommended way to check for success is through the `isOK()`
+        helper function.
+ +/
+EntropyResult getEntropy(scope void[] buffer) @safe
+{
+    return getEntropyImpl(buffer);
+}
+
+///
+@safe unittest
+{
+    int[4] bytes;
+    if (getEntropy(cast(void[]) bytes).isOK)
+    {
+        // Success; data in `bytes` may be used.
+    }
+
+    assert((cast(void[]) bytes).length == bytes.sizeof);
+}
+
+// Convenience overload
+/// ditto
+EntropyResult getEntropy(scope ubyte[] buffer) @safe
+{
+    return getEntropy(cast(void[]) buffer);
+}
+
+///
+@safe unittest
+{
+    ubyte[16] bytes;
+    if (getEntropy(bytes).isOK)
+    {
+        // Success; data in `bytes` may be used.
+    }
+}
+
+// Convenience wrapper
+/// ditto
+/++
+    Retrieves random data from an applicable system CSPRNG.
+
+    Params:
+        buffer = An output buffer to store the retrieved entropy in.
+        length = Length of the provided `buffer`.
+                 Specifying a wrong value here, will lead to memory corruption.
+
+    Returns:
+        An `EntropyResult` that either reports success
+        or the type of error that has occurred.
+
+        In case of an error, the data in `buffer` MUST NOT be used.
+        The recommended way to check for success is through the `isOK()`
+        helper function.
+ +/
+EntropyResult getEntropy(scope void* buffer, size_t length) @system
+{
+    return getEntropy(buffer[0 .. length]);
+}
+
+///
+@system unittest
+{
+    ubyte[16] bytes;
+    if (getEntropy(cast(void*) bytes.ptr, bytes.length).isOK)
+    {
+        // Success; data in `bytes` may be used.
+    }
+}
+
+///
+@system unittest
+{
+    int number = void;
+    if (getEntropy(&number, number.sizeof).isOK)
+    {
+        // Success; value of `number` may be used.
+    }
+}
+
+/++
+    Manually set the entropy source to use for the current thread.
+
+    As a rule of thumb, this SHOULD NOT be done.
+
+    It might be useful in cases where the default entropy source — as chosen by
+    the maintainer of the used compiler package — is unavailable on a system.
+    Usually, `EntropySource.tryAll` will be the most reasonable option
+    in such cases.
+
+    Params:
+        source = The requested default entropy source to use for the current thread.
+
+    Examples:
+
+    ---
+    // Using `forceEntropySource` almost always is a bad idea.
+    // As a rule of thumb, this SHOULD NOT be done.
+    forceEntropySource(EntropySource.none);
+    ---
+ +/
+void forceEntropySource(EntropySource source) @safe
+{
+    _entropySource = source;
+}
+
+// (In-)Convenience wrapper
+/++
+    Retrieves random data from the requested entropy source.
+
+    In general, it’s a $(B bad idea) to let users pick sources themselves.
+    A sane option should be used by default instead.
+
+    This overload only exists because its used by Phobos.
+
+    See_also:
+        Use `forceEntropySource` instead.
+
+    Params:
+        buffer = An output buffer to store the retrieved entropy in.
+                 The length of it will determine the amount of entropy to be
+                 obtained.
+        length = Length of the provided `buffer`.
+                 Specifying a wrong value here, will lead to memory corruption.
+        source = The entropy source to use for the operation.
+
+    Returns:
+        An `EntropyResult` that either reports success
+        or the type of error that has occurred.
+
+        In case of an error, the data in `buffer` MUST NOT be used.
+        The recommended way to check for success is through the `isOK()`
+        helper function.
+ +/
+EntropyResult getEntropy(scope void* buffer, size_t length, EntropySource source) @system
+{
+    const sourcePrevious = _entropySource;
+    scope (exit) _entropySource = sourcePrevious;
+
+    _entropySource = source;
+    return getEntropy(buffer[0 .. length]);
+}
+
+///
+@system unittest
+{
+    ubyte[4] bytes;
+
+    // `EntropySource.none` always fails.
+    assert(!getEntropy(bytes.ptr, bytes.length, EntropySource.none).isOK);
+}
+
+/++
+    A CSPRNG suitable to retrieve cryptographically-secure random data from.
+
+    (No actual low-level entropy sources are provided on purpose.)
+ +/
+enum EntropySource
+{
+    /// Implements a $(I hunting) strategy for finding an entropy source that
+    /// is available at runtime.
+    ///
+    /// Try supported sources one-by-one until one is available.
+    /// This exists to enable the use of this the entropy library
+    /// in a backwards compatibility way.
+    ///
+    /// It is recommended against using this in places that do not strictly
+    /// have to to meet compatibility requirements.
+    /// Like any kind of crypto-agility, this approach may suffer from
+    /// practical issues.
+    ///
+    /// See_also:
+    /// While the following article focuses on cipher agility in protocols,
+    /// it elaborates why agility can lead to problems:
+    /// $(LINK https://web.archive.org/web/20191102211148/https://paragonie.com/blog/2019/10/against-agility-in-cryptography-protocols)
+    tryAll = -1,
+
+    /// Always fail.
+    none = 0,
+
+    /// `/dev/urandom`
+    charDevURandom = 1,
+
+    /// `/dev/random`
+    charDevRandom = 2,
+
+    /// `getrandom` syscall or wrapper
+    getrandom = 3,
+
+    /// `arc4random`
+    arc4random = 4,
+
+    // `getentropy`
+    getentropy = 5,
+
+    /// Windows legacy CryptoAPI
+    cryptGenRandom = 6,
+
+    /// Windows Cryptography API: Next Generation (“BCrypt”)
+    bcryptGenRandom = 7,
+}
+
+///
+enum EntropyStatus
+{
+    /// success
+    ok = 0,
+
+    /// catch-all error
+    unknownError = 1,
+
+    /// An entropy source was unavailable.
+    unavailable,
+
+    /// A dependency providing the entropy source turned out unavailable.
+    unavailableLibrary,
+
+    /// The requested entropy source is not supported on this platform.
+    unavailablePlatform,
+
+    /// Could not retrieve entropy from the selected source.
+    readError,
+}
+
+/++
+    Status report returned by `getEntropy` functions.
+
+    Use the `isOK` helper function to test for success.
+ +/
+struct EntropyResult
+{
+    ///
+    EntropyStatus status;
+
+    ///
+    EntropySource source;
+
+    /++
+        Returns:
+            A human-readable status message.
+     +/
+    string toString() const @nogc nothrow pure @safe
+    {
+        if (status == EntropyStatus.ok)
+            return "getEntropy(): OK.";
+
+        if (source == EntropySource.none)
+        {
+            if (status == EntropyStatus.unavailable)
+                return "getEntropy(): Error - No suitable entropy source was available.";
+        }
+        else if (source == EntropySource.getrandom)
+        {
+            if (status == EntropyStatus.unavailableLibrary)
+                return "getEntropy(): `dlopen(\"libc\")` failed.";
+            if (status == EntropyStatus.unavailable)
+                return "getEntropy(): `dlsym(\"libc\", \"getrandom\")` failed.";
+            if (status == EntropyStatus.readError)
+                return "getEntropy(): `getrandom()` failed.";
+        }
+        else if (source == EntropySource.getentropy)
+        {
+            if (status == EntropyStatus.readError)
+                return "getEntropy(): `getentropy()` failed.";
+        }
+        else if (source == EntropySource.charDevURandom)
+        {
+            if (status == EntropyStatus.unavailable)
+                return "getEntropy(): `/dev/urandom` is unavailable.";
+            if (status == EntropyStatus.readError)
+                return "getEntropy(): Reading from `/dev/urandom` failed.";
+        }
+        else if (source == EntropySource.charDevURandom)
+        {
+            if (status == EntropyStatus.unavailable)
+                return "getEntropy(): `/dev/random` is unavailable.";
+            if (status == EntropyStatus.readError)
+                return "getEntropy(): Reading from `/dev/random` failed.";
+        }
+        else if (source == EntropySource.bcryptGenRandom)
+        {
+            if (status == EntropyStatus.unavailableLibrary)
+                return "getEntropy(): `LoadLibraryA(\"Bcrypt.dll\")` failed.";
+            if (status == EntropyStatus.unavailable)
+                return "getEntropy(): `GetProcAddress(hBcrypt , \"BCryptGenRandom\")` failed.";
+            if (status == EntropyStatus.readError)
+                return "getEntropy(): `BCryptGenRandom()` failed.";
+        }
+
+        // generic errors
+        {
+            if (status == EntropyStatus.unavailable ||
+                status == EntropyStatus.unavailableLibrary)
+                return "getEntropy(): An entropy source was unavailable.";
+            if (status == EntropyStatus.unavailablePlatform)
+                return "getEntropy(): The requested entropy source is not supported on this platform.";
+            if (status == EntropyStatus.readError)
+                return "getEntropy(): Could not retrieve entropy from the selected source.";
+
+            return "getEntropy(): An unknown error occurred.";
+        }
+    }
+}
+
+///
+@safe unittest
+{
+    ubyte[4] data;
+    EntropyResult result = getEntropy(data[]);
+
+    if (result.isOK)
+    {
+        // Success; data in `bytes` may be used.
+    }
+    else
+    {
+        // Failure
+
+        if (result.isUnavailable)
+        {
+            // System’s entropy source was unavailable.
+        }
+
+        // Call `toString` to obtain a user-readable error message.
+        assert(result.toString() !is null);
+        assert(result.toString().length > 0);
+    }
+}
+
+/++
+    Determines whether an `EntropyResult` reports the success of an operation.
+
+    Params:
+        value = test subject
+
+    Returns:
+        `true` on success
+ +/
+pragma(inline, true) bool isOK(const EntropyResult value) pure @safe
+{
+    return value.status == EntropyStatus.ok;
+}
+
+/++
+    Determines whether an `EntropyResult` reports the unvailability of the
+    requested entropy source.
+
+    Params:
+        value = test subject
+
+    Returns:
+        `true` if entropy source requested to use with the operation was unavailable.
+ +/
+pragma(inline, true) bool isUnavailable(const EntropyResult value) pure @safe
+{
+    return (
+        value.status == EntropyStatus.unavailable ||
+        value.status == EntropyStatus.unavailableLibrary ||
+        value.status == EntropyStatus.unavailablePlatform
+    );
+}
+
+package(std):
+
+// If the system let us down, we'll let the system down.
+pragma(inline, true) void crashOnError(const EntropyResult value) pure @safe
+{
+    if (value.isOK)
+        return;
+
+    assert(false, value.toString());
+}
+
+/+
+    Building blocks and implementation helpers
+ +/
+private
+{
+    /++
+        A “Chunks” implementation that works with `void[]`.
+     +/
+    struct VoidChunks
+    {
+        void[] _data;
+        size_t _chunkSize;
+
+    @nogc nothrow pure @safe:
+
+        this(void[] data, size_t chunkSize)
+        {
+            _data = data;
+            _chunkSize = chunkSize;
+        }
+
+        bool empty() const
+        {
+            return _data.length == 0;
+        }
+
+        inout(void)[] front() inout
+        {
+            if (_data.length < _chunkSize)
+                return _data;
+
+            return _data[0 .. _chunkSize];
+        }
+
+        void popFront()
+        {
+            if (_data.length <= _chunkSize)
+            {
+                _data = null;
+                return;
+            }
+
+            _data = _data[_chunkSize .. $];
+        }
+    }
+
+    struct SrcFunPair(EntropySource source, alias func)
+    {
+        enum  src = source;
+        alias fun = func;
+    }
+
+    template isValidSupportedSource(SupportedSource)
+    {
+        enum isValidSupportedSource = (
+            is(SupportedSource == SrcFunPair!Args, Args...) &&
+            SupportedSource.src != EntropySource.tryAll &&
+            SupportedSource.src != EntropySource.none
+        );
+    }
+
+    /++
+        `getEntropyImpl()` implementation helper.
+        To be instantiated and mixed in with platform-specific configuration.
+
+        Params:
+            defaultSource = Default entropy source of the platform
+            SupportedSources = Sequence of `SrcFunPair`
+                               representing the supported sources of the platform
+    +/
+    mixin template entropyImpl(EntropySource defaultSource, SupportedSources...)
+    if (allSatisfy!(isValidSupportedSource, SupportedSources))
+    {
+    private:
+        /// Preconfigured entropy source preset of the platform.
+        enum defaultEntropySource = defaultSource;
+
+        EntropyResult getEntropyImpl(scope void[] buffer) @safe
+        {
+            switch (_entropySource)
+            {
+                static foreach (source; SupportedSources)
+                {
+                    case source.src:
+                        return source.fun(buffer);
+                }
+
+            case EntropySource.tryAll:
+                {
+                    const result = _tryEntropySources(buffer);
+                    result.saveSourceForNextUse();
+                    return result;
+                }
+
+            case EntropySource.none:
+                return getEntropyViaNone(buffer);
+
+            default:
+                return EntropyResult(EntropyStatus.unavailablePlatform, _entropySource);
+            }
+        }
+
+        EntropyResult _tryEntropySources(scope void[] buffer) @safe
+        {
+            EntropyResult result;
+
+            static foreach (source; SupportedSources)
+            {
+                result = source.fun(buffer);
+                if (!result.isUnavailable)
+                    return result;
+            }
+
+            result = EntropyResult(
+                EntropyStatus.unavailable,
+                EntropySource.none,
+            );
+
+            return result;
+        }
+    }
+}
+
+version (Darwin) mixin entropyImpl!(
+    EntropySource.arc4random,
+    SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random),
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (DragonFlyBSD) mixin entropyImpl!(
+    EntropySource.getentropy,
+    SrcFunPair!(EntropySource.getentropy, getEntropyViaGetentropy),
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (FreeBSD) mixin entropyImpl!(
+    EntropySource.getentropy,
+    SrcFunPair!(EntropySource.getentropy, getEntropyViaGetentropy),
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (linux) mixin entropyImpl!(
+    EntropySource.getrandom,
+    SrcFunPair!(EntropySource.getrandom, getEntropyViaGetrandom),
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (NetBSD) mixin entropyImpl!(
+    EntropySource.arc4random,
+    SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random),
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (OpenBSD) mixin entropyImpl!(
+    EntropySource.arc4random,
+    SrcFunPair!(EntropySource.arc4random, getEntropyViaARC4Random),
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (Posix) mixin entropyImpl!(
+    EntropySource.charDevURandom,
+    SrcFunPair!(EntropySource.charDevURandom, getEntropyViaCharDevURandom),
+    SrcFunPair!(EntropySource.charDevRandom, getEntropyViaCharDevRandom),
+);
+else version (Windows) mixin entropyImpl!(
+    EntropySource.bcryptGenRandom,
+    SrcFunPair!(EntropySource.bcryptGenRandom, getEntropyViaBCryptGenRandom),
+);
+else mixin entropyImpl!(
+    EntropySource.none,
+);
+
+private
+{
+    static EntropySource _entropySource = defaultEntropySource;
+
+    void saveSourceForNextUse(const EntropyResult result) @safe
+    {
+        if (!result.isOK)
+            return;
+
+        _entropySource = result.source;
+    }
+}
+
+version (all)
+{
+private:
+
+    EntropyResult getEntropyViaNone(scope void[]) @safe
+    {
+        return EntropyResult(EntropyStatus.unavailable, EntropySource.none);
+    }
+}
+
+version (Posix)
+{
+private:
+
+    EntropyResult getEntropyViaCharDevURandom(scope void[] buffer) @trusted
+    {
+        const status = getEntropyViaCharDev(buffer, "/dev/urandom".ptr);
+        return EntropyResult(status, EntropySource.charDevURandom);
+    }
+
+    EntropyResult getEntropyViaCharDevRandom(scope void[] buffer) @trusted
+    {
+        const status = getEntropyViaCharDev(buffer, "/dev/random".ptr);
+        return EntropyResult(status, EntropySource.charDevRandom);
+    }
+
+    EntropyStatus getEntropyViaCharDev(scope void[] buffer, const(char)* charDevName) @system
+    {
+        import core.stdc.stdio : fclose, fopen, fread;
+
+        auto charDev = fopen(charDevName, "r");
+        if (charDev is null)
+            return EntropyStatus.unavailable;
+
+        scope (exit)
+            fclose(charDev);
+
+        const bytesRead = fread(buffer.ptr, 1, buffer.length, charDev);
+        if (bytesRead != buffer.length)
+            return EntropyStatus.readError;
+
+        return EntropyStatus.ok;
+    }
+}
+
+version (linux)
+{
+private:
+
+    EntropyResult getEntropyViaGetrandom(scope void[] buffer) @trusted
+    {
+        const status = syscallGetrandom(buffer, 0);
+        return EntropyResult(status, EntropySource.getrandom);
+    }
+
+    EntropyStatus syscallGetrandom(scope void[] buffer, uint flags) @system
+    {
+        import core.sys.linux.errno : EINTR, ENOSYS, errno;
+        import core.sys.linux.sys.syscall : SYS_getrandom;
+        import core.sys.linux.unistd : syscall;
+
+        while (buffer.length > 0)
+        {
+            const got = syscall(SYS_getrandom, buffer.ptr, buffer.length, flags);
+
+            if (got == -1)
+            {
+                switch (errno)
+                {
+                case EINTR:
+                    break; // That’s fine.
+                case ENOSYS:
+                    return EntropyStatus.unavailable;
+                default:
+                    return EntropyStatus.readError;
+                }
+            }
+
+            if (got > 0)
+                buffer = buffer[got .. $];
+        }
+
+        return EntropyStatus.ok;
+    }
+}
+
+// BSD
+private
+{
+    version (Darwin)
+        version = SecureARC4Random;
+    version (DragonFlyBSD)
+        version = UseGetentropy;
+    version (FreeBSD)
+        version = UseGetentropy;
+    version (NetBSD)
+        version = SecureARC4Random;
+    version (OpenBSD)
+        version = SecureARC4Random;
+
+    version (SecureARC4Random)
+    {
+        EntropyResult getEntropyViaARC4Random(scope void[] buffer) @trusted
+        {
+            arc4random_buf(buffer.ptr, buffer.length);
+            return EntropyResult(EntropyStatus.ok, EntropySource.arc4random);
+        }
+
+        private extern(C) void arc4random_buf(scope void* buf, size_t nbytes) @system;
+    }
+
+    version (UseGetentropy)
+    {
+        EntropyResult getEntropyViaGetentropy(scope void[] buffer) @trusted
+        {
+            const status = callGetentropy(buffer);
+            return EntropyResult(status, EntropySource.getentropy);
+        }
+
+        private EntropyStatus callGetentropy(scope void[] buffer) @system
+        {
+            /+
+                genentropy(3):
+                The maximum buflen permitted is 256 bytes.
+            +/
+            foreach (chunk; VoidChunks(buffer, 256))
+            {
+                const status = getentropy(buffer.ptr, buffer.length);
+                if (status != 0)
+                    return EntropyStatus.readError;
+            }
+
+            return EntropyStatus.ok;
+        }
+
+        private extern(C) int getentropy(scope void* buf, size_t buflen) @system;
+    }
+}
+
+version (Windows)
+{
+    import core.sys.windows.bcrypt : BCryptGenRandom, BCRYPT_USE_SYSTEM_PREFERRED_RNG;
+    import core.sys.windows.windef : HMODULE, PUCHAR, ULONG;
+    import core.sys.windows.ntdef : NT_SUCCESS;
+
+private:
+
+    EntropyResult getEntropyViaBCryptGenRandom(scope void[] buffer) @trusted
+    {
+        const loaded = loadBcrypt();
+        if (loaded != EntropyStatus.ok)
+            return EntropyResult(loaded, EntropySource.bcryptGenRandom);
+
+        const status = callBcryptGenRandom(buffer);
+        return EntropyResult(status, EntropySource.bcryptGenRandom);
+    }
+
+    EntropyStatus callBcryptGenRandom(scope void[] buffer) @system
+    {
+        foreach (chunk; VoidChunks(buffer, ULONG.max))
+        {
+            assert(chunk.length <= ULONG.max, "Bad chunk length.");
+
+            const gotRandom = ptrBCryptGenRandom(
+                null,
+                cast(PUCHAR) buffer.ptr,
+                cast(ULONG) buffer.length,
+                BCRYPT_USE_SYSTEM_PREFERRED_RNG,
+            );
+
+            if (!NT_SUCCESS(gotRandom))
+                return EntropyStatus.readError;
+        }
+
+        return EntropyStatus.ok;
+    }
+
+    static
+    {
+        HMODULE hBcrypt = null;
+        typeof(BCryptGenRandom)* ptrBCryptGenRandom;
+    }
+
+    EntropyStatus loadBcrypt() @system
+    {
+        import core.sys.windows.winbase : GetProcAddress, LoadLibraryA;
+
+        if (hBcrypt !is null)
+            return EntropyStatus.ok;
+
+        hBcrypt = LoadLibraryA("Bcrypt.dll");
+        if (!hBcrypt)
+            return EntropyStatus.unavailableLibrary;
+
+        ptrBCryptGenRandom = cast(typeof(ptrBCryptGenRandom)) GetProcAddress(hBcrypt, "BCryptGenRandom");
+        if (!ptrBCryptGenRandom)
+            return EntropyStatus.unavailable;
+
+        return EntropyStatus.ok;
+    }
+
+    // Will free `Bcrypt.dll`.
+    void freeBcrypt() @system
+    {
+        import core.sys.windows.winbase : FreeLibrary;
+
+        if (hBcrypt is null)
+            return;
+
+        if (!FreeLibrary(hBcrypt))
+        {
+            return; // Error
+        }
+
+        hBcrypt = null;
+        ptrBCryptGenRandom = null;
+    }
+
+    static ~this() @system
+    {
+        freeBcrypt();
+    }
+}
index 703ecb1e6b35e2416dd746874d9c8a0215753582..d5f5dd2e81427fdb1c6238f7bf4d24bd70d534f2 100644 (file)
@@ -1643,7 +1643,12 @@ real digamma(real x)
     negative = 0;
     nz = 0.0;
 
-    if ( x <= 0.0 )
+    if ( x == 0.0 )
+    {
+        return signbit(x) == 1 ? real.infinity : -real.infinity;
+    }
+
+    if ( x < 0.0 )
     {
         negative = 1;
         q = x;
@@ -1718,6 +1723,10 @@ done:
     assert(digamma(1.0)== -EULERGAMMA);
     assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA) >= real.mant_dig-7);
     assert(feqrel(digamma(1.0L/6), -PI/2 *sqrt(3.0L) - 2* LN2 -1.5*log(3.0L) - EULERGAMMA) >= real.mant_dig-7);
+    assert(digamma(-0.0) == real.infinity);
+    assert(!digamma(nextDown(-0.0)).isNaN());
+    assert(digamma(+0.0) == -real.infinity);
+    assert(!digamma(nextUp(+0.0)).isNaN());
     assert(digamma(-5.0).isNaN());
     assert(feqrel(digamma(2.5), -EULERGAMMA - 2*LN2 + 2.0 + 2.0L/3) >= real.mant_dig-9);
     assert(isIdentical(digamma(NaN(0xABC)), NaN(0xABC)));
index 64ab9bf371449ad0a310f6932756eb109adb7708..10c4a2d5310d083f24f8a1314710c80545a7c3ad 100644 (file)
@@ -29,6 +29,7 @@
  *      NAN = $(RED NAN)
  *      SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
  *      GAMMA = &#915;
+ *      PSI = &Psi;
  *      THETA = &theta;
  *      INTEGRAL = &#8747;
  *      INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
  *      BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
  *      CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
  *      PLUSMN = &plusmn;
+ *      MNPLUS = &mnplus;
  *      INFIN = &infin;
  *      PLUSMNINF = &plusmn;&infin;
+ *      MNPLUSINF = &mnplus;&infin;
  *      PI = &pi;
  *      LT = &lt;
  *      GT = &gt;
@@ -166,19 +169,47 @@ real beta(real x, real y)
     assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
 }
 
-/** Digamma function
+/** Digamma function, $(PSI)(x)
  *
- *  The digamma function is the logarithmic derivative of the gamma function.
+ * $(PSI)(x), is the logarithmic derivative of the gamma function, $(GAMMA)(x).
  *
- *  digamma(x) = d/dx logGamma(x)
+ * $(PSI)(x) = $(SUP d)$(SUB /, dx) ln|$(GAMMA)(x)|  (the derivative of `logGamma(x)`)
  *
- *  See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse).
+ * Params:
+ *   x = the domain value
+ *
+ * Returns:
+ *   It returns $(PSI)(x).
+ *
+ * $(TABLE_SV
+ *   $(SVH x,             digamma(x)   )
+ *   $(SV  integer < 0,   $(NAN)       )
+ *   $(SV  $(PLUSMN)0.0,  $(MNPLUSINF) )
+ *   $(SV  +$(INFIN),     +$(INFIN)    )
+ *   $(SV  -$(INFIN),     $(NAN)       )
+ *   $(SV  $(NAN),        $(NAN)       )
+ * )
+ *
+ * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse).
  */
 real digamma(real x)
 {
     return std.internal.math.gammafunction.digamma(x);
 }
 
+///
+@safe unittest
+{
+    const euler = 0.57721_56649_01532_86060_65121L;
+
+    assert(isClose(digamma(1), -euler));
+    assert(digamma(+0.) == -real.infinity);
+    assert(digamma(-0.) == +real.infinity);
+    assert(digamma(+real.infinity) == +real.infinity);
+    assert(isNaN(digamma(-1)));
+    assert(isNaN(digamma(-real.infinity)));
+}
+
 /** Log Minus Digamma function
  *
  *  logmdigamma(x) = log(x) - digamma(x)
index 918984fd52eef973bc6963bf606cb77014b6d34f..345d3b7a87bd9eb4941b6c5059f982b856b167bc 100644 (file)
@@ -3733,7 +3733,16 @@ public:
         }
         else if (range.length == 1)
         {
-            buf[0] = range[0];
+            static if (isNumeric!(ElementType!R))
+            {
+                buf[0].re = range[0];
+                buf[0].im = 0;
+            }
+            else
+            {
+                buf[0].re = range[0].re;
+                buf[0].im = range[0].im;
+            }
             return;
         }
         else if (range.length == 2)
@@ -3931,6 +3940,26 @@ void inverseFft(Ret, R)(R range, Ret buf)
     assert(isClose(twoInv[1].im, 0, 0.0, 1e-10));
 }
 
+// https://github.com/dlang/phobos/issues/10796
+@system unittest
+{
+    import std.algorithm;
+    import std.range;
+    static struct C { float re, im; } // User-defined complex
+
+    float[8] arr = [1,2,3,4,5,6,7,8];
+    C[8] fft1;
+    fft(arr[], fft1[]);
+    assert(isClose(fft1[].map!"a.re",
+        [36.0, -4, -4, -4, -4, -4, -4, -4], 1e-4));
+    assert(isClose(fft1[].map!"a.im",
+        [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568], 1e-4));
+
+    auto inv = inverseFft(fft1[]);
+    assert(isClose(inv[].map!"a.re", arr[], 1e-6));
+    assert(inv[].map!"a.im".maxElement < 1e-10);
+}
+
 // Swaps the real and imaginary parts of a complex number.  This is useful
 // for inverse FFTs.
 C swapRealImag(C)(C input)
@@ -4112,11 +4141,24 @@ struct Stride(R)
 // using a generic slow DFT.  This seems to be the best base case.  (Size 1
 // can be coded inline as buf[0] = range[0]).
 void slowFourier2(Ret, R)(R range, Ret buf)
+if (isComplexLike!(ElementType!Ret))
+in (range.length == 2)
+in (buf.length == 2)
 {
-    assert(range.length == 2);
-    assert(buf.length == 2);
-    buf[0] = range[0] + range[1];
-    buf[1] = range[0] - range[1];
+    static if (isNumeric!(ElementType!R))
+    {
+        buf[0].re = range[0] + range[1];
+        buf[0].im = 0;
+        buf[1].re = range[0] - range[1];
+        buf[1].im = 0;
+    }
+    else
+    {
+        buf[0].re = range[0].re + range[1].re;
+        buf[0].im = range[0].im + range[1].im;
+        buf[1].re = range[0].re - range[1].re;
+        buf[1].im = range[0].im - range[1].im;
+    }
 }
 
 // Hard-coded base case for FFT of size 4.  Doesn't work as well as the size
index fb4e5469088f3eff13a7eaa125091c6e4c0e7d5b..0176b0ae653282cdff9d89967e5d9757ef4e8035 100644 (file)
@@ -1773,43 +1773,9 @@ else
 }
 
 version (linux)
-{
-    // `getrandom()` was introduced in Linux 3.17.
-
-    // Shim for missing bindings in druntime
-    version (none)
-        import core.sys.linux.sys.random : getrandom;
-    else
-    {
-        import core.sys.posix.sys.types : ssize_t;
-        extern extern(C) ssize_t getrandom(
-            void* buf,
-            size_t buflen,
-            uint flags,
-        ) @system nothrow @nogc;
-    }
-}
-
+    version = SeedUseGetEntropy;
 version (Windows)
-{
-    pragma(lib, "Bcrypt.lib");
-
-    private bool bcryptGenRandom(T)(out T result) @trusted
-    {
-        import core.sys.windows.windef : PUCHAR, ULONG;
-        import core.sys.windows.ntdef : NT_SUCCESS;
-        import core.sys.windows.bcrypt : BCryptGenRandom, BCRYPT_USE_SYSTEM_PREFERRED_RNG;
-
-        const gotRandom = BCryptGenRandom(
-            null,
-            cast(PUCHAR) &result,
-            ULONG(T.sizeof),
-            BCRYPT_USE_SYSTEM_PREFERRED_RNG,
-        );
-
-        return NT_SUCCESS(gotRandom);
-    }
-}
+    version = SeedUseGetEntropy;
 
 /**
 A "good" seed for initializing random number engines. Initializing
@@ -1851,36 +1817,15 @@ how excellent the source of entropy is.
 */
 @property uint unpredictableSeed() @trusted nothrow @nogc
 {
-    version (linux)
+    version (SeedUseGetEntropy)
     {
-        uint buffer;
-
-        /*
-            getrandom(2):
-            If the _urandom_ source has been initialized, reads of up to
-            256 bytes will always return as many bytes as requested and
-            will not be interrupted by signals. No such guarantees apply
-            for larger buffer sizes.
-            */
-        static assert(buffer.sizeof <= 256);
-
-        const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
-        assert(status == buffer.sizeof);
+        import std.internal.entropy : crashOnError, EntropySource, getEntropy;
 
+        uint buffer;
+        const status = (() @trusted => getEntropy(&buffer, buffer.sizeof, EntropySource.tryAll))();
+        crashOnError(status);
         return buffer;
     }
-    else version (Windows)
-    {
-        uint result;
-        if (!bcryptGenRandom!uint(result))
-        {
-            version (none)
-                return fallbackSeed();
-            else
-                assert(false, "BCryptGenRandom() failed.");
-        }
-        return result;
-    }
     else version (AnyARC4Random)
     {
         return arc4random();
@@ -1930,36 +1875,15 @@ if (isUnsigned!UIntType)
         /// ditto
         @property UIntType unpredictableSeed() @nogc nothrow @trusted
         {
-            version (linux)
+            version (SeedUseGetEntropy)
             {
-                UIntType buffer;
-
-                /*
-                    getrandom(2):
-                    If the _urandom_ source has been initialized, reads of up to
-                    256 bytes will always return as many bytes as requested and
-                    will not be interrupted by signals. No such guarantees apply
-                    for larger buffer sizes.
-                 */
-                static assert(buffer.sizeof <= 256);
-
-                const status = (() @trusted => getrandom(&buffer, buffer.sizeof, 0))();
-                assert(status == buffer.sizeof);
+                import std.internal.entropy : crashOnError, EntropySource, getEntropy;
 
+                UIntType buffer;
+                const status = (() @trusted => getEntropy(&buffer, buffer.sizeof, EntropySource.tryAll))();
+                crashOnError(status);
                 return buffer;
             }
-            else version (Windows)
-            {
-                UIntType result;
-                if (!bcryptGenRandom!UIntType(result))
-                {
-                    version (none)
-                        return fallbackSeed();
-                    else
-                        assert(false, "BCryptGenRandom() failed.");
-                }
-                return result;
-            }
             else version (AnyARC4Random)
             {
                 static if (UIntType.sizeof <= uint.sizeof)
index 82580599a7dcd2f7f3967899a3ca15b567434ee5..4620b98071e971f00d73bc3b4415c441558fbe31 100644 (file)
@@ -3881,9 +3881,11 @@ pure @safe nothrow @nogc unittest
 }
 
 /++
-    Convenience function which calls
+    `drop` is a convenience function which calls
     $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`.
-    `drop` makes it easier to pop elements from a range
+    Unlike `popFrontN`, the range argument is passed by copy, not by `ref`.
+
+    `drop` makes it easier to pop elements from a range rvalue
     and then pass it to another function within a single expression,
     whereas `popFrontN` would require multiple statements.
 
@@ -3916,7 +3918,10 @@ if (isInputRange!R)
 {
     import std.algorithm.comparison : equal;
 
-    assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]);
+    auto a = [0, 2, 1, 5, 0, 3];
+    assert(a.drop(3) == [5, 0, 3]);
+    assert(a.length == 6); // original unchanged
+
     assert("hello world".drop(6) == "world");
     assert("hello world".drop(50).empty);
     assert("hello world".take(6).drop(3).equal("lo "));
@@ -3993,8 +3998,8 @@ if (isBidirectionalRange!R)
         `range` with `n` elements dropped
 
     See_Also:
-        $(REF popFrontExcatly, std, range, primitives),
-        $(REF popBackExcatly, std, range, primitives)
+        $(REF popFrontExactly, std, range, primitives),
+        $(REF popBackExactly, std, range, primitives)
 +/
 R dropExactly(R)(R range, size_t n)
 if (isInputRange!R)
@@ -4030,9 +4035,11 @@ if (isBidirectionalRange!R)
 }
 
 /++
-    Convenience function which calls
-    `range.popFront()` and returns `range`. `dropOne`
-    makes it easier to pop an element from a range
+    `dropOne` is a convenience function which calls
+    `range.popFront()` and returns `range`.
+    Unlike `popFront`, the range argument is passed by copy, not by `ref`.
+
+    `dropOne` makes it easier to pop an element from a range rvalue
     and then pass it to another function within a single expression,
     whereas `popFront` would require multiple statements.
 
index bcc9d7c13afa4a85c16dd6d5321ba02442cbf71e..12661e71328ca9a371ec678056ace3f276124c64 100644 (file)
@@ -6864,6 +6864,11 @@ if (isConvertibleToString!Range)
  *  tabsize = column spacing of tabs in firstindent[] and indent[]
  * Returns:
  *  resulting paragraph as an allocated string
+ * Bugs:
+ *  Columns are counted as the number of code points in the string. This may
+ *  not correspond with the actual number of columns displayed if the string
+ *  contains combining characters, modifiers, zero-width characters, or
+ *  double-width characters.
  */
 
 S wrap(S)(S s, in size_t columns = 80, S firstindent = null,
index 9bdd9775f9cb4d231431a3ee00d2492b992af0fe..400df02aaac61d2a9e4cc6ceb876f2a4101a4ea8 100644 (file)
@@ -7611,6 +7611,8 @@ enum bool isSomeFunction(alias T) =
 /**
 Detect whether `T` is a callable object, which can be called with the
 function call operator `$(LPAREN)...$(RPAREN)`.
+
+$(NOTE Implicit Function Template Instantiation is *not* attempted - see below.)
  */
 template isCallable(alias callable)
 {
@@ -7634,7 +7636,7 @@ template isCallable(alias callable)
     }
 }
 
-/// Functions, lambdas, and aggregate types with (static) opCall.
+/// Functions, function pointers, delegates, lambdas.
 @safe unittest
 {
     void f() { }
@@ -7643,6 +7645,17 @@ template isCallable(alias callable)
     static assert( isCallable!f);
     static assert( isCallable!g);
 
+    auto fp = &f;
+    static assert( isCallable!fp);
+    static assert( isCallable!((int x) {}));
+
+    int x;
+    static assert(!isCallable!x);
+}
+
+/// Aggregate types with (static) opCall.
+@safe unittest
+{
     class C { int opCall(int) { return 0; } }
     auto c = new C;
     struct S { static int opCall(int) { return 0; } }
@@ -7657,7 +7670,7 @@ template isCallable(alias callable)
     static assert(!isCallable!I);
 }
 
-/// Templates
+/// Template functions are only detected if they are instantiable with `!()`.
 @safe unittest
 {
     void f()() { }
@@ -7669,9 +7682,11 @@ template isCallable(alias callable)
     static assert( isCallable!g);
     static assert( isCallable!S1);
     static assert( isCallable!S2);
+
+    static assert(!isCallable!((x) {}));
 }
 
-/// Overloaded functions and function templates.
+/// Overloaded functions and function templates instantiable with `!()`.
 @safe unittest
 {
     static struct Wrapper
index d7f86d17aed30a5a45922ebd3e5c1764435ffa38..cd2772109c044b3814fe7cffe28496cbe854566a 100644 (file)
@@ -3836,22 +3836,22 @@ struct Nullable(T)
      * Returns:
      *     A `string` if `writer` and `fmt` are not set; `void` otherwise.
      */
-    string toString()
+    string toString()()
     {
         import std.array : appender;
         auto app = appender!string();
         auto spec = singleSpec("%s");
-        toString(app, spec);
+        this.toString(app, spec);
         return app.data;
     }
 
     /// ditto
-    string toString() const
+    string toString()() const
     {
         import std.array : appender;
         auto app = appender!string();
         auto spec = singleSpec("%s");
-        toString(app, spec);
+        this.toString(app, spec);
         return app.data;
     }
 
@@ -6677,8 +6677,8 @@ private static:
                 if (atts & FA.property) poatts ~= " @property";
                 if (atts & FA.safe    ) poatts ~= " @safe";
                 if (atts & FA.trusted ) poatts ~= " @trusted";
-                if (atts & FA.scope_ )  poatts ~= " scope";
                 if (atts & FA.return_ ) poatts ~= " return";
+                if (atts & FA.scope_ )  poatts ~= " scope";
                 return poatts;
             }
             enum postAtts = make_postAtts();
@@ -10786,18 +10786,18 @@ private template replaceTypeInFunctionTypeUnless(alias pred, From, To, fun)
         {
             if (i)
                 result ~= ", ";
+            if (storageClasses[i] & ParameterStorageClass.return_)
+                result ~= "return ";
             if (storageClasses[i] & ParameterStorageClass.scope_)
                 result ~= "scope ";
-            if (storageClasses[i] & ParameterStorageClass.in_)
-                result ~= "in ";
             if (storageClasses[i] & ParameterStorageClass.out_)
                 result ~= "out ";
             if (storageClasses[i] & ParameterStorageClass.ref_)
                 result ~= "ref ";
+            if (storageClasses[i] & ParameterStorageClass.in_)
+                result ~= "in ";
             if (storageClasses[i] & ParameterStorageClass.lazy_)
                 result ~= "lazy ";
-            if (storageClasses[i] & ParameterStorageClass.return_)
-                result ~= "return ";
 
             result ~= "PX[" ~ i.stringof ~ "]";
         }
index 5c3ba05d83d30b7d91c0259fe39f5df3949f5cbb..4f8cac52d3c122957ac49051c0a53d37e02a2f5f 100644 (file)
@@ -41,6 +41,8 @@ void main()
     testTombstonePurging();
     testClear();
     testTypeInfoCollect();
+    testNew();
+    testAliasThis();
 }
 
 void testKeysValues1()
@@ -947,3 +949,42 @@ void testTypeInfoCollect()
     s = null; // clear any reference to the entry
     GC.collect(); // used to segfault.
 }
+
+void testNew()
+{
+    auto aa = new long[int]; // call _d_newAA
+    assert(aa.length == 0);
+    foreach (i; 0 .. 100)
+        aa[i] = i * 2;
+    assert(aa.length == 100);
+
+    // not supported in CTFE (it doesn't do much anyway):
+    // static auto aa = new long[int];
+}
+
+void testAliasThis()
+{
+    static struct S
+    {
+        __gshared int numCopies;
+
+        ubyte[long] aa;
+        S* next;
+
+        this(this) { numCopies++; }
+
+        alias aa this;
+    }
+    S s;
+    long key = 1;
+    s.aa[1] = 1; // create and insert
+    assert(S.numCopies == 0);
+    if (auto p = 1 in s)
+        *p = 2;
+    if (auto p = key in s)
+        *p = 3;
+    if (auto p = () { return 1; }() in s)
+        *p = 4;
+    s.remove(1);
+    assert(S.numCopies == 0);
+}
index b85aa889da94b9f928956f474701b707f6326229..1aee31c6acbbf4c0086f1cdb104f5e4ffaf6ebb1 100644 (file)
@@ -4,6 +4,8 @@ import core.stdc.stdlib : calloc, malloc, realloc;
 
 static import core.memory;
 
+import core.thread.threadbase : ThreadBase;
+
 extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"];
 
 extern (C) pragma(crt_constructor) void register_mygc()
@@ -209,6 +211,14 @@ nothrow @nogc:
         return false;
     }
 
+    void initThread(ThreadBase thread) nothrow @nogc
+    {
+    }
+
+    void cleanupThread(ThreadBase thread) nothrow @nogc
+    {
+    }
+
 private:
     // doesn't care for alignment
     static void* sentinelAdd(void* p, size_t value)