[committed] d: Merge dmd, druntime 4ccb01fde5, phobos eab6595ad

Message ID 20250111001015.460152-1-ibuclaw@gdcproject.org
State New
Headers
Series [committed] d: Merge dmd, druntime 4ccb01fde5, phobos eab6595ad |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply

Commit Message

Iain Buclaw Jan. 11, 2025, 12:10 a.m. UTC
  Hi,

This patch merges the D front-end and runtime library with upstream dmd
4ccb01fde5, and the standard library with phobos eab6595ad.

D front-end changes:

	- Added pragma for ImportC to allow setting `nothrow', `@nogc'
	  or `pure'.
	- Mixin templates can now use assignment syntax.

D runtime changes:

	- Removed `ThreadBase.criticalRegionLock' from `core.thread'.
	- Added `expect', `[un]likely', `trap' to `core.builtins'.

Phobos changes:

	- Import latest fixes from phobos v2.110.0-beta.1.

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

Regards,
Iain.

---
gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd 4ccb01fde5.
	* Make-lang.in (D_FRONTEND_OBJS): Rename d/foreachvar.o to
	d/visitor-foreachvar.o, d/visitor.o to d/visitor-package.o, and
	d/statement_rewrite_walker.o to d/visitor-statement_rewrite_walker.o.
	(D_FRONTEND_OBJS): Rename
	d/{parsetime,permissive,postorder,transitive}visitor.o to
	d/visitor-{parsetime,permissive,postorder,transitive}.o.
	(D_FRONTEND_OBJS): Remove d/sapply.o.
	(d.tags): Add dmd/common/*.h.
	(d/visitor-%.o:): New rule.
	* d-codegen.cc (get_frameinfo): Update for new front-end interface.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime 4ccb01fde5.
	* src/MERGE: Merge upstream phobos eab6595ad.
---
 gcc/d/Make-lang.in                            |   21 +-
 gcc/d/d-codegen.cc                            |    2 +-
 gcc/d/dmd/MERGE                               |    2 +-
 gcc/d/dmd/README.md                           |   17 +-
 gcc/d/dmd/canthrow.d                          |    2 +-
 gcc/d/dmd/clone.d                             |  128 +-
 gcc/d/dmd/cparse.d                            |  125 ++
 gcc/d/dmd/cxxfrontend.d                       |    5 +
 gcc/d/dmd/declaration.h                       |    3 +-
 gcc/d/dmd/delegatize.d                        |    2 +-
 gcc/d/dmd/dinterpret.d                        |   12 +-
 gcc/d/dmd/dmodule.d                           |   20 +-
 gcc/d/dmd/dstruct.d                           |   18 +-
 gcc/d/dmd/dsymbolsem.d                        |  379 ++--
 gcc/d/dmd/dtemplate.d                         |  407 +++--
 gcc/d/dmd/escape.d                            |    5 +-
 gcc/d/dmd/expression.d                        |   75 +-
 gcc/d/dmd/expressionsem.d                     |  974 +++++-----
 gcc/d/dmd/func.d                              |  192 +-
 gcc/d/dmd/funcsem.d                           |   35 +-
 gcc/d/dmd/globals.d                           |    2 +
 gcc/d/dmd/globals.h                           |    1 +
 gcc/d/dmd/hdrgen.d                            |    4 +-
 gcc/d/dmd/imphint.d                           |    2 +
 gcc/d/dmd/init.d                              |    3 +-
 gcc/d/dmd/init.h                              |    3 +-
 gcc/d/dmd/initsem.d                           |  331 ++--
 gcc/d/dmd/lambdacomp.d                        |   91 +-
 gcc/d/dmd/nogc.d                              |   86 +-
 gcc/d/dmd/ob.d                                |    3 +-
 gcc/d/dmd/opover.d                            | 1608 ++++++++---------
 gcc/d/dmd/optimize.d                          |    4 +-
 gcc/d/dmd/parse.d                             |   55 +-
 gcc/d/dmd/postordervisitor.d                  |  153 --
 gcc/d/dmd/safe.d                              |  151 +-
 gcc/d/dmd/semantic3.d                         |   69 +
 gcc/d/dmd/sideeffect.d                        |    2 +-
 gcc/d/dmd/statement.d                         |    2 +-
 gcc/d/dmd/target.d                            |    1 +
 gcc/d/dmd/target.h                            |    1 +
 gcc/d/dmd/traits.d                            |    4 +-
 gcc/d/dmd/typesem.d                           |  519 +++---
 gcc/d/dmd/{ => visitor}/foreachvar.d          |    4 +-
 gcc/d/dmd/{visitor.d => visitor/package.d}    |   13 +-
 .../parsetime.d}                              |    2 +-
 .../permissive.d}                             |    4 +-
 gcc/d/dmd/{sapply.d => visitor/postorder.d}   |  151 +-
 .../{ => visitor}/statement_rewrite_walker.d  |    2 +-
 .../transitive.d}                             |    8 +-
 .../gdc.test/compilable/imports/imp23812.c    |   35 +
 gcc/testsuite/gdc.test/compilable/test23812.d |   75 +
 gcc/testsuite/gdc.test/compilable/test24762.d |   11 +
 .../gdc.test/fail_compilation/fail18243.d     |    6 +-
 .../gdc.test/fail_compilation/fail_arrayexp.d |    1 +
 .../gdc.test/fail_compilation/fail_opover.d   |   47 +-
 .../gdc.test/fail_compilation/ice15788.d      |    4 +-
 .../gdc.test/fail_compilation/imphint.d       |   98 +-
 .../fail_compilation/imports/a18243.d         |    4 +-
 .../fail_compilation/imports/b18243.d         |    3 +
 .../nested_template_constraint.d              |   18 +
 .../gdc.test/fail_compilation/test16443.d     |   12 +
 libphobos/libdruntime/MERGE                   |    2 +-
 libphobos/libdruntime/core/builtins.d         |   43 +
 libphobos/libdruntime/core/stdc/fenv.d        |    8 +
 libphobos/libdruntime/core/sys/posix/signal.d |    5 +
 .../libdruntime/core/sys/windows/winsock2.d   |   64 +-
 libphobos/libdruntime/core/thread/osthread.d  |  158 +-
 .../libdruntime/core/thread/threadbase.d      |  103 +-
 libphobos/src/MERGE                           |    2 +-
 libphobos/src/std/algorithm/sorting.d         |   20 +-
 libphobos/src/std/format/package.d            |   12 +-
 libphobos/src/std/format/write.d              |    2 +-
 libphobos/src/std/uni/package.d               |    6 +-
 73 files changed, 3338 insertions(+), 3104 deletions(-)
 delete mode 100644 gcc/d/dmd/postordervisitor.d
 rename gcc/d/dmd/{ => visitor}/foreachvar.d (99%)
 rename gcc/d/dmd/{visitor.d => visitor/package.d} (97%)
 rename gcc/d/dmd/{parsetimevisitor.d => visitor/parsetime.d} (99%)
 rename gcc/d/dmd/{permissivevisitor.d => visitor/permissive.d} (93%)
 rename gcc/d/dmd/{sapply.d => visitor/postorder.d} (50%)
 rename gcc/d/dmd/{ => visitor}/statement_rewrite_walker.d (98%)
 rename gcc/d/dmd/{transitivevisitor.d => visitor/transitive.d} (99%)
 create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp23812.c
 create mode 100644 gcc/testsuite/gdc.test/compilable/test23812.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/test24762.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test16443.d
  

Patch

diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index 679ef579a11..ae575cf465e 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -135,7 +135,6 @@  D_FRONTEND_OBJS = \
 	d/expression.o \
 	d/expressionsem.o \
 	d/file_manager.o \
-	d/foreachvar.o \
 	d/func.o \
 	d/funcsem.o \
 	d/globals.o \
@@ -165,9 +164,6 @@  D_FRONTEND_OBJS = \
 	d/opover.o \
 	d/optimize.o \
 	d/parse.o \
-	d/parsetimevisitor.o \
-	d/permissivevisitor.o \
-	d/postordervisitor.o \
 	d/pragmasem.o \
 	d/printast.o \
 	d/root-aav.o \
@@ -189,12 +185,10 @@  D_FRONTEND_OBJS = \
 	d/root-utf.o \
 	d/rootobject.o \
 	d/safe.o \
-	d/sapply.o \
 	d/semantic2.o \
 	d/semantic3.o \
 	d/sideeffect.o \
 	d/statement.o \
-	d/statement_rewrite_walker.o \
 	d/statementsem.o \
 	d/staticassert.o \
 	d/staticcond.o \
@@ -205,11 +199,16 @@  D_FRONTEND_OBJS = \
 	d/timetrace.o \
 	d/tokens.o \
 	d/traits.o \
-	d/transitivevisitor.o \
 	d/typesem.o \
 	d/typinf.o \
 	d/utils.o \
-	d/visitor.o
+	d/visitor-foreachvar.o \
+	d/visitor-package.o \
+	d/visitor-parsetime.o \
+	d/visitor-permissive.o \
+	d/visitor-postorder.o \
+	d/visitor-statement_rewrite_walker.o \
+	d/visitor-transitive.o
 
 # Language-specific object files for D.
 D_OBJS = \
@@ -296,7 +295,7 @@  d.srcextra:
 
 d.tags: force
 	cd $(srcdir)/d; \
-	$(ETAGS) -o TAGS.sub *.cc *.h dmd/*.h dmd/root/*.h; \
+        $(ETAGS) -o TAGS.sub *.cc *.h dmd/*.h dmd/common/*.h dmd/root/*.h; \
 	$(ETAGS) --include TAGS.sub --include ../TAGS.sub
 
 d.man: doc/gdc.1
@@ -424,3 +423,7 @@  d/common-%.o: d/dmd/common/%.d
 d/root-%.o: d/dmd/root/%.d
 	$(DCOMPILE) $(D_INCLUDES) $<
 	$(DPOSTCOMPILE)
+
+d/visitor-%.o: d/dmd/visitor/%.d
+	$(DCOMPILE) $(D_INCLUDES) $<
+	$(DPOSTCOMPILE)
diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index 8ecd81302da..e9962e6d56b 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -2915,7 +2915,7 @@  get_frameinfo (FuncDeclaration *fd)
          symbols, give it a decent error for now.  */
       if (requiresClosure != fd->requiresClosure
 	  && (fd->nrvo_var || !global.params.useGC))
-	fd->checkClosure ();
+	dmd::checkClosure (fd);
 
       /* Set-up a closure frame, this will be allocated on the heap.  */
       FRAMEINFO_CREATES_FRAME (ffi) = 1;
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 654fbed86d8..acb7d98123f 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-6884b433d21d9b6356e5c83ffc6eb06a62a5cad1
+4ccb01fde535c7ad6ad4bdae2516c99420751814
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md
index 00bbf84b6a1..a2c940fce01 100644
--- a/gcc/d/dmd/README.md
+++ b/gcc/d/dmd/README.md
@@ -98,14 +98,14 @@ 
 
 | File                                                                                                      | Purpose                                                                          |
 |-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
-| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parsetimevisitor.d)                 | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes   |
-| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/permissivevisitor.d)               | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes    |
-| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/strictvisitor.d)                       | Visitor that forces derived classes to implement `visit` for every possible node |
-| [visitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor.d)                                   | A visitor implementing `visit` for all nodes present in the compiler             |
-| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/transitivevisitor.d)               | Provide a mixin template with visit methods for the parse time AST               |
-| [postordervisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/postordervisitor.d)                                       | Depth-first expression visitor                                                   |
-| [sapply.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sapply.d)                                     | Depth-first statement visitor                                                    |
-| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node               |
+| [visitor/parsetime.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/parsetime.d)                 | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes   |
+| [visitor/permissive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/permissive.d)               | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes    |
+| [visitor/strict.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/strict.d)                       | Visitor that forces derived classes to implement `visit` for every possible node |
+| [visitor/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/package.d)                                   | A visitor implementing `visit` for all nodes present in the compiler             |
+| [visitor/transitive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/transitive.d)               | Provide a mixin template with visit methods for the parse time AST               |
+| [visitor/postorder.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/postorder.d)                 | Depth-first expression & statement visitor                                                   |
+| [visitor/statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node               |
+| [visitor/foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/foreachvar.d)   | Used in `ob.d` to iterate over all variables in an expression |
 
 **Semantic passes**
 
@@ -270,4 +270,3 @@ 
 |---------------------------------------------------------------------------------|---------------------------------------------------------------|
 | [asttypename.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only)   |
 | [printast.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d)       | Print the AST data structure                                  |
-| [foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/foreachvar.d)   | Used in `ob.d` to iterate over all variables in an expression |
diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
index e59755058a7..dfc91e1c852 100644
--- a/gcc/d/dmd/canthrow.d
+++ b/gcc/d/dmd/canthrow.d
@@ -26,9 +26,9 @@  import dmd.func;
 import dmd.globals;
 import dmd.init;
 import dmd.mtype;
-import dmd.postordervisitor;
 import dmd.tokens;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 /**
  * Status indicating what kind of throwable might be caused by an expression.
diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d
index 4989ab47731..8746baacd0e 100644
--- a/gcc/d/dmd/clone.d
+++ b/gcc/d/dmd/clone.d
@@ -57,7 +57,7 @@  StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
         return s1;
     StorageClass s2 = (f.storage_class & STC.disable);
 
-    auto tf = cast(TypeFunction)f.type;
+    auto tf = f.type.isTypeFunction();
     if (tf.trust == TRUST.safe)
         s2 |= STC.safe;
     else if (tf.trust == TRUST.system)
@@ -96,6 +96,8 @@  StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure
  *      sc = current scope
  * Returns:
  *      if found, returns FuncDeclaration of opAssign, otherwise null
+ * References:
+ *      https://dlang.org/spec/operatoroverloading.html#assignment
  */
 FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
 {
@@ -106,12 +108,12 @@  FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
         scope er = new NullExp(ad.loc, ad.type);    // dummy rvalue
         scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue
         el.type = ad.type;
-        auto a = new Expressions(1);
         const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it.
         sc = sc.push();
         sc.tinst = null;
         sc.minst = null;
 
+        auto a = new Expressions(1);
         (*a)[0] = er;
         auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet);
         if (!f)
@@ -144,7 +146,11 @@  FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc)
 /*******************************************
  * We need an opAssign for the struct if
  * it has a destructor or a postblit.
- * We need to generate one if a user-specified one does not exist.
+ * (We will later generate one if a user-specified one does not exist)
+ * Params:
+ *      sd = struct to check
+ * Returns:
+ *      true if an opAssign is needed
  */
 private bool needOpAssign(StructDeclaration sd)
 {
@@ -174,9 +180,8 @@  private bool needOpAssign(StructDeclaration sd)
         if (v.overlapped)               // if field of a union
             continue;                   // user must handle it themselves
         Type tv = v.type.baseElemOf();
-        if (tv.ty == Tstruct)
+        if (auto ts = tv.isTypeStruct())
         {
-            auto ts = cast(TypeStruct)tv;
             if (ts.sym.isUnionDeclaration())
                 continue;
             if (needOpAssign(ts.sym))
@@ -250,7 +255,7 @@  private bool needOpAssign(StructDeclaration sd)
  *      sd = struct to generate opAssign for
  *      sc = context
  * Returns:
- *      generated `opAssign` function
+ *      generated `opAssign` function, or null if it is not needed
  */
 FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
 {
@@ -280,10 +285,10 @@  FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
         if (v.overlapped)
             continue;
         Type tv = v.type.baseElemOf();
-        if (tv.ty != Tstruct)
-            continue;
-        StructDeclaration sdv = (cast(TypeStruct)tv).sym;
-        stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc));
+        if (auto tvs = tv.isTypeStruct())
+        {
+            stc = mergeFuncAttrs(stc, hasIdentityOpAssign(tvs.sym, sc));
+        }
     }
 
     if (sd.dtor || sd.postblit)
@@ -313,8 +318,7 @@  FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
     else if (sd.dtor)
     {
         //printf("\tswap copy\n");
-        auto tdtor = cast(TypeFunction)sd.dtor.type;
-        assert(tdtor.ty == Tfunction);
+        auto tdtor = sd.dtor.type.isTypeFunction();
 
         auto idswap = Identifier.generateId("__swap");
         auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc));
@@ -406,8 +410,11 @@  FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc)
 
 /*******************************************
  * We need an opEquals for the struct if
- * any fields has an opEquals.
- * Generate one if a user-specified one does not exist.
+ * any field has an opEquals and a user-specified one does not exist.
+ * Params:
+ *      sd = struct to check
+ * Returns:
+ *      true if need to generate one
  */
 bool needOpEquals(StructDeclaration sd)
 {
@@ -432,9 +439,8 @@  bool needOpEquals(StructDeclaration sd)
             continue;
         Type tv = v.type.toBasetype();
         auto tvbase = tv.baseElemOf();
-        if (tvbase.ty == Tstruct)
+        if (auto ts = tvbase.isTypeStruct())
         {
-            auto ts = cast(TypeStruct)tvbase;
             if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1)
                 continue;
             if (needOpEquals(ts.sym))
@@ -464,6 +470,10 @@  Lneed:
 
 /*******************************************
  * Check given aggregate actually has an identity opEquals or not.
+ *      ad = aggregate to check
+ *      sc = context
+ * Returns:
+ *      identity opEquals if it is there, null if not
  */
 private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc)
 {
@@ -562,7 +572,7 @@  FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
                 parameters.push(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 = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx);
+                tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction();
             }
             fd = fd.overloadExactMatch(tfeqptr);
             if (fd)
@@ -598,7 +608,7 @@  FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc)
     Expression e2 = new IdentifierExp(loc, Id.p);
     Expression e = new EqualExp(EXP.equal, loc, e1, e2);
     fop.fbody = new ReturnStatement(loc, e);
-    uint errors = global.startGagging(); // Do not report errors
+    const errors = global.startGagging(); // Do not report errors
     Scope* sc2 = sc.push();
     sc2.stc = 0;
     sc2.linkage = LINK.d;
@@ -637,11 +647,10 @@  FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
                 parameters.push(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 = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx);
+                tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction();
             }
-            fd = fd.overloadExactMatch(tfcmpptr);
-            if (fd)
-                return fd;
+            if (auto fdo = fd.overloadExactMatch(tfcmpptr))
+                return fdo;
         }
     }
     else
@@ -738,7 +747,13 @@  FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc)
 /*******************************************
  * We need a toHash for the struct if
  * any fields has a toHash.
- * Generate one if a user-specified one does not exist.
+ * (will generate one if a user-specified one does not exist)
+ * Params:
+ *      sd = struct to check
+ * Returns:
+ *      need to generate toHash()
+ * References:
+ *   https://dlang.org/spec/hash-map.html#using_struct_as_key
  */
 private bool needToHash(StructDeclaration sd)
 {
@@ -759,9 +774,8 @@  private bool needToHash(StructDeclaration sd)
             continue;
         Type tv = v.type.toBasetype();
         auto tvbase = tv.baseElemOf();
-        if (tvbase.ty == Tstruct)
+        if (auto ts = tvbase.isTypeStruct())
         {
-            auto ts = cast(TypeStruct)tvbase;
             if (ts.sym.isUnionDeclaration())
                 continue;
             if (needToHash(ts.sym))
@@ -802,13 +816,12 @@  FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
         {
             tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d);
             tftohash.mod = MODFlags.const_;
-            tftohash = cast(TypeFunction)tftohash.merge();
+            tftohash = tftohash.merge().isTypeFunction();
         }
         if (FuncDeclaration fd = s.isFuncDeclaration())
         {
-            fd = fd.overloadExactMatch(tftohash);
-            if (fd)
-                return fd;
+            if (auto fdo = fd.overloadExactMatch(tftohash))
+                return fdo;
         }
     }
     if (!needToHash(sd))
@@ -899,10 +912,10 @@  void buildDtors(AggregateDeclaration ad, Scope* sc)
                 continue;
             if (v.overlapped)
                 continue;
-            auto tv = v.type.baseElemOf();
-            if (tv.ty != Tstruct)
+            auto tvs = v.type.baseElemOf().isTypeStruct();
+            if (!tvs)
                 continue;
-            auto sdv = (cast(TypeStruct)tv).sym;
+            auto sdv = tvs.sym;
             if (!sdv.dtor)
                 continue;
 
@@ -924,8 +937,8 @@  void buildDtors(AggregateDeclaration ad, Scope* sc)
             }
 
             Expression ex;
-            tv = v.type.toBasetype();
-            if (tv.ty == Tstruct)
+            Type tv = v.type.toBasetype();
+            if (tv.isTypeStruct())
             {
                 // this.v.__xdtor()
 
@@ -1178,6 +1191,14 @@  private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
  *     invs[0](), invs[1](), ...;
  * }
  * ---
+ * Params:
+ *      ad = aggregate for creating invariant
+ *      sc = context
+ * Returns:
+ *      generated invariant, null if not needed
+ * References:
+ * https://dlang.org/spec/class.html#invariants
+ * https://dlang.org/spec/struct.html#Invariant
  */
 FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc)
 {
@@ -1231,6 +1252,11 @@  FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc)
  * all the members.
  * Note the close similarity with AggregateDeclaration::buildDtor(),
  * and the ordering changes (runs forward instead of backwards).
+ * Params:
+ *      sd = struct to create postblit for
+ *      sc = context
+ * Returns:
+ *      generated postblit, or null if not
  */
 FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
 {
@@ -1261,10 +1287,10 @@  FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
         if (structField.overlapped)
             continue;
         // if it's a struct declaration or an array of structs
-        Type tv = structField.type.baseElemOf();
-        if (tv.ty != Tstruct)
+        TypeStruct tvs = structField.type.baseElemOf().isTypeStruct();
+        if (!tvs)
             continue;
-        auto sdv = (cast(TypeStruct)tv).sym;
+        auto sdv = tvs.sym;
         // which has a postblit declaration
         if (!sdv.postblit)
             continue;
@@ -1274,15 +1300,15 @@  FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
         // block to destroy any prior successfully postblitted fields should
         // this field's postblit fail.
         // Don't generate it for betterC code since it cannot throw exceptions.
-        if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isNothrow && global.params.useExceptions)
+        if (fieldsToDestroy.length > 0 && !sdv.postblit.type.isTypeFunction().isNothrow && global.params.useExceptions)
         {
              // create a list of destructors that need to be called
             Expression[] dtorCalls;
             foreach(sf; fieldsToDestroy)
             {
                 Expression ex;
-                tv = sf.type.toBasetype();
-                if (tv.ty == Tstruct)
+                Type tv = sf.type.toBasetype();
+                if (auto tvs2 = tv.isTypeStruct())
                 {
                     // this.v.__xdtor()
 
@@ -1296,9 +1322,7 @@  FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
                     if (stc & STC.safe)
                         stc = (stc & ~STC.safe) | STC.trusted;
 
-                    auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym;
-
-                    ex = new DotVarExp(loc, ex, sfv.dtor, false);
+                    ex = new DotVarExp(loc, ex, tvs2.sym.dtor, false);
                     ex = new CallExp(loc, ex);
 
                     dtorCalls ~= ex;
@@ -1360,8 +1384,8 @@  FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
         }
 
         Expression ex;
-        tv = structField.type.toBasetype();
-        if (tv.ty == Tstruct)
+        Type tv = structField.type.toBasetype();
+        if (tv.isTypeStruct())
         {
             // this.v.__xpostblit()
 
@@ -1613,16 +1637,8 @@  bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
             return 0;
         }
 
-        auto tf = ctorDecl.type.toTypeFunction();
-        const dim = tf.parameterList.length;
-        if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
-        {
-            auto param = tf.parameterList[0];
-            if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
-            {
-                rvalueCtor = ctorDecl;
-            }
-        }
+        if (isRvalueConstructor(sd, ctorDecl))
+            rvalueCtor = ctorDecl;
         return 0;
     });
 
@@ -1687,6 +1703,8 @@  LcheckFields:
  * Returns:
  *  `true` if `struct` sd defines a copy constructor (explicitly or generated),
  *  `false` otherwise.
+ * References:
+ *   https://dlang.org/spec/struct.html#struct-copy-constructor
  */
 bool buildCopyCtor(StructDeclaration sd, Scope* sc)
 {
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index e02ff43097b..354d83bd4ba 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -45,6 +45,9 @@  final class CParser(AST) : Parser!AST
         // #pragma pack stack
         Array!Identifier* records;      // identifers (or null)
         Array!structalign_t* packs;     // parallel alignment values
+
+        STC defaultStorageClasses;
+        Array!STC* defaultStorageClassesStack;
     }
 
     /* C cannot be parsed without determining if an identifier is a type or a variable.
@@ -3019,6 +3022,7 @@  final class CParser(AST) : Parser!AST
                         StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
                         if (specifier._pure)
                             stc |= STC.pure_;
+                        stc |= defaultStorageClasses;
                         AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
                         //tf = tf.addSTC(storageClass);  // TODO
                         insertTx(ts, tf, t);  // ts -> ... -> tf -> t
@@ -5670,6 +5674,8 @@  final class CParser(AST) : Parser!AST
         scan(&n);
         if (n.value == TOK.identifier && n.ident == Id.pack)
             return pragmaPack(loc, true);
+        if (n.value == TOK.identifier && n.ident == Id.attribute)
+            return pragmaAttribute(loc);
         if (n.value != TOK.endOfLine)
             skipToNextLine();
     }
@@ -5877,6 +5883,125 @@  final class CParser(AST) : Parser!AST
             skipToNextLine();
     }
 
+    /*********
+     * # pragma attribute(...)
+     * Sets default storage classes
+     * Params:
+     *  startloc = location to use for error messages
+     */
+    private void pragmaAttribute(const ref Loc startloc)
+    {
+        const loc = startloc;
+
+        if (!defaultStorageClassesStack)
+        {
+            defaultStorageClassesStack = new Array!STC;
+        }
+
+        Token n;
+        Lexer.scan(&n);
+        if (n.value != TOK.leftParenthesis)
+        {
+            error(loc, "left parenthesis expected to follow `#pragma attribute`");
+            if (n.value != TOK.endOfLine)
+                skipToNextLine();
+            return;
+        }
+
+        void closingParen()
+        {
+            if (n.value != TOK.rightParenthesis)
+            {
+                error(loc, "right parenthesis expected to close `#pragma attribute(`");
+            }
+            if (n.value != TOK.endOfLine)
+                skipToNextLine();
+        }
+
+        Lexer.scan(&n);
+
+        /* # pragma attribute (push, ...)
+         */
+        if (n.value == TOK.identifier && n.ident == Id.push)
+        {
+            Lexer.scan(&n);
+            if (n.value != TOK.comma)
+            {
+                error(loc, "comma expected to follow `#pragma attribute(push`");
+                if (n.value != TOK.endOfLine)
+                    skipToNextLine();
+                return;
+            }
+
+            while (1)
+            {
+                Lexer.scan(&n);
+                if (n.value == TOK.endOfLine)
+                {
+                    error(loc, "right parenthesis expected to close `#pragma attribute(push, `");
+                    break;
+                }
+
+                if (n.value == TOK.rightParenthesis)
+                    break;
+
+                if (n.value == TOK.identifier)
+                {
+                    if (n.ident == Id._nothrow)
+                        defaultStorageClasses |= STC.nothrow_;
+                    else if (n.ident == Id.nogc)
+                        defaultStorageClasses |= STC.nogc;
+                    else if (n.ident == Id._pure)
+                        defaultStorageClasses |= STC.pure_;
+                    // Ignore unknown identifiers
+                }
+                else
+                {
+                    error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars());
+                    break;
+                }
+
+                Lexer.scan(&n);
+
+                if (n.value == TOK.rightParenthesis)
+                    break;
+
+                if (n.value != TOK.comma)
+                {
+                    error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars());
+                    break;
+                }
+            }
+
+            this.defaultStorageClassesStack.push(defaultStorageClasses);
+
+            return closingParen();
+        }
+
+        /* # pragma attribute(pop)
+         */
+        if (n.value == TOK.identifier && n.ident == Id.pop)
+        {
+            scan(&n);
+            size_t len = this.defaultStorageClassesStack.length;
+
+            if (len)
+            {
+                this.defaultStorageClassesStack.setDim(len - 1);
+                if (len == 1)   // stack is now empty
+                    defaultStorageClasses = STC.init;
+                else
+                    defaultStorageClasses = (*this.defaultStorageClassesStack)[len - 2];
+            }
+
+            return closingParen();
+        }
+
+        error(loc, "unrecognized `#pragma attribute(%s)`", n.toChars());
+        if (n.value != TOK.endOfLine)
+            skipToNextLine();
+    }
+
     //}
 
     /******************************************************************************/
diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d
index 09e364d7cbf..7cb4011e920 100644
--- a/gcc/d/dmd/cxxfrontend.d
+++ b/gcc/d/dmd/cxxfrontend.d
@@ -425,6 +425,11 @@  void semanticTypeInfoMembers(StructDeclaration sd)
     return dmd.semantic3.semanticTypeInfoMembers(sd);
 }
 
+bool checkClosure(FuncDeclaration fd)
+{
+    import dmd.semantic3;
+    return dmd.semantic3.checkClosure(fd);
+}
 /***********************************************************
  * statementsem.d
  */
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index 4ac22b93588..c19aa0692fa 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -34,6 +34,7 @@  namespace dmd
 {
     bool functionSemantic(FuncDeclaration* fd);
     bool functionSemantic3(FuncDeclaration* fd);
+    bool checkClosure(FuncDeclaration* fd);
     MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names);
     PURE isPure(FuncDeclaration *f);
 }
@@ -719,7 +720,6 @@  public:
     bool isAbstract() override final;
     bool isSafe();
     bool isTrusted();
-    bool isNogc();
 
     virtual bool isNested() const;
     AggregateDeclaration *isThis() override;
@@ -732,7 +732,6 @@  public:
     const char *kind() const override;
     bool isUnique();
     bool needsClosure();
-    bool checkClosure();
     bool hasNestedFrameRefs();
     ParameterList getParameterList();
 
diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d
index 95064b0d16d..f302d557216 100644
--- a/gcc/d/dmd/delegatize.d
+++ b/gcc/d/dmd/delegatize.d
@@ -25,10 +25,10 @@  import dmd.init;
 import dmd.initsem;
 import dmd.location;
 import dmd.mtype;
-import dmd.postordervisitor;
 import dmd.statement;
 import dmd.tokens;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 
 /*********************************
diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d
index 7a715a3943d..4e04a272141 100644
--- a/gcc/d/dmd/dinterpret.d
+++ b/gcc/d/dmd/dinterpret.d
@@ -6880,10 +6880,10 @@  private Expression scrubReturnValue(const ref Loc loc, Expression e)
     Expression scrubSE(StructLiteralExp sle)
     {
         sle.ownedByCtfe = OwnedBy.code;
-        if (!(sle.stageflags & stageScrub))
+        if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub))
         {
             const old = sle.stageflags;
-            sle.stageflags |= stageScrub;       // prevent infinite recursion
+            sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion
             if (auto ex = scrubArray(sle.elements, true))
                 return ex;
             sle.stageflags = old;
@@ -6960,10 +6960,10 @@  private Expression scrubCacheValue(Expression e)
     Expression scrubSE(StructLiteralExp sle)
     {
         sle.ownedByCtfe = OwnedBy.cache;
-        if (!(sle.stageflags & stageScrub))
+        if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub))
         {
             const old = sle.stageflags;
-            sle.stageflags |= stageScrub;       // prevent infinite recursion
+            sle.stageflags |= StructLiteralExp.StageFlags.scrub;  // prevent infinite recursion
             if (auto ex = scrubArrayCache(sle.elements))
                 return ex;
             sle.stageflags = old;
@@ -7037,10 +7037,10 @@  private Expression copyRegionExp(Expression e)
 
     static void copySE(StructLiteralExp sle)
     {
-        if (1 || !(sle.stageflags & stageScrub))
+        if (1 || !(sle.stageflags & StructLiteralExp.StageFlags.scrub))
         {
             const old = sle.stageflags;
-            sle.stageflags |= stageScrub;       // prevent infinite recursion
+            sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion
             copyArray(sle.elements);
             sle.stageflags = old;
         }
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index 583b4679f0b..eda20a2141a 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -349,8 +349,8 @@  extern (C++) final class Module : Package
     const(char)[] arg;           // original argument name
     ModuleDeclaration* md;      // if !=null, the contents of the ModuleDeclaration declaration
     const FileName srcfile;     // input source file
-    const FileName objfile;     // output .obj file
-    const FileName hdrfile;     // 'header' file
+    FileName objfile;           // output .obj file
+    FileName hdrfile;           // 'header' file
     FileName docfile;           // output documentation file
     const(ubyte)[] src;         /// Raw content of the file
     uint errors;                // if any errors in file
@@ -579,6 +579,22 @@  extern (C++) final class Module : Package
                 argdoc = arg;
             else
                 argdoc = FileName.name(arg);
+
+            if (global.params.fullyQualifiedObjectFiles)
+            {
+                const fqn = md ? md.toString() : toString();
+                argdoc = FileName.replaceName(argdoc, fqn);
+
+                // add ext, otherwise forceExt will make nested.module into nested.<ext>
+                const bufferLength = argdoc.length + 1 + ext.length + /* null terminator */ 1;
+                char[] s = new char[bufferLength];
+                s[0 .. argdoc.length] = argdoc[];
+                s[argdoc.length] = '.';
+                s[$-1-ext.length .. $-1] = ext[];
+                s[$-1] = 0;
+                argdoc = s;
+            }
+
             // If argdoc doesn't have an absolute path, make it relative to dir
             if (!FileName.absolute(argdoc))
             {
diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d
index 64f19d932eb..8b4ac7d8846 100644
--- a/gcc/d/dmd/dstruct.d
+++ b/gcc/d/dmd/dstruct.d
@@ -431,13 +431,15 @@  extern (C++) class StructDeclaration : AggregateDeclaration
         if (ispod != ThreeState.none)
             return (ispod == ThreeState.yes);
 
-        ispod = ThreeState.yes;
-
         import dmd.clone;
+
         bool hasCpCtorLocal;
         needCopyCtor(this, hasCpCtorLocal);
 
-        if (enclosing || search(this, loc, Id.postblit) || search(this, loc, Id.dtor) || hasCpCtorLocal)
+        if (enclosing                      || // is nested
+            search(this, loc, Id.postblit) || // has postblit
+            search(this, loc, Id.dtor)     || // has destructor
+            hasCpCtorLocal)                   // has copy constructor
         {
             ispod = ThreeState.no;
             return false;
@@ -453,12 +455,9 @@  extern (C++) class StructDeclaration : AggregateDeclaration
                 return false;
             }
 
-            Type tv = v.type.baseElemOf();
-            if (tv.ty == Tstruct)
+            if (auto ts = v.type.baseElemOf().isTypeStruct())
             {
-                auto ts = cast(TypeStruct)tv;
-                StructDeclaration sd = ts.sym;
-                if (!sd.isPOD())
+                if (!ts.sym.isPOD())
                 {
                     ispod = ThreeState.no;
                     return false;
@@ -466,7 +465,8 @@  extern (C++) class StructDeclaration : AggregateDeclaration
             }
         }
 
-        return (ispod == ThreeState.yes);
+        ispod = ThreeState.yes;
+        return true;
     }
 
     /***************************************
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index e0eebb4c6cd..2958d955f4f 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -242,38 +242,53 @@  package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
 }
 
 /*
-Tests whether the `ctor` that is part of `ti` is an rvalue constructor
-(i.e. a constructor that receives a single parameter of the same type as
-`Unqual!typeof(this)`). If that is the case and `sd` contains a copy
-constructor, than an error is issued.
+If sd has a copy constructor and ctor is an rvalue constructor,
+issue an error.
 
 Params:
-    sd = struct declaration that may contin both an rvalue and copy constructor
-    ctor = constructor that will be checked if it is an evalue constructor
+    sd = struct declaration that may contain both an rvalue and copy constructor
+    ctor = constructor that will be checked if it is an rvalue constructor
     ti = template instance the ctor is part of
 
 Return:
-    `false` if ctor is not an rvalue constructor or if `sd` does not contain a
-    copy constructor. `true` otherwise
+    `true` if sd has a copy constructor and ctor is an rvalue constructor
 */
 bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
 {
-    auto loc = ctor.loc;
-    auto tf = cast(TypeFunction)ctor.type;
-    auto dim = tf.parameterList.length;
-    if (sd && sd.hasCopyCtor && (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
+    if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
+    {
+        .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
+        .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
+                ti.toPrettyChars(), sd.toChars());
+
+        return true;
+    }
+
+    return false;
+}
+
+/************************************************
+ * Check if ctor is an rvalue constructor.
+ * A constructor that receives a single parameter of the same type as
+ * `Unqual!typeof(this)` is an rvalue constructor.
+ * Params:
+ *      sd = struct that ctor is a member of
+ *      ctor = constructor to test
+ * Returns:
+ *      true if it is an rvalue constructor
+ */
+bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
+{
+    auto tf = ctor.type.isTypeFunction();
+    const dim = tf.parameterList.length;
+    if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
     {
         auto param = tf.parameterList[0];
         if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
         {
-            .error(loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
-            .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`",
-                    ti.toPrettyChars(), sd.toChars());
-
             return true;
         }
     }
-
     return false;
 }
 
@@ -1643,85 +1658,88 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 imp.mod.checkImportDeprecation(imp.loc, sc);
             }
         }
-        if (imp.mod)
+        if (!imp.mod)
         {
-            // Modules need a list of each imported module
-
-            // if inside a template instantiation, the instantianting
-            // module gets the import.
-            // https://issues.dlang.org/show_bug.cgi?id=17181
-            Module importer = sc._module;
-            if (sc.minst && sc.tinst)
-            {
-                importer = sc.minst;
-                if (!sc.tinst.importedModules.contains(imp.mod))
-                    sc.tinst.importedModules.push(imp.mod);
-            }
-            //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars());
-            if (!importer.aimports.contains(imp.mod))
-                importer.aimports.push(imp.mod);
+            imp.semanticRun = PASS.semanticdone;
+            addImportDep(global.params.moduleDeps, imp, sc._module);
+        }
 
-            if (sc.explicitVisibility)
-                imp.visibility = sc.visibility;
+        // Modules need a list of each imported module
 
-            if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
-            {
-                ScopeDsymbol scopesym = sc.getScopesym();
+        // if inside a template instantiation, the instantianting
+        // module gets the import.
+        // https://issues.dlang.org/show_bug.cgi?id=17181
+        Module importer = sc._module;
+        if (sc.minst && sc.tinst)
+        {
+            importer = sc.minst;
+            if (!sc.tinst.importedModules.contains(imp.mod))
+                sc.tinst.importedModules.push(imp.mod);
+        }
+        //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars());
+        if (!importer.aimports.contains(imp.mod))
+            importer.aimports.push(imp.mod);
 
-                if (!imp.isstatic)
-                {
-                    scopesym.importScope(imp.mod, imp.visibility);
-                }
+        if (sc.explicitVisibility)
+            imp.visibility = sc.visibility;
 
+        if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import
+        {
+            ScopeDsymbol scopesym = sc.getScopesym();
 
-                imp.addPackageAccess(scopesym);
+            if (!imp.isstatic)
+            {
+                scopesym.importScope(imp.mod, imp.visibility);
             }
 
-            // if a module has errors it means that parsing has failed.
-            if (!imp.mod.errors)
-                imp.mod.dsymbolSemantic(null);
 
-            if (imp.mod.needmoduleinfo)
-            {
-                //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
-                importer.needmoduleinfo = 1;
-            }
+            imp.addPackageAccess(scopesym);
+        }
+
+        // if a module has errors it means that parsing has failed.
+        if (!imp.mod.errors)
+            imp.mod.dsymbolSemantic(null);
+
+        if (imp.mod.needmoduleinfo)
+        {
+            //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars());
+            importer.needmoduleinfo = 1;
+        }
 
-            sc = sc.push(imp.mod);
-            sc.visibility = imp.visibility;
-            for (size_t i = 0; i < imp.aliasdecls.length; i++)
+        sc = sc.push(imp.mod);
+        sc.visibility = imp.visibility;
+        for (size_t i = 0; i < imp.aliasdecls.length; i++)
+        {
+            AliasDeclaration ad = imp.aliasdecls[i];
+            //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
+            Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports);
+            if (sym)
             {
-                AliasDeclaration ad = imp.aliasdecls[i];
-                //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
-                Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports);
-                if (sym)
+                import dmd.access : symbolIsVisible;
+                if (!symbolIsVisible(sc, sym) && !sym.errors)
                 {
-                    import dmd.access : symbolIsVisible;
-                    if (!symbolIsVisible(sc, sym) && !sym.errors)
-                    {
-                        .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
-                            imp.names[i].toChars(), sc._module.toChars());
-                        sym.errors = true;
-                    }
-                    ad.dsymbolSemantic(sc);
-                    // If the import declaration is in non-root module,
-                    // analysis of the aliased symbol is deferred.
-                    // Therefore, don't see the ad.aliassym or ad.type here.
+                    .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars,
+                        imp.names[i].toChars(), sc._module.toChars());
+                    sym.errors = true;
                 }
+                ad.dsymbolSemantic(sc);
+                // If the import declaration is in non-root module,
+                // analysis of the aliased symbol is deferred.
+                // Therefore, don't see the ad.aliassym or ad.type here.
+            }
+            else
+            {
+                Dsymbol s = imp.mod.search_correct(imp.names[i]);
+                // https://issues.dlang.org/show_bug.cgi?id=23908
+                // Don't suggest symbols from the importer's module
+                if (s && s.parent != importer)
+                    .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
                 else
-                {
-                    Dsymbol s = imp.mod.search_correct(imp.names[i]);
-                    // https://issues.dlang.org/show_bug.cgi?id=23908
-                    // Don't suggest symbols from the importer's module
-                    if (s && s.parent != importer)
-                        .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars());
-                    else
-                        .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
-                    ad.type = Type.terror;
-                }
+                    .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars());
+                ad.type = Type.terror;
             }
-            sc = sc.pop();
         }
+        sc = sc.pop();
 
         imp.semanticRun = PASS.semanticdone;
         addImportDep(global.params.moduleDeps, imp, sc._module);
@@ -1734,20 +1752,24 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
         ad.semanticRun = PASS.semantic;
         Dsymbols* d = ad.include(sc);
         //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d);
-        if (d)
+        if (!d)
         {
-            Scope* sc2 = ad.newScope(sc);
-            bool errors;
-            for (size_t i = 0; i < d.length; i++)
-            {
-                Dsymbol s = (*d)[i];
-                s.dsymbolSemantic(sc2);
-                errors |= s.errors;
-            }
-            ad.errors |= errors;
-            if (sc2 != sc)
-                sc2.pop();
+            ad.semanticRun = PASS.semanticdone;
+            return;
         }
+
+        Scope* sc2 = ad.newScope(sc);
+        bool errors;
+        for (size_t i = 0; i < d.length; i++)
+        {
+            Dsymbol s = (*d)[i];
+            s.dsymbolSemantic(sc2);
+            errors |= s.errors;
+        }
+        ad.errors |= errors;
+        if (sc2 != sc)
+            sc2.pop();
+
         ad.semanticRun = PASS.semanticdone;
     }
 
@@ -2579,48 +2601,51 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
         sc.pop();
     }
 
-    override void visit(StaticCtorDeclaration scd)
+    void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor)
     {
-        //printf("StaticCtorDeclaration::semantic()\n");
-        if (scd.semanticRun >= PASS.semanticdone)
+        if (sd.semanticRun >= PASS.semanticdone)
             return;
-        if (scd._scope)
+        if (sd._scope)
         {
-            sc = scd._scope;
-            scd._scope = null;
+            sc = sd._scope;
+            sd._scope = null;
         }
-
-        scd.parent = sc.parent;
-        Dsymbol p = scd.parent.pastMixin();
+        sd.parent = sc.parent;
+        Dsymbol p = sd.parent.pastMixin();
+        const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration());
+        const(char)* what = isDestructor ? "destructor" : "constructor";
         if (!p.isScopeDsymbol())
         {
-            const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : "");
-            error(scd.loc, "`%sstatic` constructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars());
-            scd.type = Type.terror;
-            scd.errors = true;
+            const(char)* s = isShared ? "shared " : "";
+            error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars());
+            sd.type = Type.terror;
+            sd.errors = true;
             return;
         }
-        if (!scd.type)
-            scd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, scd.storage_class);
 
-        /* If the static ctor appears within a template instantiation,
+        if (!sd.type)
+            sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class);
+
+        /* If the static [dc]tor appears within a template instantiation,
          * it could get called multiple times by the module constructors
          * for different modules. Thus, protect it with a gate.
          */
-        if (scd.isInstantiated() && scd.semanticRun < PASS.semantic)
+        if (sd.isInstantiated() && sd.semanticRun < PASS.semantic)
         {
             /* Add this prefix to the constructor:
              * ```
              * static int gate;
-             * if (++gate != 1) return;
+             * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor
              * ```
              * or, for shared constructor:
              * ```
              * shared int gate;
-             * if (core.atomic.atomicOp!"+="(gate, 1) != 1) return;
+             * enum op  = isDestructor ? "-=" : "+=";
+             * enum cmp = isDestructor ? 0 : 1;
+             * if (core.atomic.atomicOp!op(gate, 1) != cmp) return;
              * ```
              */
-            const bool isShared = !!scd.isSharedStaticCtorDeclaration();
+
             auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
             v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0);
 
@@ -2631,49 +2656,56 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
             Expression e;
             if (isShared)
             {
-                e = doAtomicOp("+=", v.ident, IntegerExp.literal!(1));
+                e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1));
                 if (e is null)
                 {
-                    .error(scd.loc, "%s `%s` shared static constructor within a template require `core.atomic : atomicOp` to be present", scd.kind, scd.toPrettyChars);
+                    .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what);
                     return;
                 }
             }
             else
             {
+                IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1);
                 e = new AddAssignExp(
-                    Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!1);
+                    Loc.initial, new IdentifierExp(Loc.initial, v.ident), one);
             }
-
-            e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!1);
+            IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1;
+            e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp);
             s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
 
             sa.push(s);
-            if (scd.fbody)
-                sa.push(scd.fbody);
+            if (sd.fbody)
+                sa.push(sd.fbody);
 
-            scd.fbody = new CompoundStatement(Loc.initial, sa);
+            sd.fbody = new CompoundStatement(Loc.initial, sa);
+            if (isDestructor)
+                (cast(StaticDtorDeclaration)sd).vgate = v;
         }
-
         const LINK save = sc.linkage;
         if (save != LINK.d)
         {
-            const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : "");
-            deprecation(scd.loc, "`%sstatic` constructor can only be of D linkage", s);
+            const(char)* s = isShared ? "shared " : "";
+            deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what);
             // Just correct it
             sc.linkage = LINK.d;
         }
-        funcDeclarationSemantic(sc, scd);
+        funcDeclarationSemantic(sc, sd);
         sc.linkage = save;
 
         // We're going to need ModuleInfo
-        Module m = scd.getModule();
+        Module m = sd.getModule();
         if (!m)
             m = sc._module;
         if (m)
         {
             m.needmoduleinfo = 1;
-            //printf("module1 %s needs moduleinfo\n", m.toChars());
+            //printf("module2 %s needs moduleinfo\n", m.toChars());
         }
+    }
+    override void visit(StaticCtorDeclaration scd)
+    {
+        //printf("StaticCtorDeclaration::semantic()\n");
+        visitStaticCDtorDeclaration(scd, false);
 
         foreachUda(scd, sc, (Expression e) {
             import dmd.attrib : isEnumAttribute;
@@ -2696,100 +2728,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
     override void visit(StaticDtorDeclaration sdd)
     {
-        if (sdd.semanticRun >= PASS.semanticdone)
-            return;
-        if (sdd._scope)
-        {
-            sc = sdd._scope;
-            sdd._scope = null;
-        }
-
-        sdd.parent = sc.parent;
-        Dsymbol p = sdd.parent.pastMixin();
-        if (!p.isScopeDsymbol())
-        {
-            const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : "");
-            error(sdd.loc, "`%sstatic` destructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars());
-            sdd.type = Type.terror;
-            sdd.errors = true;
-            return;
-        }
-        if (!sdd.type)
-            sdd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sdd.storage_class);
-
-        /* If the static ctor appears within a template instantiation,
-         * it could get called multiple times by the module constructors
-         * for different modules. Thus, protect it with a gate.
-         */
-        if (sdd.isInstantiated() && sdd.semanticRun < PASS.semantic)
-        {
-            /* Add this prefix to the constructor:
-             * ```
-             * static int gate;
-             * if (--gate != 0) return;
-             * ```
-             * or, for shared constructor:
-             * ```
-             * shared int gate;
-             * if (core.atomic.atomicOp!"-="(gate, 1) != 0) return;
-             * ```
-             */
-            const bool isShared = !!sdd.isSharedStaticDtorDeclaration();
-            auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null);
-            v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0);
-
-            auto sa = new Statements();
-            Statement s = new ExpStatement(Loc.initial, v);
-            sa.push(s);
-
-            Expression e;
-            if (isShared)
-            {
-                e = doAtomicOp("-=", v.ident, IntegerExp.literal!(1));
-                if (e is null)
-                {
-                    .error(sdd.loc, "%s `%s` shared static destructo within a template require `core.atomic : atomicOp` to be present", sdd.kind, sdd.toPrettyChars);
-                    return;
-                }
-            }
-            else
-            {
-                e = new AddAssignExp(
-                    Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!(-1));
-            }
-
-            e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!0);
-            s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial);
-
-            sa.push(s);
-            if (sdd.fbody)
-                sa.push(sdd.fbody);
-
-            sdd.fbody = new CompoundStatement(Loc.initial, sa);
-
-            sdd.vgate = v;
-        }
-
-        const LINK save = sc.linkage;
-        if (save != LINK.d)
-        {
-            const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : "");
-            deprecation(sdd.loc, "`%sstatic` destructor can only be of D linkage", s);
-            // Just correct it
-            sc.linkage = LINK.d;
-        }
-        funcDeclarationSemantic(sc, sdd);
-        sc.linkage = save;
-
-        // We're going to need ModuleInfo
-        Module m = sdd.getModule();
-        if (!m)
-            m = sc._module;
-        if (m)
-        {
-            m.needmoduleinfo = 1;
-            //printf("module2 %s needs moduleinfo\n", m.toChars());
-        }
+        visitStaticCDtorDeclaration(sdd, true);
     }
 
     override void visit(InvariantDeclaration invd)
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index f8122316daf..fe601207408 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -1643,60 +1643,64 @@  MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa
         override void visit(TypeSArray t)
         {
             // Extra check that array dimensions must match
-            if (tparam)
+            if (!tparam)
             {
-                if (tparam.ty == Tarray)
-                {
-                    MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
-                    result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch;
-                    return;
-                }
+                visit(cast(Type)t);
+                return;
+            }
 
-                TemplateParameter tp = null;
-                Expression edim = null;
-                size_t i;
-                if (auto tsa = tparam.isTypeSArray())
+            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)
                 {
-                    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;
+                    Identifier id = tsa.dim.isVarExp().var.ident;
+                    i = templateIdentifierLookup(id, &parameters);
+                    assert(i != IDX_NOTFOUND);
+                    tp = parameters[i];
                 }
-                else if (auto taa = tparam.isTypeAArray())
+                else
+                    edim = tsa.dim;
+            }
+            else if (auto taa = tparam.isTypeAArray())
+            {
+                i = templateParameterLookup(taa.index, &parameters);
+                if (i != IDX_NOTFOUND)
+                    tp = parameters[i];
+                else
                 {
-                    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)
                     {
-                        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);
+                        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.toInteger() == t.dim.toInteger())
-                {
-                    result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
-                    return;
-                }
             }
+            if (tp && tp.matchArg(sc, t.dim, i, &parameters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger())
+            {
+                result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm);
+                return;
+            }
+
             visit(cast(Type)t);
         }
 
@@ -1721,132 +1725,137 @@  MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa
             if (!tparam)
                 return visit(cast(Type)t);
 
-            if (auto tp = tparam.isTypeFunction())
+            auto tp = tparam.isTypeFunction();
+            if (!tp)
+            {
+                visit(cast(Type)t);
+                return;
+            }
+
+            if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
             {
-                if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
-                {
-                    result = MATCH.nomatch;
-                    return;
-                }
+                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;
 
-                foreach (fparam; *tp.parameterList.parameters)
+                // 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]))
                 {
-                    // 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)
                     {
-                        auto tx = fparam.type.typeSemantic(Loc.initial, sc);
-                        if (tx.ty == Terror)
-                        {
-                            result = MATCH.nomatch;
-                            return;
-                        }
-                        fparam.type = tx;
+                        result = MATCH.nomatch;
+                        return;
                     }
+                    fparam.type = tx;
                 }
+            }
+
+            const size_t nfargs = t.parameterList.length;
+            size_t nfparams = tp.parameterList.length;
 
-                const size_t nfargs = t.parameterList.length;
-                size_t nfparams = tp.parameterList.length;
+            /* See if tuple match
+             */
+            if (nfparams > 0 && nfargs >= nfparams - 1)
+            {
+                /* 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;
 
-                /* See if tuple match
+                /* Look through parameters to find tuple matching tid.ident
                  */
-                if (nfparams > 0 && nfargs >= nfparams - 1)
+                size_t tupi = 0;
+                for (; 1; tupi++)
                 {
-                    /* 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)
+                    if (tupi == parameters.length)
                         goto L1;
+                    TemplateParameter tx = parameters[tupi];
+                    TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+                    if (tup && tup.ident.equals(tid.ident))
+                        break;
+                }
 
-                    /* Look through parameters to find tuple matching tid.ident
-                     */
-                    size_t tupi = 0;
-                    for (; 1; tupi++)
+                /* 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)
+                {
+                    // Existing deduced argument must be a tuple, and must match
+                    Tuple tup = isTuple(o);
+                    if (!tup || tup.objects.length != tuple_dim)
                     {
-                        if (tupi == parameters.length)
-                            goto L1;
-                        TemplateParameter tx = parameters[tupi];
-                        TemplateTupleParameter tup = tx.isTemplateTupleParameter();
-                        if (tup && tup.ident.equals(tid.ident))
-                            break;
+                        result = MATCH.nomatch;
+                        return;
                     }
-
-                    /* 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)
+                    for (size_t i = 0; i < tuple_dim; i++)
                     {
-                        // Existing deduced argument must be a tuple, and must match
-                        Tuple tup = isTuple(o);
-                        if (!tup || tup.objects.length != tuple_dim)
+                        Parameter arg = t.parameterList[nfparams - 1 + i];
+                        if (!arg.type.equals(tup.objects[i]))
                         {
                             result = MATCH.nomatch;
                             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
+                }
+                else
+                {
+                    // Create new tuple
+                    auto tup = new Tuple(tuple_dim);
+                    for (size_t i = 0; i < tuple_dim; i++)
                     {
-                        // 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;
+                        Parameter arg = t.parameterList[nfparams - 1 + i];
+                        tup.objects[i] = arg.type;
                     }
-                    nfparams--; // don't consider the last parameter for type deduction
-                    goto L2;
+                    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;
 
-            L1:
-                if (nfargs != nfparams)
+                Parameter a = t.parameterList[i];
+
+                if (!a.isCovariant(t.isRef, ap) ||
+                    !deduceType(a.type, sc, ap.type, parameters, dedtypes))
                 {
                     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);
         }
 
@@ -1873,78 +1882,82 @@  MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa
         override void visit(TypeInstance t)
         {
             // Extra check
-            if (tparam && tparam.ty == Tinstance && t.tempinst.tempdecl)
+            if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl)
             {
-                TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
-                assert(tempdecl);
+                visit(cast(Type)t);
+                return;
+            }
 
-                TypeInstance tp = tparam.isTypeInstance();
+            TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
+            assert(tempdecl);
 
-                //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());
+            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)
+                /* 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
                      */
-                    size_t i = templateIdentifierLookup(tp.tempinst.name, &parameters);
-                    if (i == IDX_NOTFOUND)
+                    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)
                     {
-                        /* 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)
                         {
-                            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;
-                            }
+                            // 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)
+                    }
+                    if (s)
+                    {
+                        s = s.toAlias();
+                        TemplateDeclaration td = s.isTemplateDeclaration();
+                        if (td)
                         {
-                            s = s.toAlias();
-                            TemplateDeclaration td = s.isTemplateDeclaration();
-                            if (td)
+                            if (td.overroot)
+                                td = td.overroot;
+                            for (; td; td = td.overnext)
                             {
-                                if (td.overroot)
-                                    td = td.overroot;
-                                for (; td; td = td.overnext)
-                                {
-                                    if (td == tempdecl)
-                                        goto L2;
-                                }
+                                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))
+                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;
 
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index 7feeb271a54..fb3fb9b9645 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -1305,10 +1305,9 @@  private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
         /* Check for returning a ref variable by 'ref', but should be 'return ref'
          * Infer the addition of 'return', or set result to be the offending expression.
          */
-        if ((vsr == ScopeRef.Ref ||
+        if (vsr == ScopeRef.Ref ||
              vsr == ScopeRef.RefScope ||
-             vsr == ScopeRef.Ref_ReturnScope) &&
-            !(v.storage_class & STC.foreach_))
+             vsr == ScopeRef.Ref_ReturnScope)
         {
             if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) &&
                 inferReturn(sc.func, v, /*returnScope:*/ false))
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index 7b90c0eb8b7..a72df72adb4 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -2230,13 +2230,6 @@  extern (C++) final class AssocArrayLiteralExp : Expression
     }
 }
 
-enum stageScrub             = 0x1;  /// scrubReturnValue is running
-enum stageSearchPointers    = 0x2;  /// hasNonConstPointers is running
-enum stageOptimize          = 0x4;  /// optimize is running
-enum stageApply             = 0x8;  /// apply is running
-enum stageInlineScan        = 0x10; /// inlineScan is running
-enum stageToCBuffer         = 0x20; /// toCBuffer is running
-
 /***********************************************************
  * sd( e1, e2, e3, ... )
  */
@@ -2269,7 +2262,17 @@  extern (C++) final class StructLiteralExp : Expression
      * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
      * (with infinite recursion) of this expression.
      */
-    ubyte stageflags;
+    enum StageFlags : ubyte
+    {
+        none              = 0x0,
+        scrub             = 0x1,  /// scrubReturnValue is running
+        searchPointers    = 0x2,  /// hasNonConstPointers is running
+        optimize          = 0x4,  /// optimize is running
+        apply             = 0x8,  /// apply is running
+        inlineScan        = 0x10, /// inlineScan is running
+        toCBuffer         = 0x20 /// toCBuffer is running
+    }
+    StageFlags stageflags;
 
     bool useStaticInit;     /// if this is true, use the StructDeclaration's init symbol
     bool isOriginal = false; /// used when moving instances to indicate `this is this.origin`
@@ -3028,28 +3031,6 @@  extern (C++) abstract class UnaExp : Expression
         return e;
     }
 
-    /********************************
-     * The type for a unary expression is incompatible.
-     * Print error message.
-     * Returns:
-     *  ErrorExp
-     */
-    extern (D) final Expression incompatibleTypes()
-    {
-        if (e1.type.toBasetype() == Type.terror)
-            return e1;
-
-        if (e1.op == EXP.type)
-        {
-            error(loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(op).ptr, e1.toChars(), EXPtoString(op).ptr);
-        }
-        else
-        {
-            error(loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(op).ptr, e1.toChars(), e1.type.toChars());
-        }
-        return ErrorExp.get();
-    }
-
     /*********************
      * Mark the operand as will never be dereferenced,
      * which is useful info for @safe checks.
@@ -3094,40 +3075,6 @@  extern (C++) abstract class BinExp : Expression
         return e;
     }
 
-    /********************************
-     * The types for a binary expression are incompatible.
-     * Print error message.
-     * Returns:
-     *  ErrorExp
-     */
-    extern (D) final Expression incompatibleTypes()
-    {
-        if (e1.type.toBasetype() == Type.terror)
-            return e1;
-        if (e2.type.toBasetype() == Type.terror)
-            return e2;
-
-        // CondExp uses 'a ? b : c' but we're comparing 'b : c'
-        const(char)* thisOp = (op == EXP.question) ? ":" : EXPtoString(op).ptr;
-        if (e1.op == EXP.type || e2.op == EXP.type)
-        {
-            error(loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types",
-                e1.toChars(), thisOp, e2.toChars(), EXPtoString(op).ptr);
-        }
-        else if (e1.type.equals(e2.type))
-        {
-            error(loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`",
-                e1.toChars(), thisOp, e2.toChars(), e1.type.toChars());
-        }
-        else
-        {
-            auto ts = toAutoQualChars(e1.type, e2.type);
-            error(loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`",
-                e1.toChars(), thisOp, e2.toChars(), ts[0], ts[1]);
-        }
-        return ErrorExp.get();
-    }
-
     extern (D) final bool checkIntegralBin()
     {
         bool r1 = e1.checkIntegral();
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 6e4b5a505e7..57336987764 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -63,12 +63,12 @@  import dmd.location;
 import dmd.mtype;
 import dmd.mustuse;
 import dmd.nspace;
+import dmd.nogc;
 import dmd.objc;
 import dmd.opover;
 import dmd.optimize;
 import dmd.parse;
 import dmd.printast;
-import dmd.postordervisitor;
 import dmd.root.array;
 import dmd.root.ctfloat;
 import dmd.root.filename;
@@ -88,6 +88,7 @@  import dmd.typinf;
 import dmd.utils;
 import dmd.utils : arrayCastBigEndian;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 enum LOGSEMANTIC = false;
 
@@ -339,6 +340,61 @@  StringExp toUTF8(StringExp se, Scope* sc)
     }
     return se;
 }
+/********************************
+ * The type for a unary expression is incompatible.
+ * Print error message.
+ * Returns:
+ *  ErrorExp
+ */
+extern (D) Expression incompatibleTypes(UnaExp e)
+{
+    if (e.e1.type.toBasetype() == Type.terror)
+        return e.e1;
+
+    if (e.e1.op == EXP.type)
+    {
+        error(e.loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(e.op).ptr, e.e1.toChars(), EXPtoString(e.op).ptr);
+    }
+    else
+    {
+        error(e.loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(e.op).ptr, e.e1.toChars(), e.e1.type.toChars());
+    }
+    return ErrorExp.get();
+}
+
+/********************************
+ * The types for a binary expression are incompatible.
+ * Print error message.
+ * Returns:
+ *  ErrorExp
+ */
+extern (D) Expression incompatibleTypes(BinExp e)
+{
+    if (e.e1.type.toBasetype() == Type.terror)
+        return e.e1;
+    if (e.e2.type.toBasetype() == Type.terror)
+        return e.e2;
+
+    // CondExp uses 'a ? b : c' but we're comparing 'b : c'
+    const(char)* thisOp = (e.op == EXP.question) ? ":" : EXPtoString(e.op).ptr;
+    if (e.e1.op == EXP.type || e.e2.op == EXP.type)
+    {
+        error(e.loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types",
+            e.e1.toChars(), thisOp, e.e2.toChars(), EXPtoString(e.op).ptr);
+    }
+    else if (e.e1.type.equals(e.e2.type))
+    {
+        error(e.loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`",
+            e.e1.toChars(), thisOp, e.e2.toChars(), e.e1.type.toChars());
+    }
+    else
+    {
+        auto ts = toAutoQualChars(e.e1.type, e.e2.type);
+        error(e.loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`",
+            e.e1.toChars(), thisOp, e.e2.toChars(), ts[0], ts[1]);
+    }
+    return ErrorExp.get();
+}
 
 private Expression reorderSettingAAElem(BinExp exp, Scope* sc)
 {
@@ -577,25 +633,21 @@  TupleDeclaration isAliasThisTuple(Expression e)
     Type t = e.type.toBasetype();
     while (true)
     {
-        if (Dsymbol s = t.toDsymbol(null))
+        Dsymbol s = t.toDsymbol(null);
+        if (!s)
+            return null;
+        auto ad = s.isAggregateDeclaration();
+        if (!ad)
+            return null;
+        s = ad.aliasthis ? ad.aliasthis.sym : null;
+        if (s && s.isVarDeclaration())
         {
-            if (auto ad = s.isAggregateDeclaration())
-            {
-                s = ad.aliasthis ? ad.aliasthis.sym : null;
-                if (s && s.isVarDeclaration())
-                {
-                    TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration();
-                    if (td && td.isexp)
-                        return td;
-                }
-                if (Type att = t.aliasthisOf())
-                {
-                    t = att;
-                    continue;
-                }
-            }
+            TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration();
+            if (td && td.isexp)
+                return td;
         }
-        return null;
+        if (Type att = t.aliasthisOf())
+            t = att;
     }
 }
 
@@ -610,6 +662,13 @@  Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
     AggregateDeclaration ad = isAggregate(ae.e1.type);
     Dsymbol slice = search_function(ad, Id.slice);
     //printf("slice = %s %s\n", slice.kind(), slice.toChars());
+    Expression fallback()
+    {
+        if (ae.arguments.length == 1)
+            return null;
+        error(ae.loc, "multi-dimensional slicing requires template `opSlice`");
+        return ErrorExp.get();
+    }
     foreach (i, e; *ae.arguments)
     {
         if (i == 0)
@@ -617,11 +676,7 @@  Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
 
         if (e.op == EXP.interval && !(slice && slice.isTemplateDeclaration()))
         {
-        Lfallback:
-            if (ae.arguments.length == 1)
-                return null;
-            error(ae.loc, "multi-dimensional slicing requires template `opSlice`");
-            return ErrorExp.get();
+            return fallback();
         }
         //printf("[%d] e = %s\n", i, e.toChars());
 
@@ -661,7 +716,7 @@  Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0)
             sc = sc.pop();
             global.endGagging(xerrors);
             if (!fslice)
-                goto Lfallback;
+                return fallback();
 
             e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs);
             e = new CallExp(ae.loc, e, fargs);
@@ -787,40 +842,40 @@  extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
  */
 private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
 {
-    if (auto ts = e.type.baseElemOf().isTypeStruct())
-    {
-        StructDeclaration sd = ts.sym;
-        if (sd.postblit || sd.hasCopyCtor)
-        {
-            /* Create a variable tmp, and replace the argument e with:
-             *      (tmp = e),tmp
-             * and let AssignExp() handle the construction.
-             * This is not the most efficient, ideally tmp would be constructed
-             * directly onto the stack.
-             */
-            auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
-            if (sd.hasCopyCtor && destinationType)
-            {
-                // https://issues.dlang.org/show_bug.cgi?id=22619
-                // If the destination type is inout we can preserve it
-                // only if inside an inout function; if we are not inside
-                // an inout function, then we will preserve the type of
-                // the source
-                if (destinationType.hasWild && !(sc.func.storage_class & STC.wild))
-                    tmp.type = e.type;
-                else
-                    tmp.type = destinationType;
-            }
-            tmp.storage_class |= STC.nodtor;
-            tmp.dsymbolSemantic(sc);
-            Expression de = new DeclarationExp(e.loc, tmp);
-            Expression ve = new VarExp(e.loc, tmp);
-            de.type = Type.tvoid;
-            ve.type = e.type;
-            return Expression.combine(de, ve);
-        }
-    }
-    return e;
+    auto ts = e.type.baseElemOf().isTypeStruct();
+
+    if (!ts)
+        return e;
+    StructDeclaration sd = ts.sym;
+    if (!sd.postblit && !sd.hasCopyCtor)
+        return e;
+
+    /* Create a variable tmp, and replace the argument e with:
+     *      (tmp = e),tmp
+     * and let AssignExp() handle the construction.
+     * This is not the most efficient, ideally tmp would be constructed
+     * directly onto the stack.
+     */
+    auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
+    if (sd.hasCopyCtor && destinationType)
+    {
+        // https://issues.dlang.org/show_bug.cgi?id=22619
+        // If the destination type is inout we can preserve it
+        // only if inside an inout function; if we are not inside
+        // an inout function, then we will preserve the type of
+        // the source
+        if (destinationType.hasWild && !(sc.func.storage_class & STC.wild))
+            tmp.type = e.type;
+        else
+            tmp.type = destinationType;
+    }
+    tmp.storage_class |= STC.nodtor;
+    tmp.dsymbolSemantic(sc);
+    Expression de = new DeclarationExp(e.loc, tmp);
+    Expression ve = new VarExp(e.loc, tmp);
+    de.type = Type.tvoid;
+    ve.type = e.type;
+    return Expression.combine(de, ve);
 }
 
 /************************************************
@@ -1033,41 +1088,41 @@  private void hookDtors(CondExp ce, Scope* sc)
         override void visit(DeclarationExp e)
         {
             auto v = e.declaration.isVarDeclaration();
-            if (v && !v.isDataseg())
+            if (!v || v.isDataseg())
+                return;
+
+            if (v._init)
             {
-                if (v._init)
-                {
-                    if (auto ei = v._init.isExpInitializer())
-                        walkPostorder(ei.exp, this);
-                }
+                if (auto ei = v._init.isExpInitializer())
+                    walkPostorder(ei.exp, this);
+            }
 
-                if (v.edtor)
-                    walkPostorder(v.edtor, this);
+            if (v.edtor)
+                walkPostorder(v.edtor, this);
 
-                if (v.needsScopeDtor())
-                {
-                    if (!vcond)
-                    {
-                        vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd);
-                        vcond.dsymbolSemantic(sc);
+            if (!v.needsScopeDtor())
+                return;
 
-                        Expression de = new DeclarationExp(ce.econd.loc, vcond);
-                        de = de.expressionSemantic(sc);
+            if (!vcond)
+            {
+                vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd);
+                vcond.dsymbolSemantic(sc);
 
-                        Expression ve = new VarExp(ce.econd.loc, vcond);
-                        ce.econd = Expression.combine(de, ve);
-                    }
+                Expression de = new DeclarationExp(ce.econd.loc, vcond);
+                de = de.expressionSemantic(sc);
 
-                    //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
-                    Expression ve = new VarExp(vcond.loc, vcond);
-                    if (isThen)
-                        v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor);
-                    else
-                        v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor);
-                    v.edtor = v.edtor.expressionSemantic(sc);
-                    //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
-                }
+                Expression ve = new VarExp(ce.econd.loc, vcond);
+                ce.econd = Expression.combine(de, ve);
             }
+
+            //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
+            Expression ve = new VarExp(vcond.loc, vcond);
+            if (isThen)
+                v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor);
+            else
+                v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor);
+            v.edtor = v.edtor.expressionSemantic(sc);
+            //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
         }
     }
 
@@ -2188,31 +2243,32 @@  private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
     if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT || f.ident == Id._d_newarraymTX)
         return false;
 
-    if (!f.isNogc())
+    if (f.isNogc())
+        return false;
+
+    if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
     {
-        if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f))
-        {
-            if (loc.linnum == 0) // e.g. implicitly generated dtor
-                loc = sc.func.loc;
+        if (loc.linnum == 0) // e.g. implicitly generated dtor
+            loc = sc.func.loc;
 
-            // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
-            // so don't print anything to avoid double error messages.
-            if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
-                || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
-                || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
-            {
-                error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
-                    sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
+        // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
+        // so don't print anything to avoid double error messages.
+        if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
+            || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
+            || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT))
+        {
+            error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
+                sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
 
-                if (!f.isDtorDeclaration)
-                    f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
-            }
+            if (!f.isDtorDeclaration)
+                f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc);
+        }
 
-            f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc");
+        f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc");
 
-            return true;
-        }
+        return true;
     }
+
     return false;
 }
 
@@ -2222,30 +2278,31 @@  private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc)
  */
 private bool checkPostblit(Type t, ref Loc loc, Scope* sc)
 {
-    if (auto ts = t.baseElemOf().isTypeStruct())
+    auto ts = t.baseElemOf().isTypeStruct();
+    if (!ts)
+        return false;
+
+    if (global.params.useTypeInfo && Type.dtypeinfo)
     {
-        if (global.params.useTypeInfo && Type.dtypeinfo)
-        {
-            // https://issues.dlang.org/show_bug.cgi?id=11395
-            // Require TypeInfo generation for array concatenation
-            semanticTypeInfo(sc, t);
-        }
+        // https://issues.dlang.org/show_bug.cgi?id=11395
+        // Require TypeInfo generation for array concatenation
+        semanticTypeInfo(sc, t);
+    }
 
-        StructDeclaration sd = ts.sym;
-        if (sd.postblit)
-        {
-            if (sd.postblit.checkDisabled(loc, sc))
-                return true;
+    StructDeclaration sd = ts.sym;
+    if (!sd.postblit)
+        return false;
 
-            //checkDeprecated(sc, sd.postblit);        // necessary?
-            sd.postblit.checkPurity(loc, sc);
-            sd.postblit.checkSafety(loc, sc);
-            sd.postblit.checkNogc(loc, sc);
-            //checkAccess(sd, loc, sc, sd.postblit);   // necessary?
-            return false;
-        }
-    }
+    if (sd.postblit.checkDisabled(loc, sc))
+        return true;
+
+    //checkDeprecated(sc, sd.postblit);        // necessary?
+    sd.postblit.checkPurity(loc, sc);
+    sd.postblit.checkSafety(loc, sc);
+    sd.postblit.checkNogc(loc, sc);
+    //checkAccess(sd, loc, sc, sd.postblit);   // necessary?
     return false;
+
 }
 
 /***************************************
@@ -2741,48 +2798,49 @@  private Expression rewriteOpAssign(BinExp exp)
 private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const bool reportErrors = true)
 {
     Expressions* exps = argumentList.arguments;
+    if (!exps)
+        return false;
+
+    expandTuples(exps, argumentList.names);
+
     bool err = false;
-    if (exps)
+    for (size_t i = 0; i < exps.length; i++)
     {
-        expandTuples(exps, argumentList.names);
-
-        for (size_t i = 0; i < exps.length; i++)
+        Expression arg = (*exps)[i];
+        arg = resolveProperties(sc, arg);
+        arg = arg.arrayFuncConv(sc);
+        if (arg.op == EXP.type)
         {
-            Expression arg = (*exps)[i];
-            arg = resolveProperties(sc, arg);
-            arg = arg.arrayFuncConv(sc);
-            if (arg.op == EXP.type)
-            {
-                // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
-                arg = resolveAliasThis(sc, arg);
+            // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
+            arg = resolveAliasThis(sc, arg);
 
-                if (arg.op == EXP.type)
-                {
-                    if (reportErrors)
-                    {
-                        error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars());
-                        arg = ErrorExp.get();
-                    }
-                    err = true;
-                }
-            }
-            else if (arg.type.toBasetype().ty == Tfunction)
+            if (arg.op == EXP.type)
             {
                 if (reportErrors)
                 {
-                    error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars());
+                    error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars());
                     arg = ErrorExp.get();
                 }
                 err = true;
             }
-            else if (checkNonAssignmentArrayOp(arg))
+        }
+        else if (arg.type.toBasetype().ty == Tfunction)
+        {
+            if (reportErrors)
             {
+                error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars());
                 arg = ErrorExp.get();
-                err = true;
             }
-            (*exps)[i] = arg;
+            err = true;
         }
+        else if (checkNonAssignmentArrayOp(arg))
+        {
+            arg = ErrorExp.get();
+            err = true;
+        }
+        (*exps)[i] = arg;
     }
+
     return err;
 }
 
@@ -2907,143 +2965,144 @@  private bool functionParameters(const ref Loc loc, Scope* sc,
     {
         Expression arg = (i < nargs) ? (*arguments)[i] : null;
 
-        if (i < nparams)
+        if (i >= nparams)
+            break;
+
+        bool errorArgs()
         {
-            bool errorArgs()
-            {
-                error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
-                return true;
-            }
+            error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs);
+            return true;
+        }
 
-            Parameter p = tf.parameterList[i];
+        Parameter p = tf.parameterList[i];
 
-            if (!arg)
+        if (!arg)
+        {
+            if (!p.defaultArg)
             {
-                if (!p.defaultArg)
-                {
-                    if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams)
-                        goto L2;
-                    return errorArgs();
-                }
-                arg = p.defaultArg;
-                if (!arg.type)
-                    arg = arg.expressionSemantic(sc);
-                arg = inlineCopy(arg, sc);
-                // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
-                arg = arg.resolveLoc(loc, sc);
-                if (i >= nargs)
-                {
-                    arguments.push(arg);
-                    nargs++;
-                }
-                else
-                    (*arguments)[i] = arg;
+                if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams)
+                    goto L2;
+                return errorArgs();
+            }
+            arg = p.defaultArg;
+            if (!arg.type)
+                arg = arg.expressionSemantic(sc);
+            arg = inlineCopy(arg, sc);
+            // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
+            arg = arg.resolveLoc(loc, sc);
+            if (i >= nargs)
+            {
+                arguments.push(arg);
+                nargs++;
             }
             else
+                (*arguments)[i] = arg;
+        }
+        else
+        {
+            if (arg.isDefaultInitExp())
             {
-                if (arg.isDefaultInitExp())
-                {
-                    arg = arg.resolveLoc(loc, sc);
-                    (*arguments)[i] = arg;
-                }
+                arg = arg.resolveLoc(loc, sc);
+                (*arguments)[i] = arg;
             }
+        }
 
 
-            if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic
+        if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic
+        {
+            //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
             {
-                //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
+                MATCH m;
+                if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch)
                 {
-                    MATCH m;
-                    if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch)
-                    {
-                        if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
-                            goto L2;
-                        else if (nargs != nparams)
-                            return errorArgs();
-                        goto L1;
-                    }
+                    if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m)
+                        goto L2;
+                    else if (nargs != nparams)
+                        return errorArgs();
+                    goto L1;
                 }
-            L2:
-                Type tb = p.type.toBasetype();
-                switch (tb.ty)
+            }
+        L2:
+            Type tb = p.type.toBasetype();
+            switch (tb.ty)
+            {
+            case Tsarray:
+            case Tarray:
                 {
-                case Tsarray:
-                case Tarray:
-                    {
-                        /* Create a static array variable v of type arg.type:
-                         *  T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
-                         *
-                         * The array literal in the initializer of the hidden variable
-                         * is now optimized.
-                         * https://issues.dlang.org/show_bug.cgi?id=2356
-                         */
-                        Type tbn = (cast(TypeArray)tb).next;    // array element type
-                        Type tret = p.isLazyArray();
+                    /* Create a static array variable v of type arg.type:
+                     *  T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
+                     *
+                     * The array literal in the initializer of the hidden variable
+                     * is now optimized.
+                     * https://issues.dlang.org/show_bug.cgi?id=2356
+                     */
+                    Type tbn = (cast(TypeArray)tb).next;    // array element type
+                    Type tret = p.isLazyArray();
 
-                        auto elements = new Expressions(nargs - i);
-                        foreach (u; 0 .. elements.length)
-                        {
-                            Expression a = (*arguments)[i + u];
-                            assert(a);
-                            if (tret && a.implicitConvTo(tret))
-                            {
-                                // p is a lazy array of delegates, tret is return type of the delegates
-                                a = a.implicitCastTo(sc, tret)
-                                     .optimize(WANTvalue)
-                                     .toDelegate(tret, sc);
-                            }
-                            else
-                                a = a.implicitCastTo(sc, tbn);
-                            a = a.addDtorHook(sc);
-                            (*elements)[u] = a;
-                        }
-                        // https://issues.dlang.org/show_bug.cgi?id=14395
-                        // Convert to a static array literal, or its slice.
-                        arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements);
-                        if (tb.ty == Tarray)
+                    auto elements = new Expressions(nargs - i);
+                    foreach (u; 0 .. elements.length)
+                    {
+                        Expression a = (*arguments)[i + u];
+                        assert(a);
+                        if (tret && a.implicitConvTo(tret))
                         {
-                            arg = new SliceExp(loc, arg, null, null);
-                            arg.type = p.type;
+                            // p is a lazy array of delegates, tret is return type of the delegates
+                            a = a.implicitCastTo(sc, tret)
+                                 .optimize(WANTvalue)
+                                 .toDelegate(tret, sc);
                         }
-                        break;
+                        else
+                            a = a.implicitCastTo(sc, tbn);
+                        a = a.addDtorHook(sc);
+                        (*elements)[u] = a;
                     }
-                case Tclass:
+                    // https://issues.dlang.org/show_bug.cgi?id=14395
+                    // Convert to a static array literal, or its slice.
+                    arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements);
+                    if (tb.ty == Tarray)
                     {
-                        /* Set arg to be:
-                         *      new Tclass(arg0, arg1, ..., argn)
-                         */
-                        auto args = new Expressions(nargs - i);
-                        foreach (u; i .. nargs)
-                            (*args)[u - i] = (*arguments)[u];
-                        arg = new NewExp(loc, null, p.type, args);
-                        break;
-                    }
-                default:
-                    if (!arg)
-                    {
-                        error(loc, "not enough arguments");
-                        return true;
+                        arg = new SliceExp(loc, arg, null, null);
+                        arg.type = p.type;
                     }
                     break;
                 }
-                arg = arg.expressionSemantic(sc);
-                //printf("\targ = '%s'\n", arg.toChars());
-                arguments.setDim(i + 1);
-                (*arguments)[i] = arg;
-                nargs = i + 1;
-                done = true;
+            case Tclass:
+                {
+                    /* Set arg to be:
+                     *      new Tclass(arg0, arg1, ..., argn)
+                     */
+                    auto args = new Expressions(nargs - i);
+                    foreach (u; i .. nargs)
+                        (*args)[u - i] = (*arguments)[u];
+                    arg = new NewExp(loc, null, p.type, args);
+                    break;
+                }
+            default:
+                if (!arg)
+                {
+                    error(loc, "not enough arguments");
+                    return true;
+                }
+                break;
             }
+            arg = arg.expressionSemantic(sc);
+            //printf("\targ = '%s'\n", arg.toChars());
+            arguments.setDim(i + 1);
+            (*arguments)[i] = arg;
+            nargs = i + 1;
+            done = true;
+        }
 
-        L1:
-            if (!(p.isLazy() && p.type.ty == Tvoid))
+    L1:
+        if (!(p.isLazy() && p.type.ty == Tvoid))
+        {
+            if (ubyte wm = arg.type.deduceWild(p.type, p.isReference()))
             {
-                if (ubyte wm = arg.type.deduceWild(p.type, p.isReference()))
-                {
-                    wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm;
-                    //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch);
-                }
+                wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm;
+                //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch);
             }
         }
+
         if (done)
             break;
     }
@@ -3933,35 +3992,37 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             if (!sc2.scopesym)
                 continue;
 
-            if (auto ss = sc2.scopesym.isWithScopeSymbol())
+            auto ss = sc2.scopesym.isWithScopeSymbol();
+            if (!ss)
+                continue;
+
+            if (ss.withstate.wthis)
             {
-                if (ss.withstate.wthis)
+                Expression e;
+                e = new VarExp(exp.loc, ss.withstate.wthis);
+                e = new DotIdExp(exp.loc, e, exp.ident);
+                e = e.trySemantic(sc);
+                if (e)
                 {
-                    Expression e;
-                    e = new VarExp(exp.loc, ss.withstate.wthis);
-                    e = new DotIdExp(exp.loc, e, exp.ident);
-                    e = e.trySemantic(sc);
-                    if (e)
-                    {
-                        result = e;
-                        return;
-                    }
+                    result = e;
+                    return;
                 }
-                // Try Type.opDispatch (so the static version)
-                else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type)
+            }
+            // Try Type.opDispatch (so the static version)
+            else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type)
+            {
+                Type t = ss.withstate.exp.isTypeExp().type;
+                if (!t)
+                    continue;
+
+                Expression e;
+                e = new TypeExp(exp.loc, t);
+                e = new DotIdExp(exp.loc, e, exp.ident);
+                e = e.trySemantic(sc);
+                if (e)
                 {
-                    if (Type t = ss.withstate.exp.isTypeExp().type)
-                    {
-                        Expression e;
-                        e = new TypeExp(exp.loc, t);
-                        e = new DotIdExp(exp.loc, e, exp.ident);
-                        e = e.trySemantic(sc);
-                        if (e)
-                        {
-                            result = e;
-                            return;
-                        }
-                    }
+                    result = e;
+                    return;
                 }
             }
         }
@@ -5531,48 +5592,48 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
     private void genIdent(FuncExp exp, Scope* sc)
     {
-        if (exp.fd.ident == Id.empty)
-        {
-            const(char)[] s;
-            if (exp.fd.fes)
-                s = "__foreachbody";
-            else if (exp.fd.tok == TOK.reserved)
-                s = "__lambda";
-            else if (exp.fd.tok == TOK.delegate_)
-                s = "__dgliteral";
-            else
-                s = "__funcliteral";
+        if (exp.fd.ident != Id.empty)
+            return;
 
-            DsymbolTable symtab;
-            if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+        const(char)[] s;
+        if (exp.fd.fes)
+            s = "__foreachbody";
+        else if (exp.fd.tok == TOK.reserved)
+            s = "__lambda";
+        else if (exp.fd.tok == TOK.delegate_)
+            s = "__dgliteral";
+        else
+            s = "__funcliteral";
+
+        DsymbolTable symtab;
+        if (FuncDeclaration func = sc.parent.isFuncDeclaration())
+        {
+            if (func.localsymtab is null)
             {
-                if (func.localsymtab is null)
-                {
-                    // Inside template constraint, symtab is not set yet.
-                    // Initialize it lazily.
-                    func.localsymtab = new DsymbolTable();
-                }
-                symtab = func.localsymtab;
+                // Inside template constraint, symtab is not set yet.
+                // Initialize it lazily.
+                func.localsymtab = new DsymbolTable();
             }
-            else
+            symtab = func.localsymtab;
+        }
+        else
+        {
+            ScopeDsymbol sds = sc.parent.isScopeDsymbol();
+            if (!sds.symtab)
             {
-                ScopeDsymbol sds = sc.parent.isScopeDsymbol();
-                if (!sds.symtab)
-                {
-                    // Inside template constraint, symtab may not be set yet.
-                    // Initialize it lazily.
-                    assert(sds.isTemplateInstance());
-                    sds.symtab = new DsymbolTable();
-                }
-                symtab = sds.symtab;
+                // Inside template constraint, symtab may not be set yet.
+                // Initialize it lazily.
+                assert(sds.isTemplateInstance());
+                sds.symtab = new DsymbolTable();
             }
-            assert(symtab);
-            Identifier id = Identifier.generateId(s, symtab.length() + 1);
-            exp.fd.ident = id;
-            if (exp.td)
-                exp.td.ident = id;
-            symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd);
+            symtab = sds.symtab;
         }
+        assert(symtab);
+        Identifier id = Identifier.generateId(s, symtab.length() + 1);
+        exp.fd.ident = id;
+        if (exp.td)
+            exp.td.ident = id;
+        symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd);
     }
 
     override void visit(FuncExp exp)
@@ -5709,70 +5770,69 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
      */
     Expression callExpSemantic(FuncExp exp, Scope* sc, Expressions* arguments)
     {
-        if ((!exp.type || exp.type == Type.tvoid) && exp.td && arguments && arguments.length)
+        if ((exp.type && exp.type != Type.tvoid) || !exp.td ||! arguments || !arguments.length)
+            return exp.expressionSemantic(sc);
+
+        for (size_t k = 0; k < arguments.length; k++)
         {
-            for (size_t k = 0; k < arguments.length; k++)
-            {
-                Expression checkarg = (*arguments)[k];
-                if (checkarg.op == EXP.error)
-                    return checkarg;
-            }
+            Expression checkarg = (*arguments)[k];
+            if (checkarg.op == EXP.error)
+                return checkarg;
+        }
 
-            genIdent(exp, sc);
+        genIdent(exp, sc);
 
-            assert(exp.td.parameters && exp.td.parameters.length);
-            exp.td.dsymbolSemantic(sc);
+        assert(exp.td.parameters && exp.td.parameters.length);
+        exp.td.dsymbolSemantic(sc);
 
-            TypeFunction tfl = cast(TypeFunction)exp.fd.type;
-            size_t dim = tfl.parameterList.length;
-            if (arguments.length < dim)
-            {
-                // Default arguments are always typed, so they don't need inference.
-                Parameter p = tfl.parameterList[arguments.length];
-                if (p.defaultArg)
-                    dim = arguments.length;
-            }
+        TypeFunction tfl = cast(TypeFunction)exp.fd.type;
+        size_t dim = tfl.parameterList.length;
+        if (arguments.length < dim)
+        {
+            // Default arguments are always typed, so they don't need inference.
+            Parameter p = tfl.parameterList[arguments.length];
+            if (p.defaultArg)
+                dim = arguments.length;
+        }
 
-            if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) ||
-                arguments.length < dim)
-            {
-                OutBuffer buf;
-                foreach (idx, ref arg; *arguments)
-                    buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars());
-                error(exp.loc, "function literal `%s%s` is not callable using argument types `(%s)`",
-                          exp.fd.toChars(), parametersTypeToChars(tfl.parameterList),
-                          buf.peekChars());
-                errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d",
-                                      arguments.length < dim ? "few".ptr : "many".ptr,
-                                      cast(int)dim, cast(int)arguments.length);
-                return ErrorExp.get();
-            }
+        if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) ||
+            arguments.length < dim)
+        {
+            OutBuffer buf;
+            foreach (idx, ref arg; *arguments)
+                buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars());
+            error(exp.loc, "function literal `%s%s` is not callable using argument types `(%s)`",
+                      exp.fd.toChars(), parametersTypeToChars(tfl.parameterList),
+                      buf.peekChars());
+            errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d",
+                                  arguments.length < dim ? "few".ptr : "many".ptr,
+                                  cast(int)dim, cast(int)arguments.length);
+            return ErrorExp.get();
+        }
 
-            auto tiargs = new Objects();
-            tiargs.reserve(exp.td.parameters.length);
+        auto tiargs = new Objects();
+        tiargs.reserve(exp.td.parameters.length);
 
-            for (size_t i = 0; i < exp.td.parameters.length; i++)
+        for (size_t i = 0; i < exp.td.parameters.length; i++)
+        {
+            TemplateParameter tp = (*exp.td.parameters)[i];
+            assert(dim <= tfl.parameterList.length);
+            foreach (u, p; tfl.parameterList)
             {
-                TemplateParameter tp = (*exp.td.parameters)[i];
-                assert(dim <= tfl.parameterList.length);
-                foreach (u, p; tfl.parameterList)
-                {
-                    if (u == dim)
-                        break;
+                if (u == dim)
+                    break;
 
-                    if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
-                    {
-                        Expression e = (*arguments)[u];
-                        tiargs.push(e.type);
-                        break;
-                    }
+                if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
+                {
+                    Expression e = (*arguments)[u];
+                    tiargs.push(e.type);
+                    break;
                 }
             }
-
-            auto ti = new TemplateInstance(exp.loc, exp.td, tiargs);
-            return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc);
         }
-        return exp.expressionSemantic(sc);
+
+        auto ti = new TemplateInstance(exp.loc, exp.td, tiargs);
+        return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc);
     }
 
     override void visit(CallExp exp)
@@ -6866,15 +6926,10 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         while (1)
         {
             AttribDeclaration ad = s.isAttribDeclaration();
-            if (ad)
-            {
-                if (ad.decl && ad.decl.length == 1)
-                {
-                    s = (*ad.decl)[0];
-                    continue;
-                }
-            }
-            break;
+            if (!ad)
+                break;
+            if (ad.decl && ad.decl.length == 1)
+                s = (*ad.decl)[0];
         }
 
         //printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc);
@@ -6970,27 +7025,26 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     // Disallow shadowing
                     for (Scope* scx = sc.enclosing; scx && (scx.func == sc.func || (fes_enclosing_func && scx.func == fes_enclosing_func)); scx = scx.enclosing)
                     {
-                        Dsymbol s2;
-                        if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2)
+                        if (!scx.scopesym || !scx.scopesym.symtab)
+                            continue;
+                        Dsymbol s2 = scx.scopesym.symtab.lookup(s.ident);
+                        if (s2 is null || s == s2)
+                            continue;
+                        // allow STC.local symbols to be shadowed
+                        // TODO: not really an optimal design
+                        auto decl = s2.isDeclaration();
+                        if (decl && (decl.storage_class & STC.local))
+                            continue;
+                        if (sc.func.fes)
                         {
-                            // allow STC.local symbols to be shadowed
-                            // TODO: not really an optimal design
-                            auto decl = s2.isDeclaration();
-                            if (!decl || !(decl.storage_class & STC.local))
-                            {
-                                if (sc.func.fes)
-                                {
-                                    deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
-                                    deprecationSupplemental(s2.loc, "declared here");
-
-                                }
-                                else
-                                {
-                                    error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
-                                    errorSupplemental(s2.loc, "declared here");
-                                    return setError();
-                                }
-                            }
+                            deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
+                            deprecationSupplemental(s2.loc, "declared here");
+                        }
+                        else
+                        {
+                            error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars());
+                            errorSupplemental(s2.loc, "declared here");
+                            return setError();
                         }
                     }
                 }
@@ -7058,56 +7112,55 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
         ta.checkComplexTransition(exp.loc, sc);
 
-        Expression e;
         auto tb = ta.toBasetype();
         if (ea && tb.ty == Tclass)
         {
             if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp)
             {
                 error(exp.loc, "runtime type information is not supported for `extern(C++)` classes");
-                e = ErrorExp.get();
+                return setError();
             }
             else if (!Type.typeinfoclass)
             {
                 error(exp.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
-                e = ErrorExp.get();
+                return setError();
             }
             else
             {
                 /* Get the dynamic type, which is .classinfo
                 */
                 ea = ea.expressionSemantic(sc);
-                e = new TypeidExp(ea.loc, ea);
+                Expression e = new TypeidExp(ea.loc, ea);
                 e.type = Type.typeinfoclass.type;
+                result = e;
+                return;
             }
         }
         else if (ta.ty == Terror)
         {
-            e = ErrorExp.get();
+            return setError();
         }
-        else
-        {
-            // Handle this in the glue layer
-            e = new TypeidExp(exp.loc, ta);
 
-            bool genObjCode = true;
+        // Handle this in the glue layer
+        Expression e = new TypeidExp(exp.loc, ta);
 
-            // https://issues.dlang.org/show_bug.cgi?id=23650
-            // We generate object code for typeinfo, required
-            // by typeid, only if in non-speculative context
-            if (sc.traitsCompiles)
-            {
-                genObjCode = false;
-            }
+        bool genObjCode = true;
+
+        // https://issues.dlang.org/show_bug.cgi?id=23650
+        // We generate object code for typeinfo, required
+        // by typeid, only if in non-speculative context
+        if (sc.traitsCompiles)
+        {
+            genObjCode = false;
+        }
 
-            e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode);
-            semanticTypeInfo(sc, ta);
+        e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode);
+        semanticTypeInfo(sc, ta);
 
-            if (ea)
-            {
-                e = new CommaExp(exp.loc, ea, e); // execute ea
-                e = e.expressionSemantic(sc);
-            }
+        if (ea)
+        {
+            e = new CommaExp(exp.loc, ea, e); // execute ea
+            e = e.expressionSemantic(sc);
         }
         result = e;
     }
@@ -7441,41 +7494,37 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             MATCH m = deduceType(e.targ, sc, e.tspec, *e.parameters, dedtypes, null, 0, e.tok == TOK.equal);
 
             if (m == MATCH.nomatch || (m != MATCH.exact && e.tok == TOK.equal))
-            {
                 return no();
-            }
-            else
-            {
-                tded = cast(Type)dedtypes[0];
-                if (!tded)
-                    tded = e.targ;
-                Objects tiargs = Objects(1);
-                tiargs[0] = e.targ;
 
-                /* Declare trailing parameters
-                 */
-                for (size_t i = 1; i < e.parameters.length; i++)
-                {
-                    TemplateParameter tp = (*e.parameters)[i];
-                    Declaration s = null;
+            tded = cast(Type)dedtypes[0];
+            if (!tded)
+                tded = e.targ;
+            Objects tiargs = Objects(1);
+            tiargs[0] = e.targ;
 
-                    m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s);
-                    if (m == MATCH.nomatch)
-                        return no();
-                    s.dsymbolSemantic(sc);
-                    if (!sc.insert(s))
-                    {
-                        Dsymbol pscopesym;
-                        auto conflict = sc.search(Loc.initial, s.ident, pscopesym);
-                        error(e.loc, "declaration `%s` is already defined", s.toPrettyChars());
-                        errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
-                                          conflict.kind(), conflict.toChars());
-                    }
+            /* Declare trailing parameters
+             */
+            for (size_t i = 1; i < e.parameters.length; i++)
+            {
+                TemplateParameter tp = (*e.parameters)[i];
+                Declaration s = null;
 
-                    unSpeculative(sc, s);
+                m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s);
+                if (m == MATCH.nomatch)
+                    return no();
+                s.dsymbolSemantic(sc);
+                if (!sc.insert(s))
+                {
+                    Dsymbol pscopesym;
+                    auto conflict = sc.search(Loc.initial, s.ident, pscopesym);
+                    error(e.loc, "declaration `%s` is already defined", s.toPrettyChars());
+                    errorSupplemental(conflict.loc, "`%s` `%s` is defined here",
+                                        conflict.kind(), conflict.toChars());
                 }
-                return yes();
+
+                unSpeculative(sc, s);
             }
+            return yes();
         }
         else if (e.id)
         {
@@ -9753,8 +9802,11 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (isAggregate(exp.e1.type))
+        if (auto ad = isAggregate(exp.e1.type))
+        {
             error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars());
+            errorSupplemental(ad.loc, "`%s` declared here", ad.toPrettyChars());
+        }
         else if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple)
             error(exp.loc, "static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars());
         else if (isIndexableNonAggregate(exp.e1.type))
@@ -12153,7 +12205,8 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
         }
 
-        if (tb1.ty == Tpointer && tb2.ty == Tpointer)
+        if (tb1.ty == Tpointer && tb2.ty == Tpointer ||
+	    tb1.ty == Tnull && tb2.ty == Tnull)
         {
             result = exp.incompatibleTypes();
             return;
@@ -12250,6 +12303,11 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         {
             err |= exp.e2.checkArithmetic(exp.op) || exp.e2.checkSharedAccess(sc);
         }
+        if (t1.ty == Tnull && t2.ty == Tnull)
+        {
+            exp.incompatibleTypes();
+            return setError();
+        }
         if (err)
             return setError();
 
diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d
index 9bd4522192f..9d59fc99104 100644
--- a/gcc/d/dmd/func.d
+++ b/gcc/d/dmd/func.d
@@ -46,17 +46,14 @@  import dmd.init;
 import dmd.location;
 import dmd.mtype;
 import dmd.objc;
-import dmd.root.aav;
 import dmd.common.outbuffer;
 import dmd.rootobject;
 import dmd.root.string;
 import dmd.root.stringtable;
 import dmd.semantic2;
 import dmd.semantic3;
-import dmd.statement_rewrite_walker;
 import dmd.statement;
 import dmd.tokens;
-import dmd.typesem;
 import dmd.visitor;
 
 version (IN_GCC) {}
@@ -452,26 +449,6 @@  extern (C++) class FuncDeclaration : Declaration
         if (!fd)
             return false;
 
-        version (none)
-        {
-            /* Disable this check because:
-             *  const void foo();
-             * semantic() isn't run yet on foo(), so the const hasn't been
-             * applied yet.
-             */
-            if (type)
-            {
-                printf("type = %s\n", type.toChars());
-                printf("fd.type = %s\n", fd.type.toChars());
-            }
-            // fd.type can be NULL for overloaded constructors
-            if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration())
-            {
-                //printf("\tfalse: conflict %s\n", kind());
-                return false;
-            }
-        }
-
         if (overnext)
         {
             td = overnext.isTemplateDeclaration();
@@ -697,29 +674,6 @@  extern (C++) class FuncDeclaration : Declaration
         return false;
     }
 
-    /*****************************************
-     * Initialize for inferring the attributes of this function.
-     */
-    final void initInferAttributes()
-    {
-        //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars());
-        TypeFunction tf = type.toTypeFunction();
-        if (tf.purity == PURE.impure) // purity not specified
-            purityInprocess = true;
-
-        if (tf.trust == TRUST.default_)
-            safetyInprocess = true;
-
-        if (!tf.isNothrow)
-            nothrowInprocess = true;
-
-        if (!tf.isNogc)
-            nogcInprocess = true;
-
-        // Initialize for inferring STC.scope_
-        scopeInprocess = true;
-    }
-
     extern (D) final uint saveFlags()
     {
         return bitFields;
@@ -798,69 +752,6 @@  extern (C++) class FuncDeclaration : Declaration
         return setUnsafe(false, f.loc, null, f, null);
     }
 
-    final bool isNogc()
-    {
-        //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
-        if (nogcInprocess)
-            setGC(loc, null);
-        return type.toTypeFunction().isNogc;
-    }
-
-    extern (D) final bool isNogcBypassingInference()
-    {
-        return !nogcInprocess && isNogc();
-    }
-
-    /**************************************
-     * The function is doing something that may allocate with the GC,
-     * so mark it as not nogc (not no-how).
-     *
-     * Params:
-     *     loc = location of impure action
-     *     fmt = format string for error message. Must include "%s `%s`" for the function kind and name.
-     *     arg0 = (optional) argument to format string
-     *
-     * Returns:
-     *      true if function is marked as @nogc, meaning a user error occurred
-     */
-    extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null)
-    {
-        //printf("setGC() %s\n", toChars());
-        if (nogcInprocess && semanticRun < PASS.semantic3 && _scope)
-        {
-            this.semantic2(_scope);
-            this.semantic3(_scope);
-        }
-
-        if (nogcInprocess)
-        {
-            nogcInprocess = false;
-            if (fmt)
-                nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC
-            else if (arg0)
-                nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function
-
-            type.toTypeFunction().isNogc = false;
-            if (fes)
-                fes.func.setGC(Loc.init, null, null);
-        }
-        else if (isNogc())
-            return true;
-        return false;
-    }
-
-    /**************************************
-     * The function calls non-`@nogc` function f, mark it as not nogc.
-     * Params:
-     *     f = function being called
-     * Returns:
-     *      true if function is marked as @nogc, meaning a user error occurred
-     */
-    extern (D) final bool setGCCall(FuncDeclaration f)
-    {
-        return setGC(loc, null, f);
-    }
-
     /**************************************
      * The function is doing something that may throw an exception, register that in case nothrow is being inferred
      *
@@ -888,18 +779,6 @@  extern (C++) class FuncDeclaration : Declaration
         return setThrow(loc, null, f);
     }
 
-    extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn)
-    {
-        if (!global.params.v.gc)
-            return;
-
-        Module m = getModule();
-        if (m && m.isRoot() && !inUnittest())
-        {
-            message(loc, "vgc: %s", warn);
-        }
-    }
-
     /****************************************
      * Determine if function needs a static frame pointer.
      * Returns:
@@ -1142,75 +1021,6 @@  extern (C++) class FuncDeclaration : Declaration
         return true;
     }
 
-    /***********************************************
-     * Check that the function contains any closure.
-     * If it's @nogc, report suitable errors.
-     * This is mostly consistent with FuncDeclaration::needsClosure().
-     *
-     * Returns:
-     *      true if any errors occur.
-     */
-    extern (C++) final bool checkClosure()
-    {
-        //printf("checkClosure() %s\n", toPrettyChars());
-        if (!needsClosure())
-            return false;
-
-        if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this))
-        {
-            .error(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars());
-            if (global.gag)     // need not report supplemental errors
-                return true;
-        }
-        else if (!global.params.useGC)
-        {
-            .error(loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars());
-            if (global.gag)     // need not report supplemental errors
-                return true;
-        }
-        else
-        {
-            printGCUsage(loc, "using closure causes GC allocation");
-            return false;
-        }
-
-        FuncDeclarations a;
-        foreach (v; closureVars)
-        {
-            foreach (f; v.nestedrefs)
-            {
-                assert(f !is this);
-
-            LcheckAncestorsOfANestedRef:
-                for (Dsymbol s = f; s && s !is this; s = s.toParentP(this))
-                {
-                    auto fx = s.isFuncDeclaration();
-                    if (!fx)
-                        continue;
-                    if (fx.isThis() ||
-                        fx.tookAddressOf ||
-                        checkEscapingSiblings(fx, this))
-                    {
-                        foreach (f2; a)
-                        {
-                            if (f2 == f)
-                                break LcheckAncestorsOfANestedRef;
-                        }
-                        a.push(f);
-                        .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
-                            f.kind, f.toPrettyChars(), v.toChars());
-                        if (v.ident != Id.This)
-                            .errorSupplemental(v.loc, "`%s` declared here", v.toChars());
-
-                        break LcheckAncestorsOfANestedRef;
-                    }
-                }
-            }
-        }
-
-        return true;
-    }
-
     /***********************************************
      * Determine if function's variables are referenced by a function
      * nested within it.
@@ -1539,7 +1349,7 @@  private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc)
  * Returns:
  *      true if any closures were needed
  */
-private bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
+bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null)
 {
     static struct PrevSibling
     {
diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d
index 2cd89f75057..2ffd3ad5abb 100644
--- a/gcc/d/dmd/funcsem.d
+++ b/gcc/d/dmd/funcsem.d
@@ -56,7 +56,6 @@  import dmd.root.string;
 import dmd.root.stringtable;
 import dmd.semantic2;
 import dmd.semantic3;
-import dmd.statement_rewrite_walker;
 import dmd.statement;
 import dmd.statementsem;
 import dmd.target;
@@ -64,6 +63,7 @@  import dmd.templatesem;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
+import dmd.visitor.statement_rewrite_walker;
 
 version (IN_GCC) {}
 else version (IN_LLVM) {}
@@ -1123,6 +1123,28 @@  Ldone:
     }
 }
 
+/*****************************************
+ * Initialize for inferring the attributes of this function.
+ */
+private void initInferAttributes(FuncDeclaration fd)
+{
+    //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars());
+    TypeFunction tf = fd.type.toTypeFunction();
+    if (tf.purity == PURE.impure) // purity not specified
+        fd.purityInprocess = true;
+
+    if (tf.trust == TRUST.default_)
+        fd.safetyInprocess = true;
+
+    if (!tf.isNothrow)
+        fd.nothrowInprocess = true;
+
+    if (!tf.isNogc)
+        fd.nogcInprocess = true;
+
+    // Initialize for inferring STC.scope_
+    fd.scopeInprocess = true;
+}
 
 /****************************************************
  * Resolve forward reference of function signature -
@@ -1621,6 +1643,8 @@  FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
     {
         .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`",
                od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars());
+        if (!global.gag || global.params.v.showGaggedErrors)
+            printCandidates(loc, od, sc.isDeprecated());
         return null;
     }
 
@@ -1745,7 +1769,6 @@  FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
  *      showDeprecated = If `false`, `deprecated` function won't be shown
  */
 private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated)
-if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
 {
     // max num of overloads to print (-v or -verror-supplements overrides this).
     const uint DisplayLimit = global.params.v.errorSupplementCount();
@@ -1799,13 +1822,16 @@  if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration))
             if (!print)
                 return true;
 
+            // if td.onemember is a function, toCharsMaybeConstraints can print it
+            // without us recursing, otherwise we have to handle it.
             // td.onemember may not have overloads set
             // (see fail_compilation/onemember_overloads.d)
             // assume if more than one member it is overloaded internally
-            bool recurse = td.onemember && td.members.length > 1;
+            bool recurse = td.onemember && (!td.onemember.isFuncDeclaration ||
+                td.members.length > 1);
             OutBuffer buf;
             HdrGenState hgs;
-            hgs.skipConstraints = true;
+            hgs.skipConstraints = true; // failing constraint should get printed below
             hgs.showOneMember = !recurse;
             toCharsMaybeConstraints(td, buf, hgs);
             const tmsg = buf.peekChars();
@@ -2731,7 +2757,6 @@  Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Express
  */
 void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret)
 {
-    import dmd.statement_rewrite_walker;
     extern (C++) final class RetWalker : StatementRewriteWalker
     {
         alias visit = typeof(super).visit;
diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d
index 2bdc1856e18..ea884455161 100644
--- a/gcc/d/dmd/globals.d
+++ b/gcc/d/dmd/globals.d
@@ -248,6 +248,8 @@  extern (C++) struct Param
     const(char)[] exefile;
     const(char)[] mapfile;
 
+    bool fullyQualifiedObjectFiles; // prepend module names to object files to prevent name conflicts with -od
+
     // Time tracing
     bool timeTrace = false; /// Whether profiling of compile time is enabled
     uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report
diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h
index e2aa82a15ae..45efab09475 100644
--- a/gcc/d/dmd/globals.h
+++ b/gcc/d/dmd/globals.h
@@ -250,6 +250,7 @@  struct Param
     DString resfile;
     DString exefile;
     DString mapfile;
+    bool fullyQualifiedObjectFiles;
     bool timeTrace;
     uint32_t timeTraceGranularityUs;
     const char* timeTraceFile;
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index 198b49e35a3..e3f1dc283f7 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -2363,12 +2363,12 @@  private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
         // to themselves, need to avoid infinite recursion:
         // struct S { this(int){ this.s = &this; } S* s; }
         // const foo = new S(0);
-        if (e.stageflags & stageToCBuffer)
+        if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer)
             buf.writestring("<recursion>");
         else
         {
             const old = e.stageflags;
-            e.stageflags |= stageToCBuffer;
+            e.stageflags |= StructLiteralExp.StageFlags.toCBuffer;
             argsToBuffer(e.elements, buf, hgs);
             e.stageflags = old;
         }
diff --git a/gcc/d/dmd/imphint.d b/gcc/d/dmd/imphint.d
index ea2f13d9179..9f9993fe668 100644
--- a/gcc/d/dmd/imphint.d
+++ b/gcc/d/dmd/imphint.d
@@ -80,6 +80,8 @@  shared static this()
         "writeln": "std.stdio",
         "__va_argsave_t": "core.stdc.stdarg",
         "__va_list_tag": "core.stdc.stdarg",
+        "InterpolationHeader": "core.interpolation",
+        "InterpolationFooter": "core.interpolation",
     ];
 }
 
diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d
index 7d9e3e6033d..934ccc6e8ad 100644
--- a/gcc/d/dmd/init.d
+++ b/gcc/d/dmd/init.d
@@ -38,6 +38,7 @@  extern (C++) class Initializer : ASTNode
 {
     Loc loc;
     InitKind kind;
+    bool semanticDone = false; /// initializerSemantic has been run on this
 
     override DYNCAST dyncast() const
     {
@@ -176,7 +177,6 @@  extern (C++) final class ArrayInitializer : Initializer
     Initializers value;     // of Initializer *'s
     uint dim;               // length of array being initialized
     Type type;              // type that array will be used to initialize
-    bool sem;               // true if semantic() is run
     bool isCarray;          // C array semantics
 
     extern (D) this(const ref Loc loc)
@@ -256,7 +256,6 @@  extern (C++) final class CInitializer : Initializer
 {
     DesigInits initializerList; /// initializer-list
     Type type;              /// type that array will be used to initialize
-    bool sem;               /// true if semantic() is run
 
     extern (D) this(const ref Loc loc)
     {
diff --git a/gcc/d/dmd/init.h b/gcc/d/dmd/init.h
index 2ee69f622b3..951168be32f 100644
--- a/gcc/d/dmd/init.h
+++ b/gcc/d/dmd/init.h
@@ -33,6 +33,7 @@  class Initializer : public ASTNode
 public:
     Loc loc;
     unsigned char kind;
+    d_bool semanticDone;
 
     DYNCAST dyncast() const override { return DYNCAST_INITIALIZER; }
 
@@ -85,7 +86,6 @@  public:
     Initializers value; // of Initializer *'s
     unsigned dim;       // length of array being initialized
     Type *type;         // type that array will be used to initialize
-    d_bool sem;           // true if semantic() is run
     d_bool isCarray;      // C array semantics
 
     bool isAssociativeArray() const;
@@ -119,7 +119,6 @@  class CInitializer final : public Initializer
 public:
     DesigInits initializerList;
     Type *type;         // type that array will be used to initialize
-    d_bool sem;           // true if semantic() is run
 
     void accept(Visitor *v) override { v->visit(this); }
 };
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index ab25689d41e..a72fd90ec98 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -106,6 +106,9 @@  Expression toAssocArrayLiteral(ArrayInitializer ai)
 Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedInterpret needInterpret)
 {
     //printf("initializerSemantic() tx: %p %s\n", tx, tx.toChars());
+    if (init.semanticDone)
+        return init;
+
     Type t = tx;
 
     static Initializer err()
@@ -203,11 +206,6 @@  Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
         const(uint) amax = 0x80000000;
         bool errors = false;
         //printf("ArrayInitializer::semantic(%s), ai: %s\n", t.toChars(), toChars(i));
-        if (i.sem) // if semantic() already run
-        {
-            return i;
-        }
-        i.sem = true;
         t = t.toBasetype();
         switch (t.ty)
         {
@@ -692,51 +690,49 @@  Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
                 auto di = ci.initializerList[index];
                 if (di.designatorList && fieldi != 0)
                     break;          // back to top level
-                else
+
+                VarDeclaration field;
+                while (1)   // skip field if it overlaps with previously seen fields
                 {
-                    VarDeclaration field;
-                    while (1)   // skip field if it overlaps with previously seen fields
-                    {
-                        field = sd.fields[fieldi];
-                        ++fieldi;
-                        if (!overlaps(field, sd.fields[], si))
-                            break;
-                        if (fieldi == nfields)
-                            break;
-                    }
-                    auto tn = field.type.toBasetype();
-                    auto tnsa = tn.isTypeSArray();
-                    auto tns = tn.isTypeStruct();
-                    auto ix = di.initializer;
-                    if (tnsa && ix.isExpInitializer())
-                    {
-                        ExpInitializer ei = ix.isExpInitializer();
-                        if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral())
-                        {
-                            si.addInit(field.ident, ei);
-                            ++index;
-                        }
-                        else
-                            si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template
-                    }
-                    else if (tns && ix.isExpInitializer())
+                    field = sd.fields[fieldi];
+                    ++fieldi;
+                    if (!overlaps(field, sd.fields[], si))
+                        break;
+                    if (fieldi == nfields)
+                        break;
+                }
+                auto tn = field.type.toBasetype();
+                auto tnsa = tn.isTypeSArray();
+                auto tns = tn.isTypeStruct();
+                auto ix = di.initializer;
+                if (tnsa && ix.isExpInitializer())
+                {
+                    ExpInitializer ei = ix.isExpInitializer();
+                    if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral())
                     {
-                        /* Disambiguate between an exp representing the entire
-                         * struct, and an exp representing the first field of the struct
-                         */
-                        if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
-                        {
-                            si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret));
-                            ++index;
-                        }
-                        else                                // field initializers for struct
-                            si.addInit(field.ident, subStruct(tns, index)); // the first field
+                        si.addInit(field.ident, ei);
+                        ++index;
                     }
                     else
+                        si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template
+                }
+                else if (tns && ix.isExpInitializer())
+                {
+                    /* Disambiguate between an exp representing the entire
+                     * struct, and an exp representing the first field of the struct
+                     */
+                    if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
                     {
-                        si.addInit(field.ident, ix);
+                        si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret));
                         ++index;
                     }
+                    else                                // field initializers for struct
+                        si.addInit(field.ident, subStruct(tns, index)); // the first field
+                }
+                else
+                {
+                    si.addInit(field.ident, ix);
+                    ++index;
                 }
             }
             //printf("subStruct() returns ai: %s, index: %d\n", si.toChars(), cast(int)index);
@@ -837,112 +833,110 @@  Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
                     error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars());
                     return err();
                 }
-                else
-                {
-                    if (fieldi == nfields)
-                        break;
 
-                    auto ix = di.initializer;
+                if (fieldi == nfields)
+                    break;
 
-                    /* If a C initializer is wrapped in a C initializer, with no designators,
-                     * peel off the outer one
-                     */
-                    if (ix.isCInitializer())
+                auto ix = di.initializer;
+
+                /* If a C initializer is wrapped in a C initializer, with no designators,
+                 * peel off the outer one
+                 */
+                if (ix.isCInitializer())
+                {
+                    CInitializer cix = ix.isCInitializer();
+                    if (cix.initializerList.length == 1)
                     {
-                        CInitializer cix = ix.isCInitializer();
-                        if (cix.initializerList.length == 1)
+                        DesigInit dix = cix.initializerList[0];
+                        if (!dix.designatorList)
                         {
-                            DesigInit dix = cix.initializerList[0];
-                            if (!dix.designatorList)
-                            {
-                                Initializer inix = dix.initializer;
-                                if (inix.isCInitializer())
-                                    ix = inix;
-                            }
+                            Initializer inix = dix.initializer;
+                            if (inix.isCInitializer())
+                                ix = inix;
                         }
                     }
+                }
 
-                    if (auto cix = ix.isCInitializer())
+                if (auto cix = ix.isCInitializer())
+                {
+                    /* ImportC loses the structure from anonymous structs, but this is retained
+                     * by the initializer syntax. if a CInitializer has a Designator, it is probably
+                     * a nested anonymous struct
+                     */
+                    int found;
+                    foreach (dix; cix.initializerList)
                     {
-                        /* ImportC loses the structure from anonymous structs, but this is retained
-                         * by the initializer syntax. if a CInitializer has a Designator, it is probably
-                         * a nested anonymous struct
-                         */
-                        int found;
-                        foreach (dix; cix.initializerList)
+                        Designators* dlistx = dix.designatorList;
+                        if (!dlistx)
+                            continue;
+                        if ((*dlistx).length == 1 && (*dlistx)[0].ident)
                         {
-                            Designators* dlistx = dix.designatorList;
-                            if (!dlistx)
-                                continue;
-                            if ((*dlistx).length == 1 && (*dlistx)[0].ident)
+                            auto id = (*dlistx)[0].ident;
+                            foreach (k, f; sd.fields[])         // linear search for now
                             {
-                                auto id = (*dlistx)[0].ident;
-                                foreach (k, f; sd.fields[])         // linear search for now
+                                if (f.ident == id)
                                 {
-                                    if (f.ident == id)
-                                    {
-                                        fieldi = k;
-                                        si.addInit(id, dix.initializer);
-                                        ++fieldi;
-                                        ++index;
-                                        ++found;
-                                        break;
-                                    }
+                                    fieldi = k;
+                                    si.addInit(id, dix.initializer);
+                                    ++fieldi;
+                                    ++index;
+                                    ++found;
+                                    break;
                                 }
                             }
-                            else {
-                                error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci));
-                            }
                         }
-
-                        if (found == cix.initializerList.length)
-                            continue Loop1;
+                        else {
+                            error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci));
+                        }
                     }
 
-                    VarDeclaration field;
-                    while (1)   // skip field if it overlaps with previously seen fields
-                    {
-                        field = sd.fields[fieldi];
-                        ++fieldi;
-                        if (!overlaps(field, sd.fields[], si))
-                            break;
-                        if (fieldi == nfields)
-                            break;
-                    }
+                    if (found == cix.initializerList.length)
+                        continue Loop1;
+                }
 
-                    auto tn = field.type.toBasetype();
-                    auto tnsa = tn.isTypeSArray();
-                    auto tns = tn.isTypeStruct();
+                VarDeclaration field;
+                while (1)   // skip field if it overlaps with previously seen fields
+                {
+                    field = sd.fields[fieldi];
+                    ++fieldi;
+                    if (!overlaps(field, sd.fields[], si))
+                        break;
+                    if (fieldi == nfields)
+                        break;
+                }
 
-                    if (tnsa && ix.isExpInitializer())
-                    {
-                        ExpInitializer ei = ix.isExpInitializer();
-                        if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral())
-                        {
-                            si.addInit(field.ident, ei);
-                            ++index;
-                        }
-                        else
-                            si.addInit(field.ident, subArray(tnsa, index));
-                    }
-                    else if (tns && ix.isExpInitializer())
+                auto tn = field.type.toBasetype();
+                auto tnsa = tn.isTypeSArray();
+                auto tns = tn.isTypeStruct();
+
+                if (tnsa && ix.isExpInitializer())
+                {
+                    ExpInitializer ei = ix.isExpInitializer();
+                    if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral())
                     {
-                        /* Disambiguate between an exp representing the entire
-                         * struct, and an exp representing the first field of the struct
-                         */
-                        if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
-                        {
-                            si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret));
-                            ++index;
-                        }
-                        else                                // field initializers for struct
-                            si.addInit(field.ident, subStruct(tns, index)); // the first field
+                        si.addInit(field.ident, ei);
+                        ++index;
                     }
                     else
+                        si.addInit(field.ident, subArray(tnsa, index));
+                }
+                else if (tns && ix.isExpInitializer())
+                {
+                    /* Disambiguate between an exp representing the entire
+                     * struct, and an exp representing the first field of the struct
+                     */
+                    if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct
                     {
-                        si.addInit(field.ident, di.initializer);
+                        si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret));
                         ++index;
                     }
+                    else                                // field initializers for struct
+                        si.addInit(field.ident, subStruct(tns, index)); // the first field
+                }
+                else
+                {
+                    si.addInit(field.ident, di.initializer);
+                    ++index;
                 }
             }
             return initializerSemantic(si, sc, t, needInterpret);
@@ -1059,6 +1053,7 @@  Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn
 
     mixin VisitInitializer!Initializer visit;
     auto result = visit.VisitInitializer(init);
+    result.semanticDone = true;
     return (result !is null) ? result : new ErrorInitializer();
 }
 
@@ -1099,64 +1094,50 @@  Initializer inferType(Initializer init, Scope* sc)
     {
         //printf("ArrayInitializer::inferType() %s\n", toChars());
         Expressions* keys = null;
-        Expressions* values;
-        if (init.isAssociativeArray())
+        Expressions* values = new Expressions(init.value.length);
+        Initializer no()
         {
+            if (keys)
+                error(init.loc, "not an associative array initializer");
+            else
+                error(init.loc, "cannot infer type from array initializer");
+            return new ErrorInitializer();
+        }
+        const bool isAssoc = init.isAssociativeArray();
+        if (isAssoc)
             keys = new Expressions(init.value.length);
-            values = new Expressions(init.value.length);
-            for (size_t i = 0; i < init.value.length; i++)
+        else
+            values.zero();
+
+        for (size_t i = 0; i < init.value.length; i++)
+        {
+            if (isAssoc)
             {
                 Expression e = init.index[i];
                 if (!e)
-                    goto Lno;
+                    return no();
                 (*keys)[i] = e;
-                Initializer iz = init.value[i];
-                if (!iz)
-                    goto Lno;
-                iz = iz.inferType(sc);
-                if (iz.isErrorInitializer())
-                {
-                    return iz;
-                }
-                (*values)[i] = iz.isExpInitializer().exp;
-                assert(!(*values)[i].isErrorExp());
             }
-            Expression e = new AssocArrayLiteralExp(init.loc, keys, values);
-            auto ei = new ExpInitializer(init.loc, e);
-            return ei.inferType(sc);
-        }
-        else
-        {
-            auto elements = new Expressions(init.value.length);
-            elements.zero();
-            for (size_t i = 0; i < init.value.length; i++)
-            {
+            else
                 assert(!init.index[i]); // already asserted by isAssociativeArray()
-                Initializer iz = init.value[i];
-                if (!iz)
-                    goto Lno;
-                iz = iz.inferType(sc);
-                if (iz.isErrorInitializer())
-                {
-                    return iz;
-                }
-                (*elements)[i] = iz.isExpInitializer().exp;
-                assert(!(*elements)[i].isErrorExp());
+            Initializer iz = init.value[i];
+            if (!iz)
+                return no();
+            iz = iz.inferType(sc);
+            if (iz.isErrorInitializer())
+            {
+                return iz;
             }
-            Expression e = new ArrayLiteralExp(init.loc, null, elements);
-            auto ei = new ExpInitializer(init.loc, e);
-            return ei.inferType(sc);
+            (*values)[i] = iz.isExpInitializer().exp;
+            assert(!(*values)[i].isErrorExp());
         }
-    Lno:
-        if (keys)
-        {
-            error(init.loc, "not an associative array initializer");
-        }
-        else
-        {
-            error(init.loc, "cannot infer type from array initializer");
-        }
-        return new ErrorInitializer();
+
+        Expression e;
+        e = isAssoc
+            ? new AssocArrayLiteralExp(init.loc, keys, values)
+            : new ArrayLiteralExp(init.loc, null, values);
+        auto ei = new ExpInitializer(init.loc, e);
+        return ei.inferType(sc);
     }
 
     Initializer visitExp(ExpInitializer init)
@@ -1486,10 +1467,10 @@  private bool hasNonConstPointers(Expression e)
         }
         if (auto se = ae.e1.isStructLiteralExp())
         {
-            if (!(se.stageflags & stageSearchPointers))
+            if (!(se.stageflags & StructLiteralExp.StageFlags.searchPointers))
             {
                 const old = se.stageflags;
-                se.stageflags |= stageSearchPointers;
+                se.stageflags |= StructLiteralExp.StageFlags.searchPointers;
                 bool ret = checkArray(se.elements);
                 se.stageflags = old;
                 return ret;
diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d
index a1db8d52548..0ef2dbd8e0e 100644
--- a/gcc/d/dmd/lambdacomp.d
+++ b/gcc/d/dmd/lambdacomp.d
@@ -240,39 +240,39 @@  public:
         auto id = exp.ident.toChars();
 
         // If it's not an argument
-        if (!checkArgument(id))
+        if (checkArgument(id))
+            return;
+
+        // we must check what the identifier expression is.
+        Dsymbol scopesym;
+        Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
+
+        // If it's an unknown symbol, consider the function incomparable
+        if (!s)
         {
-            // we must check what the identifier expression is.
-            Dsymbol scopesym;
-            Dsymbol s = sc.search(exp.loc, exp.ident, scopesym);
-            if (s)
-            {
-                auto v = s.isVarDeclaration();
-                // If it's a VarDeclaration, it must be a manifest constant
-                if (v && (v.storage_class & STC.manifest))
-                {
-                    v.getConstInitializer.accept(this);
-                }
-                else if (auto em = s.isEnumDeclaration())
-                {
-                    d = em;
-                    et = ExpType.EnumDecl;
-                }
-                else if (auto fd = s.isFuncDeclaration())
-                {
-                    writeMangledName(fd);
-                }
-                // For anything else, the function is deemed uncomparable
-                else
-                {
-                    buf.setsize(0);
-                }
-            }
-            // If it's an unknown symbol, consider the function incomparable
-            else
-            {
-                buf.setsize(0);
-            }
+            buf.setsize(0);
+            return;
+        }
+
+        auto v = s.isVarDeclaration();
+        // If it's a VarDeclaration, it must be a manifest constant
+        if (v && (v.storage_class & STC.manifest))
+        {
+            v.getConstInitializer.accept(this);
+        }
+        else if (auto em = s.isEnumDeclaration())
+        {
+            d = em;
+            et = ExpType.EnumDecl;
+        }
+        else if (auto fd = s.isFuncDeclaration())
+        {
+            writeMangledName(fd);
+        }
+        // For anything else, the function is deemed uncomparable
+        else
+        {
+            buf.setsize(0);
         }
     }
 
@@ -450,21 +450,22 @@  public:
             printf("StructLiteralExp: %s\n", e.toChars);
 
         auto ty = cast(TypeStruct)e.stype;
-        if (ty)
+        if (!ty)
         {
-            writeMangledName(ty.sym);
-            auto dim = e.elements.length;
-            foreach (i; 0..dim)
-            {
-                auto elem = (*e.elements)[i];
-                if (elem)
-                    elem.accept(this);
-                else
-                    buf.writestring("null_");
-            }
-        }
-        else
             buf.setsize(0);
+            return;
+        }
+
+        writeMangledName(ty.sym);
+        auto dim = e.elements.length;
+        foreach (i; 0..dim)
+        {
+            auto elem = (*e.elements)[i];
+            if (elem)
+                elem.accept(this);
+            else
+                buf.writestring("null_");
+        }
     }
 
     override void visit(ArrayLiteralExp) { buf.setsize(0); }
diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d
index 0676d56f555..cedc4a43c8f 100644
--- a/gcc/d/dmd/nogc.d
+++ b/gcc/d/dmd/nogc.d
@@ -18,17 +18,23 @@  import core.stdc.stdio;
 import dmd.aggregate;
 import dmd.astenums;
 import dmd.declaration;
+import dmd.dmodule;
 import dmd.dscope;
 import dmd.dtemplate : isDsymbol;
+import dmd.dsymbol : PASS;
 import dmd.errors;
 import dmd.expression;
 import dmd.func;
 import dmd.globals;
 import dmd.init;
+import dmd.location;
 import dmd.mtype;
-import dmd.postordervisitor;
+import dmd.rootobject : RootObject, DYNCAST;
+import dmd.semantic2;
+import dmd.semantic3;
 import dmd.tokens;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 /**************************************
  * Look for GC-allocations
@@ -234,6 +240,18 @@  Expression checkGC(Scope* sc, Expression e)
     return e;
 }
 
+extern (D) void printGCUsage(FuncDeclaration fd, const ref Loc loc, const(char)* warn)
+{
+    if (!global.params.v.gc)
+        return;
+
+    Module m = fd.getModule();
+    if (m && m.isRoot() && !fd.inUnittest())
+    {
+        message(loc, "vgc: %s", warn);
+    }
+}
+
 /**
  * Removes `_d_HookTraceImpl` if found from `fd`.
  * This is needed to be able to find hooks that are called though the hook's `*Trace` wrapper.
@@ -244,7 +262,6 @@  private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
 {
     import dmd.id : Id;
     import dmd.dsymbol : Dsymbol;
-    import dmd.rootobject : RootObject, DYNCAST;
 
     if (fd.ident != Id._d_HookTraceImpl)
         return fd;
@@ -256,3 +273,68 @@  private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd)
     assert(s, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!");
     return s.isFuncDeclaration;
 }
+
+/**************************************
+ * The function is doing something that may allocate with the GC,
+ * so mark it as not nogc (not no-how).
+ *
+ * Params:
+ *     fd = function
+ *     loc = location of GC action
+ *     fmt = format string for error message. Must include "%s `%s`" for the function kind and name.
+ *     arg0 = (optional) argument to format string
+ *
+ * Returns:
+ *      true if function is marked as @nogc, meaning a user error occurred
+ */
+extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject arg0 = null)
+{
+    //printf("setGC() %s\n", toChars());
+    if (fd.nogcInprocess && fd.semanticRun < PASS.semantic3 && fd._scope)
+    {
+        fd.semantic2(fd._scope);
+        fd.semantic3(fd._scope);
+    }
+
+    if (fd.nogcInprocess)
+    {
+        fd.nogcInprocess = false;
+        if (fmt)
+            fd.nogcViolation = new AttributeViolation(loc, fmt, fd, arg0); // action that requires GC
+        else if (arg0)
+            fd.nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function
+
+        fd.type.toTypeFunction().isNogc = false;
+        if (fd.fes)
+            fd.fes.func.setGC(Loc.init, null, null);
+    }
+    else if (fd.isNogc())
+        return true;
+    return false;
+}
+
+/**************************************
+ * The function calls non-`@nogc` function f, mark it as not nogc.
+ * Params:
+ *     fd = function doin the call
+ *     f = function being called
+ * Returns:
+ *      true if function is marked as @nogc, meaning a user error occurred
+ */
+extern (D) bool setGCCall(FuncDeclaration fd, FuncDeclaration f)
+{
+    return fd.setGC(fd.loc, null, f);
+}
+
+ bool isNogc(FuncDeclaration fd)
+{
+    //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess));
+    if (fd.nogcInprocess)
+        fd.setGC(fd.loc, null);
+    return fd.type.toTypeFunction().isNogc;
+}
+
+extern (D) bool isNogcBypassingInference(FuncDeclaration fd)
+{
+    return !fd.nogcInprocess && fd.isNogc();
+}
diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d
index ed732332380..ee4b6525edc 100644
--- a/gcc/d/dmd/ob.d
+++ b/gcc/d/dmd/ob.d
@@ -32,7 +32,7 @@  import dmd.dtemplate;
 import dmd.errors;
 import dmd.escape;
 import dmd.expression;
-import dmd.foreachvar;
+
 import dmd.func;
 import dmd.globals;
 import dmd.hdrgen;
@@ -46,6 +46,7 @@  import dmd.stmtstate;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
+import dmd.visitor.foreachvar;
 
 import dmd.root.bitarray;
 import dmd.common.outbuffer;
diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d
index 22fbbc4fb83..c126b1d28c4 100644
--- a/gcc/d/dmd/opover.d
+++ b/gcc/d/dmd/opover.d
@@ -277,164 +277,17 @@  private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE
  */
 Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
 {
-        Expression visit(Expression e)
-        {
-            assert(0);
-        }
-
-        Expression visitUna(UnaExp e)
-        {
-            //printf("UnaExp::op_overload() (%s)\n", e.toChars());
-            Expression result;
-            if (auto ae = e.e1.isArrayExp())
-            {
-                ae.e1 = ae.e1.expressionSemantic(sc);
-                ae.e1 = resolveProperties(sc, ae.e1);
-                Expression ae1old = ae.e1;
-                const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
-                IntervalExp ie = null;
-                if (maybeSlice && ae.arguments.length)
-                {
-                    ie = (*ae.arguments)[0].isIntervalExp();
-                }
-                Type att = null; // first cyclic `alias this` type
-                while (true)
-                {
-                    if (ae.e1.op == EXP.error)
-                    {
-                        return ae.e1;
-                    }
-                    Expression e0 = null;
-                    Expression ae1save = ae.e1;
-                    ae.lengthVar = null;
-                    Type t1b = ae.e1.type.toBasetype();
-                    AggregateDeclaration ad = isAggregate(t1b);
-                    if (!ad)
-                        break;
-                    if (search_function(ad, Id.opIndexUnary))
-                    {
-                        // Deal with $
-                        result = resolveOpDollar(sc, ae, &e0);
-                        if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
-                            goto Lfallback;
-                        if (result.op == EXP.error)
-                            return result;
-                        /* Rewrite op(a[arguments]) as:
-                         *      a.opIndexUnary!(op)(arguments)
-                         */
-                        Expressions* a = ae.arguments.copy();
-                        Objects* tiargs = opToArg(sc, e.op);
-                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs);
-                        result = new CallExp(e.loc, result, a);
-                        if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
-                            result = result.trySemantic(sc);
-                        else
-                            result = result.expressionSemantic(sc);
-                        if (result)
-                        {
-                            return Expression.combine(e0, result);
-                        }
-                    }
-                Lfallback:
-                    if (maybeSlice && search_function(ad, Id.opSliceUnary))
-                    {
-                        // Deal with $
-                        result = resolveOpDollar(sc, ae, ie, &e0);
-                        if (result.op == EXP.error)
-                            return result;
-                        /* Rewrite op(a[i..j]) as:
-                         *      a.opSliceUnary!(op)(i, j)
-                         */
-                        auto a = new Expressions();
-                        if (ie)
-                        {
-                            a.push(ie.lwr);
-                            a.push(ie.upr);
-                        }
-                        Objects* tiargs = opToArg(sc, e.op);
-                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs);
-                        result = new CallExp(e.loc, result, a);
-                        result = result.expressionSemantic(sc);
-                        result = Expression.combine(e0, result);
-                        return result;
-                    }
-                    // Didn't find it. Forward to aliasthis
-                    if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
-                    {
-                        /* Rewrite op(a[arguments]) as:
-                         *      op(a.aliasthis[arguments])
-                         */
-                        ae.e1 = resolveAliasThis(sc, ae1save, true);
-                        if (ae.e1)
-                            continue;
-                    }
-                    break;
-                }
-                ae.e1 = ae1old; // recovery
-                ae.lengthVar = null;
-            }
-            e.e1 = e.e1.expressionSemantic(sc);
-            e.e1 = resolveProperties(sc, e.e1);
-            Type att = null; // first cyclic `alias this` type
-            while (1)
-            {
-                if (e.e1.op == EXP.error)
-                {
-                    return e.e1;
-                }
-
-                AggregateDeclaration ad = isAggregate(e.e1.type);
-                if (!ad)
-                    break;
-
-                Dsymbol fd = null;
-                /* Rewrite as:
-                 *      e1.opUnary!(op)()
-                 */
-                fd = search_function(ad, Id.opUnary);
-                if (fd)
-                {
-                    Objects* tiargs = opToArg(sc, e.op);
-                    result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
-                    result = new CallExp(e.loc, result);
-                    result = result.expressionSemantic(sc);
-                    return result;
-                }
-                // D1-style operator overloads, deprecated
-                if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus)
-                {
-                    auto id = opId(e);
-                    fd = search_function(ad, id);
-                    if (fd)
-                    {
-                        // @@@DEPRECATED_2.110@@@.
-                        // Deprecated in 2.088, made an error in 2.100
-                        error(e.loc, "`%s` is obsolete.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
-                        return ErrorExp.get();
-                    }
-                }
-                // Didn't find it. Forward to aliasthis
-                if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
-                {
-                    /* Rewrite op(e1) as:
-                     *      op(e1.aliasthis)
-                     */
-                    //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars());
-                    if (auto e1 = resolveAliasThis(sc, e.e1, true))
-                    {
-                        e.e1 = e1;
-                        continue;
-                    }
-                    break;
-                }
-                break;
-            }
-            return result;
-        }
+    Expression visit(Expression e)
+    {
+        assert(0);
+    }
 
-        Expression visitArray(ArrayExp ae)
+    Expression visitUna(UnaExp e)
+    {
+        //printf("UnaExp::op_overload() (%s)\n", e.toChars());
+        Expression result;
+        if (auto ae = e.e1.isArrayExp())
         {
-            //printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
             ae.e1 = ae.e1.expressionSemantic(sc);
             ae.e1 = resolveProperties(sc, ae.e1);
             Expression ae1old = ae.e1;
@@ -444,7 +297,6 @@  Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
             {
                 ie = (*ae.arguments)[0].isIntervalExp();
             }
-            Expression result;
             Type att = null; // first cyclic `alias this` type
             while (true)
             {
@@ -458,43 +310,23 @@  Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 Type t1b = ae.e1.type.toBasetype();
                 AggregateDeclaration ad = isAggregate(t1b);
                 if (!ad)
-                {
-                    // If the non-aggregate expression ae.e1 is indexable or sliceable,
-                    // convert it to the corresponding concrete expression.
-                    if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type)
-                    {
-                        // Convert to SliceExp
-                        if (maybeSlice)
-                        {
-                            result = new SliceExp(ae.loc, ae.e1, ie);
-                            result = result.expressionSemantic(sc);
-                            return result;
-                        }
-                        // Convert to IndexExp
-                        if (ae.arguments.length == 1)
-                        {
-                            result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
-                            result = result.expressionSemantic(sc);
-                            return result;
-                        }
-                    }
                     break;
-                }
-                if (search_function(ad, Id.index))
+                if (search_function(ad, Id.opIndexUnary))
                 {
                     // Deal with $
                     result = resolveOpDollar(sc, ae, &e0);
-                    if (!result) // a[i..j] might be: a.opSlice(i, j)
+                    if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j)
                         goto Lfallback;
                     if (result.op == EXP.error)
                         return result;
-                    /* Rewrite e1[arguments] as:
-                     *      e1.opIndex(arguments)
+                    /* Rewrite op(a[arguments]) as:
+                     *      a.opIndexUnary!(op)(arguments)
                      */
                     Expressions* a = ae.arguments.copy();
-                    result = new DotIdExp(ae.loc, ae.e1, Id.index);
-                    result = new CallExp(ae.loc, result, a);
-                    if (maybeSlice) // a[] might be: a.opSlice()
+                    Objects* tiargs = opToArg(sc, e.op);
+                    result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs);
+                    result = new CallExp(e.loc, result, a);
+                    if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)()
                         result = result.trySemantic(sc);
                     else
                         result = result.expressionSemantic(sc);
@@ -504,27 +336,14 @@  Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                     }
                 }
             Lfallback:
-                if (maybeSlice && ae.e1.op == EXP.type)
-                {
-                    result = new SliceExp(ae.loc, ae.e1, ie);
-                    result = result.expressionSemantic(sc);
-                    result = Expression.combine(e0, result);
-                    return result;
-                }
-                if (maybeSlice && search_function(ad, Id.slice))
+                if (maybeSlice && search_function(ad, Id.opSliceUnary))
                 {
                     // Deal with $
                     result = resolveOpDollar(sc, ae, ie, &e0);
-
                     if (result.op == EXP.error)
-                    {
-                        if (!e0 && !search_function(ad, Id.dollar)) {
-                            ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
-                        }
                         return result;
-                    }
-                    /* Rewrite a[i..j] as:
-                     *      a.opSlice(i, j)
+                    /* Rewrite op(a[i..j]) as:
+                     *      a.opSliceUnary!(op)(i, j)
                      */
                     auto a = new Expressions();
                     if (ie)
@@ -532,8 +351,9 @@  Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                         a.push(ie.lwr);
                         a.push(ie.upr);
                     }
-                    result = new DotIdExp(ae.loc, ae.e1, Id.slice);
-                    result = new CallExp(ae.loc, result, a);
+                    Objects* tiargs = opToArg(sc, e.op);
+                    result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs);
+                    result = new CallExp(e.loc, result, a);
                     result = result.expressionSemantic(sc);
                     result = Expression.combine(e0, result);
                     return result;
@@ -541,7 +361,6 @@  Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
                 // Didn't find it. Forward to aliasthis
                 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
                 {
-                    //printf("att arr e1 = %s\n", this.e1.type.toChars());
                     /* Rewrite op(a[arguments]) as:
                      *      op(a.aliasthis[arguments])
                      */
@@ -553,728 +372,909 @@  Expression op_overload(Expression e, Scope* sc, EXP* pop = null)
             }
             ae.e1 = ae1old; // recovery
             ae.lengthVar = null;
-            return result;
         }
-
-        /***********************************************
-         * This is mostly the same as UnaryExp::op_overload(), but has
-         * a different rewrite.
-         */
-        Expression visitCast(CastExp e, Type att = null)
+        e.e1 = e.e1.expressionSemantic(sc);
+        e.e1 = resolveProperties(sc, e.e1);
+        Type att = null; // first cyclic `alias this` type
+        while (1)
         {
-            //printf("CastExp::op_overload() (%s)\n", e.toChars());
-            Expression result;
-            if (AggregateDeclaration ad = isAggregate(e.e1.type))
+            if (e.e1.op == EXP.error)
             {
-                Dsymbol fd = null;
-                /* Rewrite as:
-                 *      e1.opCast!(T)()
-                 */
-                fd = search_function(ad, Id._cast);
+                return e.e1;
+            }
+
+            AggregateDeclaration ad = isAggregate(e.e1.type);
+            if (!ad)
+                break;
+
+            Dsymbol fd = null;
+            /* Rewrite as:
+             *      e1.opUnary!(op)()
+             */
+            fd = search_function(ad, Id.opUnary);
+            if (fd)
+            {
+                Objects* tiargs = opToArg(sc, e.op);
+                result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
+                result = new CallExp(e.loc, result);
+                result = result.expressionSemantic(sc);
+                return result;
+            }
+            // D1-style operator overloads, deprecated
+            if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus)
+            {
+                auto id = opId(e);
+                fd = search_function(ad, id);
                 if (fd)
                 {
-                    version (all)
-                    {
-                        // Backwards compatibility with D1 if opCast is a function, not a template
-                        if (fd.isFuncDeclaration())
-                        {
-                            // Rewrite as:  e1.opCast()
-                            return build_overload(e.loc, sc, e.e1, null, fd);
-                        }
-                    }
-                    auto tiargs = new Objects();
-                    tiargs.push(e.to);
-                    result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
-                    result = new CallExp(e.loc, result);
-                    result = result.expressionSemantic(sc);
-                    return result;
+                    // @@@DEPRECATED_2.110@@@.
+                    // Deprecated in 2.088, made an error in 2.100
+                    error(e.loc, "`%s` is obsolete.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
+                    return ErrorExp.get();
                 }
-                // Didn't find it. Forward to aliasthis
-                if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
+            }
+            // Didn't find it. Forward to aliasthis
+            if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
+            {
+                /* Rewrite op(e1) as:
+                 *      op(e1.aliasthis)
+                 */
+                //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars());
+                if (auto e1 = resolveAliasThis(sc, e.e1, true))
                 {
-                    /* Rewrite op(e1) as:
-                     *      op(e1.aliasthis)
-                     */
-                    if (auto e1 = resolveAliasThis(sc, e.e1, true))
-                    {
-                        result = e.copy();
-                        (cast(UnaExp)result).e1 = e1;
-                        result = visitCast(result.isCastExp(), att);
-                        return result;
-                    }
+                    e.e1 = e1;
+                    continue;
                 }
+                break;
             }
-            return result;
+            break;
         }
+        return result;
+    }
 
-        Expression visitBin(BinExp e)
+    Expression visitArray(ArrayExp ae)
+    {
+        //printf("ArrayExp::op_overload() (%s)\n", ae.toChars());
+        ae.e1 = ae.e1.expressionSemantic(sc);
+        ae.e1 = resolveProperties(sc, ae.e1);
+        Expression ae1old = ae.e1;
+        const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
+        IntervalExp ie = null;
+        if (maybeSlice && ae.arguments.length)
         {
-            //printf("BinExp::op_overload() (%s)\n", e.toChars());
-            Identifier id = opId(e);
-            Identifier id_r = opId_r(e);
-            int argsset = 0;
-            AggregateDeclaration ad1 = isAggregate(e.e1.type);
-            AggregateDeclaration ad2 = isAggregate(e.e2.type);
-            if (e.op == EXP.assign && ad1 == ad2)
-            {
-                StructDeclaration sd = ad1.isStructDeclaration();
-                if (sd &&
-                    (!sd.hasIdentityAssign ||
-                     /* Do a blit if we can and the rvalue is something like .init,
-                      * where a postblit is not necessary.
-                      */
-                     (sd.hasBlitAssign && !e.e2.isLvalue())))
-                {
-                    /* This is bitwise struct assignment. */
-                    return null;
-                }
-            }
-            Dsymbol s = null;
-            Dsymbol s_r = null;
-            Objects* tiargs = null;
-            if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
+            ie = (*ae.arguments)[0].isIntervalExp();
+        }
+        Expression result;
+        Type att = null; // first cyclic `alias this` type
+        while (true)
+        {
+            if (ae.e1.op == EXP.error)
             {
-                // Bug4099 fix
-                if (ad1 && search_function(ad1, Id.opUnary))
-                    return null;
+                return ae.e1;
             }
-            if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus)
+            Expression e0 = null;
+            Expression ae1save = ae.e1;
+            ae.lengthVar = null;
+            Type t1b = ae.e1.type.toBasetype();
+            AggregateDeclaration ad = isAggregate(t1b);
+            if (!ad)
             {
-                /* Try opBinary and opBinaryRight
-                 */
-                if (ad1)
+                // If the non-aggregate expression ae.e1 is indexable or sliceable,
+                // convert it to the corresponding concrete expression.
+                if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type)
                 {
-                    s = search_function(ad1, Id.opBinary);
-                    if (s && !s.isTemplateDeclaration())
+                    // Convert to SliceExp
+                    if (maybeSlice)
                     {
-                        error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
-                        return ErrorExp.get();
+                        result = new SliceExp(ae.loc, ae.e1, ie);
+                        result = result.expressionSemantic(sc);
+                        return result;
                     }
-                }
-                if (ad2)
-                {
-                    s_r = search_function(ad2, Id.opBinaryRight);
-                    if (s_r && !s_r.isTemplateDeclaration())
+                    // Convert to IndexExp
+                    if (ae.arguments.length == 1)
                     {
-                        error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
-                        return ErrorExp.get();
+                        result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]);
+                        result = result.expressionSemantic(sc);
+                        return result;
                     }
-                    if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
-                        s_r = null;
-                }
-                // Set tiargs, the template argument list, which will be the operator string
-                if (s || s_r)
-                {
-                    id = Id.opBinary;
-                    id_r = Id.opBinaryRight;
-                    tiargs = opToArg(sc, e.op);
                 }
+                break;
             }
-            if (!s && !s_r)
+            if (search_function(ad, Id.index))
             {
-                // Try the D1-style operators, deprecated
-                if (ad1 && id)
-                {
-                    s = search_function(ad1, id);
-                    if (s && id != Id.assign)
-                    {
-                        // @@@DEPRECATED_2.110@@@.
-                        // Deprecated in 2.088, made an error in 2.100
-                        if (id == Id.postinc || id == Id.postdec)
-                            error(e.loc, "`%s` is obsolete.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
-                        else
-                            error(e.loc, "`%s` is obsolete.  Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
-                        return ErrorExp.get();
-                    }
-                }
-                if (ad2 && id_r)
+                // Deal with $
+                result = resolveOpDollar(sc, ae, &e0);
+                if (!result) // a[i..j] might be: a.opSlice(i, j)
+                    goto Lfallback;
+                if (result.op == EXP.error)
+                    return result;
+                /* Rewrite e1[arguments] as:
+                 *      e1.opIndex(arguments)
+                 */
+                Expressions* a = ae.arguments.copy();
+                result = new DotIdExp(ae.loc, ae.e1, Id.index);
+                result = new CallExp(ae.loc, result, a);
+                if (maybeSlice) // a[] might be: a.opSlice()
+                    result = result.trySemantic(sc);
+                else
+                    result = result.expressionSemantic(sc);
+                if (result)
                 {
-                    s_r = search_function(ad2, id_r);
-                    // https://issues.dlang.org/show_bug.cgi?id=12778
-                    // If both x.opBinary(y) and y.opBinaryRight(x) found,
-                    // and they are exactly same symbol, x.opBinary(y) should be preferred.
-                    if (s_r && s_r == s)
-                        s_r = null;
-                    if (s_r)
-                    {
-                        // @@@DEPRECATED_2.110@@@.
-                        // Deprecated in 2.088, made an error in 2.100
-                        error(e.loc, "`%s` is obsolete.  Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr);
-                        return ErrorExp.get();
-                    }
+                    return Expression.combine(e0, result);
                 }
             }
-            Expressions* args1 = new Expressions();
-            Expressions* args2 = new Expressions();
-            if (s || s_r)
+        Lfallback:
+            if (maybeSlice && ae.e1.op == EXP.type)
             {
-                /* Try:
-                 *      a.opfunc(b)
-                 *      b.opfunc_r(a)
-                 * and see which is better.
-                 */
-                args1.setDim(1);
-                (*args1)[0] = e.e1;
-                expandTuples(args1);
-                args2.setDim(1);
-                (*args2)[0] = e.e2;
-                expandTuples(args2);
-                argsset = 1;
-                MatchAccumulator m;
-                if (s)
-                {
-                    functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
-                    if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
-                    {
-                        return ErrorExp.get();
-                    }
-                }
-                FuncDeclaration lastf = m.lastf;
-                if (s_r)
+                result = new SliceExp(ae.loc, ae.e1, ie);
+                result = result.expressionSemantic(sc);
+                result = Expression.combine(e0, result);
+                return result;
+            }
+            if (maybeSlice && search_function(ad, Id.slice))
+            {
+                // Deal with $
+                result = resolveOpDollar(sc, ae, ie, &e0);
+
+                if (result.op == EXP.error)
                 {
-                    functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
-                    if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
-                    {
-                        return ErrorExp.get();
+                    if (!e0 && !search_function(ad, Id.dollar)) {
+                        ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars());
                     }
+                    return result;
                 }
-                if (m.count > 1)
-                {
-                    // Error, ambiguous
-                    error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
-                }
-                else if (m.last == MATCH.nomatch)
-                {
-                    if (tiargs)
-                        goto L1;
-                    m.lastf = null;
-                }
-                if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
-                {
-                    // Kludge because operator overloading regards e++ and e--
-                    // as unary, but it's implemented as a binary.
-                    // Rewrite (e1 ++ e2) as e1.postinc()
-                    // Rewrite (e1 -- e2) as e1.postdec()
-                    return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
-                }
-                else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
-                {
-                    // Rewrite (e1 op e2) as e1.opfunc(e2)
-                    return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
-                }
-                else
+                /* Rewrite a[i..j] as:
+                 *      a.opSlice(i, j)
+                 */
+                auto a = new Expressions();
+                if (ie)
                 {
-                    // Rewrite (e1 op e2) as e2.opfunc_r(e1)
-                    return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
+                    a.push(ie.lwr);
+                    a.push(ie.upr);
                 }
+                result = new DotIdExp(ae.loc, ae.e1, Id.slice);
+                result = new CallExp(ae.loc, result, a);
+                result = result.expressionSemantic(sc);
+                result = Expression.combine(e0, result);
+                return result;
             }
-        L1:
-            version (all)
+            // Didn't find it. Forward to aliasthis
+            if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
             {
-                // Retained for D1 compatibility
-                if (isCommutative(e.op) && !tiargs)
-                {
-                    s = null;
-                    s_r = null;
-                    if (ad1 && id_r)
-                    {
-                        s_r = search_function(ad1, id_r);
-                    }
-                    if (ad2 && id)
-                    {
-                        s = search_function(ad2, id);
-                        if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
-                            s = null;
-                    }
-                    if (s || s_r)
-                    {
-                        /* Try:
-                         *  a.opfunc_r(b)
-                         *  b.opfunc(a)
-                         * and see which is better.
-                         */
-                        if (!argsset)
-                        {
-                            args1.setDim(1);
-                            (*args1)[0] = e.e1;
-                            expandTuples(args1);
-                            args2.setDim(1);
-                            (*args2)[0] = e.e2;
-                            expandTuples(args2);
-                        }
-                        MatchAccumulator m;
-                        if (s_r)
-                        {
-                            functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
-                            if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
-                            {
-                                return ErrorExp.get();
-                            }
-                        }
-                        FuncDeclaration lastf = m.lastf;
-                        if (s)
-                        {
-                            functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
-                            if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
-                            {
-                                return ErrorExp.get();
-                            }
-                        }
-                        if (m.count > 1)
-                        {
-                            // Error, ambiguous
-                            error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
-                        }
-                        else if (m.last == MATCH.nomatch)
-                        {
-                            m.lastf = null;
-                        }
-
-                        if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
-                        {
-                            // Rewrite (e1 op e2) as e1.opfunc_r(e2)
-                            return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
-                        }
-                        else
-                        {
-                            // Rewrite (e1 op e2) as e2.opfunc(e1)
-                            Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
-                            // When reversing operands of comparison operators,
-                            // need to reverse the sense of the op
-                            if (pop)
-                                *pop = reverseRelation(e.op);
-                            return result;
-                        }
-                    }
-                }
+                //printf("att arr e1 = %s\n", this.e1.type.toChars());
+                /* Rewrite op(a[arguments]) as:
+                 *      op(a.aliasthis[arguments])
+                 */
+                ae.e1 = resolveAliasThis(sc, ae1save, true);
+                if (ae.e1)
+                    continue;
             }
+            break;
+        }
+        ae.e1 = ae1old; // recovery
+        ae.lengthVar = null;
+        return result;
+    }
 
-            Expression rewrittenLhs;
-            if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+    /***********************************************
+     * This is mostly the same as UnaryExp::op_overload(), but has
+     * a different rewrite.
+     */
+    Expression visitCast(CastExp e, Type att = null)
+    {
+        //printf("CastExp::op_overload() (%s)\n", e.toChars());
+        Expression result;
+        if (AggregateDeclaration ad = isAggregate(e.e1.type))
+        {
+            Dsymbol fd = null;
+            /* Rewrite as:
+             *      e1.opCast!(T)()
+             */
+            fd = search_function(ad, Id._cast);
+            if (fd)
             {
-                if (Expression result = checkAliasThisForLhs(ad1, sc, e))
+                version (all)
                 {
-                    /* https://issues.dlang.org/show_bug.cgi?id=19441
-                     *
-                     * alias this may not be used for partial assignment.
-                     * If a struct has a single member which is aliased this
-                     * directly or aliased to a ref getter function that returns
-                     * the mentioned member, then alias this may be
-                     * used since the object will be fully initialised.
-                     * If the struct is nested, the context pointer is considered
-                     * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis`
-                     * condition.
-                     */
-                    if (result.op != EXP.assign)
-                        return result;     // i.e: Rewrote `e1 = e2` -> `e1(e2)`
-
-                    auto ae = result.isAssignExp();
-                    if (ae.e1.op != EXP.dotVariable)
-                        return result;     // i.e: Rewrote `e1 = e2` -> `e1() = e2`
-
-                    auto dve = ae.e1.isDotVarExp();
-                    if (auto ad = dve.var.isMember2())
+                    // Backwards compatibility with D1 if opCast is a function, not a template
+                    if (fd.isFuncDeclaration())
                     {
-                        // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2`
-                        // Ensure that `var` is the only field member in `ad`
-                        if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis))
-                        {
-                            if (dve.var == ad.aliasthis.sym)
-                                return result;
-                        }
+                        // Rewrite as:  e1.opCast()
+                        return build_overload(e.loc, sc, e.e1, null, fd);
                     }
-                    rewrittenLhs = ae.e1;
                 }
+                auto tiargs = new Objects();
+                tiargs.push(e.to);
+                result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs);
+                result = new CallExp(e.loc, result);
+                result = result.expressionSemantic(sc);
+                return result;
             }
-            if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+            // Didn't find it. Forward to aliasthis
+            if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type))
             {
-                if (Expression result = checkAliasThisForRhs(ad2, sc, e))
+                /* Rewrite op(e1) as:
+                 *      op(e1.aliasthis)
+                 */
+                if (auto e1 = resolveAliasThis(sc, e.e1, true))
+                {
+                    result = e.copy();
+                    (cast(UnaExp)result).e1 = e1;
+                    result = visitCast(result.isCastExp(), att);
                     return result;
+                }
             }
-            if (rewrittenLhs)
-            {
-                error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`",
-                        e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars());
-                return ErrorExp.get();
-            }
-            return null;
         }
+        return result;
+    }
 
-        Expression visitEqual(EqualExp e)
+    Expression visitBin(BinExp e)
+    {
+        //printf("BinExp::op_overload() (%s)\n", e.toChars());
+        Identifier id = opId(e);
+        Identifier id_r = opId_r(e);
+        int argsset = 0;
+        AggregateDeclaration ad1 = isAggregate(e.e1.type);
+        AggregateDeclaration ad2 = isAggregate(e.e2.type);
+        if (e.op == EXP.assign && ad1 == ad2)
         {
-            //printf("EqualExp::op_overload() (%s)\n", e.toChars());
-            Type t1 = e.e1.type.toBasetype();
-            Type t2 = e.e2.type.toBasetype();
-
-            /* Array equality is handled by expressionSemantic() potentially
-             * lowering to object.__equals(), which takes care of overloaded
-             * operators for the element types.
-             */
-            if ((t1.ty == Tarray || t1.ty == Tsarray) &&
-                (t2.ty == Tarray || t2.ty == Tsarray))
+            StructDeclaration sd = ad1.isStructDeclaration();
+            if (sd &&
+                (!sd.hasIdentityAssign ||
+                 /* Do a blit if we can and the rvalue is something like .init,
+                  * where a postblit is not necessary.
+                  */
+                 (sd.hasBlitAssign && !e.e2.isLvalue())))
             {
+                /* This is bitwise struct assignment. */
                 return null;
             }
-
-            /* Check for class equality with null literal or typeof(null).
-             */
-            if (t1.ty == Tclass && e.e2.op == EXP.null_ ||
-                t2.ty == Tclass && e.e1.op == EXP.null_)
-            {
-                error(e.loc, "use `%s` instead of `%s` when comparing with `null`",
-                    EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr,
-                    EXPtoString(e.op).ptr);
-                return ErrorExp.get();
-            }
-            if (t1.ty == Tclass && t2.ty == Tnull ||
-                t1.ty == Tnull && t2.ty == Tclass)
-            {
-                // Comparing a class with typeof(null) should not call opEquals
+        }
+        Dsymbol s = null;
+        Dsymbol s_r = null;
+        Objects* tiargs = null;
+        if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
+        {
+            // Bug4099 fix
+            if (ad1 && search_function(ad1, Id.opUnary))
                 return null;
-            }
-
-            /* Check for class equality.
+        }
+        if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus)
+        {
+            /* Try opBinary and opBinaryRight
              */
-            if (t1.ty == Tclass && t2.ty == Tclass)
+            if (ad1)
             {
-                ClassDeclaration cd1 = t1.isClassHandle();
-                ClassDeclaration cd2 = t2.isClassHandle();
-                if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
+                s = search_function(ad1, Id.opBinary);
+                if (s && !s.isTemplateDeclaration())
                 {
-                    /* Rewrite as:
-                     *      .object.opEquals(e1, e2)
-                     */
-                    if (!ClassDeclaration.object)
-                    {
-                        error(e.loc, "cannot compare classes for equality because `object.Object` was not declared");
-                        return null;
-                    }
-
-                    Expression e1x = e.e1;
-                    Expression e2x = e.e2;
-
-                    /* The explicit cast is necessary for interfaces
-                     * https://issues.dlang.org/show_bug.cgi?id=4088
-                     */
-                    Type to = ClassDeclaration.object.getType();
-                    if (cd1.isInterfaceDeclaration())
-                        e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
-                    if (cd2.isInterfaceDeclaration())
-                        e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
-
-                    Expression result = new IdentifierExp(e.loc, Id.empty);
-                    result = new DotIdExp(e.loc, result, Id.object);
-                    result = new DotIdExp(e.loc, result, Id.eq);
-                    result = new CallExp(e.loc, result, e1x, e2x);
-                    if (e.op == EXP.notEqual)
-                        result = new NotExp(e.loc, result);
-                    result = result.expressionSemantic(sc);
-                    return result;
+                    error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars());
+                    return ErrorExp.get();
                 }
             }
-
-            if (Expression result = compare_overload(e, sc, Id.eq, null))
+            if (ad2)
             {
-                if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
+                s_r = search_function(ad2, Id.opBinaryRight);
+                if (s_r && !s_r.isTemplateDeclaration())
                 {
-                    result = new NotExp(result.loc, result);
-                    result = result.expressionSemantic(sc);
+                    error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars());
+                    return ErrorExp.get();
                 }
-                return result;
+                if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778
+                    s_r = null;
             }
-
-            /* Check for pointer equality.
-             */
-            if (t1.ty == Tpointer || t2.ty == Tpointer)
+            // Set tiargs, the template argument list, which will be the operator string
+            if (s || s_r)
             {
-                /* Rewrite:
-                 *      ptr1 == ptr2
-                 * as:
-                 *      ptr1 is ptr2
-                 *
-                 * This is just a rewriting for deterministic AST representation
-                 * as the backend input.
-                 */
-                auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
-                Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
-                return r.expressionSemantic(sc);
+                id = Id.opBinary;
+                id_r = Id.opBinaryRight;
+                tiargs = opToArg(sc, e.op);
             }
-
-            /* Check for struct equality without opEquals.
-             */
-            if (t1.ty == Tstruct && t2.ty == Tstruct)
+        }
+        if (!s && !s_r)
+        {
+            // Try the D1-style operators, deprecated
+            if (ad1 && id)
             {
-                auto sd = t1.isTypeStruct().sym;
-                if (sd != t2.isTypeStruct().sym)
-                    return null;
-
-                import dmd.clone : needOpEquals;
-                if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
+                s = search_function(ad1, id);
+                if (s && id != Id.assign)
                 {
-                    // Use bitwise equality.
-                    auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
-                    Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
-                    return r.expressionSemantic(sc);
+                    // @@@DEPRECATED_2.110@@@.
+                    // Deprecated in 2.088, made an error in 2.100
+                    if (id == Id.postinc || id == Id.postdec)
+                        error(e.loc, "`%s` is obsolete.  Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
+                    else
+                        error(e.loc, "`%s` is obsolete.  Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr);
+                    return ErrorExp.get();
                 }
-
-                /* Do memberwise equality.
-                 * https://dlang.org/spec/expression.html#equality_expressions
-                 * Rewrite:
-                 *      e1 == e2
-                 * as:
-                 *      e1.tupleof == e2.tupleof
-                 *
-                 * If sd is a nested struct, and if it's nested in a class, it will
-                 * also compare the parent class's equality. Otherwise, compares
-                 * the identity of parent context through void*.
-                 */
-                e = e.copy().isEqualExp();
-                e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
-                e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
-
-                auto sc2 = sc.push();
-                sc2.noAccessCheck = true;
-                Expression r = e.expressionSemantic(sc2);
-                sc2.pop();
-                return r;
             }
-
-            /* Check for tuple equality.
-             */
-            if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple)
+            if (ad2 && id_r)
             {
-                auto tup1 = e.e1.isTupleExp();
-                auto tup2 = e.e2.isTupleExp();
-                size_t dim = tup1.exps.length;
-                if (dim != tup2.exps.length)
+                s_r = search_function(ad2, id_r);
+                // https://issues.dlang.org/show_bug.cgi?id=12778
+                // If both x.opBinary(y) and y.opBinaryRight(x) found,
+                // and they are exactly same symbol, x.opBinary(y) should be preferred.
+                if (s_r && s_r == s)
+                    s_r = null;
+                if (s_r)
                 {
-                    error(e.loc, "mismatched sequence lengths, `%d` and `%d`",
-                        cast(int)dim, cast(int)tup2.exps.length);
+                    // @@@DEPRECATED_2.110@@@.
+                    // Deprecated in 2.088, made an error in 2.100
+                    error(e.loc, "`%s` is obsolete.  Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr);
                     return ErrorExp.get();
                 }
-
-                Expression result;
-                if (dim == 0)
+            }
+        }
+        Expressions* args1 = new Expressions();
+        Expressions* args2 = new Expressions();
+        if (s || s_r)
+        {
+            /* Try:
+             *      a.opfunc(b)
+             *      b.opfunc_r(a)
+             * and see which is better.
+             */
+            args1.setDim(1);
+            (*args1)[0] = e.e1;
+            expandTuples(args1);
+            args2.setDim(1);
+            (*args2)[0] = e.e2;
+            expandTuples(args2);
+            argsset = 1;
+            MatchAccumulator m;
+            if (s)
+            {
+                functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
+                if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
                 {
-                    // zero-length tuple comparison should always return true or false.
-                    result = IntegerExp.createBool(e.op == EXP.equal);
+                    return ErrorExp.get();
                 }
-                else
+            }
+            FuncDeclaration lastf = m.lastf;
+            if (s_r)
+            {
+                functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
+                if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
                 {
-                    for (size_t i = 0; i < dim; i++)
-                    {
-                        auto ex1 = (*tup1.exps)[i];
-                        auto ex2 = (*tup2.exps)[i];
-                        auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
-
-                        if (!result)
-                            result = eeq;
-                        else if (e.op == EXP.equal)
-                            result = new LogicalExp(e.loc, EXP.andAnd, result, eeq);
-                        else
-                            result = new LogicalExp(e.loc, EXP.orOr, result, eeq);
-                    }
-                    assert(result);
+                    return ErrorExp.get();
                 }
-                result = Expression.combine(tup1.e0, tup2.e0, result);
-                result = result.expressionSemantic(sc);
-
-                return result;
             }
-            return null;
-        }
-
-        Expression visitCmp(CmpExp e)
-        {
-            //printf("CmpExp:: () (%s)\n", e.toChars());
-            return compare_overload(e, sc, Id.cmp, pop);
+            if (m.count > 1)
+            {
+                // Error, ambiguous
+                error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+            }
+            else if (m.last == MATCH.nomatch)
+            {
+                if (tiargs)
+                    goto L1;
+                m.lastf = null;
+            }
+            if (e.op == EXP.plusPlus || e.op == EXP.minusMinus)
+            {
+                // Kludge because operator overloading regards e++ and e--
+                // as unary, but it's implemented as a binary.
+                // Rewrite (e1 ++ e2) as e1.postinc()
+                // Rewrite (e1 -- e2) as e1.postdec()
+                return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s);
+            }
+            else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch)
+            {
+                // Rewrite (e1 op e2) as e1.opfunc(e2)
+                return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+            }
+            else
+            {
+                // Rewrite (e1 op e2) as e2.opfunc_r(e1)
+                return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r);
+            }
         }
-
-        /*********************************
-         * Operator overloading for op=
-         */
-        Expression visitBinAssign(BinAssignExp e)
+    L1:
+        version (all)
         {
-            //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
-            if (auto ae = e.e1.isArrayExp())
+            // Retained for D1 compatibility
+            if (isCommutative(e.op) && !tiargs)
             {
-                ae.e1 = ae.e1.expressionSemantic(sc);
-                ae.e1 = resolveProperties(sc, ae.e1);
-                Expression ae1old = ae.e1;
-                const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
-                IntervalExp ie = null;
-                if (maybeSlice && ae.arguments.length)
+                s = null;
+                s_r = null;
+                if (ad1 && id_r)
                 {
-                    ie = (*ae.arguments)[0].isIntervalExp();
+                    s_r = search_function(ad1, id_r);
                 }
-                Type att = null; // first cyclic `alias this` type
-                while (true)
+                if (ad2 && id)
                 {
-                    if (ae.e1.op == EXP.error)
+                    s = search_function(ad2, id);
+                    if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778
+                        s = null;
+                }
+                if (s || s_r)
+                {
+                    /* Try:
+                     *  a.opfunc_r(b)
+                     *  b.opfunc(a)
+                     * and see which is better.
+                     */
+                    if (!argsset)
                     {
-                        return ae.e1;
+                        args1.setDim(1);
+                        (*args1)[0] = e.e1;
+                        expandTuples(args1);
+                        args2.setDim(1);
+                        (*args2)[0] = e.e2;
+                        expandTuples(args2);
                     }
-                    Expression e0 = null;
-                    Expression ae1save = ae.e1;
-                    ae.lengthVar = null;
-                    Type t1b = ae.e1.type.toBasetype();
-                    AggregateDeclaration ad = isAggregate(t1b);
-                    if (!ad)
-                        break;
-                    if (search_function(ad, Id.opIndexOpAssign))
+                    MatchAccumulator m;
+                    if (s_r)
                     {
-                        // Deal with $
-                        Expression result = resolveOpDollar(sc, ae, &e0);
-                        if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
-                            goto Lfallback;
-                        if (result.op == EXP.error)
-                            return result;
-                        result = e.e2.expressionSemantic(sc);
-                        if (result.op == EXP.error)
-                            return result;
-                        e.e2 = result;
-                        /* Rewrite a[arguments] op= e2 as:
-                         *      a.opIndexOpAssign!(op)(e2, arguments)
-                         */
-                        Expressions* a = ae.arguments.copy();
-                        a.insert(0, e.e2);
-                        Objects* tiargs = opToArg(sc, e.op);
-                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
-                        result = new CallExp(e.loc, result, a);
-                        if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
-                            result = result.trySemantic(sc);
-                        else
-                            result = result.expressionSemantic(sc);
-                        if (result)
+                        functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
+                        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
                         {
-                            return Expression.combine(e0, result);
+                            return ErrorExp.get();
                         }
                     }
-                Lfallback:
-                    if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
+                    FuncDeclaration lastf = m.lastf;
+                    if (s)
                     {
-                        // Deal with $
-                        Expression result = resolveOpDollar(sc, ae, ie, &e0);
-                        if (result.op == EXP.error)
-                            return result;
-                        result = e.e2.expressionSemantic(sc);
-                        if (result.op == EXP.error)
-                            return result;
-                        e.e2 = result;
-                        /* Rewrite (a[i..j] op= e2) as:
-                         *      a.opSliceOpAssign!(op)(e2, i, j)
-                         */
-                        auto a = new Expressions();
-                        a.push(e.e2);
-                        if (ie)
+                        functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1));
+                        if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
                         {
-                            a.push(ie.lwr);
-                            a.push(ie.upr);
+                            return ErrorExp.get();
                         }
-                        Objects* tiargs = opToArg(sc, e.op);
-                        result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
-                        result = new CallExp(e.loc, result, a);
-                        result = result.expressionSemantic(sc);
-                        result = Expression.combine(e0, result);
+                    }
+                    if (m.count > 1)
+                    {
+                        // Error, ambiguous
+                        error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+                    }
+                    else if (m.last == MATCH.nomatch)
+                    {
+                        m.lastf = null;
+                    }
+
+                    if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch)
+                    {
+                        // Rewrite (e1 op e2) as e1.opfunc_r(e2)
+                        return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r);
+                    }
+                    else
+                    {
+                        // Rewrite (e1 op e2) as e2.opfunc(e1)
+                        Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s);
+                        // When reversing operands of comparison operators,
+                        // need to reverse the sense of the op
+                        if (pop)
+                            *pop = reverseRelation(e.op);
                         return result;
                     }
-                    // Didn't find it. Forward to aliasthis
-                    if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
+                }
+            }
+        }
+
+        Expression rewrittenLhs;
+        if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+        {
+            if (Expression result = checkAliasThisForLhs(ad1, sc, e))
+            {
+                /* https://issues.dlang.org/show_bug.cgi?id=19441
+                 *
+                 * alias this may not be used for partial assignment.
+                 * If a struct has a single member which is aliased this
+                 * directly or aliased to a ref getter function that returns
+                 * the mentioned member, then alias this may be
+                 * used since the object will be fully initialised.
+                 * If the struct is nested, the context pointer is considered
+                 * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis`
+                 * condition.
+                 */
+                if (result.op != EXP.assign)
+                    return result;     // i.e: Rewrote `e1 = e2` -> `e1(e2)`
+
+                auto ae = result.isAssignExp();
+                if (ae.e1.op != EXP.dotVariable)
+                    return result;     // i.e: Rewrote `e1 = e2` -> `e1() = e2`
+
+                auto dve = ae.e1.isDotVarExp();
+                if (auto ad = dve.var.isMember2())
+                {
+                    // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2`
+                    // Ensure that `var` is the only field member in `ad`
+                    if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis))
                     {
-                        /* Rewrite (a[arguments] op= e2) as:
-                         *      a.aliasthis[arguments] op= e2
-                         */
-                        ae.e1 = resolveAliasThis(sc, ae1save, true);
-                        if (ae.e1)
-                            continue;
+                        if (dve.var == ad.aliasthis.sym)
+                            return result;
                     }
-                    break;
                 }
-                ae.e1 = ae1old; // recovery
-                ae.lengthVar = null;
+                rewrittenLhs = ae.e1;
             }
-            Expression result = e.binSemanticProp(sc);
-            if (result)
+        }
+        if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943
+        {
+            if (Expression result = checkAliasThisForRhs(ad2, sc, e))
                 return result;
-            // Don't attempt 'alias this' if an error occurred
-            if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
+        }
+        if (rewrittenLhs)
+        {
+            error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`",
+                    e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars());
+            return ErrorExp.get();
+        }
+        return null;
+    }
+
+    Expression visitEqual(EqualExp e)
+    {
+        //printf("EqualExp::op_overload() (%s)\n", e.toChars());
+        Type t1 = e.e1.type.toBasetype();
+        Type t2 = e.e2.type.toBasetype();
+
+        /* Array equality is handled by expressionSemantic() potentially
+         * lowering to object.__equals(), which takes care of overloaded
+         * operators for the element types.
+         */
+        if ((t1.ty == Tarray || t1.ty == Tsarray) &&
+            (t2.ty == Tarray || t2.ty == Tsarray))
+        {
+            return null;
+        }
+
+        /* Check for class equality with null literal or typeof(null).
+         */
+        if (t1.ty == Tclass && e.e2.op == EXP.null_ ||
+            t2.ty == Tclass && e.e1.op == EXP.null_)
+        {
+            error(e.loc, "use `%s` instead of `%s` when comparing with `null`",
+                EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr,
+                EXPtoString(e.op).ptr);
+            return ErrorExp.get();
+        }
+        if (t1.ty == Tclass && t2.ty == Tnull ||
+            t1.ty == Tnull && t2.ty == Tclass)
+        {
+            // Comparing a class with typeof(null) should not call opEquals
+            return null;
+        }
+
+        /* Check for class equality.
+         */
+        if (t1.ty == Tclass && t2.ty == Tclass)
+        {
+            ClassDeclaration cd1 = t1.isClassHandle();
+            ClassDeclaration cd2 = t2.isClassHandle();
+            if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp))
             {
-                return ErrorExp.get();
+                /* Rewrite as:
+                 *      .object.opEquals(e1, e2)
+                 */
+                if (!ClassDeclaration.object)
+                {
+                    error(e.loc, "cannot compare classes for equality because `object.Object` was not declared");
+                    return null;
+                }
+
+                Expression e1x = e.e1;
+                Expression e2x = e.e2;
+
+                /* The explicit cast is necessary for interfaces
+                 * https://issues.dlang.org/show_bug.cgi?id=4088
+                 */
+                Type to = ClassDeclaration.object.getType();
+                if (cd1.isInterfaceDeclaration())
+                    e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf());
+                if (cd2.isInterfaceDeclaration())
+                    e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf());
+
+                Expression result = new IdentifierExp(e.loc, Id.empty);
+                result = new DotIdExp(e.loc, result, Id.object);
+                result = new DotIdExp(e.loc, result, Id.eq);
+                result = new CallExp(e.loc, result, e1x, e2x);
+                if (e.op == EXP.notEqual)
+                    result = new NotExp(e.loc, result);
+                result = result.expressionSemantic(sc);
+                return result;
             }
-            Identifier id = opId(e);
-            Expressions* args2 = new Expressions();
-            AggregateDeclaration ad1 = isAggregate(e.e1.type);
-            Dsymbol s = null;
-            Objects* tiargs = null;
-            /* Try opOpAssign
+        }
+
+        if (Expression result = compare_overload(e, sc, Id.eq, null))
+        {
+            if (lastComma(result).op == EXP.call && e.op == EXP.notEqual)
+            {
+                result = new NotExp(result.loc, result);
+                result = result.expressionSemantic(sc);
+            }
+            return result;
+        }
+
+        /* Check for pointer equality.
+         */
+        if (t1.ty == Tpointer || t2.ty == Tpointer)
+        {
+            /* Rewrite:
+             *      ptr1 == ptr2
+             * as:
+             *      ptr1 is ptr2
+             *
+             * This is just a rewriting for deterministic AST representation
+             * as the backend input.
              */
-            if (ad1)
+            auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
+            Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
+            return r.expressionSemantic(sc);
+        }
+
+        /* Check for struct equality without opEquals.
+         */
+        if (t1.ty == Tstruct && t2.ty == Tstruct)
+        {
+            auto sd = t1.isTypeStruct().sym;
+            if (sd != t2.isTypeStruct().sym)
+                return null;
+
+            import dmd.clone : needOpEquals;
+            if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd))
             {
-                s = search_function(ad1, Id.opOpAssign);
-                if (s && !s.isTemplateDeclaration())
-                {
-                    error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars());
-                    return ErrorExp.get();
-                }
+                // Use bitwise equality.
+                auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity;
+                Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2);
+                return r.expressionSemantic(sc);
             }
-            // Set tiargs, the template argument list, which will be the operator string
-            if (s)
+
+            /* Do memberwise equality.
+             * https://dlang.org/spec/expression.html#equality_expressions
+             * Rewrite:
+             *      e1 == e2
+             * as:
+             *      e1.tupleof == e2.tupleof
+             *
+             * If sd is a nested struct, and if it's nested in a class, it will
+             * also compare the parent class's equality. Otherwise, compares
+             * the identity of parent context through void*.
+             */
+            e = e.copy().isEqualExp();
+            e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof);
+            e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof);
+
+            auto sc2 = sc.push();
+            sc2.noAccessCheck = true;
+            Expression r = e.expressionSemantic(sc2);
+            sc2.pop();
+            return r;
+        }
+
+        /* Check for tuple equality.
+         */
+        if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple)
+        {
+            auto tup1 = e.e1.isTupleExp();
+            auto tup2 = e.e2.isTupleExp();
+            size_t dim = tup1.exps.length;
+            if (dim != tup2.exps.length)
             {
-                id = Id.opOpAssign;
-                tiargs = opToArg(sc, e.op);
+                error(e.loc, "mismatched sequence lengths, `%d` and `%d`",
+                    cast(int)dim, cast(int)tup2.exps.length);
+                return ErrorExp.get();
             }
 
-            // Try D1-style operator overload, deprecated
-            if (!s && ad1 && id)
+            Expression result;
+            if (dim == 0)
             {
-                s = search_function(ad1, id);
-                if (s)
+                // zero-length tuple comparison should always return true or false.
+                result = IntegerExp.createBool(e.op == EXP.equal);
+            }
+            else
+            {
+                for (size_t i = 0; i < dim; i++)
                 {
-                    // @@@DEPRECATED_2.110@@@.
-                    // Deprecated in 2.088, made an error in 2.100
-                    scope char[] op = EXPtoString(e.op).dup;
-                    op[$-1] = '\0'; // remove trailing `=`
-                    error(e.loc, "`%s` is obsolete.  Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
-                    return ErrorExp.get();
+                    auto ex1 = (*tup1.exps)[i];
+                    auto ex2 = (*tup2.exps)[i];
+                    auto eeq = new EqualExp(e.op, e.loc, ex1, ex2);
+
+                    if (!result)
+                        result = eeq;
+                    else if (e.op == EXP.equal)
+                        result = new LogicalExp(e.loc, EXP.andAnd, result, eeq);
+                    else
+                        result = new LogicalExp(e.loc, EXP.orOr, result, eeq);
                 }
+                assert(result);
             }
+            result = Expression.combine(tup1.e0, tup2.e0, result);
+            result = result.expressionSemantic(sc);
 
-            if (s)
+            return result;
+        }
+        return null;
+    }
+
+    Expression visitCmp(CmpExp e)
+    {
+        //printf("CmpExp:: () (%s)\n", e.toChars());
+        return compare_overload(e, sc, Id.cmp, pop);
+    }
+
+    /*********************************
+     * Operator overloading for op=
+     */
+    Expression visitBinAssign(BinAssignExp e)
+    {
+        //printf("BinAssignExp::op_overload() (%s)\n", e.toChars());
+        if (auto ae = e.e1.isArrayExp())
+        {
+            ae.e1 = ae.e1.expressionSemantic(sc);
+            ae.e1 = resolveProperties(sc, ae.e1);
+            Expression ae1old = ae.e1;
+            const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval);
+            IntervalExp ie = null;
+            if (maybeSlice && ae.arguments.length)
             {
-                /* Try:
-                 *      a.opOpAssign(b)
-                 */
-                args2.setDim(1);
-                (*args2)[0] = e.e2;
-                expandTuples(args2);
-                MatchAccumulator m;
-                functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
-                if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+                ie = (*ae.arguments)[0].isIntervalExp();
+            }
+            Type att = null; // first cyclic `alias this` type
+            while (true)
+            {
+                if (ae.e1.op == EXP.error)
                 {
-                    return ErrorExp.get();
+                    return ae.e1;
                 }
-                if (m.count > 1)
+                Expression e0 = null;
+                Expression ae1save = ae.e1;
+                ae.lengthVar = null;
+                Type t1b = ae.e1.type.toBasetype();
+                AggregateDeclaration ad = isAggregate(t1b);
+                if (!ad)
+                    break;
+                if (search_function(ad, Id.opIndexOpAssign))
                 {
-                    // Error, ambiguous
-                    error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+                    // Deal with $
+                    Expression result = resolveOpDollar(sc, ae, &e0);
+                    if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j)
+                        goto Lfallback;
+                    if (result.op == EXP.error)
+                        return result;
+                    result = e.e2.expressionSemantic(sc);
+                    if (result.op == EXP.error)
+                        return result;
+                    e.e2 = result;
+                    /* Rewrite a[arguments] op= e2 as:
+                     *      a.opIndexOpAssign!(op)(e2, arguments)
+                     */
+                    Expressions* a = ae.arguments.copy();
+                    a.insert(0, e.e2);
+                    Objects* tiargs = opToArg(sc, e.op);
+                    result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs);
+                    result = new CallExp(e.loc, result, a);
+                    if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2)
+                        result = result.trySemantic(sc);
+                    else
+                        result = result.expressionSemantic(sc);
+                    if (result)
+                    {
+                        return Expression.combine(e0, result);
+                    }
                 }
-                else if (m.last == MATCH.nomatch)
+            Lfallback:
+                if (maybeSlice && search_function(ad, Id.opSliceOpAssign))
                 {
-                    if (tiargs)
-                        goto L1;
-                    m.lastf = null;
+                    // Deal with $
+                    Expression result = resolveOpDollar(sc, ae, ie, &e0);
+                    if (result.op == EXP.error)
+                        return result;
+                    result = e.e2.expressionSemantic(sc);
+                    if (result.op == EXP.error)
+                        return result;
+                    e.e2 = result;
+                    /* Rewrite (a[i..j] op= e2) as:
+                     *      a.opSliceOpAssign!(op)(e2, i, j)
+                     */
+                    auto a = new Expressions();
+                    a.push(e.e2);
+                    if (ie)
+                    {
+                        a.push(ie.lwr);
+                        a.push(ie.upr);
+                    }
+                    Objects* tiargs = opToArg(sc, e.op);
+                    result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs);
+                    result = new CallExp(e.loc, result, a);
+                    result = result.expressionSemantic(sc);
+                    result = Expression.combine(e0, result);
+                    return result;
                 }
-                // Rewrite (e1 op e2) as e1.opOpAssign(e2)
-                return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+                // Didn't find it. Forward to aliasthis
+                if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type))
+                {
+                    /* Rewrite (a[arguments] op= e2) as:
+                     *      a.aliasthis[arguments] op= e2
+                     */
+                    ae.e1 = resolveAliasThis(sc, ae1save, true);
+                    if (ae.e1)
+                        continue;
+                }
+                break;
             }
-        L1:
-            result = checkAliasThisForLhs(ad1, sc, e);
-            if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
-                return result;
+            ae.e1 = ae1old; // recovery
+            ae.lengthVar = null;
+        }
+        Expression result = e.binSemanticProp(sc);
+        if (result)
+            return result;
+        // Don't attempt 'alias this' if an error occurred
+        if (e.e1.type.ty == Terror || e.e2.type.ty == Terror)
+        {
+            return ErrorExp.get();
+        }
+        Identifier id = opId(e);
+        Expressions* args2 = new Expressions();
+        AggregateDeclaration ad1 = isAggregate(e.e1.type);
+        Dsymbol s = null;
+        Objects* tiargs = null;
+        /* Try opOpAssign
+         */
+        if (ad1)
+        {
+            s = search_function(ad1, Id.opOpAssign);
+            if (s && !s.isTemplateDeclaration())
+            {
+                error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars());
+                return ErrorExp.get();
+            }
+        }
+        // Set tiargs, the template argument list, which will be the operator string
+        if (s)
+        {
+            id = Id.opOpAssign;
+            tiargs = opToArg(sc, e.op);
+        }
 
-            return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
+        // Try D1-style operator overload, deprecated
+        if (!s && ad1 && id)
+        {
+            s = search_function(ad1, id);
+            if (s)
+            {
+                // @@@DEPRECATED_2.110@@@.
+                // Deprecated in 2.088, made an error in 2.100
+                scope char[] op = EXPtoString(e.op).dup;
+                op[$-1] = '\0'; // remove trailing `=`
+                error(e.loc, "`%s` is obsolete.  Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr);
+                return ErrorExp.get();
+            }
         }
 
+        if (s)
+        {
+            /* Try:
+             *      a.opOpAssign(b)
+             */
+            args2.setDim(1);
+            (*args2)[0] = e.e2;
+            expandTuples(args2);
+            MatchAccumulator m;
+            functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2));
+            if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors()))
+            {
+                return ErrorExp.get();
+            }
+            if (m.count > 1)
+            {
+                // Error, ambiguous
+                error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars());
+            }
+            else if (m.last == MATCH.nomatch)
+            {
+                if (tiargs)
+                    goto L1;
+                m.lastf = null;
+            }
+            // Rewrite (e1 op e2) as e1.opOpAssign(e2)
+            return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s);
+        }
+    L1:
+        result = checkAliasThisForLhs(ad1, sc, e);
+        if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs
+            return result;
+
+        return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e);
+    }
+
     if (pop)
         *pop = e.op;
 
diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d
index 9db271072d0..e409a7907e9 100644
--- a/gcc/d/dmd/optimize.d
+++ b/gcc/d/dmd/optimize.d
@@ -372,10 +372,10 @@  Expression optimize(Expression e, int result, bool keepLvalue = false)
 
     void visitStructLiteral(StructLiteralExp e)
     {
-        if (e.stageflags & stageOptimize)
+        if (e.stageflags & StructLiteralExp.StageFlags.optimize)
             return;
         const old = e.stageflags;
-        e.stageflags |= stageOptimize;
+        e.stageflags |= StructLiteralExp.StageFlags.optimize;
         if (e.elements)
         {
             foreach (ref ex; (*e.elements)[])
diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d
index 012f764676b..bb2411825fa 100644
--- a/gcc/d/dmd/parse.d
+++ b/gcc/d/dmd/parse.d
@@ -110,43 +110,44 @@  class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         parseModuleAttributes(msg, isdeprecated);
 
         // ModuleDeclaration leads off
-        if (token.value == TOK.module_)
+        if (token.value != TOK.module_)
+            return true;
+
+        const loc = token.loc;
+        nextToken();
+
+        /* parse ModuleFullyQualifiedName
+         * https://dlang.org/spec/module.html#ModuleFullyQualifiedName
+         */
+
+        if (token.value != TOK.identifier)
         {
-            const loc = token.loc;
-            nextToken();
+            error("identifier expected following `module`");
+            return false;
+        }
 
-            /* parse ModuleFullyQualifiedName
-             * https://dlang.org/spec/module.html#ModuleFullyQualifiedName
-             */
+        Identifier[] a;
+        Identifier id = token.ident;
 
+        while (nextToken() == TOK.dot)
+        {
+            a ~= id;
+            nextToken();
             if (token.value != TOK.identifier)
             {
-                error("identifier expected following `module`");
+                error("identifier expected following `package`");
                 return false;
             }
+            id = token.ident;
+        }
 
-            Identifier[] a;
-            Identifier id = token.ident;
-
-            while (nextToken() == TOK.dot)
-            {
-                a ~= id;
-                nextToken();
-                if (token.value != TOK.identifier)
-                {
-                    error("identifier expected following `package`");
-                    return false;
-                }
-                id = token.ident;
-            }
+        md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
 
-            md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated);
+        if (token.value != TOK.semicolon)
+            error("`;` expected following module declaration instead of `%s`", token.toChars());
+        nextToken();
+        addComment(mod, comment);
 
-            if (token.value != TOK.semicolon)
-                error("`;` expected following module declaration instead of `%s`", token.toChars());
-            nextToken();
-            addComment(mod, comment);
-        }
         return true;
     }
 
diff --git a/gcc/d/dmd/postordervisitor.d b/gcc/d/dmd/postordervisitor.d
deleted file mode 100644
index fe189d47e94..00000000000
--- a/gcc/d/dmd/postordervisitor.d
+++ /dev/null
@@ -1,153 +0,0 @@ 
-/**
- * A depth-first visitor for expressions.
- *
- * Copyright:   Copyright (C) 1999-2024 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/src/dmd/apply.d, _apply.d)
- * Documentation:  https://dlang.org/phobos/dmd_apply.html
- * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d
- */
-
-module dmd.postordervisitor;
-
-import dmd.arraytypes;
-import dmd.dtemplate;
-import dmd.expression;
-import dmd.root.array;
-import dmd.visitor;
-
-bool walkPostorder(Expression e, StoppableVisitor v)
-{
-    scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v);
-    e.accept(pv);
-    return v.stop;
-}
-
-/**************************************
- * An Expression tree walker that will visit each Expression e in the tree,
- * in depth-first evaluation order, and call fp(e,param) on it.
- * fp() signals whether the walking continues with its return value:
- * Returns:
- *      0       continue
- *      1       done
- * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
- * Creating an iterator for this would be much more complex.
- */
-private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor
-{
-    alias visit = typeof(super).visit;
-public:
-    StoppableVisitor v;
-
-    extern (D) this(StoppableVisitor v) scope @safe
-    {
-        this.v = v;
-    }
-
-    bool doCond(Expression e)
-    {
-        if (!stop && e)
-            e.accept(this);
-        return stop;
-    }
-
-    extern(D) bool doCond(Expression[] e)
-    {
-        for (size_t i = 0; i < e.length && !stop; i++)
-            doCond(e[i]);
-        return stop;
-    }
-
-    bool applyTo(Expression e)
-    {
-        e.accept(v);
-        stop = v.stop;
-        return true;
-    }
-
-    override void visit(Expression e)
-    {
-        applyTo(e);
-    }
-
-    override void visit(NewExp e)
-    {
-        //printf("NewExp::apply(): %s\n", toChars());
-        doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(NewAnonClassExp e)
-    {
-        //printf("NewAnonClassExp::apply(): %s\n", toChars());
-        doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(TypeidExp e)
-    {
-        doCond(isExpression(e.obj)) || applyTo(e);
-    }
-
-    override void visit(UnaExp e)
-    {
-        doCond(e.e1) || applyTo(e);
-    }
-
-    override void visit(BinExp e)
-    {
-        doCond(e.e1) || doCond(e.e2) || applyTo(e);
-    }
-
-    override void visit(AssertExp e)
-    {
-        //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
-        doCond(e.e1) || doCond(e.msg) || applyTo(e);
-    }
-
-    override void visit(CallExp e)
-    {
-        //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
-        doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(ArrayExp e)
-    {
-        //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
-        doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(SliceExp e)
-    {
-        doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e);
-    }
-
-    override void visit(ArrayLiteralExp e)
-    {
-        doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(AssocArrayLiteralExp e)
-    {
-        doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(StructLiteralExp e)
-    {
-        if (e.stageflags & stageApply)
-            return;
-        const old = e.stageflags;
-        e.stageflags |= stageApply;
-        doCond(e.elements.peekSlice()) || applyTo(e);
-        e.stageflags = old;
-    }
-
-    override void visit(TupleExp e)
-    {
-        doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e);
-    }
-
-    override void visit(CondExp e)
-    {
-        doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e);
-    }
-}
diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d
index b0eb3d1abfa..34cab800868 100644
--- a/gcc/d/dmd/safe.d
+++ b/gcc/d/dmd/safe.d
@@ -51,102 +51,103 @@  bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
     if (e.op != EXP.dotVariable)
         return false;
     auto dve = cast(DotVarExp)e;
-    if (VarDeclaration v = dve.var.isVarDeclaration())
-    {
-        if (!sc.func)
-            return false;
-        auto ad = v.isMember2();
-        if (!ad)
-            return false;
+    VarDeclaration v = dve.var.isVarDeclaration();
+    if (!v)
+        return false;
+    if (!sc.func)
+        return false;
+    auto ad = v.isMember2();
+    if (!ad)
+        return false;
 
-        import dmd.globals : global;
-        if (v.isSystem())
-        {
-            if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc,
-                "cannot access `@system` field `%s.%s` in `@safe` code", ad, v))
-                return true;
-        }
+    import dmd.globals : global;
+    if (v.isSystem())
+    {
+        if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc,
+            "cannot access `@system` field `%s.%s` in `@safe` code", ad, v))
+            return true;
+    }
 
-        // This branch shouldn't be here, but unfortunately calling `ad.determineSize`
-        // breaks code with circular reference errors. Specifically, test23589.d fails
-        if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference())
-            return false;
+    // This branch shouldn't be here, but unfortunately calling `ad.determineSize`
+    // breaks code with circular reference errors. Specifically, test23589.d fails
+    if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference())
+        return false;
 
-        // needed to set v.overlapped and v.overlapUnsafe
-        if (ad.sizeok != Sizeok.done)
-            ad.determineSize(ad.loc);
+    // needed to set v.overlapped and v.overlapUnsafe
+    if (ad.sizeok != Sizeok.done)
+        ad.determineSize(ad.loc);
 
-        import dmd.globals : FeatureState;
-        const hasPointers = v.type.hasPointers();
-        if (hasPointers)
+    import dmd.globals : FeatureState;
+    const hasPointers = v.type.hasPointers();
+    if (hasPointers)
+    {
+        if (v.overlapped)
         {
-            if (v.overlapped)
+            if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc,
+                "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v))
             {
-                if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc,
-                    "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v))
-                {
-                    return true;
-                }
-                else
-                {
-                    // @@@DEPRECATED_2.116@@@
-                    // https://issues.dlang.org/show_bug.cgi?id=20655
-                    // Inferring `@system` because of union access breaks code,
-                    // so make it a deprecation safety violation as of 2.106
-                    // To turn into an error, remove `isSafeBypassingInference` check in the
-                    // above if statement and remove the else branch
-                    sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc,
-                        "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v);
-                }
+                return true;
             }
-        }
-
-        if (v.type.hasInvariant())
-        {
-            if (v.overlapped)
+            else
             {
-                if (sc.setUnsafe(!printmsg, e.loc,
-                    "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
-                    ad, v))
-                    return true;
+                // @@@DEPRECATED_2.116@@@
+                // https://issues.dlang.org/show_bug.cgi?id=20655
+                // Inferring `@system` because of union access breaks code,
+                // so make it a deprecation safety violation as of 2.106
+                // To turn into an error, remove `isSafeBypassingInference` check in the
+                // above if statement and remove the else branch
+                sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc,
+                    "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v);
             }
         }
+    }
 
-        // @@@DEPRECATED_2.119@@@
-        // https://issues.dlang.org/show_bug.cgi?id=24477
-        // Should probably be turned into an error in a new edition
-        if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview(
-            FeatureState.default_, !printmsg, e.loc,
-            "cannot access overlapped field `%s.%s` with unsafe bit patterns in `@safe` code", ad, v)
-        )
+    if (v.type.hasInvariant())
+    {
+        if (v.overlapped)
         {
-            return true;
+            if (sc.setUnsafe(!printmsg, e.loc,
+                "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields",
+                ad, v))
+                return true;
         }
+    }
 
-        if (readonly || !e.type.isMutable())
-            return false;
+    // @@@DEPRECATED_2.119@@@
+    // https://issues.dlang.org/show_bug.cgi?id=24477
+    // Should probably be turned into an error in a new edition
+    if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview(
+        FeatureState.default_, !printmsg, e.loc,
+        "cannot access overlapped field `%s.%s` with unsafe bit patterns in `@safe` code", ad, v)
+    )
+    {
+        return true;
+    }
 
-        if (hasPointers && v.type.toBasetype().ty != Tstruct)
-        {
-            if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) ||
-                 (v.offset & (target.ptrsize - 1)))
-            {
-                if (sc.setUnsafe(!printmsg, e.loc,
-                    "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v))
-                    return true;
-            }
-        }
+    if (readonly || !e.type.isMutable())
+        return false;
 
-        if (v.overlapUnsafe)
+    if (hasPointers && v.type.toBasetype().ty != Tstruct)
+    {
+        if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) ||
+             (v.offset & (target.ptrsize - 1)))
         {
             if (sc.setUnsafe(!printmsg, e.loc,
-                "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
-                ad, v))
-            {
+                "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v))
                 return true;
-            }
         }
     }
+
+    if (v.overlapUnsafe)
+    {
+        if (sc.setUnsafe(!printmsg, e.loc,
+            "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes",
+            ad, v))
+        {
+            return true;
+        }
+    }
+
     return false;
 }
 
diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d
index afdc87f0b3d..24bdd449d5e 100644
--- a/gcc/d/dmd/semantic3.d
+++ b/gcc/d/dmd/semantic3.d
@@ -1685,3 +1685,72 @@  void semanticTypeInfoMembers(StructDeclaration sd)
         sd.dtor.semantic3(sd.dtor._scope);
     }
 }
+
+/***********************************************
+ * Check that the function contains any closure.
+ * If it's @nogc, report suitable errors.
+ * This is mostly consistent with FuncDeclaration::needsClosure().
+ *
+ * Returns:
+ *      true if any errors occur.
+ */
+extern (D) bool checkClosure(FuncDeclaration fd)
+{
+    //printf("checkClosure() %s\n", toPrettyChars());
+    if (!fd.needsClosure())
+        return false;
+
+    if (fd.setGC(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd))
+    {
+        .error(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars());
+        if (global.gag)     // need not report supplemental errors
+            return true;
+    }
+    else if (!global.params.useGC)
+    {
+        .error(fd.loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars());
+        if (global.gag)     // need not report supplemental errors
+            return true;
+    }
+    else
+    {
+        fd.printGCUsage(fd.loc, "using closure causes GC allocation");
+        return false;
+    }
+
+    FuncDeclarations a;
+    foreach (v; fd.closureVars)
+    {
+        foreach (f; v.nestedrefs)
+        {
+            assert(f !is fd);
+
+        LcheckAncestorsOfANestedRef:
+            for (Dsymbol s = f; s && s !is fd; s = s.toParentP(fd))
+            {
+                auto fx = s.isFuncDeclaration();
+                if (!fx)
+                    continue;
+                if (fx.isThis() ||
+                    fx.tookAddressOf ||
+                    checkEscapingSiblings(fx, fd))
+                {
+                    foreach (f2; a)
+                    {
+                        if (f2 == f)
+                            break LcheckAncestorsOfANestedRef;
+                    }
+                    a.push(f);
+                    .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
+                        f.kind, f.toPrettyChars(), v.toChars());
+                    if (v.ident != Id.This)
+                        .errorSupplemental(v.loc, "`%s` declared here", v.toChars());
+
+                    break LcheckAncestorsOfANestedRef;
+                }
+            }
+        }
+    }
+
+    return true;
+}
diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d
index 3ed594021e7..84b9e4e6530 100644
--- a/gcc/d/dmd/sideeffect.d
+++ b/gcc/d/dmd/sideeffect.d
@@ -24,10 +24,10 @@  import dmd.id;
 import dmd.identifier;
 import dmd.init;
 import dmd.mtype;
-import dmd.postordervisitor;
 import dmd.tokens;
 import dmd.typesem;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 /**************************************************
  * Front-end expression rewriting should create temporary variables for
diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d
index 81891ff7aa6..b53c3d085b9 100644
--- a/gcc/d/dmd/statement.d
+++ b/gcc/d/dmd/statement.d
@@ -30,10 +30,10 @@  import dmd.identifier;
 import dmd.location;
 import dmd.mtype;
 import dmd.rootobject;
-import dmd.sapply;
 import dmd.staticassert;
 import dmd.tokens;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 /***********************************************************
  * Specification: https://dlang.org/spec/statement.html
diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d
index d86be85b733..cc44b6c0c3b 100644
--- a/gcc/d/dmd/target.d
+++ b/gcc/d/dmd/target.d
@@ -114,6 +114,7 @@  extern (C++) struct Target
     /// Architecture name
     const(char)[] architectureName;
     CPU cpu;                // CPU instruction set to target
+    bool isAArch64;         // generate 64 bit Arm code
     bool isX86_64;          // generate 64 bit code for x86_64; true by default for 64 bit dmd
     bool isX86;             // generate 32 bit Intel x86 code
     bool isLP64;            // pointers are 64 bits
diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h
index a380bb8bbe5..2284b8322e7 100644
--- a/gcc/d/dmd/target.h
+++ b/gcc/d/dmd/target.h
@@ -153,6 +153,7 @@  struct Target
 
     DString architectureName;    // name of the platform architecture (e.g. X86_64)
     CPU cpu;                // CPU instruction set to target
+    d_bool isAArch64;         // generate 64 bit Arm code
     d_bool isX86_64;          // generate 64 bit code for x86_64; true by default for 64 bit dmd
     d_bool isX86;             // generate 32 bit Intel x86 code
     d_bool isLP64;            // pointers are 64 bits
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
index 946df2d36a2..1e7386147b1 100644
--- a/gcc/d/dmd/traits.d
+++ b/gcc/d/dmd/traits.d
@@ -338,7 +338,9 @@  Expression semanticTraits(TraitsExp e, Scope* sc)
         const save = sc.stc;
         if (e.ident == Id.isDeprecated)
             sc.stc |= STC.deprecated_;
-        if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1))
+        Scope* sc2 = sc.startCTFE();
+        scope(exit) { sc2.endCTFE(); }
+        if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1))
         {
             sc.stc = save;
             return ErrorExp.get();
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index 6bb5177cf0a..a35a6eba73f 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -53,6 +53,7 @@  import dmd.initsem;
 import dmd.location;
 import dmd.visitor;
 import dmd.mtype;
+import dmd.nogc;
 import dmd.objc;
 import dmd.opover;
 import dmd.optimize;
@@ -891,66 +892,70 @@  private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct,
     if (dmd.expressionsem.trySemantic(e, sc))
         return true;
 
-    if (pMessage)
-    {
-        /* https://issues.dlang.org/show_bug.cgi?id=22202
-         *
-         * If a function was deduced by semantic on the CallExp,
-         * it means that resolveFuncCall completed succesfully.
-         * Therefore, there exists a callable copy constructor,
-         * however, it cannot be called because scope constraints
-         * such as purity, safety or nogc.
-         */
-        OutBuffer buf;
-        auto callExp = e.isCallExp();
-        if (auto f = callExp.f)
-        {
-            char[] s;
-            if (!f.isPure && sc.func.setImpure())
-                s ~= "pure ";
-            if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe())
-                s ~= "@safe ";
-            if (!f.isNogc && sc.func.setGC(arg.loc, null))
-                s ~= "nogc ";
-            if (f.isDisabled() && !f.isGenerated())
-            {
-                /* https://issues.dlang.org/show_bug.cgi?id=24301
-                 * Copy constructor is explicitly disabled
-                 */
-                buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`",
-                    f.type.toChars());
-            }
-            else if (s)
-            {
-                s[$-1] = '\0';
-                buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr);
-            }
-            else if (f.isGenerated() && f.isDisabled())
-            {
-                /* https://issues.dlang.org/show_bug.cgi?id=23097
-                 * Compiler generated copy constructor failed.
-                 */
-                buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable",
-                           argStruct.toChars());
-            }
-            else
-            {
-                /* Although a copy constructor may exist, no suitable match was found.
-                 * i.e: `inout` constructor creates `const` object, not mutable.
-                 * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202.
-                 */
-                goto Lnocpctor;
-            }
-        }
-        else
-        {
-        Lnocpctor:
-            buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies",
-                       argStruct.toChars(), arg.type.toChars(), tprm.toChars());
-        }
+    if (!pMessage)
+        return false;
 
+    /* https://issues.dlang.org/show_bug.cgi?id=22202
+     *
+     * If a function was deduced by semantic on the CallExp,
+     * it means that resolveFuncCall completed succesfully.
+     * Therefore, there exists a callable copy constructor,
+     * however, it cannot be called because scope constraints
+     * such as purity, safety or nogc.
+     */
+    OutBuffer buf;
+    auto callExp = e.isCallExp();
+    void nocpctor()
+    {
+        buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies",
+                   argStruct.toChars(), arg.type.toChars(), tprm.toChars());
+    }
+    auto f = callExp.f;
+    if (!f)
+    {
+        nocpctor();
         *pMessage = buf.extractChars();
+        return false;
+    }
+    char[] s;
+    if (!f.isPure && sc.func.setImpure())
+        s ~= "pure ";
+    if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe())
+        s ~= "@safe ";
+    if (!f.isNogc && sc.func.setGC(arg.loc, null))
+        s ~= "nogc ";
+    if (f.isDisabled() && !f.isGenerated())
+    {
+        /* https://issues.dlang.org/show_bug.cgi?id=24301
+         * Copy constructor is explicitly disabled
+         */
+        buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`",
+            f.type.toChars());
+    }
+    else if (s)
+    {
+        s[$-1] = '\0';
+        buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr);
+    }
+    else if (f.isGenerated() && f.isDisabled())
+    {
+        /* https://issues.dlang.org/show_bug.cgi?id=23097
+         * Compiler generated copy constructor failed.
+         */
+        buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable",
+                   argStruct.toChars());
     }
+    else
+    {
+        /* Although a copy constructor may exist, no suitable match was found.
+         * i.e: `inout` constructor creates `const` object, not mutable.
+         * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202.
+         */
+        nocpctor();
+    }
+
+    *pMessage = buf.extractChars();
+
     return false;
 }
 
@@ -1017,101 +1022,102 @@  private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p,
     }
 
     // Non-lvalues do not match ref or out parameters
-    if (p.isReference())
-    {
-        // https://issues.dlang.org/show_bug.cgi?id=13783
-        // Don't use toBasetype() to handle enum types.
-        Type ta = targ;
-        Type tp = tprm;
-        //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars());
+    if (!p.isReference())
+        return m;
 
-        if (m && !arg.isLvalue())
-        {
-            if (p.storageClass & STC.out_)
-            {
-                if (pMessage) *pMessage = tf.getParamError(arg, p);
-                return MATCH.nomatch;
-            }
+    // https://issues.dlang.org/show_bug.cgi?id=13783
+    // Don't use toBasetype() to handle enum types.
+    Type ta = targ;
+    Type tp = tprm;
+    //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars());
 
-            if (arg.op == EXP.string_ && tp.ty == Tsarray)
-            {
-                if (ta.ty != Tsarray)
-                {
-                    Type tn = tp.nextOf().castMod(ta.nextOf().mod);
-                    dinteger_t dim = (cast(StringExp)arg).len;
-                    ta = tn.sarrayOf(dim);
-                }
-            }
-            else if (arg.op == EXP.slice && tp.ty == Tsarray)
-            {
-                // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
-                if (ta.ty != Tsarray)
-                {
-                    Type tn = ta.nextOf();
-                    dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
-                    ta = tn.sarrayOf(dim);
-                }
-            }
-            else if (p.storageClass & STC.constscoperef)
-            {
-                // Allow converting a literal to an `in` which is `ref`
-                if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray)
-                {
-                    Type tn = tp.nextOf();
-                    dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
-                    ta = tn.sarrayOf(dim);
-                }
+    if (m && !arg.isLvalue())
+    {
+        if (p.storageClass & STC.out_)
+        {
+            if (pMessage) *pMessage = tf.getParamError(arg, p);
+            return MATCH.nomatch;
+        }
 
-                // Need to make this a rvalue through a temporary
-                m = MATCH.convert;
-            }
-            else if (global.params.rvalueRefParam != FeatureState.enabled ||
-                     p.storageClass & STC.out_ ||
-                     !arg.type.isCopyable())  // can't copy to temp for ref parameter
+        if (arg.op == EXP.string_ && tp.ty == Tsarray)
+        {
+            if (ta.ty != Tsarray)
             {
-                if (pMessage) *pMessage = tf.getParamError(arg, p);
-                return MATCH.nomatch;
+                Type tn = tp.nextOf().castMod(ta.nextOf().mod);
+                dinteger_t dim = (cast(StringExp)arg).len;
+                ta = tn.sarrayOf(dim);
             }
-            else
+        }
+        else if (arg.op == EXP.slice && tp.ty == Tsarray)
+        {
+            // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
+            if (ta.ty != Tsarray)
             {
-                /* in functionParameters() we'll convert this
-                 * rvalue into a temporary
-                 */
-                m = MATCH.convert;
+                Type tn = ta.nextOf();
+                dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
+                ta = tn.sarrayOf(dim);
             }
         }
-
-        /* If the match is not already perfect or if the arg
-           is not a lvalue then try the `alias this` chain
-           see  https://issues.dlang.org/show_bug.cgi?id=15674
-           and https://issues.dlang.org/show_bug.cgi?id=21905
-        */
-        if (ta != tp || !arg.isLvalue())
+        else if (p.storageClass & STC.constscoperef)
         {
-            Type firsttab = ta.toBasetype();
-            while (1)
+            // Allow converting a literal to an `in` which is `ref`
+            if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray)
             {
-                Type tab = ta.toBasetype();
-                Type tat = tab.aliasthisOf();
-                if (!tat || !tat.implicitConvTo(tprm))
-                    break;
-                if (tat == tab || tat == firsttab)
-                    break;
-                ta = tat;
+                Type tn = tp.nextOf();
+                dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
+                ta = tn.sarrayOf(dim);
             }
-        }
 
-        /* A ref variable should work like a head-const reference.
-         * e.g. disallows:
-         *  ref T      <- an lvalue of const(T) argument
-         *  ref T[dim] <- an lvalue of const(T[dim]) argument
-         */
-        if (!ta.constConv(tp))
+            // Need to make this a rvalue through a temporary
+            m = MATCH.convert;
+        }
+        else if (global.params.rvalueRefParam != FeatureState.enabled ||
+                 p.storageClass & STC.out_ ||
+                 !arg.type.isCopyable())  // can't copy to temp for ref parameter
         {
             if (pMessage) *pMessage = tf.getParamError(arg, p);
             return MATCH.nomatch;
         }
+        else
+        {
+            /* in functionParameters() we'll convert this
+             * rvalue into a temporary
+             */
+            m = MATCH.convert;
+        }
+    }
+
+    /* If the match is not already perfect or if the arg
+       is not a lvalue then try the `alias this` chain
+       see  https://issues.dlang.org/show_bug.cgi?id=15674
+       and https://issues.dlang.org/show_bug.cgi?id=21905
+    */
+    if (ta != tp || !arg.isLvalue())
+    {
+        Type firsttab = ta.toBasetype();
+        while (1)
+        {
+            Type tab = ta.toBasetype();
+            Type tat = tab.aliasthisOf();
+            if (!tat || !tat.implicitConvTo(tprm))
+                break;
+            if (tat == tab || tat == firsttab)
+                break;
+            ta = tat;
+        }
     }
+
+    /* A ref variable should work like a head-const reference.
+     * e.g. disallows:
+     *  ref T      <- an lvalue of const(T) argument
+     *  ref T[dim] <- an lvalue of const(T[dim]) argument
+     */
+    if (!ta.constConv(tp))
+    {
+        if (pMessage) *pMessage = tf.getParamError(arg, p);
+        return MATCH.nomatch;
+    }
+
     return m;
 }
 
@@ -2676,53 +2682,51 @@  Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco);
             return t.addMod(mtype.mod);
         }
-        else
-        {
-            if (s)
-            {
-                auto td = s.isTemplateDeclaration;
-                if (td && td.onemember && td.onemember.isAggregateDeclaration)
-                    .error(loc, "template %s `%s` is used as a type without instantiation"
-                        ~ "; to instantiate it use `%s!(arguments)`",
-                        s.kind, s.toPrettyChars, s.ident.toChars);
-                else
-                    .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars);
-                //assert(0);
-            }
-            else if (e.op == EXP.variable) // special case: variable is used as a type
-            {
-                /*
-                    N.B. This branch currently triggers for the following code
-                    template test(x* x)
-                    {
 
-                    }
-                    i.e. the compiler prints "variable x is used as a type"
-                    which isn't a particularly good error message (x is a variable?).
-                */
-                Dsymbol varDecl = mtype.toDsymbol(sc);
-                const(Loc) varDeclLoc = varDecl.getLoc();
-                Module varDeclModule = varDecl.getModule(); //This can be null
-
-                .error(loc, "variable `%s` is used as a type", mtype.toChars());
-                //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574
-                if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported
+        if (s)
+        {
+            auto td = s.isTemplateDeclaration;
+            if (td && td.onemember && td.onemember.isAggregateDeclaration)
+                .error(loc, "template %s `%s` is used as a type without instantiation"
+                    ~ "; to instantiate it use `%s!(arguments)`",
+                    s.kind, s.toPrettyChars, s.ident.toChars);
+            else
+                .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars);
+            //assert(0);
+        }
+        else if (e.op == EXP.variable) // special case: variable is used as a type
+        {
+            /*
+                N.B. This branch currently triggers for the following code
+                template test(x* x)
                 {
-                    const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc();
-                    .errorSupplemental(
-                        varDeclModuleImportLoc,
-                        "variable `%s` is imported here from: `%s`",
-                        varDecl.toChars,
-                        varDeclModule.toPrettyChars,
-                    );
+
                 }
+                i.e. the compiler prints "variable x is used as a type"
+                which isn't a particularly good error message (x is a variable?).
+            */
+            Dsymbol varDecl = mtype.toDsymbol(sc);
+            const(Loc) varDeclLoc = varDecl.getLoc();
+            Module varDeclModule = varDecl.getModule(); //This can be null
 
-                .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars);
+            .error(loc, "variable `%s` is used as a type", mtype.toChars());
+            //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574
+            if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported
+            {
+                const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc();
+                .errorSupplemental(
+                    varDeclModuleImportLoc,
+                    "variable `%s` is imported here from: `%s`",
+                    varDecl.toChars,
+                    varDeclModule.toPrettyChars,
+                );
             }
-            else
-                .error(loc, "`%s` is used as a type", mtype.toChars());
-            return error();
+
+            .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars);
         }
+        else
+            .error(loc, "`%s` is used as a type", mtype.toChars());
+        return error();
     }
 
     Type visitInstance(TypeInstance mtype)
@@ -3216,37 +3220,36 @@  Type merge(Type type)
     }
 
     //printf("merge(%s)\n", toChars());
-    if (!type.deco)
-    {
-        OutBuffer buf;
-        buf.reserve(32);
+    if (type.deco)
+        return type;
 
-        mangleToBuffer(type, buf);
+    OutBuffer buf;
+    buf.reserve(32);
 
-        auto sv = type.stringtable.update(buf[]);
-        if (sv.value)
-        {
-            Type t = sv.value;
-            debug
-            {
-                import core.stdc.stdio;
-                if (!t.deco)
-                    printf("t = %s\n", t.toChars());
-            }
-            assert(t.deco);
-            //printf("old value, deco = '%s' %p\n", t.deco, t.deco);
-            return t;
-        }
-        else
+    mangleToBuffer(type, buf);
+
+    auto sv = type.stringtable.update(buf[]);
+    if (sv.value)
+    {
+        Type t = sv.value;
+        debug
         {
-            Type t = stripDefaultArgs(type);
-            sv.value = t;
-            type.deco = t.deco = cast(char*)sv.toDchars();
-            //printf("new value, deco = '%s' %p\n", t.deco, t.deco);
-            return t;
+            import core.stdc.stdio;
+            if (!t.deco)
+                printf("t = %s\n", t.toChars());
         }
+        assert(t.deco);
+        //printf("old value, deco = '%s' %p\n", t.deco, t.deco);
+        return t;
+    }
+    else
+    {
+        Type t = stripDefaultArgs(type);
+        sv.value = t;
+        type.deco = t.deco = cast(char*)sv.toDchars();
+        //printf("new value, deco = '%s' %p\n", t.deco, t.deco);
+        return t;
     }
-    return type;
 }
 
 /*************************************
@@ -3300,14 +3303,14 @@  Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
             const sz = mt.size(loc);
             if (sz == SIZE_INVALID)
                 return ErrorExp.get();
-            e = new IntegerExp(loc, sz, Type.tsize_t);
+            return new IntegerExp(loc, sz, Type.tsize_t);
         }
         else if (ident == Id.__xalignof)
         {
             const explicitAlignment = mt.alignment();
             const naturalAlignment = mt.alignsize();
             const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get());
-            e = new IntegerExp(loc, actualAlignment, Type.tsize_t);
+            return new IntegerExp(loc, actualAlignment, Type.tsize_t);
         }
         else if (ident == Id._init)
         {
@@ -3317,13 +3320,14 @@  Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
             {
                 e.isStructLiteralExp().useStaticInit = true;
             }
+            return e;
         }
         else if (ident == Id._mangleof)
         {
             if (!mt.deco)
             {
                 error(loc, "forward reference of type `%s.mangleof`", mt.toChars());
-                e = ErrorExp.get();
+                return ErrorExp.get();
             }
             else
             {
@@ -3332,6 +3336,7 @@  Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
                 sc.eSink = global.errorSink;
                 e = e.expressionSemantic(&sc);
             }
+            return e;
         }
         else if (ident == Id.stringof)
         {
@@ -3340,74 +3345,74 @@  Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
             Scope sc;
             sc.eSink = global.errorSink;
             e = e.expressionSemantic(&sc);
+            return e;
         }
         else if (flag && mt != Type.terror)
         {
             return null;
         }
+
+        Dsymbol s = null;
+        if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum)
+            s = mt.toDsymbol(null);
+        if (s)
+            s = s.search_correct(ident);
+        if (s && !symbolIsVisible(scope_, s))
+            s = null;
+
+        if (mt == Type.terror)
+            return ErrorExp.get();
+
+        if (s)
+            error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars());
+        else if (ident == Id.call && mt.ty == Tclass)
+            error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars());
+
+        else if (const n = importHint(ident.toString()))
+                error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr);
         else
         {
-            Dsymbol s = null;
-            if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum)
-                s = mt.toDsymbol(null);
-            if (s)
-                s = s.search_correct(ident);
-            if (s && !symbolIsVisible(scope_, s))
-                s = null;
-            if (mt != Type.terror)
-            {
-                if (s)
-                    error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars());
-                else if (ident == Id.call && mt.ty == Tclass)
-                    error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars());
-
-                else if (const n = importHint(ident.toString()))
-                        error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr);
-                else
+            if (src)
+            {
+                error(loc, "no property `%s` for `%s` of type `%s`",
+                    ident.toChars(), src.toChars(), mt.toPrettyChars(true));
+                auto s2 = scope_.search_correct(ident);
+                // UFCS
+                if (s2 && s2.isFuncDeclaration)
+                    errorSupplemental(loc, "did you mean %s `%s`?",
+                        s2.kind(), s2.toChars());
+                else if (src.type.ty == Tpointer)
                 {
-                    if (src)
+                    // structPtr.field
+                    auto tn = (cast(TypeNext) src.type).nextOf();
+                    if (auto as = tn.isAggregate())
                     {
-                        error(loc, "no property `%s` for `%s` of type `%s`",
-                            ident.toChars(), src.toChars(), mt.toPrettyChars(true));
-                        auto s2 = scope_.search_correct(ident);
-                        // UFCS
-                        if (s2 && s2.isFuncDeclaration)
-                            errorSupplemental(loc, "did you mean %s `%s`?",
-                                s2.kind(), s2.toChars());
-                        else if (src.type.ty == Tpointer)
+                        if (auto s3 = as.search_correct(ident))
                         {
-                            // structPtr.field
-                            auto tn = (cast(TypeNext) src.type).nextOf();
-                            if (auto as = tn.isAggregate())
-                            {
-                                if (auto s3 = as.search_correct(ident))
-                                {
-                                    errorSupplemental(loc, "did you mean %s `%s`?",
-                                        s3.kind(), s3.toChars());
-                                }
-                            }
+                            errorSupplemental(loc, "did you mean %s `%s`?",
+                                s3.kind(), s3.toChars());
                         }
                     }
-                    else
-                        error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
+                }
+            }
+            else
+                error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true));
 
-                    if (auto dsym = mt.toDsymbol(scope_))
-                    {
-                        if (auto sym = dsym.isAggregateDeclaration())
-                        {
-                            if (auto fd = search_function(sym, Id.opDispatch))
-                                errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message");
-                            else if (!sym.members)
-                                errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true));
-                        }
-                        errorSupplemental(dsym.loc, "%s `%s` defined here",
-                            dsym.kind, dsym.toChars());
-                    }
+            if (auto dsym = mt.toDsymbol(scope_))
+            {
+                if (auto sym = dsym.isAggregateDeclaration())
+                {
+                    if (auto fd = search_function(sym, Id.opDispatch))
+                        errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message");
+                    else if (!sym.members)
+                        errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true));
                 }
+                errorSupplemental(dsym.loc, "%s `%s` defined here",
+                    dsym.kind, dsym.toChars());
             }
-            e = ErrorExp.get();
         }
-        return e;
+
+        return ErrorExp.get();
     }
 
     Expression visitError(TypeError)
diff --git a/gcc/d/dmd/foreachvar.d b/gcc/d/dmd/visitor/foreachvar.d
similarity index 99%
rename from gcc/d/dmd/foreachvar.d
rename to gcc/d/dmd/visitor/foreachvar.d
index 53b3c041d1e..229ade58d76 100644
--- a/gcc/d/dmd/foreachvar.d
+++ b/gcc/d/dmd/visitor/foreachvar.d
@@ -9,7 +9,7 @@ 
  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/foreachvar.d
  */
 
-module dmd.foreachvar;
+module dmd.visitor.foreachvar;
 
 import core.stdc.stdio;
 import core.stdc.stdlib;
@@ -32,13 +32,13 @@  import dmd.identifier;
 import dmd.init;
 import dmd.initsem;
 import dmd.mtype;
-import dmd.postordervisitor;
 import dmd.printast;
 import dmd.root.array;
 import dmd.rootobject;
 import dmd.statement;
 import dmd.tokens;
 import dmd.visitor;
+import dmd.visitor.postorder;
 
 /*********************************************
  * Visit each Expression in e, and call dgVar() on each variable declared in it.
diff --git a/gcc/d/dmd/visitor.d b/gcc/d/dmd/visitor/package.d
similarity index 97%
rename from gcc/d/dmd/visitor.d
rename to gcc/d/dmd/visitor/package.d
index 9082909ba2e..8c1d034b234 100644
--- a/gcc/d/dmd/visitor.d
+++ b/gcc/d/dmd/visitor/package.d
@@ -12,12 +12,10 @@ 
 module dmd.visitor;
 
 import dmd.astcodegen;
-import dmd.astenums;
-import dmd.parsetimevisitor;
 import dmd.tokens;
-import dmd.transitivevisitor;
-import dmd.expression;
 import dmd.rootobject;
+import dmd.visitor.parsetime;
+import dmd.visitor.transitive;
 
 /**
  * Classic Visitor class which implements visit methods for all the AST
@@ -151,10 +149,11 @@  extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor
     {
         // CTFE can generate struct literals that contain an AddrExp pointing to themselves,
         // need to avoid infinite recursion.
-        if (!(e.stageflags & stageToCBuffer))
+        alias flag = ASTCodegen.StructLiteralExp.StageFlags.toCBuffer;
+        if (!(e.stageflags & flag))
         {
             const old = e.stageflags;
-            e.stageflags |= stageToCBuffer;
+            e.stageflags |= flag;
             foreach (el; *e.elements)
                 if (el)
                     el.accept(this);
@@ -250,7 +249,7 @@  extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor
     override void visit(ASTCodegen.LoweredAssignExp e)
     {
         e.lowering.accept(this);
-        visit(cast(AssignExp)e);
+        visit(cast(ASTCodegen.AssignExp)e);
     }
 }
 
diff --git a/gcc/d/dmd/parsetimevisitor.d b/gcc/d/dmd/visitor/parsetime.d
similarity index 99%
rename from gcc/d/dmd/parsetimevisitor.d
rename to gcc/d/dmd/visitor/parsetime.d
index c03f78d4de9..914ca413c77 100644
--- a/gcc/d/dmd/parsetimevisitor.d
+++ b/gcc/d/dmd/visitor/parsetime.d
@@ -7,7 +7,7 @@ 
  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parsetimevisitor.d
  */
 
-module dmd.parsetimevisitor;
+module dmd.visitor.parsetime;
 
 /** Basic and dumm visitor which implements a visit method for each AST node
   * implemented in AST. This visitor is the parent of strict, transitive
diff --git a/gcc/d/dmd/permissivevisitor.d b/gcc/d/dmd/visitor/permissive.d
similarity index 93%
rename from gcc/d/dmd/permissivevisitor.d
rename to gcc/d/dmd/visitor/permissive.d
index 5d7f3fcba2c..ef1f279002f 100644
--- a/gcc/d/dmd/permissivevisitor.d
+++ b/gcc/d/dmd/visitor/permissive.d
@@ -5,9 +5,9 @@ 
  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/permissivevisitor.d
  */
 
-module dmd.permissivevisitor;
+module dmd.visitor.permissive;
 
-import dmd.parsetimevisitor;
+import dmd.visitor.parsetime;
 
 /** PermissiveVisitor overrides all the visit methods in  the parent class
   * that assert(0) in order to facilitate the traversal of subsets of the AST.
diff --git a/gcc/d/dmd/sapply.d b/gcc/d/dmd/visitor/postorder.d
similarity index 50%
rename from gcc/d/dmd/sapply.d
rename to gcc/d/dmd/visitor/postorder.d
index 340fbad78d4..af12f1e9e9e 100644
--- a/gcc/d/dmd/sapply.d
+++ b/gcc/d/dmd/visitor/postorder.d
@@ -1,19 +1,29 @@ 
 /**
- * Provides a depth-first statement visitor.
+ * A depth-first visitor for expressions and statements.
  *
  * Copyright:   Copyright (C) 1999-2024 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/src/dmd/sparse.d, _sparse.d)
- * Documentation:  https://dlang.org/phobos/dmd_sapply.html
- * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sapply.d
+ * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/apply.d, _apply.d)
+ * Documentation:  https://dlang.org/phobos/dmd_apply.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d
  */
 
-module dmd.sapply;
+module dmd.visitor.postorder;
 
+import dmd.dtemplate;
+import dmd.expression;
+import dmd.root.array;
 import dmd.statement;
 import dmd.visitor;
 
+bool walkPostorder(Expression e, StoppableVisitor v)
+{
+    scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v);
+    e.accept(pv);
+    return v.stop;
+}
+
 bool walkPostorder(Statement s, StoppableVisitor v)
 {
     scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v);
@@ -21,6 +31,135 @@  bool walkPostorder(Statement s, StoppableVisitor v)
     return v.stop;
 }
 
+private:
+/**************************************
+ * An Expression tree walker that will visit each Expression e in the tree,
+ * in depth-first evaluation order, and call fp(e,param) on it.
+ * fp() signals whether the walking continues with its return value:
+ * Returns:
+ *      0       continue
+ *      1       done
+ * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
+ * Creating an iterator for this would be much more complex.
+ */
+extern (C++) final class PostorderExpressionVisitor : StoppableVisitor
+{
+    alias visit = typeof(super).visit;
+public:
+    StoppableVisitor v;
+
+    extern (D) this(StoppableVisitor v) scope @safe
+    {
+        this.v = v;
+    }
+
+    bool doCond(Expression e)
+    {
+        if (!stop && e)
+            e.accept(this);
+        return stop;
+    }
+
+    extern(D) bool doCond(Expression[] e)
+    {
+        for (size_t i = 0; i < e.length && !stop; i++)
+            doCond(e[i]);
+        return stop;
+    }
+
+    bool applyTo(Expression e)
+    {
+        e.accept(v);
+        stop = v.stop;
+        return true;
+    }
+
+    override void visit(Expression e)
+    {
+        applyTo(e);
+    }
+
+    override void visit(NewExp e)
+    {
+        //printf("NewExp::apply(): %s\n", toChars());
+        doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(NewAnonClassExp e)
+    {
+        //printf("NewAnonClassExp::apply(): %s\n", toChars());
+        doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(TypeidExp e)
+    {
+        doCond(isExpression(e.obj)) || applyTo(e);
+    }
+
+    override void visit(UnaExp e)
+    {
+        doCond(e.e1) || applyTo(e);
+    }
+
+    override void visit(BinExp e)
+    {
+        doCond(e.e1) || doCond(e.e2) || applyTo(e);
+    }
+
+    override void visit(AssertExp e)
+    {
+        //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
+        doCond(e.e1) || doCond(e.msg) || applyTo(e);
+    }
+
+    override void visit(CallExp e)
+    {
+        //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
+        doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(ArrayExp e)
+    {
+        //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
+        doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(SliceExp e)
+    {
+        doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e);
+    }
+
+    override void visit(ArrayLiteralExp e)
+    {
+        doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(AssocArrayLiteralExp e)
+    {
+        doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(StructLiteralExp e)
+    {
+        if (e.stageflags & StructLiteralExp.StageFlags.apply)
+            return;
+        const old = e.stageflags;
+        e.stageflags |= StructLiteralExp.StageFlags.apply;
+        doCond(e.elements.peekSlice()) || applyTo(e);
+        e.stageflags = old;
+    }
+
+    override void visit(TupleExp e)
+    {
+        doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e);
+    }
+
+    override void visit(CondExp e)
+    {
+        doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e);
+    }
+}
+
 /**************************************
  * A Statement tree walker that will visit each Statement s in the tree,
  * in depth-first evaluation order, and call fp(s,param) on it.
@@ -31,7 +170,7 @@  bool walkPostorder(Statement s, StoppableVisitor v)
  * It's a bit slower than using virtual functions, but more encapsulated and less brittle.
  * Creating an iterator for this would be much more complex.
  */
-private extern (C++) final class PostorderStatementVisitor : StoppableVisitor
+extern (C++) final class PostorderStatementVisitor : StoppableVisitor
 {
     alias visit = typeof(super).visit;
 public:
diff --git a/gcc/d/dmd/statement_rewrite_walker.d b/gcc/d/dmd/visitor/statement_rewrite_walker.d
similarity index 98%
rename from gcc/d/dmd/statement_rewrite_walker.d
rename to gcc/d/dmd/visitor/statement_rewrite_walker.d
index 221c5021c52..aeb5ac8263d 100644
--- a/gcc/d/dmd/statement_rewrite_walker.d
+++ b/gcc/d/dmd/visitor/statement_rewrite_walker.d
@@ -9,7 +9,7 @@ 
  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement_rewrite_walker.d
  */
 
-module dmd.statement_rewrite_walker;
+module dmd.visitor.statement_rewrite_walker;
 
 import core.stdc.stdio;
 
diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/visitor/transitive.d
similarity index 99%
rename from gcc/d/dmd/transitivevisitor.d
rename to gcc/d/dmd/visitor/transitive.d
index bf1d38e61ca..952460c18cb 100644
--- a/gcc/d/dmd/transitivevisitor.d
+++ b/gcc/d/dmd/visitor/transitive.d
@@ -3,10 +3,10 @@ 
  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/transitivevisitor.d
  */
 
-module dmd.transitivevisitor;
+module dmd.visitor.transitive;
 
 import dmd.astenums;
-import dmd.permissivevisitor;
+import dmd.visitor.permissive;
 import dmd.tokens;
 import dmd.rootobject;
 
@@ -26,7 +26,7 @@  extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST
  * is used for semantic time AST node traversal, so in order to not duplicate the code,
  * the template mixin is used.
  */
-package mixin template ParseVisitMethods(AST)
+package(dmd.visitor) mixin template ParseVisitMethods(AST)
 {
     import dmd.root.array;
 
@@ -854,7 +854,7 @@  package mixin template ParseVisitMethods(AST)
     override void visit(AST.FuncLiteralDeclaration f)
     {
         //printf("Visiting FuncLiteralDeclaration\n");
-        if (f.type.ty == Terror)
+        if (f.type.isTypeError())
             return;
         auto tf = f.type.isTypeFunction();
         if (!f.inferRetType && tf.next)
diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp23812.c b/gcc/testsuite/gdc.test/compilable/imports/imp23812.c
new file mode 100644
index 00000000000..26a600a776e
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/imp23812.c
@@ -0,0 +1,35 @@ 
+
+void funcDefault(void);
+
+#pragma attribute(push, nothrow)
+void funcNothrow(void);
+#pragma attribute(pop)
+
+#pragma attribute(push, nogc)
+void funcNogc(void);
+#pragma attribute(pop)
+
+#pragma attribute(push, pure)
+void funcPure(void);
+#pragma attribute(pop)
+
+#pragma attribute(push, nothrow, nogc)
+void funcNothrowNogc(void);
+#pragma attribute(pop)
+
+void funcDefault2(void);
+
+#pragma attribute(push, nothrow)
+#pragma attribute(push, nogc)
+void funcNothrowNogc2(void);
+#pragma attribute(pop)
+void funcNothrow2(void);
+#pragma attribute(pop)
+
+#pragma attribute(push, nothrow)
+void funcWithCallback(void (*f)(void));
+struct Callbacks
+{
+    void (*f)(void);
+};
+#pragma attribute(pop)
diff --git a/gcc/testsuite/gdc.test/compilable/test23812.d b/gcc/testsuite/gdc.test/compilable/test23812.d
new file mode 100644
index 00000000000..502a9d0a2a9
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test23812.d
@@ -0,0 +1,75 @@ 
+// EXTRA_FILES: imports/imp23812.c
+
+import imports.imp23812;
+
+void callDefault()
+{
+    funcDefault();
+    funcDefault2();
+}
+
+static assert(!__traits(compiles, () nothrow { funcDefault(); } ));
+static assert(!__traits(compiles, () @nogc { funcDefault(); } ));
+static assert(!__traits(compiles, () pure { funcDefault(); } ));
+
+static assert(!__traits(compiles, () nothrow { funcDefault2(); } ));
+static assert(!__traits(compiles, () @nogc { funcDefault2(); } ));
+static assert(!__traits(compiles, () pure { funcDefault2(); } ));
+
+void callNothrow() nothrow
+{
+    funcNothrow();
+    funcNothrow2();
+}
+
+static assert(!__traits(compiles, () @nogc { funcNothrow(); } ));
+static assert(!__traits(compiles, () pure { funcNothrow(); } ));
+
+static assert(!__traits(compiles, () @nogc { funcNothrow2(); } ));
+static assert(!__traits(compiles, () pure { funcNothrow2(); } ));
+
+void callNogc() @nogc
+{
+    funcNogc();
+}
+
+static assert(!__traits(compiles, () nothrow { funcNogc(); } ));
+static assert(!__traits(compiles, () pure { funcNogc(); } ));
+
+void callPure() pure
+{
+    funcPure();
+}
+
+static assert(!__traits(compiles, () nothrow { funcPure(); } ));
+static assert(!__traits(compiles, () @nogc { funcPure(); } ));
+
+void callNothrowNogc() nothrow @nogc
+{
+    funcNothrowNogc();
+    funcNothrowNogc2();
+}
+
+static assert(!__traits(compiles, () pure { funcNothrowNogc(); } ));
+
+static assert(!__traits(compiles, () pure { funcNothrowNogc2(); } ));
+
+extern(C) void callbackDefault()
+{
+}
+
+extern(C) void callbackNothrow() nothrow
+{
+}
+
+void callFuncWithCallback() nothrow
+{
+    funcWithCallback(&callbackNothrow);
+
+    Callbacks callbacks;
+    callbacks.f = &callbackNothrow;
+}
+
+static assert(!__traits(compiles, () { funcWithCallback(&callbackDefault); } ));
+
+static assert(!__traits(compiles, () { Callbacks callbacks; callbacks.f = &callbackDefault; } ));
diff --git a/gcc/testsuite/gdc.test/compilable/test24762.d b/gcc/testsuite/gdc.test/compilable/test24762.d
new file mode 100644
index 00000000000..91df3c57ab3
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test24762.d
@@ -0,0 +1,11 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=24762
+
+struct S { int m; }
+
+string m() { return "m"; }
+@nogc void f()
+{
+    S s;
+    enum t = m();
+    auto x = __traits(getMember, s, m()); // Error: `@nogc` function `nogc.f` cannot call non-@nogc function `nogc.m`
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail18243.d b/gcc/testsuite/gdc.test/fail_compilation/fail18243.d
index f31319b0d35..016fff9fcec 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail18243.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail18243.d
@@ -1,8 +1,10 @@ 
 /*
-EXTRA_FILES: imports/a18243.d
+EXTRA_FILES: imports/a18243.d imports/b18243.d
 TEST_OUTPUT:
 ---
-fail_compilation/fail18243.d(15): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)`
+fail_compilation/fail18243.d(17): Error: none of the overloads of `isNaN` are callable using argument types `!()(float)`
+fail_compilation/imports/b18243.d(3):        Candidates are: `isNaN(T)(T x)`
+fail_compilation/imports/a18243.d(5):                        `imports.a18243.isNaN()`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d
index 1a766ff80dd..c20541392e6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_arrayexp.d
@@ -6,6 +6,7 @@  fail_compilation/fail_arrayexp.d(25): Error: cannot use `[]` operator on express
 fail_compilation/fail_arrayexp.d(26): Error: static array of `const(int)[]` with multiple lengths not allowed
 fail_compilation/fail_arrayexp.d(27): Error: only one index allowed to index `string`
 fail_compilation/fail_arrayexp.d(28): Error: no `[]` operator overload for type `U`
+fail_compilation/fail_arrayexp.d(16):        `fail_arrayexp.U` declared here
 fail_compilation/fail_arrayexp.d(29): Error: only one index allowed to index `(int, string)`
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d b/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d
index 2a62f53bbd3..368755ae78c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail_opover.d
@@ -3,8 +3,34 @@ 
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail_opover.d(13): Error: no `[]` operator overload for type `object.Object`
-fail_compilation/fail_opover.d(17): Error: no `[]` operator overload for type `TestS`
+fail_compilation/fail_opover.d(39): Error: no `[]` operator overload for type `object.Object`
+$p:object.d$(110):        `object.Object` declared here
+fail_compilation/fail_opover.d(43): Error: no `[]` operator overload for type `TestS`
+fail_compilation/fail_opover.d(41):        `fail_opover.test1.TestS` declared here
+fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(57): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(58): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(59): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(60): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(61): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(62): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(63): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(64): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(65): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
+fail_compilation/fail_opover.d(66): Error: no `[]` operator overload for type `S`
+fail_compilation/fail_opover.d(48):        `fail_opover.test2.S` declared here
 ---
 */
 void test1()
@@ -17,23 +43,6 @@  void test1()
     s[] = error;
 }
 
-/*
-TEST_OUTPUT:
----
-fail_compilation/fail_opover.d(46): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(47): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(48): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(49): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(50): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(51): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(52): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(53): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(54): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(55): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(56): Error: no `[]` operator overload for type `S`
-fail_compilation/fail_opover.d(57): Error: no `[]` operator overload for type `S`
----
-*/
 void test2()
 {
     struct S
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice15788.d b/gcc/testsuite/gdc.test/fail_compilation/ice15788.d
index 941b179fb7b..66942545d11 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice15788.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice15788.d
@@ -2,7 +2,9 @@ 
 EXTRA_FILES: imports/range15788.d
 TEST_OUTPUT:
 ---
-fail_compilation/ice15788.d(18): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)`
+fail_compilation/ice15788.d(20): Error: none of the overloads of `iota` are callable using argument types `!()(S, S)`
+fail_compilation/imports/range15788.d(3):        Candidates are: `iota(B, E, S)(B, E, S)`
+fail_compilation/ice15788.d(13):                        `ice15788.iota()`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imphint.d b/gcc/testsuite/gdc.test/fail_compilation/imphint.d
index 101e7862f3a..7ca81f47aa5 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/imphint.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/imphint.d
@@ -1,53 +1,60 @@ 
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/imphint.d(53): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed?
-fail_compilation/imphint.d(54): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed?
-fail_compilation/imphint.d(55): Error: `sin` is not defined, perhaps `import std.math;` is needed?
-fail_compilation/imphint.d(56): Error: `cos` is not defined, perhaps `import std.math;` is needed?
-fail_compilation/imphint.d(57): Error: `sqrt` is not defined, perhaps `import std.math;` is needed?
-fail_compilation/imphint.d(58): Error: `fabs` is not defined, perhaps `import std.math;` is needed?
-fail_compilation/imphint.d(61): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed?
-fail_compilation/imphint.d(62): Error: `appender` is not defined, perhaps `import std.array;` is needed?
-fail_compilation/imphint.d(63): Error: `array` is not defined, perhaps `import std.array;` is needed?
-fail_compilation/imphint.d(64): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
-fail_compilation/imphint.d(65): Error: `chdir` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(66): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(67): Error: `drop` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(68): Error: `each` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(69): Error: `empty` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(70): Error: `enumerate` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(71): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(72): Error: `enforce` is not defined, perhaps `import std.exception;` is needed?
-fail_compilation/imphint.d(73): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(74): Error: `exists` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(75): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(76): Error: `format` is not defined, perhaps `import std.format;` is needed?
-fail_compilation/imphint.d(77): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed?
-fail_compilation/imphint.d(78): Error: `front` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(79): Error: `iota` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(80): Error: `isDir` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(81): Error: `isFile` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(82): Error: `join` is not defined, perhaps `import std.array;` is needed?
-fail_compilation/imphint.d(83): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(84): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
-fail_compilation/imphint.d(85): Error: `map` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(86): Error: `max` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(87): Error: `min` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(88): Error: `mkdir` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(89): Error: `popFront` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(90): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
-fail_compilation/imphint.d(91): Error: `replace` is not defined, perhaps `import std.array;` is needed?
-fail_compilation/imphint.d(92): Error: `rmdir` is not defined, perhaps `import std.file;` is needed?
-fail_compilation/imphint.d(93): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(94): Error: `split` is not defined, perhaps `import std.array;` is needed?
-fail_compilation/imphint.d(95): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed?
-fail_compilation/imphint.d(96): Error: `take` is not defined, perhaps `import std.range;` is needed?
-fail_compilation/imphint.d(97): Error: `text` is not defined, perhaps `import std.conv;` is needed?
-fail_compilation/imphint.d(98): Error: `to` is not defined, perhaps `import std.conv;` is needed?
+fail_compilation/imphint.d(60): Error: `printf` is not defined, perhaps `import core.stdc.stdio;` is needed?
+fail_compilation/imphint.d(61): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed?
+fail_compilation/imphint.d(62): Error: `sin` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(63): Error: `cos` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(64): Error: `sqrt` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(65): Error: `fabs` is not defined, perhaps `import std.math;` is needed?
+fail_compilation/imphint.d(68): Error: `AliasSeq` is not defined, perhaps `import std.meta;` is needed?
+fail_compilation/imphint.d(69): Error: `appender` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(70): Error: `array` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(71): Error: `calloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(72): Error: `chdir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(73): Error: `dirEntries` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(74): Error: `drop` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(75): Error: `each` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(76): Error: `empty` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(77): Error: `enumerate` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(78): Error: `endsWith` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(79): Error: `enforce` is not defined, perhaps `import std.exception;` is needed?
+fail_compilation/imphint.d(80): Error: `equal` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(81): Error: `exists` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(82): Error: `filter` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(83): Error: `format` is not defined, perhaps `import std.format;` is needed?
+fail_compilation/imphint.d(84): Error: `free` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(85): Error: `front` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(86): Error: `iota` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(87): Error: `isDir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(88): Error: `isFile` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(89): Error: `join` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(90): Error: `joiner` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(91): Error: `malloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(92): Error: `map` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(93): Error: `max` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(94): Error: `min` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(95): Error: `mkdir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(96): Error: `popFront` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(97): Error: `realloc` is not defined, perhaps `import core.stdc.stdlib;` is needed?
+fail_compilation/imphint.d(98): Error: `replace` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(99): Error: `rmdir` is not defined, perhaps `import std.file;` is needed?
+fail_compilation/imphint.d(100): Error: `sort` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(101): Error: `split` is not defined, perhaps `import std.array;` is needed?
+fail_compilation/imphint.d(102): Error: `startsWith` is not defined, perhaps `import std.algorithm;` is needed?
+fail_compilation/imphint.d(103): Error: `take` is not defined, perhaps `import std.range;` is needed?
+fail_compilation/imphint.d(104): Error: `text` is not defined, perhaps `import std.conv;` is needed?
+fail_compilation/imphint.d(105): Error: `to` is not defined, perhaps `import std.conv;` is needed?
+fail_compilation/imphint.d(107): Error: `InterpolationHeader` is not defined, perhaps `import core.interpolation;` ?
+fail_compilation/imphint.d(108): Error: template `heresy` is not callable using argument types `!()(InterpolationHeader, InterpolationFooter)`
+fail_compilation/imphint.d(107):        Candidate is: `heresy(Args...)(InterpolationHeader header, Args args, InterpolationFooter footer)`
 ---
 */
 
+
+
+
+
 void foo()
 {
     printf("hello world\n");
@@ -96,4 +103,7 @@  void foo()
     take();
     text();
     to();
+
+    void heresy(Args...)(InterpolationHeader header, Args args, InterpolationFooter footer) {}
+    heresy(i"");
 }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d
index 73df7511ceb..35da731b444 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a18243.d
@@ -1,5 +1,5 @@ 
-module a18243;
+module imports.a18243;
 
-import std.math : isNaN;
+import imports.b18243 : isNaN;
 
 public bool isNaN() { return false; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d b/gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d
new file mode 100644
index 00000000000..d59128f1f31
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/imports/b18243.d
@@ -0,0 +1,3 @@ 
+module imports.b18243;
+
+bool isNaN(T)(T x) { return false; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d b/gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d
new file mode 100644
index 00000000000..9a89c0ae86b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/nested_template_constraint.d
@@ -0,0 +1,18 @@ 
+/*
+TEST_OUTPUT:
+---
+fail_compilation/nested_template_constraint.d(17): Error: template `foo` is not callable using argument types `!()(string, int)`
+fail_compilation/nested_template_constraint.d(10):        Candidate is: `foo(int x = 0)`
+fail_compilation/nested_template_constraint.d(11):          - Containing: `foo(T, U)(T t, U u)`
+---
+*/
+
+template foo(int x = 0) {
+    void foo(T, U)(T t, U u)
+        if (is(T == int) && is(U == int)) {}
+}
+
+void main()
+{
+    foo("hello", 4);
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16443.d b/gcc/testsuite/gdc.test/fail_compilation/test16443.d
new file mode 100644
index 00000000000..a83dc53bbe2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/test16443.d
@@ -0,0 +1,12 @@ 
+/* TEST_OUTPUT:
+---
+fail_compilation/test16443.d(10): Error: incompatible types for `(null) + (null)`: both operands are of type `typeof(null)`
+fail_compilation/test16443.d(11): Error: incompatible types for `(null) - (null)`: both operands are of type `typeof(null)`
+---
+*/
+
+void foo()
+{
+    auto a = null + null;
+    auto b = null - null;
+}
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 654fbed86d8..acb7d98123f 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@ 
-6884b433d21d9b6356e5c83ffc6eb06a62a5cad1
+4ccb01fde535c7ad6ad4bdae2516c99420751814
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/libphobos/libdruntime/core/builtins.d b/libphobos/libdruntime/core/builtins.d
index 1ed80f7d94a..f10bb9f6f8b 100644
--- a/libphobos/libdruntime/core/builtins.d
+++ b/libphobos/libdruntime/core/builtins.d
@@ -51,3 +51,46 @@  version (LDC)
 
 /// Writes `s` to `stderr` during CTFE (does nothing at runtime).
 void __ctfeWrite(scope const(char)[] s) @nogc @safe pure nothrow {}
+
+version (GNU)
+{
+    /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fexpect
+    alias expect = __builtin_expect;
+    /// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005ftrap
+    alias trap = __builtin_trap;
+}
+else version (LDC)
+{
+    /// https://llvm.org/docs/LangRef.html#llvm-expect-intrinsic
+    alias expect = llvm_expect;
+    debug
+        /// https://llvm.org/docs/LangRef.html#llvm-debugtrap-intrinsic
+        alias trap = llvm_debugtrap;
+    else
+        /// https://llvm.org/docs/LangRef.html#llvm-trap-intrinsic
+        alias trap = llvm_trap;
+}
+else version (DigitalMars)
+{
+    pragma(inline, true)
+    T expect(T)(T val, T expected) if (__traits(isIntegral, T))
+    {
+        return val;
+    }
+
+    pragma(inline, true)
+    void trap()
+    {
+        debug
+        {
+            version(D_InlineAsm_X86)
+                asm nothrow @nogc pure @trusted { int 3; }
+        }
+        assert(0);
+    }
+}
+
+/// Provide static branch hints
+pragma(inline, true) bool likely(bool b)   { return !!expect(b, true);  }
+///
+pragma(inline, true) bool unlikely(bool b) { return !!expect(b, false); }
diff --git a/libphobos/libdruntime/core/stdc/fenv.d b/libphobos/libdruntime/core/stdc/fenv.d
index b9b10aae3d9..ce77d5c1e6b 100644
--- a/libphobos/libdruntime/core/stdc/fenv.d
+++ b/libphobos/libdruntime/core/stdc/fenv.d
@@ -432,6 +432,14 @@  else version (CRuntime_Musl)
         }
         alias ushort fexcept_t;
     }
+    else version (LoongArch64)
+    {
+        struct fenv_t
+        {
+            uint __cw;
+        }
+        alias uint fexcept_t;
+    }
     else
     {
         static assert(false, "Architecture not supported.");
diff --git a/libphobos/libdruntime/core/sys/posix/signal.d b/libphobos/libdruntime/core/sys/posix/signal.d
index ed4ec1270d9..e3d80fb0218 100644
--- a/libphobos/libdruntime/core/sys/posix/signal.d
+++ b/libphobos/libdruntime/core/sys/posix/signal.d
@@ -2758,6 +2758,11 @@  else version (CRuntime_Musl)
         enum MINSIGSTKSZ = 2048;
         enum SIGSTKSZ    = 8192;
     }
+    else version (LoongArch64)
+    {
+        enum MINSIGSTKSZ = 4096;
+        enum SIGSTKSZ    = 16384;
+    }
     else
         static assert(0, "unimplemented");
 
diff --git a/libphobos/libdruntime/core/sys/windows/winsock2.d b/libphobos/libdruntime/core/sys/windows/winsock2.d
index b036df39dfe..626f79e89c5 100644
--- a/libphobos/libdruntime/core/sys/windows/winsock2.d
+++ b/libphobos/libdruntime/core/sys/windows/winsock2.d
@@ -405,7 +405,7 @@  const(SOCKET)* stop = start + set.fd_count;
 
 
 // Adds.
-void FD_SET(SOCKET fd, fd_set* set)     pure @nogc
+void FD_SET(SOCKET fd, fd_set* set) pure @nogc
 {
     uint c = set.fd_count;
     set.fd_array.ptr[c] = fd;
@@ -547,13 +547,29 @@  enum: uint
 
 enum: int
 {
-    AI_PASSIVE = 0x1,
-    AI_CANONNAME = 0x2,
-    AI_NUMERICHOST = 0x4,
-    AI_ADDRCONFIG = 0x0400,
-    AI_NON_AUTHORITATIVE = 0x04000,
-    AI_SECURE = 0x08000,
-    AI_RETURN_PREFERRED_NAMES = 0x010000,
+    AI_PASSIVE = 0x1, // Socket address will be used in bind() call
+    AI_CANONNAME = 0x2, // Return canonical name in first ai_canonname
+    AI_NUMERICHOST = 0x4, // Nodename must be a numeric address string
+    AI_NUMERICSERV = 0x8, // Servicename must be a numeric port number
+    AI_DNS_ONLY = 0x10, // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.)
+    AI_FORCE_CLEAR_TEXT = 0x20, // Force clear text DNS query
+    AI_BYPASS_DNS_CACHE = 0x40, // Bypass DNS cache
+    AI_RETURN_TTL = 0x80, // Return record TTL
+    AI_ALL = 0x0100, // Query both IP6 and IP4 with AI_V4MAPPED
+    AI_ADDRCONFIG = 0x0400, // Resolution only if global address configured
+    AI_V4MAPPED = 0x0800, // On v6 failure, query v4 and convert to V4MAPPED format
+    AI_NON_AUTHORITATIVE = 0x04000, // LUP_NON_AUTHORITATIVE
+    AI_SECURE = 0x08000, // LUP_SECURE
+    AI_RETURN_PREFERRED_NAMES = 0x010000, // LUP_RETURN_PREFERRED_NAMES
+    AI_FQDN = 0x00020000, // Return the FQDN in ai_canonname
+    AI_FILESERVER = 0x00040000, // Resolving fileserver name resolution
+    AI_DISABLE_IDN_ENCODING = 0x00080000, // Disable Internationalized Domain Names handling
+    AI_SECURE_WITH_FALLBACK = 0x00100000, // Forces clear text fallback if the secure DNS query fails
+    AI_EXCLUSIVE_CUSTOM_SERVERS = 0x00200000, // Use exclusively the custom DNS servers
+    AI_RETURN_RESPONSE_FLAGS = 0x10000000, // Requests extra information about the DNS results
+    AI_REQUIRE_SECURE = 0x20000000, // Forces the DNS query to be done over seucre protocols
+    AI_RESOLUTION_HANDLE = 0x40000000, // Request resolution handle
+    AI_EXTENDED = 0x80000000, // Indicates this is extended ADDRINFOEX(2/..) struct
 }
 
 
@@ -702,12 +718,12 @@  struct hostent
 // Note: These are Winsock2!!
 struct WSAOVERLAPPED;
 alias LPWSAOVERLAPPED = WSAOVERLAPPED*;
-alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint);
+alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint) nothrow @nogc;
 int WSAIoctl(SOCKET s, uint dwIoControlCode,
     void* lpvInBuffer, uint cbInBuffer,
     void* lpvOutBuffer, uint cbOutBuffer,
     uint* lpcbBytesReturned,
-    LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+    LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) @nogc;
 
 
 enum IOC_VENDOR = 0x18000000;
@@ -720,3 +736,31 @@  struct tcp_keepalive
     uint keepalivetime;
     uint keepaliveinterval;
 }
+
+
+struct pollfd
+{
+    SOCKET  fd;        // Socket handle
+    short   events;    // Requested events to monitor
+    short   revents;   // Returned events indicating status
+}
+alias WSAPOLLFD = pollfd;
+alias PWSAPOLLFD = pollfd*;
+alias LPWSAPOLLFD = pollfd*;
+
+enum: short {
+    POLLRDNORM = 0x0100,
+    POLLRDBAND = 0x0200,
+    POLLIN = (POLLRDNORM | POLLRDBAND),
+    POLLPRI = 0x0400,
+
+    POLLWRNORM = 0x0010,
+    POLLOUT = (POLLWRNORM),
+    POLLWRBAND = 0x0020,
+
+    POLLERR = 0x0001,
+    POLLHUP = 0x0002,
+    POLLNVAL = 0x0004
+}
+
+int WSAPoll(LPWSAPOLLFD fdArray, uint fds, int timeout) @nogc;
diff --git a/libphobos/libdruntime/core/thread/osthread.d b/libphobos/libdruntime/core/thread/osthread.d
index 307048135b8..cf93094937c 100644
--- a/libphobos/libdruntime/core/thread/osthread.d
+++ b/libphobos/libdruntime/core/thread/osthread.d
@@ -1154,52 +1154,6 @@  unittest
     t.join();
 }
 
-unittest
-{
-    // NOTE: This entire test is based on the assumption that no
-    //       memory is allocated after the child thread is
-    //       started. If an allocation happens, a collection could
-    //       trigger, which would cause the synchronization below
-    //       to cause a deadlock.
-    // NOTE: DO NOT USE LOCKS IN CRITICAL REGIONS IN NORMAL CODE.
-
-    import core.sync.semaphore;
-
-    auto sema = new Semaphore(),
-         semb = new Semaphore();
-
-    auto thr = new Thread(
-    {
-        thread_enterCriticalRegion();
-        assert(thread_inCriticalRegion());
-        sema.notify();
-
-        semb.wait();
-        assert(thread_inCriticalRegion());
-
-        thread_exitCriticalRegion();
-        assert(!thread_inCriticalRegion());
-        sema.notify();
-
-        semb.wait();
-        assert(!thread_inCriticalRegion());
-    });
-
-    thr.start();
-
-    sema.wait();
-    synchronized (ThreadBase.criticalRegionLock)
-        assert(thr.m_isInCriticalRegion);
-    semb.notify();
-
-    sema.wait();
-    synchronized (ThreadBase.criticalRegionLock)
-        assert(!thr.m_isInCriticalRegion);
-    semb.notify();
-
-    thr.join();
-}
-
 // https://issues.dlang.org/show_bug.cgi?id=22124
 unittest
 {
@@ -1212,36 +1166,6 @@  unittest
     static assert(!__traits(compiles, () @nogc => fun(thread, 3) ));
 }
 
-unittest
-{
-    import core.sync.semaphore;
-
-    shared bool inCriticalRegion;
-    auto sema = new Semaphore(),
-         semb = new Semaphore();
-
-    auto thr = new Thread(
-    {
-        thread_enterCriticalRegion();
-        inCriticalRegion = true;
-        sema.notify();
-        semb.wait();
-
-        Thread.sleep(dur!"msecs"(1));
-        inCriticalRegion = false;
-        thread_exitCriticalRegion();
-    });
-    thr.start();
-
-    sema.wait();
-    assert(inCriticalRegion);
-    semb.notify();
-
-    thread_suspendAll();
-    assert(!inCriticalRegion);
-    thread_resumeAll();
-}
-
 @nogc @safe nothrow
 unittest
 {
@@ -1740,21 +1664,11 @@  private extern(D) void* getStackBottom() nothrow @nogc
  */
 private extern (D) bool suspend( Thread t ) nothrow @nogc
 {
-    Duration waittime = dur!"usecs"(10);
- Lagain:
     if (!t.isRunning)
     {
         Thread.remove(t);
         return false;
     }
-    else if (t.m_isInCriticalRegion)
-    {
-        Thread.criticalRegionLock.unlock_nothrow();
-        Thread.sleep(waittime);
-        if (waittime < dur!"msecs"(10)) waittime *= 2;
-        Thread.criticalRegionLock.lock_nothrow();
-        goto Lagain;
-    }
 
     version (Windows)
     {
@@ -1962,6 +1876,13 @@  private extern (D) bool suspend( Thread t ) nothrow @nogc
     return true;
 }
 
+/**
+ * Runs the necessary operations required before stopping the world.
+ */
+extern (C) void thread_preStopTheWorld() nothrow {
+    Thread.slock.lock_nothrow();
+}
+
 /**
  * Suspend all threads but the calling thread for "stop the world" garbage
  * collection runs.  This function may be called multiple times, and must
@@ -1994,13 +1915,11 @@  extern (C) void thread_suspendAll() nothrow
         return;
     }
 
-    Thread.slock.lock_nothrow();
+    thread_preStopTheWorld();
     {
         if ( ++suspendDepth > 1 )
             return;
 
-        Thread.criticalRegionLock.lock_nothrow();
-        scope (exit) Thread.criticalRegionLock.unlock_nothrow();
         size_t cnt;
         bool suspendedSelf;
         Thread t = ThreadBase.sm_tbeg.toThread;
@@ -2466,6 +2385,39 @@  else version (Posix)
         __gshared sem_t suspendCount;
 
 
+        extern (C) bool thread_preSuspend( void* sp ) nothrow {
+            // NOTE: Since registers are being pushed and popped from the
+            //       stack, any other stack data used by this function should
+            //       be gone before the stack cleanup code is called below.
+            Thread obj = Thread.getThis();
+            if (obj is null)
+            {
+                return false;
+            }
+
+            if ( !obj.m_lock )
+            {
+                obj.m_curr.tstack = sp;
+            }
+
+            return true;
+        }
+
+        extern (C) bool thread_postSuspend() nothrow {
+            Thread obj = Thread.getThis();
+            if (obj is null)
+            {
+                return false;
+            }
+
+            if ( !obj.m_lock )
+            {
+                obj.m_curr.tstack = obj.m_curr.bstack;
+            }
+
+            return true;
+        }
+
         extern (C) void thread_suspendHandler( int sig ) nothrow
         in
         {
@@ -2475,15 +2427,13 @@  else version (Posix)
         {
             void op(void* sp) nothrow
             {
-                // NOTE: Since registers are being pushed and popped from the
-                //       stack, any other stack data used by this function should
-                //       be gone before the stack cleanup code is called below.
-                Thread obj = Thread.getThis();
-                assert(obj !is null);
+                bool supported = thread_preSuspend(getStackTop());
+                assert(supported, "Tried to suspend a detached thread!");
 
-                if ( !obj.m_lock )
+                scope(exit)
                 {
-                    obj.m_curr.tstack = getStackTop();
+                    supported = thread_postSuspend();
+                    assert(supported, "Tried to suspend a detached thread!");
                 }
 
                 sigset_t    sigres = void;
@@ -2499,11 +2449,6 @@  else version (Posix)
                 assert( status == 0 );
 
                 sigsuspend( &sigres );
-
-                if ( !obj.m_lock )
-                {
-                    obj.m_curr.tstack = obj.m_curr.bstack;
-                }
             }
             callWithStackShell(&op);
         }
@@ -2641,11 +2586,7 @@  private
 
     bool ll_dllHasExternalReferences() nothrow
     {
-        version (CRuntime_DigitalMars)
-            enum internalReferences = 1; // only the watchdog thread
-        else
-            int internalReferences =  msvcUsesUCRT ? 1 + ll_countLowLevelThreadsWithDLLUnloadCallback() : 1;
-
+        int internalReferences =  msvcUsesUCRT ? 1 + ll_countLowLevelThreadsWithDLLUnloadCallback() : 1;
         int refcnt = dll_getRefCount(ll_dllModule);
         return refcnt > internalReferences;
     }
@@ -2706,10 +2647,7 @@  private
         // if a thread is created from a DLL, the MS runtime (starting with VC2015) increments the DLL reference count
         // to avoid the DLL being unloaded while the thread is still running. Mimick this behavior here for all
         // runtimes not doing this
-        version (CRuntime_DigitalMars)
-            enum needRef = true;
-        else
-            bool needRef = !msvcUsesUCRT;
+        bool needRef = !msvcUsesUCRT;
 
         if (needRef)
         {
diff --git a/libphobos/libdruntime/core/thread/threadbase.d b/libphobos/libdruntime/core/thread/threadbase.d
index 58dd259e30d..cb13e9ac0a0 100644
--- a/libphobos/libdruntime/core/thread/threadbase.d
+++ b/libphobos/libdruntime/core/thread/threadbase.d
@@ -456,7 +456,6 @@  package:
     string              m_name;
     size_t              m_sz;
     bool                m_isDaemon;
-    bool                m_isInCriticalRegion;
     Throwable           m_unhandled;
 
     ///////////////////////////////////////////////////////////////////////////
@@ -564,25 +563,17 @@  package(core.thread):
         return cast(Mutex)_slock.ptr;
     }
 
-    @property static Mutex criticalRegionLock() nothrow @nogc
-    {
-        return cast(Mutex)_criticalRegionLock.ptr;
-    }
-
     __gshared align(mutexAlign) void[mutexClassInstanceSize] _slock;
-    __gshared align(mutexAlign) void[mutexClassInstanceSize] _criticalRegionLock;
 
     static void initLocks() @nogc nothrow
     {
         import core.lifetime : emplace;
         emplace!Mutex(_slock[]);
-        emplace!Mutex(_criticalRegionLock[]);
     }
 
     static void termLocks() @nogc nothrow
     {
         (cast(Mutex)_slock.ptr).__dtor();
-        (cast(Mutex)_criticalRegionLock.ptr).__dtor();
     }
 
     __gshared StackContext*  sm_cbeg;
@@ -978,6 +969,13 @@  package __gshared uint suspendDepth = 0;
 
 private alias resume = externDFunc!("core.thread.osthread.resume", void function(ThreadBase) nothrow @nogc);
 
+/**
+ * Run the necessary operation required after the world was resumed.
+ */
+extern (C) void thread_postRestartTheWorld() nothrow {
+    ThreadBase.slock.unlock_nothrow();
+}
+
 /**
  * Resume all threads but the calling thread for "stop the world" garbage
  * collection runs.  This function must be called once for each preceding
@@ -1004,7 +1002,7 @@  do
         return;
     }
 
-    scope(exit) ThreadBase.slock.unlock_nothrow();
+    scope(exit) thread_postRestartTheWorld();
     {
         if (--suspendDepth > 0)
             return;
@@ -1144,75 +1142,6 @@  extern (C) void thread_scanAll(scope ScanAllThreadsFn scan) nothrow
 
 private alias thread_yield = externDFunc!("core.thread.osthread.thread_yield", void function() @nogc nothrow);
 
-/**
- * Signals that the code following this call is a critical region. Any code in
- * this region must finish running before the calling thread can be suspended
- * by a call to thread_suspendAll.
- *
- * This function is, in particular, meant to help maintain garbage collector
- * invariants when a lock is not used.
- *
- * A critical region is exited with thread_exitCriticalRegion.
- *
- * $(RED Warning):
- * Using critical regions is extremely error-prone. For instance, using locks
- * inside a critical region can easily result in a deadlock when another thread
- * holding the lock already got suspended.
- *
- * The term and concept of a 'critical region' comes from
- * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925, Mono's SGen garbage collector).
- *
- * In:
- *  The calling thread must be attached to the runtime.
- */
-extern (C) void thread_enterCriticalRegion() @nogc
-in
-{
-    assert(ThreadBase.getThis());
-}
-do
-{
-    synchronized (ThreadBase.criticalRegionLock)
-        ThreadBase.getThis().m_isInCriticalRegion = true;
-}
-
-
-/**
- * Signals that the calling thread is no longer in a critical region. Following
- * a call to this function, the thread can once again be suspended.
- *
- * In:
- *  The calling thread must be attached to the runtime.
- */
-extern (C) void thread_exitCriticalRegion() @nogc
-in
-{
-    assert(ThreadBase.getThis());
-}
-do
-{
-    synchronized (ThreadBase.criticalRegionLock)
-        ThreadBase.getThis().m_isInCriticalRegion = false;
-}
-
-
-/**
- * Returns true if the current thread is in a critical region; otherwise, false.
- *
- * In:
- *  The calling thread must be attached to the runtime.
- */
-extern (C) bool thread_inCriticalRegion() @nogc
-in
-{
-    assert(ThreadBase.getThis());
-}
-do
-{
-    synchronized (ThreadBase.criticalRegionLock)
-        return ThreadBase.getThis().m_isInCriticalRegion;
-}
-
 
 /**
 * A callback for thread errors in D during collections. Since an allocation is not possible
@@ -1233,22 +1162,6 @@  package void onThreadError(string msg) nothrow @nogc
     throw error;
 }
 
-unittest
-{
-    assert(!thread_inCriticalRegion());
-
-    {
-        thread_enterCriticalRegion();
-
-        scope (exit)
-            thread_exitCriticalRegion();
-
-        assert(thread_inCriticalRegion());
-    }
-
-    assert(!thread_inCriticalRegion());
-}
-
 
 /**
  * Indicates whether an address has been marked by the GC.
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 4fac7249097..8daeda505f4 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@ 
-48d581a1f509a7a302ea893e28939edb5b130622
+eab6595ade1dab9a757bc0efe2f4e92f39cab0f7
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d
index 2d16c65111f..cb471532872 100644
--- a/libphobos/src/std/algorithm/sorting.d
+++ b/libphobos/src/std/algorithm/sorting.d
@@ -2385,7 +2385,11 @@  private template TimSortImpl(alias pred, R)
         size_t stackLen = 0;
 
         // Allocate temporary memory if not provided by user
-        if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
+        if (temp.length < minTemp)
+        {
+            static if (hasElaborateAssign!T) temp = new T[](minTemp);
+            else temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
+        }
 
         for (size_t i = 0; i < range.length; )
         {
@@ -3076,6 +3080,20 @@  private template TimSortImpl(alias pred, R)
     array.sort!("a < b", SwapStrategy.stable);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24773
+@safe unittest
+{
+    static struct S
+    {
+        int i = 42;
+        ~this() { assert(i == 42); }
+    }
+
+    auto array = new S[](400);
+    array.sort!((a, b) => false, SwapStrategy.stable);
+}
+
+
 // schwartzSort
 /**
 Alternative sorting method that should be used when comparing keys involves an
diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d
index a78e1b3d0dc..e02c1202a4b 100644
--- a/libphobos/src/std/format/package.d
+++ b/libphobos/src/std/format/package.d
@@ -60,7 +60,7 @@  Limitation: This package does not support localization, but
     adheres to the rounding mode of the floating point unit, if
     available.
 
-$(SECTION3 Format Strings)
+$(H3 $(LNAME2 format-strings, Format Strings))
 
 The functions contained in this package use $(I format strings). A
 format string describes the layout of another string for reading or
@@ -144,7 +144,7 @@  recommended to assign (lower- and uppercase) letters.
 Note: The $(I Parameters) of a $(I CompoundIndicator) are currently
 limited to a $(B '-') flag.
 
-$(SECTION4 Format Indicator)
+$(H4 $(LNAME2 format-indicator, Format Indicator))
 
 The $(I format indicator) can either be a single character or an
 expression surrounded by $(B %\() and $(B %\)). It specifies the
@@ -205,7 +205,7 @@  Note: Inside a $(I compound indicator), strings and characters are
 escaped automatically. To avoid this behavior, use `"%-$(LPAREN)"`
 instead of `"%$(LPAREN)"`.
 
-$(SECTION4 Flags)
+$(H4 $(LNAME2 flags, Flags))
 
 There are several flags that affect the outcome of the formatting.
 
@@ -244,7 +244,7 @@  $(BOOKTABLE ,
              sections below for more information.))
 )
 
-$(SECTION4 Width$(COMMA) Precision and Separator)
+$(H4 $(LNAME2 width-precision-separator, Width, Precision and Separator))
 
 The $(I width) parameter specifies the minimum width of the result.
 
@@ -269,7 +269,7 @@  The $(I separator) can also be followed by a $(B '?'). In that case,
 an additional argument is used to specify the symbol that should be
 used to separate the chunks.
 
-$(SECTION4 Position)
+$(H4 $(LNAME2 position, Position))
 
 By default, the arguments are processed in the provided order. With
 the $(I position) parameter it is possible to address arguments
@@ -282,7 +282,7 @@  It's also possible to use positional arguments for $(I width), $(I
 precision) and $(I separator) by adding a number and a $(B
 '$(DOLLAR)') after the $(B '*').
 
-$(SECTION4 Types)
+$(H4 $(LNAME2 types, Types))
 
 This section describes the result of combining types with format
 characters. It is organized in 2 subsections: a list of general
diff --git a/libphobos/src/std/format/write.d b/libphobos/src/std/format/write.d
index 2aa45d724b5..078fa786e76 100644
--- a/libphobos/src/std/format/write.d
+++ b/libphobos/src/std/format/write.d
@@ -28,7 +28,7 @@  $(TR $(TD $(I delegates)) $(TD yes) $(TD $(MDASH)) $(TD $(MDASH)) $(TD $(MDASH))
 
 Enums can be used with all format characters of the base type.
 
-$(SECTION3 Structs$(COMMA) Unions$(COMMA) Classes$(COMMA) and Interfaces)
+$(H3 $(LNAME2 aggregates, Structs, Unions, Classes, and Interfaces))
 
 Aggregate types can define various `toString` functions. If this
 function takes a $(REF_ALTTEXT FormatSpec, FormatSpec, std, format,
diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d
index b6d31a810ea..f7610c022eb 100644
--- a/libphobos/src/std/uni/package.d
+++ b/libphobos/src/std/uni/package.d
@@ -7005,7 +7005,7 @@  private enum TransformRes
 // Note, getting GB1 (break at start of text, unless text is empty) right
 // relies on the user starting grapheme walking from beginning of the text, and
 // not attempting to walk an empty text.
-private enum TransformRes
+private immutable TransformRes
     function(ref GraphemeState, dchar) @safe pure nothrow @nogc [] graphemeTransforms =
 [
     GraphemeState.Start: (ref state, ch)
@@ -7243,9 +7243,7 @@  if (is(C : dchar))
     static assert(c2 == 3); // \u0301 has 2 UTF-8 code units
 }
 
-// TODO: make this @nogc. Probably no big deal since the state machine is
-// already GC-free.
-@safe pure nothrow unittest
+@safe pure nothrow @nogc unittest
 {
     // grinning face ~ emoji modifier fitzpatrick type-5 ~ grinning face
     assert(graphemeStride("\U0001F600\U0001f3FE\U0001F600"d, 0) == 2);