diff mbox series

[committed] d: Merge upstream dmd 4d1bfcf14, druntime 9ba9a6ae, phobos c0cc5e917.

Message ID 20220413140411.1338597-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd 4d1bfcf14, druntime 9ba9a6ae, phobos c0cc5e917. | expand

Commit Message

Iain Buclaw April 13, 2022, 2:04 p.m. UTC
Hi,

This patch merges the D front-end with upstream dmd 4d1bfcf14, and
the standard library with druntime 9ba9a6ae and phobos c0cc5e917.

D front-end changes:

    - Import dmd v2.099.1.
    - Added `@mustuse' attribute, implmenting DIP 1038.
    - Added `.tupleof` property for static arrays

D runtime changes:

    - Import druntime v2.099.1.

Phobos changes:

    - Import phobos v2.099.1.
    - Zlib bindings have been updated to 1.2.12.

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

Regards,
Iain.

---
gcc/d/ChangeLog:

	* Make-lang.in (D_FRONTEND_OBJS): Add d/common-bitfields.o,
	d/mustuse.o.
	* d-ctfloat.cc (CTFloat::isIdentical): Don't treat NaN values as
	identical.
	* dmd/MERGE: Merge upstream dmd 4d1bfcf14.
	* expr.cc (ExprVisitor::visit (VoidInitExp *)): New.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime 9ba9a6ae.
	* src/MERGE: Merge upstream phobos c0cc5e917.
---
 gcc/d/Make-lang.in                            |   2 +
 gcc/d/d-ctfloat.cc                            |   3 +-
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/dmd/README.md                           |   2 +
 gcc/d/dmd/VERSION                             |   2 +-
 gcc/d/dmd/arrayop.d                           |   2 +-
 gcc/d/dmd/attrib.d                            |  58 ++--
 gcc/d/dmd/common/README.md                    |   1 +
 gcc/d/dmd/common/bitfields.d                  |  70 +++++
 gcc/d/dmd/common/file.d                       |   4 +
 gcc/d/dmd/common/outbuffer.d                  |   4 +
 gcc/d/dmd/common/string.d                     |   4 +
 gcc/d/dmd/constfold.d                         |   2 +-
 gcc/d/dmd/ctfeexpr.d                          |   4 +-
 gcc/d/dmd/dcast.d                             |  12 +-
 gcc/d/dmd/declaration.d                       |  23 +-
 gcc/d/dmd/dmangle.d                           |  10 +-
 gcc/d/dmd/dmodule.d                           |  24 +-
 gcc/d/dmd/dsymbol.d                           |  82 ++---
 gcc/d/dmd/dsymbolsem.d                        |  15 +-
 gcc/d/dmd/dtemplate.d                         |   6 +-
 gcc/d/dmd/escape.d                            |  51 ++--
 gcc/d/dmd/expression.d                        |   3 +-
 gcc/d/dmd/expressionsem.d                     | 160 +++++-----
 gcc/d/dmd/file_manager.d                      |  33 +-
 gcc/d/dmd/hdrgen.d                            | 282 ++++++++++++------
 gcc/d/dmd/id.d                                |   1 +
 gcc/d/dmd/initsem.d                           |  26 +-
 gcc/d/dmd/json.d                              |   5 +-
 gcc/d/dmd/lexer.d                             |   8 +-
 gcc/d/dmd/mtype.d                             |  77 +++--
 gcc/d/dmd/mtype.h                             |   2 +-
 gcc/d/dmd/mustuse.d                           | 244 +++++++++++++++
 gcc/d/dmd/opover.d                            |   4 +-
 gcc/d/dmd/optimize.d                          |  36 ++-
 gcc/d/dmd/root/aav.d                          |   2 +
 gcc/d/dmd/root/array.d                        |   2 +-
 gcc/d/dmd/root/complex.d                      |   4 +
 gcc/d/dmd/root/file.d                         |  10 +-
 gcc/d/dmd/root/longdouble.d                   |   2 +
 gcc/d/dmd/root/optional.d                     |   4 +
 gcc/d/dmd/semantic2.d                         |   2 +-
 gcc/d/dmd/statementsem.d                      |   4 +-
 gcc/d/dmd/tokens.d                            |   2 +-
 gcc/d/dmd/traits.d                            |  80 ++---
 gcc/d/dmd/typesem.d                           |  29 +-
 gcc/d/dmd/utils.d                             |   7 +-
 gcc/d/expr.cc                                 |  10 +
 .../gdc.test/compilable/extra-files/header1.d |  10 +
 .../gdc.test/compilable/imports/imp17434a.d   |   1 +
 .../gdc.test/compilable/imports/imp17434b.d   |   6 +
 .../gdc.test/compilable/interpret3.d          | 102 ++++++-
 gcc/testsuite/gdc.test/compilable/json.d      |   4 +-
 .../gdc.test/compilable/mixintype2.d          |   6 +
 .../gdc.test/compilable/must_use_assign.d     |   9 +
 .../compilable/must_use_not_reserved.d        |   5 +
 .../gdc.test/compilable/must_use_opassign.d   |  15 +
 .../gdc.test/compilable/must_use_opopassign.d |  15 +
 .../gdc.test/compilable/must_use_opunary.d    |  18 ++
 .../gdc.test/compilable/must_use_suppress.d   |  10 +
 gcc/testsuite/gdc.test/compilable/test17419.d |   5 +-
 gcc/testsuite/gdc.test/compilable/test17434.d |  11 +
 .../gdc.test/compilable/test17434a.d          |   5 +
 gcc/testsuite/gdc.test/compilable/test19097.d |  40 +++
 gcc/testsuite/gdc.test/compilable/test22988.d |  15 +
 gcc/testsuite/gdc.test/compilable/test22997.d |  14 +
 .../gdc.test/fail_compilation/diag14235.d     |   4 +-
 .../gdc.test/fail_compilation/diag8101.d      |   2 +-
 .../gdc.test/fail_compilation/diag_funclit.d  |   2 +-
 .../gdc.test/fail_compilation/diagin.d        |   2 +-
 .../gdc.test/fail_compilation/fail2656.d      |  28 +-
 .../gdc.test/fail_compilation/fail99.d        |   2 +-
 .../gdc.test/fail_compilation/fix19059.d      |   2 +-
 .../gdc.test/fail_compilation/ice10922.d      |   2 +-
 .../gdc.test/fail_compilation/ice9540.d       |   2 +-
 .../gdc.test/fail_compilation/must_use.d      |  16 +
 .../fail_compilation/must_use_comma.d         |  17 ++
 .../fail_compilation/must_use_opunary.d       |  21 ++
 .../fail_compilation/must_use_reserved.d      |  20 ++
 .../fail_compilation/must_use_template.d      |  16 +
 .../fail_compilation/must_use_union.d         |  16 +
 .../gdc.test/fail_compilation/test11176.d     |   6 +-
 .../gdc.test/fail_compilation/test17284.d     |   6 +-
 .../gdc.test/fail_compilation/test19097.d     |  64 +++-
 .../gdc.test/fail_compilation/test21008.d     |   6 +-
 gcc/testsuite/gdc.test/runnable/test20603.d   |  31 ++
 gcc/testsuite/gdc.test/runnable/xtest46.d     |  86 +++++-
 libphobos/libdruntime/MERGE                   |   2 +-
 libphobos/libdruntime/core/int128.d           | 156 ++++++----
 libphobos/libdruntime/core/lifetime.d         |  20 +-
 libphobos/src/MERGE                           |   2 +-
 libphobos/src/etc/c/zlib.d                    | 211 +++++++------
 libphobos/src/std/regex/internal/parser.d     |   2 +-
 93 files changed, 1787 insertions(+), 670 deletions(-)
 create mode 100644 gcc/d/dmd/common/bitfields.d
 create mode 100644 gcc/d/dmd/mustuse.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp17434a.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp17434b.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/must_use_assign.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/must_use_not_reserved.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/must_use_opassign.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/must_use_opopassign.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/must_use_opunary.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/must_use_suppress.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/test17434.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/test17434a.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/test22988.d
 create mode 100644 gcc/testsuite/gdc.test/compilable/test22997.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/must_use.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/must_use_comma.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/must_use_opunary.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/must_use_reserved.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/must_use_template.d
 create mode 100644 gcc/testsuite/gdc.test/fail_compilation/must_use_union.d
 create mode 100644 gcc/testsuite/gdc.test/runnable/test20603.d
diff mbox series

Patch

diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index 6c90657d65f..f3e34c54015 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -89,6 +89,7 @@  D_FRONTEND_OBJS = \
 	d/canthrow.o \
 	d/chkformat.o \
 	d/clone.o \
+	d/common-bitfields.o \
 	d/common-file.o \
 	d/common-outbuffer.o \
 	d/common-string.o \
@@ -143,6 +144,7 @@  D_FRONTEND_OBJS = \
 	d/lambdacomp.o \
 	d/lexer.o \
 	d/mtype.o \
+	d/mustuse.o \
 	d/nogc.o \
 	d/nspace.o \
 	d/ob.o \
diff --git a/gcc/d/d-ctfloat.cc b/gcc/d/d-ctfloat.cc
index 6e6d10ff0a4..c4d9a44c59b 100644
--- a/gcc/d/d-ctfloat.cc
+++ b/gcc/d/d-ctfloat.cc
@@ -55,8 +55,7 @@  CTFloat::isIdentical (real_t x, real_t y)
 {
   real_value rx = x.rv ();
   real_value ry = y.rv ();
-  return (REAL_VALUE_ISNAN (rx) && REAL_VALUE_ISNAN (ry))
-    || real_identical (&rx, &ry);
+  return real_identical (&rx, &ry);
 }
 
 /* Return true if real_t value R is NaN.  */
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index ca409df346d..f36d65e7d84 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-47871363d804f54b29ccfd444b082c19716c2301
+4d1bfcf142928cf1c097b0a2689485c1b14f4f53
 
 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 f613ab28577..b14310312cd 100644
--- a/gcc/d/dmd/README.md
+++ b/gcc/d/dmd/README.md
@@ -130,6 +130,8 @@ 
 | [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d)   | Define an implicit conversion table for basic types                                        |
 | [importc.d](https://github.com/dlang/dmd/blob/master/src/dmd/importc.d)       | Helpers specific to ImportC                                                                |
 | [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings.                                 |
+| [mustuse.d](https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d)       | Helpers related to the `@mustuse` attribute                                                |
+
 
 **Compile Time Function Execution (CTFE)**
 
diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION
index 16f49a73f4d..28a360921ab 100644
--- a/gcc/d/dmd/VERSION
+++ b/gcc/d/dmd/VERSION
@@ -1 +1 @@ 
-v2.099.1-beta.1
+v2.099.1
diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d
index 52f39d34d0f..16cbe620bc5 100644
--- a/gcc/d/dmd/arrayop.d
+++ b/gcc/d/dmd/arrayop.d
@@ -158,7 +158,7 @@  Expression arrayOp(BinExp e, Scope* sc)
 /// ditto
 Expression arrayOp(BinAssignExp e, Scope* sc)
 {
-    //printf("BinAssignExp.arrayOp() %s\n", toChars());
+    //printf("BinAssignExp.arrayOp() %s\n", e.toChars());
 
     /* Check that the elements of e1 can be assigned to
      */
diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d
index 9c2bbd672a7..1e84b55d181 100644
--- a/gcc/d/dmd/attrib.d
+++ b/gcc/d/dmd/attrib.d
@@ -44,7 +44,6 @@  import dmd.mtype;
 import dmd.objc; // for objc.addSymbols
 import dmd.common.outbuffer;
 import dmd.root.array; // for each
-import dmd.target; // for target.systemLinkage
 import dmd.tokens;
 import dmd.visitor;
 
@@ -399,7 +398,7 @@  extern (C++) final class LinkDeclaration : AttribDeclaration
     {
         super(loc, null, decl);
         //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl);
-        this.linkage = (linkage == LINK.system) ? target.systemLinkage() : linkage;
+        this.linkage = linkage;
     }
 
     static LinkDeclaration create(const ref Loc loc, LINK p, Dsymbols* decl)
@@ -994,7 +993,7 @@  extern (C++) class ConditionalDeclaration : AttribDeclaration
     // Decide if 'then' or 'else' code should be included
     override Dsymbols* include(Scope* sc)
     {
-        //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope);
+        //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
 
         if (errors)
             return null;
@@ -1057,7 +1056,7 @@  extern (C++) final class StaticIfDeclaration : ConditionalDeclaration
      */
     override Dsymbols* include(Scope* sc)
     {
-        //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope);
+        //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope);
 
         if (errors || onStack)
             return null;
@@ -1496,12 +1495,7 @@  extern (C++) final class UserAttributeDeclaration : AttribDeclaration
         if (global.params.cplusplus < CppStdRevision.cpp11)
             return;
 
-        // Avoid `if` at the call site
-        if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null)
-            return;
-
-        foreach (exp; *sym.userAttribDecl.atts)
-        {
+        foreachUdaNoSemantic(sym, (exp) {
             if (isGNUABITag(exp))
             {
                 if (sym.isCPPNamespaceDeclaration() || sym.isNspace())
@@ -1515,9 +1509,10 @@  extern (C++) final class UserAttributeDeclaration : AttribDeclaration
                     sym.errors = true;
                 }
                 // Only one `@gnuAbiTag` is allowed by semantic2
-                return;
+                return 1; // break
             }
-        }
+            return 0; // continue
+        });
     }
 }
 
@@ -1544,14 +1539,14 @@  bool isCoreUda(Dsymbol sym, Identifier ident)
 /**
  * Iterates the UDAs attached to the given symbol.
  *
- * If `dg` returns `!= 0`, it will stop the iteration and return that
- * value, otherwise it will return 0.
- *
  * Params:
  *  sym = the symbol to get the UDAs from
  *  sc = scope to use for semantic analysis of UDAs
- *  dg = called once for each UDA. If `dg` returns `!= 0`, it will stop the
- *      iteration and return that value, otherwise it will return `0`.
+ *  dg = called once for each UDA
+ *
+ * Returns:
+ *  If `dg` returns `!= 0`, stops the iteration and returns that value.
+ *  Otherwise, returns 0.
  */
 int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg)
 {
@@ -1577,3 +1572,32 @@  int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg)
         });
     });
 }
+
+/**
+ * Iterates the UDAs attached to the given symbol, without performing semantic
+ * analysis.
+ *
+ * Use this function instead of `foreachUda` if semantic analysis of `sym` is
+ * still in progress.
+ *
+ * Params:
+ *  sym = the symbol to get the UDAs from
+ *  dg = called once for each UDA
+ *
+ * Returns:
+ *  If `dg` returns `!= 0`, stops the iteration and returns that value.
+ *  Otherwise, returns 0.
+ */
+int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg)
+{
+    if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null)
+        return 0;
+
+    foreach (exp; *sym.userAttribDecl.atts)
+    {
+        if (auto result = dg(exp))
+            return result;
+    }
+
+    return 0;
+}
diff --git a/gcc/d/dmd/common/README.md b/gcc/d/dmd/common/README.md
index a9b65c3dd8d..fb282dcf0a1 100644
--- a/gcc/d/dmd/common/README.md
+++ b/gcc/d/dmd/common/README.md
@@ -2,6 +2,7 @@ 
 
 | File                                                                               | Purpose                                                         |
 |------------------------------------------------------------------------------------|-----------------------------------------------------------------|
+| [bitfields.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/bitfields.d) | Pack multiple boolean fields into bit fields                    |
 | [file.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d)           | Functions and objects dedicated to file I/O and management      |
 | [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/outbuffer.d) | An expandable buffer in which you can write text or binary data |
 | [string.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d)       | Common string functions including filename manipulation         |
diff --git a/gcc/d/dmd/common/bitfields.d b/gcc/d/dmd/common/bitfields.d
new file mode 100644
index 00000000000..d17983d66b4
--- /dev/null
+++ b/gcc/d/dmd/common/bitfields.d
@@ -0,0 +1,70 @@ 
+/**
+ * A library bitfields utility
+ *
+ * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
+ * Authors:   Dennis Korpel
+ * 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/common/bitfields.d, common/bitfields.d)
+ * Documentation: https://dlang.org/phobos/dmd_common_bitfields.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/bitfields.d
+ */
+module dmd.common.bitfields;
+
+/**
+ * Generate code for bit fields inside a struct/class body
+ * Params:
+ *   S = type of a struct with only boolean fields, which should become bit fields
+ *   T = type of bit fields variable, must have enough bits to store all booleans
+ * Returns: D code with a bit fields variable and getter / setter functions
+ */
+extern (D) string generateBitFields(S, T)()
+if (__traits(isUnsigned, T))
+{
+    string result = "extern (C++) pure nothrow @nogc @safe final {";
+    enum structName = __traits(identifier, S);
+
+    foreach (size_t i, mem; __traits(allMembers, S))
+    {
+        static assert(is(typeof(__traits(getMember, S, mem)) == bool));
+        static assert(i < T.sizeof * 8, "too many fields for bit field storage of type `"~T.stringof~"`");
+        enum mask = "(1 << "~i.stringof~")";
+        result ~= "
+        /// set or get the corresponding "~structName~" member
+        bool "~mem~"() const { return !!(bitFields & "~mask~"); }
+        /// ditto
+        bool "~mem~"(bool v)
+        {
+            v ? (bitFields |= "~mask~") : (bitFields &= ~"~mask~");
+            return v;
+        }";
+    }
+    return result ~ "}\n private "~T.stringof~" bitFields;\n";
+}
+
+///
+unittest
+{
+    static struct B
+    {
+        bool x;
+        bool y;
+        bool z;
+    }
+
+    static struct S
+    {
+        mixin(generateBitFields!(B, ubyte));
+    }
+
+    S s;
+    assert(!s.x);
+    s.x = true;
+    assert(s.x);
+    s.x = false;
+    assert(!s.x);
+
+    s.y = true;
+    assert(s.y);
+    assert(!s.x);
+    assert(!s.z);
+}
diff --git a/gcc/d/dmd/common/file.d b/gcc/d/dmd/common/file.d
index e4483d5f947..8f34b5319a9 100644
--- a/gcc/d/dmd/common/file.d
+++ b/gcc/d/dmd/common/file.d
@@ -25,6 +25,8 @@  import core.sys.posix.unistd;
 
 import dmd.common.string;
 
+nothrow:
+
 /**
 Encapsulated management of a memory-mapped file.
 
@@ -52,6 +54,8 @@  struct FileMapping(Datum)
     private const(char)* name;
     // state }
 
+  nothrow:
+
     /**
     Open `filename` and map it in memory. If `Datum` is `const`, opens for
     read-only and maps the content in memory; no error is issued if the file
diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d
index 0705c1880c0..7e46d294f3d 100644
--- a/gcc/d/dmd/common/outbuffer.d
+++ b/gcc/d/dmd/common/outbuffer.d
@@ -16,6 +16,8 @@  import core.stdc.stdio;
 import core.stdc.string;
 import core.stdc.stdlib;
 
+nothrow:
+
 // In theory these functions should also restore errno, but we don't care because
 // we abort application on error anyway.
 extern (C) private pure @system @nogc nothrow
@@ -54,6 +56,8 @@  struct OutBuffer
     int level;
     // state }
 
+  nothrow:
+
     /**
     Construct given size.
     */
diff --git a/gcc/d/dmd/common/string.d b/gcc/d/dmd/common/string.d
index d3bc24f2667..48bf9bb5b55 100644
--- a/gcc/d/dmd/common/string.d
+++ b/gcc/d/dmd/common/string.d
@@ -10,6 +10,8 @@ 
  */
 module dmd.common.string;
 
+nothrow:
+
 /**
 Defines a temporary array using a fixed-length buffer as back store. If the length
 of the buffer suffices, it is readily used. Otherwise, `malloc` is used to
@@ -26,6 +28,8 @@  struct SmallBuffer(T)
     private T[] _extent;
     private bool needsFree;
 
+  nothrow:
+
     @disable this(); // no default ctor
     @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable
 
diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d
index 75ba9ea1e0a..bf66408f1d4 100644
--- a/gcc/d/dmd/constfold.d
+++ b/gcc/d/dmd/constfold.d
@@ -936,7 +936,7 @@  UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio
     {
         if (e1.type.isreal())
         {
-            cmp = RealIdentical(e1.toReal(), e2.toReal());
+            cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal());
         }
         else if (e1.type.isimaginary())
         {
diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d
index 32aed16e796..11229d4bd4f 100644
--- a/gcc/d/dmd/ctfeexpr.d
+++ b/gcc/d/dmd/ctfeexpr.d
@@ -1269,7 +1269,7 @@  private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide
         real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary();
         real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary();
         if (identity)
-            return !RealIdentical(r1, r2);
+            return !CTFloat.isIdentical(r1, r2);
         if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered
         {
             return 1;   // they are not equal
@@ -1399,7 +1399,7 @@  bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2)
         cmp = (es1.var == es2.var && es1.offset == es2.offset);
     }
     else if (e1.type.isreal())
-        cmp = RealIdentical(e1.toReal(), e2.toReal());
+        cmp = CTFloat.isIdentical(e1.toReal(), e2.toReal());
     else if (e1.type.isimaginary())
         cmp = RealIdentical(e1.toImaginary(), e2.toImaginary());
     else if (e1.type.iscomplex())
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
index 685987b734d..83978391e42 100644
--- a/gcc/d/dmd/dcast.d
+++ b/gcc/d/dmd/dcast.d
@@ -432,7 +432,7 @@  MATCH implicitConvTo(Expression e, Type t)
                 return MATCH.nomatch;
             goto case Tuns8;
         case Tuns8:
-            //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value);
+            //printf("value = %llu %llu\n", cast(dinteger_t)cast(ubyte)value, value);
             if (cast(ubyte)value != value)
                 return MATCH.nomatch;
             break;
@@ -492,8 +492,8 @@  MATCH implicitConvTo(Expression e, Type t)
             break;
 
         case Tpointer:
-            //printf("type = %s\n", type.toBasetype()->toChars());
-            //printf("t = %s\n", t.toBasetype()->toChars());
+            //printf("type = %s\n", type.toBasetype().toChars());
+            //printf("t = %s\n", t.toBasetype().toChars());
             if (ty == Tpointer && e.type.toBasetype().nextOf().ty == t.toBasetype().nextOf().ty)
             {
                 /* Allow things like:
@@ -1107,6 +1107,10 @@  MATCH implicitConvTo(Expression e, Type t)
 
     MATCH visitCond(CondExp e)
     {
+        auto result = visit(e);
+        if (result != MATCH.nomatch)
+            return result;
+
         MATCH m1 = e.e1.implicitConvTo(t);
         MATCH m2 = e.e2.implicitConvTo(t);
         //printf("CondExp: m1 %d m2 %d\n", m1, m2);
@@ -2077,7 +2081,7 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
         if (auto tsa = tb.isTypeSArray())
         {
             size_t dim2 = cast(size_t)tsa.dim.toInteger();
-            //printf("dim from = %d, to = %d\n", (int)se.len, (int)dim2);
+            //printf("dim from = %d, to = %d\n", cast(int)se.len, cast(int)dim2);
 
             // Changing dimensions
             if (dim2 != se.len)
diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d
index c0e40a5263a..45eb582745a 100644
--- a/gcc/d/dmd/declaration.d
+++ b/gcc/d/dmd/declaration.d
@@ -1083,29 +1083,12 @@  extern (C++) class VarDeclaration : Declaration
         bool isArgDtorVar;      /// temporary created to handle scope destruction of a function argument
     }
 
-    private ushort bitFields;       // stores multiple booleans for BitFields
+    import dmd.common.bitfields : generateBitFields;
+    mixin(generateBitFields!(BitFields, ushort));
+
     byte canassign;                 // it can be assigned to
     ubyte isdataseg;                // private data for isDataseg 0 unset, 1 true, 2 false
 
-    // Generate getter and setter functions for `bitFields`
-    extern (D) mixin(() {
-        string result = "extern (C++) pure nothrow @nogc @safe final {";
-        foreach (size_t i, mem; __traits(allMembers, BitFields))
-        {
-            result ~= "
-            /// set or get the corresponding BitFields member
-            bool "~mem~"() const { return !!(bitFields & (1 << "~i.stringof~")); }
-            /// ditto
-            bool "~mem~"(bool v)
-            {
-                v ? (bitFields |= (1 << "~i.stringof~")) : (bitFields &= ~(1 << "~i.stringof~"));
-                return v;
-            }";
-        }
-        return result ~ "}";
-    }());
-
-
     final extern (D) this(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_)
     in
     {
diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d
index c3662a734d8..1f895e03af0 100644
--- a/gcc/d/dmd/dmangle.d
+++ b/gcc/d/dmd/dmangle.d
@@ -340,7 +340,6 @@  public:
         final switch (t.linkage)
         {
         case LINK.default_:
-        case LINK.system:
         case LINK.d:
             mc = 'F';
             break;
@@ -356,6 +355,8 @@  public:
         case LINK.objc:
             mc = 'Y';
             break;
+        case LINK.system:
+            assert(0);
         }
         buf.writeByte(mc);
 
@@ -1340,7 +1341,9 @@  extern (D) const(char)[] externallyMangledIdentifier(Declaration d)
     {
         if (d.linkage != LINK.d && d.localNum)
             d.error("the same declaration cannot be in multiple scopes with non-D linkage");
-        final switch (d.linkage)
+
+        const l = d.linkage == LINK.system ? target.systemLinkage() : d.linkage;
+        final switch (l)
         {
             case LINK.d:
                 break;
@@ -1354,9 +1357,10 @@  extern (D) const(char)[] externallyMangledIdentifier(Declaration d)
                 return p.toDString();
             }
             case LINK.default_:
-            case LINK.system:
                 d.error("forward declaration");
                 return d.ident.toString();
+            case LINK.system:
+                assert(0);
         }
     }
     return null;
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index c7e6418bf46..e9c4c954efd 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -69,7 +69,7 @@  void semantic3OnDependencies(Module m)
 /**
  * Remove generated .di files on error and exit
  */
-void removeHdrFilesAndFail(ref Param params, ref Modules modules)
+void removeHdrFilesAndFail(ref Param params, ref Modules modules) nothrow
 {
     if (params.doHdrGeneration)
     {
@@ -94,7 +94,7 @@  void removeHdrFilesAndFail(ref Param params, ref Modules modules)
  * Returns:
  *  the filename of the child package or module
  */
-private const(char)[] getFilename(Identifier[] packages, Identifier ident)
+private const(char)[] getFilename(Identifier[] packages, Identifier ident) nothrow
 {
     const(char)[] filename = ident.toString();
 
@@ -157,14 +157,14 @@  extern (C++) class Package : ScopeDsymbol
     uint tag;        // auto incremented tag, used to mask package tree in scopes
     Module mod;     // !=null if isPkgMod == PKG.module_
 
-    final extern (D) this(const ref Loc loc, Identifier ident)
+    final extern (D) this(const ref Loc loc, Identifier ident) nothrow
     {
         super(loc, ident);
         __gshared uint packageTag;
         this.tag = packageTag++;
     }
 
-    override const(char)* kind() const
+    override const(char)* kind() const nothrow
     {
         return "package";
     }
@@ -664,7 +664,7 @@  extern (C++) final class Module : Package
         //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
         if (auto result = global.fileManager.lookup(srcfile))
         {
-            this.src = result.data;
+            this.src = result;
             if (global.params.emitMakeDeps)
                 global.params.makeDeps.push(srcfile.toChars());
             return true;
@@ -1380,7 +1380,7 @@  extern (C++) final class Module : Package
         a.setDim(0);
     }
 
-    extern (D) static void clearCache()
+    extern (D) static void clearCache() nothrow
     {
         foreach (Module m; amodules)
             m.searchCacheIdent = null;
@@ -1391,7 +1391,7 @@  extern (C++) final class Module : Package
      * return true if it imports m.
      * Can be used to detect circular imports.
      */
-    int imports(Module m)
+    int imports(Module m) nothrow
     {
         //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
         version (none)
@@ -1414,14 +1414,14 @@  extern (C++) final class Module : Package
         return false;
     }
 
-    bool isRoot()
+    bool isRoot() nothrow
     {
         return this.importedFrom == this;
     }
 
     // true if the module source file is directly
     // listed in command line.
-    bool isCoreModule(Identifier ident)
+    bool isCoreModule(Identifier ident) nothrow
     {
         return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
     }
@@ -1440,7 +1440,7 @@  extern (C++) final class Module : Package
 
     uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
 
-    override inout(Module) isModule() inout
+    override inout(Module) isModule() inout nothrow
     {
         return this;
     }
@@ -1455,7 +1455,7 @@  extern (C++) final class Module : Package
      * Params:
      *    buf = The buffer to write to
      */
-    void fullyQualifiedName(ref OutBuffer buf)
+    void fullyQualifiedName(ref OutBuffer buf) nothrow
     {
         buf.writestring(ident.toString());
 
@@ -1469,7 +1469,7 @@  extern (C++) final class Module : Package
     /** Lazily initializes and returns the escape table.
     Turns out it eats a lot of memory.
     */
-    extern(D) Escape* escapetable()
+    extern(D) Escape* escapetable() nothrow
     {
         if (!_escapetable)
             _escapetable = new Escape();
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index cb6c278fc44..2021e2afd8e 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -112,12 +112,12 @@  struct Ungag
 {
     uint oldgag;
 
-    extern (D) this(uint old)
+    extern (D) this(uint old) nothrow
     {
         this.oldgag = old;
     }
 
-    extern (C++) ~this()
+    extern (C++) ~this() nothrow
     {
         global.gag = oldgag;
     }
@@ -255,27 +255,27 @@  extern (C++) class Dsymbol : ASTNode
     DeprecatedDeclaration depdecl;           // customized deprecation message
     UserAttributeDeclaration userAttribDecl;    // user defined attributes
 
-    final extern (D) this()
+    final extern (D) this() nothrow
     {
         //printf("Dsymbol::Dsymbol(%p)\n", this);
         loc = Loc(null, 0, 0);
     }
 
-    final extern (D) this(Identifier ident)
+    final extern (D) this(Identifier ident) nothrow
     {
         //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
         this.loc = Loc(null, 0, 0);
         this.ident = ident;
     }
 
-    final extern (D) this(const ref Loc loc, Identifier ident)
+    final extern (D) this(const ref Loc loc, Identifier ident) nothrow
     {
         //printf("Dsymbol::Dsymbol(%p, ident)\n", this);
         this.loc = loc;
         this.ident = ident;
     }
 
-    static Dsymbol create(Identifier ident)
+    static Dsymbol create(Identifier ident) nothrow
     {
         return new Dsymbol(ident);
     }
@@ -800,6 +800,22 @@  extern (C++) class Dsymbol : ASTNode
             if (isAliasDeclaration() && !_scope)
                 setScope(sc);
             Dsymbol s2 = sds.symtabLookup(this,ident);
+            /* https://issues.dlang.org/show_bug.cgi?id=17434
+             *
+             * If we are trying to add an import to the symbol table
+             * that has already been introduced, then keep the one with
+             * larger visibility. This is fine for imports because if
+             * we have multiple imports of the same file, if a single one
+             * is public then the symbol is reachable.
+             */
+            if (auto i1 = isImport())
+            {
+                if (auto i2 = s2.isImport())
+                {
+                    if (sc.explicitVisibility && sc.visibility > i2.visibility)
+                        sds.symtab.update(this);
+                }
+            }
 
             // If using C tag/prototype/forward declaration rules
             if (sc.flags & SCOPE.Cfile && !this.isImport())
@@ -933,14 +949,7 @@  extern (C++) class Dsymbol : ASTNode
                 TemplateInstance ti = st.isTemplateInstance();
                 sm = s.search(loc, ti.name);
                 if (!sm)
-                {
-                    sm = s.search_correct(ti.name);
-                    if (sm)
-                        .error(loc, "template identifier `%s` is not a member of %s `%s`, did you mean %s `%s`?", ti.name.toChars(), s.kind(), s.toPrettyChars(), sm.kind(), sm.toChars());
-                    else
-                        .error(loc, "template identifier `%s` is not a member of %s `%s`", ti.name.toChars(), s.kind(), s.toPrettyChars());
                     return null;
-                }
                 sm = sm.toAlias();
                 TemplateDeclaration td = sm.isTemplateDeclaration();
                 if (!td)
@@ -1381,16 +1390,16 @@  private:
     BitArray accessiblePackages, privateAccessiblePackages;// whitelists of accessible (imported) packages
 
 public:
-    final extern (D) this()
+    final extern (D) this() nothrow
     {
     }
 
-    final extern (D) this(Identifier ident)
+    final extern (D) this(Identifier ident) nothrow
     {
         super(ident);
     }
 
-    final extern (D) this(const ref Loc loc, Identifier ident)
+    final extern (D) this(const ref Loc loc, Identifier ident) nothrow
     {
         super(loc, ident);
     }
@@ -1604,7 +1613,7 @@  public:
         return os;
     }
 
-    void importScope(Dsymbol s, Visibility visibility)
+    void importScope(Dsymbol s, Visibility visibility) nothrow
     {
         //printf("%s.ScopeDsymbol::importScope(%s, %d)\n", toChars(), s.toChars(), visibility);
         // No circular or redundant import's
@@ -1631,7 +1640,7 @@  public:
         }
     }
 
-    extern (D) final void addAccessiblePackage(Package p, Visibility visibility)
+    extern (D) final void addAccessiblePackage(Package p, Visibility visibility) nothrow
     {
         auto pary = visibility.kind == Visibility.Kind.private_ ? &privateAccessiblePackages : &accessiblePackages;
         if (pary.length <= p.tag)
@@ -1639,7 +1648,7 @@  public:
         (*pary)[p.tag] = true;
     }
 
-    bool isPackageAccessible(Package p, Visibility visibility, int flags = 0)
+    bool isPackageAccessible(Package p, Visibility visibility, int flags = 0) nothrow
     {
         if (p.tag < accessiblePackages.length && accessiblePackages[p.tag] ||
             visibility.kind == Visibility.Kind.private_ && p.tag < privateAccessiblePackages.length && privateAccessiblePackages[p.tag])
@@ -1654,7 +1663,7 @@  public:
         return false;
     }
 
-    override final bool isforwardRef()
+    override final bool isforwardRef() nothrow
     {
         return (members is null);
     }
@@ -1742,7 +1751,7 @@  public:
      * Returns:
      *   null if already in table, `s` if inserted
      */
-    Dsymbol symtabInsert(Dsymbol s)
+    Dsymbol symtabInsert(Dsymbol s) nothrow
     {
         return symtab.insert(s);
     }
@@ -1755,7 +1764,7 @@  public:
      * Returns:
      *   Dsymbol if found, null if not
      */
-    Dsymbol symtabLookup(Dsymbol s, Identifier id)
+    Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow
     {
         return symtab.lookup(id);
     }
@@ -1838,7 +1847,7 @@  extern (C++) final class WithScopeSymbol : ScopeDsymbol
 {
     WithStatement withstate;
 
-    extern (D) this(WithStatement withstate)
+    extern (D) this(WithStatement withstate) nothrow
     {
         this.withstate = withstate;
     }
@@ -1898,7 +1907,7 @@  extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
     private RootObject arrayContent;
     Scope* sc;
 
-    extern (D) this(Scope* sc, Expression exp)
+    extern (D) this(Scope* sc, Expression exp) nothrow
     {
         super(exp.loc, null);
         assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array);
@@ -1906,13 +1915,13 @@  extern (C++) final class ArrayScopeSymbol : ScopeDsymbol
         this.arrayContent = exp;
     }
 
-    extern (D) this(Scope* sc, TypeTuple type)
+    extern (D) this(Scope* sc, TypeTuple type) nothrow
     {
         this.sc = sc;
         this.arrayContent = type;
     }
 
-    extern (D) this(Scope* sc, TupleDeclaration td)
+    extern (D) this(Scope* sc, TupleDeclaration td) nothrow
     {
         this.sc = sc;
         this.arrayContent = td;
@@ -2109,7 +2118,7 @@  extern (C++) final class OverloadSet : Dsymbol
 {
     Dsymbols a;     // array of Dsymbols
 
-    extern (D) this(Identifier ident, OverloadSet os = null)
+    extern (D) this(Identifier ident, OverloadSet os = null) nothrow
     {
         super(ident);
         if (os)
@@ -2118,7 +2127,7 @@  extern (C++) final class OverloadSet : Dsymbol
         }
     }
 
-    void push(Dsymbol s)
+    void push(Dsymbol s) nothrow
     {
         a.push(s);
     }
@@ -2152,12 +2161,13 @@  extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol
      * Can be `null` before being lazily initialized.
      */
     ScopeDsymbol forward;
-    extern (D) this(ScopeDsymbol forward)
+    extern (D) this(ScopeDsymbol forward) nothrow
     {
         super(null);
         this.forward = forward;
     }
-    override Dsymbol symtabInsert(Dsymbol s)
+
+    override Dsymbol symtabInsert(Dsymbol s) nothrow
     {
         assert(forward);
         if (auto d = s.isDeclaration())
@@ -2188,7 +2198,7 @@  extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol
      * and
      *     static foreach (i; [0]) { enum i = 2; }
      */
-    override Dsymbol symtabLookup(Dsymbol s, Identifier id)
+    override Dsymbol symtabLookup(Dsymbol s, Identifier id) nothrow
     {
         assert(forward);
         // correctly diagnose clashing foreach loop variables.
@@ -2219,7 +2229,7 @@  extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol
 
     override const(char)* kind()const{ return "local scope"; }
 
-    override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout
+    override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout nothrow
     {
         return this;
     }
@@ -2234,13 +2244,13 @@  extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol
 extern (C++) final class ExpressionDsymbol : Dsymbol
 {
     Expression exp;
-    this(Expression exp)
+    this(Expression exp) nothrow
     {
         super();
         this.exp = exp;
     }
 
-    override inout(ExpressionDsymbol) isExpressionDsymbol() inout
+    override inout(ExpressionDsymbol) isExpressionDsymbol() inout nothrow
     {
         return this;
     }
@@ -2259,7 +2269,7 @@  extern (C++) final class AliasAssign : Dsymbol
     Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym`
                       /// only one of type and aliassym can be != null
 
-    extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym)
+    extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow
     {
         super(loc, null);
         this.ident = ident;
@@ -2299,6 +2309,8 @@  extern (C++) final class DsymbolTable : RootObject
 {
     AssocArray!(Identifier, Dsymbol) tab;
 
+  nothrow:
+
    /***************************
     * Look up Identifier in symbol table
     * Params:
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index c9906363cc8..1ccaab56da6 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -51,6 +51,7 @@  import dmd.init;
 import dmd.initsem;
 import dmd.hdrgen;
 import dmd.mtype;
+import dmd.mustuse;
 import dmd.nogc;
 import dmd.nspace;
 import dmd.objc;
@@ -542,7 +543,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
                     Parameter arg = Parameter.getNth(tt.arguments, pos);
                     arg.type = arg.type.typeSemantic(dsym.loc, sc);
                     //printf("[%d] iexps.dim = %d, ", pos, iexps.dim);
-                    //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars());
+                    //printf("e = (%s %s, %s), ", Token.tochars[e.op], e.toChars(), e.type.toChars());
                     //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
 
                     if (e != ie)
@@ -581,7 +582,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
                             arg = Parameter.getNth(tt.arguments, pos + u);
                             arg.type = arg.type.typeSemantic(dsym.loc, sc);
                             //printf("[%d+%d] exps.dim = %d, ", pos, u, exps.dim);
-                            //printf("ee = (%s %s, %s), ", Token::tochars[ee.op], ee.toChars(), ee.type.toChars());
+                            //printf("ee = (%s %s, %s), ", Token.tochars[ee.op], ee.toChars(), ee.type.toChars());
                             //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
 
                             size_t iexps_dim = iexps.dim - 1 + exps.dim;
@@ -2046,6 +2047,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
 
         ed.semanticRun = PASS.semantic;
         UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage);
+        checkMustUseReserved(ed);
 
         if (!ed.members && !ed.memtype) // enum ident;
         {
@@ -3017,7 +3019,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
         if ((funcdecl.storage_class & STC.TYPECTOR) && !(ad || funcdecl.isNested()))
             funcdecl.storage_class &= ~STC.TYPECTOR;
 
-        //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration::isFinal());
+        //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration.isFinal());
 
         if (sc.flags & SCOPE.compile)
             funcdecl.flags |= FUNCFLAG.compileTimeOnly; // don't emit code for this function
@@ -3056,6 +3058,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
         funcdecl.visibility = sc.visibility;
         funcdecl.userAttribDecl = sc.userAttribDecl;
         UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl.linkage);
+        checkMustUseReserved(funcdecl);
 
         if (!funcdecl.originalType)
             funcdecl.originalType = funcdecl.type.syntaxCopy();
@@ -4092,7 +4095,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
     override void visit(PostBlitDeclaration pbd)
     {
         //printf("PostBlitDeclaration::semantic() %s\n", toChars());
-        //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), ident, Id::dtor);
+        //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor);
         //printf("stc = x%llx\n", sc.stc);
         if (pbd.semanticRun >= PASS.semanticdone)
             return;
@@ -4129,7 +4132,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
     override void visit(DtorDeclaration dd)
     {
         //printf("DtorDeclaration::semantic() %s\n", toChars());
-        //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id::dtor.toChars(), ident, Id::dtor);
+        //printf("ident: %s, %s, %p, %p\n", ident.toChars(), Id.dtor.toChars(), ident, Id.dtor);
         if (dd.semanticRun >= PASS.semanticdone)
             return;
         if (dd._scope)
@@ -4770,6 +4773,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
         }
         cldec.semanticRun = PASS.semantic;
         UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage);
+        checkMustUseReserved(cldec);
 
         if (cldec.baseok < Baseok.done)
         {
@@ -5475,6 +5479,7 @@  private extern(C++) final class DsymbolSemanticVisitor : Visitor
                 idec.classKind = ClassKind.cpp;
             idec.cppnamespace = sc.namespace;
             UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage);
+            checkMustUseReserved(idec);
 
             if (sc.linkage == LINK.objc)
                 objc.setObjc(idec);
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index a5ec63ca763..fb41e2bd05f 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -2626,7 +2626,7 @@  void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc,
             printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
             //printf("\tty = %d\n", arg.type.ty);
         }
-        //printf("stc = %llx\n", dstart.scope.stc);
+        //printf("stc = %llx\n", dstart._scope.stc);
         //printf("match:t/f = %d/%d\n", ta_last, m.last);
     }
 
@@ -4332,7 +4332,7 @@  MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
             {
                 TypeStruct tp = cast(TypeStruct)tparam;
 
-                //printf("\t%d\n", (MATCH) t.implicitConvTo(tp));
+                //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
                 if (wm && t.deduceWild(tparam, false))
                 {
                     result = MATCH.constant;
@@ -4513,7 +4513,7 @@  MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param
             {
                 TypeClass tp = cast(TypeClass)tparam;
 
-                //printf("\t%d\n", (MATCH) t.implicitConvTo(tp));
+                //printf("\t%d\n", cast(MATCH) t.implicitConvTo(tp));
                 if (wm && t.deduceWild(tparam, false))
                 {
                     result = MATCH.constant;
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index 4196c05489d..78b9a07f3a3 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -440,22 +440,33 @@  bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Exp
  *      sc = used to determine current function and module
  *      firstArg = `ref` argument through which `arg` may be assigned
  *      arg = initializer for parameter
+ *      param = parameter declaration corresponding to `arg`
  *      gag = do not print error messages
  * Returns:
  *      `true` if assignment to `firstArg` would cause an error
  */
-bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, bool gag)
+bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Parameter param, bool gag)
 {
     enum log = false;
     if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n",
         firstArg.toChars(), arg.toChars());
     //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
 
-    if (!arg.type.hasPointers())
+    if (!(param.storageClass & STC.return_))
+        return false;
+
+    if (!arg.type.hasPointers() && !param.isReference())
         return false;
 
+    // `byRef` needed for `assign(ref int* x, ref int i) {x = &i};`
+    // Note: taking address of scope pointer is not allowed
+    // `assign(ref int** x, return ref scope int* i) {x = &i};`
+    // Thus no return ref/return scope ambiguity here
+    const byRef = param.isReference() && !(param.storageClass & STC.scope_)
+        && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef
+
     scope e = new AssignExp(arg.loc, firstArg, arg);
-    return checkAssignEscape(sc, e, gag);
+    return checkAssignEscape(sc, e, gag, byRef);
 }
 
 /*****************************************************
@@ -496,23 +507,13 @@  bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
     foreach (const i; 0 .. n)
     {
         Expression arg = (*ce.arguments)[i];
-        if (!arg.type.hasPointers())
-            continue;
-
         //printf("\targ[%d]: %s\n", i, arg.toChars());
 
         if (i - j < nparams && i >= j)
         {
             Parameter p = tf.parameterList[i - j];
-
-            if (p.storageClass & STC.return_)
-            {
-                /* Fake `dve.e1 = arg;` and look for scope violations
-                 */
-                scope e = new AssignExp(arg.loc, dve.e1, arg);
-                if (checkAssignEscape(sc, e, gag))
-                    return true;
-            }
+            if (checkParamArgumentReturn(sc, dve.e1, arg, p, gag))
+                return true;
         }
     }
 
@@ -529,13 +530,14 @@  bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
  *      sc = used to determine current function and module
  *      e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack
  *      gag = do not print error messages
+ *      byRef = set to `true` if `e1` of `e` gets assigned a reference to `e2`
  * Returns:
  *      `true` if pointers to the stack can escape via assignment
  */
-bool checkAssignEscape(Scope* sc, Expression e, bool gag)
+bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef)
 {
     enum log = false;
-    if (log) printf("checkAssignEscape(e: %s)\n", e.toChars());
+    if (log) printf("checkAssignEscape(e: %s, byRef: %d)\n", e.toChars(), byRef);
     if (e.op != EXP.assign && e.op != EXP.blit && e.op != EXP.construct &&
         e.op != EXP.concatenateAssign && e.op != EXP.concatenateElemAssign && e.op != EXP.concatenateDcharAssign)
         return false;
@@ -561,7 +563,10 @@  bool checkAssignEscape(Scope* sc, Expression e, bool gag)
 
     EscapeByResults er;
 
-    escapeByValue(e2, &er);
+    if (byRef)
+        escapeByRef(e2, &er);
+    else
+        escapeByValue(e2, &er);
 
     if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
         return false;
@@ -789,8 +794,18 @@  bool checkAssignEscape(Scope* sc, Expression e, bool gag)
 
         Dsymbol p = v.toParent2();
 
+        if (vaIsFirstRef && v.isParameter() &&
+            !(v.storage_class & STC.return_) &&
+            fd.flags & FUNCFLAG.returnInprocess &&
+            p == fd)
+        {
+            //if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), fd.toChars());
+            inferReturn(fd, v, /*returnScope:*/ false);
+        }
+
         // If va's lifetime encloses v's, then error
         if (va &&
+            !(vaIsFirstRef && (v.storage_class & STC.return_)) &&
             (va.enclosesLifetimeOf(v) || (va.isReference() && !(va.storage_class & STC.temp)) || va.isDataseg()) &&
             fd.setUnsafe())
         {
diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d
index 07d885b0bee..24030eb42d0 100644
--- a/gcc/d/dmd/expression.d
+++ b/gcc/d/dmd/expression.d
@@ -1660,6 +1660,7 @@  extern (C++) abstract class Expression : ASTNode
         inout(MixinExp)     isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; }
         inout(ImportExp)    isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; }
         inout(AssertExp)    isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; }
+        inout(ThrowExp)     isThrowExp() { return op == EXP.throw_ ? cast(typeof(return))this : null; }
         inout(DotIdExp)     isDotIdExp() { return op == EXP.dotIdentifier ? cast(typeof(return))this : null; }
         inout(DotTemplateExp) isDotTemplateExp() { return op == EXP.dotTemplateDeclaration ? cast(typeof(return))this : null; }
         inout(DotVarExp)    isDotVarExp() { return op == EXP.dotVariable ? cast(typeof(return))this : null; }
@@ -2684,7 +2685,7 @@  extern (C++) final class StringExp : Expression
         const len2 = se2.len;
 
         assert(this.sz == se2.sz, "Comparing string expressions of different sizes");
-        //printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2);
+        //printf("sz = %d, len1 = %d, len2 = %d\n", sz, cast(int)len1, cast(int)len2);
         if (len1 == len2)
         {
             switch (sz)
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index 7b7c489295a..64cd7ab849c 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -56,6 +56,7 @@  import dmd.initsem;
 import dmd.inline;
 import dmd.intrange;
 import dmd.mtype;
+import dmd.mustuse;
 import dmd.nspace;
 import dmd.opover;
 import dmd.optimize;
@@ -713,7 +714,7 @@  private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2
  */
 Expression resolvePropertiesOnly(Scope* sc, Expression e1)
 {
-    //printf("e1 = %s %s\n", Token::toChars(e1.op), e1.toChars());
+    //printf("e1 = %s %s\n", Token.toChars(e1.op), e1.toChars());
 
     Expression handleOverloadSet(OverloadSet os)
     {
@@ -2033,7 +2034,7 @@  private bool functionParameters(const ref Loc loc, Scope* sc,
                  * Check arg to see if it matters.
                  */
                 if (global.params.useDIP1000 == FeatureState.enabled)
-                    err |= checkParamArgumentReturn(sc, firstArg, arg, false);
+                    err |= checkParamArgumentReturn(sc, firstArg, arg, p, false);
             }
             else if (tf.parameterEscapes(tthis, p))
             {
@@ -3261,7 +3262,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                 // printf("apply fix for issue 9490: add `this.` to `%s`...\n", e.toChars());
                 e = new DotVarExp(exp.loc, new ThisExp(exp.loc), ve.var, false);
             }
-            //printf("e = %s %s\n", Token::toChars(e.op), e.toChars());
+            //printf("e = %s %s\n", Token.toChars(e.op), e.toChars());
             e = e.expressionSemantic(sc);
         }
         else if (t)
@@ -6032,7 +6033,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             auto fileName = FileName(name.toDString);
             if (auto fmResult = global.fileManager.lookup(fileName))
             {
-                se = new StringExp(e.loc, fmResult.data);
+                se = new StringExp(e.loc, fmResult);
             }
             else
             {
@@ -6047,9 +6048,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                     // take ownership of buffer (probably leaking)
                     auto data = readResult.extractSlice();
                     se = new StringExp(e.loc, data);
-
-                    FileBuffer* fileBuffer = new FileBuffer(data);
-                    global.fileManager.add(fileName, fileBuffer);
+                    global.fileManager.add(fileName, data);
                 }
             }
         }
@@ -6388,7 +6387,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         static if (LOGSEMANTIC)
         {
             printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
-            //printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op));
+            //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op));
         }
 
         if (sc.flags & SCOPE.Cfile)
@@ -8177,7 +8176,10 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
             return;
 
         if (e.type is Type.tvoid)
+        {
+            checkMustUse(e.e1, sc);
             discardValue(e.e1);
+        }
         else if (!e.allowCommaExp && !e.isGenerated)
             e.error("Using the result of a comma expression is not allowed");
     }
@@ -8956,7 +8958,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
 
                     Parameter arg = Parameter.getNth(tt.arguments, u);
                     //printf("[%d] iexps.dim = %d, ", u, iexps.dim);
-                    //printf("e = (%s %s, %s), ", Token::tochars[e.op], e.toChars(), e.type.toChars());
+                    //printf("e = (%s %s, %s), ", Token.toChars[e.op], e.toChars(), e.type.toChars());
                     //printf("arg = (%s, %s)\n", arg.toChars(), arg.type.toChars());
 
                     if (!arg || !e.type.implicitConvTo(arg.type))
@@ -9849,7 +9851,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
          * `reorderSettingAAElem` creates a tree of comma expressions, however,
          * `checkAssignExp` expects only AssignExps.
          */
-        checkAssignEscape(sc, Expression.extractLast(res, tmp), false);
+        checkAssignEscape(sc, Expression.extractLast(res, tmp), false, false);
 
         if (auto ae = res.isConstructExp())
         {
@@ -10124,7 +10126,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  */
                 if (isRecursiveAliasThis(exp.att1, exp.e1.type))
                     return null;
-                //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
+                //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars());
                 Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident);
                 BinExp be = cast(BinExp)exp.copy();
                 be.e1 = e1;
@@ -10142,7 +10144,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
                  */
                 if (isRecursiveAliasThis(exp.att2, exp.e2.type))
                     return null;
-                //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
+                //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars());
                 Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident);
                 BinExp be = cast(BinExp)exp.copy();
                 be.e2 = e2;
@@ -10169,7 +10171,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         auto res = exp.reorderSettingAAElem(sc);
         if ((exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) &&
             global.params.useDIP1000 == FeatureState.enabled)
-            checkAssignEscape(sc, res, false);
+            checkAssignEscape(sc, res, false, false);
         result = res;
     }
 
@@ -12803,129 +12805,125 @@  bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
         return false;
     }
 
-    //printf("checkSharedAccess() %s\n", e.toChars());
-
-    static extern(C++) final class SharedCheckVisitor : SemanticTimeTransitiveVisitor
-    {
-        /// In case we don't know which expression triggered it,
-        /// e.g. for `visit(Type)` overload
-        Expression original;
-        /// Where the result is stored (`true` == error)
-        bool result;
-        /// Whether we should allow one level of dereferencing
-        bool allowRef;
+    //printf("checkSharedAccess() `%s` returnRef: %d\n", e.toChars(), returnRef);
 
-        /// Ctor
-        this(Expression oe, bool allowRef_)
-        {
-            this.original = oe;
-            this.allowRef = allowRef_;
-        }
+    /* In case we don't know which expression triggered it,
+     * e.g. for `visit(Type)` overload
+     */
+    Expression original = e;
 
-        void sharedError(Expression e)
+    bool check(Expression e, bool allowRef)
+    {
+        bool sharedError(Expression e)
         {
             // https://dlang.org/phobos/core_atomic.html
             e.error("direct access to shared `%s` is not allowed, see `core.atomic`", e.toChars());
-            this.result = true;
+            return true;
         }
 
-        /// Introduce base class overrides
-        alias visit = SemanticTimeTransitiveVisitor.visit;
-
         // Error by default
-        override void visit(Expression e)
+        bool visit(Expression e)
         {
             if (e.type.isShared())
-                this.sharedError(e);
+                return sharedError(e);
+            return false;
         }
 
-        /// Ditto
-        override void visit(Type t)
+        bool visitNew(NewExp e)
         {
+            if (e.thisexp)
+                check(e.thisexp, false);
             // Note: This handles things like `new shared(Throwable).msg`,
             // where accessing `msg` would violate `shared`.
-            if (t.isShared())
-                this.sharedError(this.original);
+            if (e.newtype.isShared())
+                return sharedError(original);
+            return false;
         }
 
-        // Those have no indirections / can be ignored
-        override void visit(ErrorExp e) {}
-        override void visit(ComplexExp e) {}
-        override void visit(IntegerExp e) {}
-        override void visit(NullExp e) {}
-
-        override void visit(VarExp e)
+        bool visitVar(VarExp e)
         {
-            if (!this.allowRef && e.var.type.isShared())
-                this.sharedError(e);
+            if (!allowRef && e.var.type.isShared())
+                return sharedError(e);
+            return false;
         }
 
-        override void visit(AddrExp e)
+        bool visitAddr(AddrExp e)
         {
-            this.allowRef = true;
-            e.e1.accept(this);
+            return check(e.e1, true);
         }
 
-        override void visit(PtrExp e)
+        bool visitPtr(PtrExp e)
         {
-            if (!this.allowRef && e.type.isShared())
-                return this.sharedError(e);
+            if (!allowRef && e.type.isShared())
+                return sharedError(e);
 
             if (e.e1.type.isShared())
-                return this.sharedError(e);
+                return sharedError(e);
 
-            this.allowRef = false;
-            e.e1.accept(this);
+            return check(e.e1, false);
         }
 
-        override void visit(DotVarExp e)
+        bool visitDotVar(DotVarExp e)
         {
             auto fd = e.var.isFuncDeclaration();
             const sharedFunc = fd && fd.type.isShared;
 
-            if (!this.allowRef && e.type.isShared() && !sharedFunc)
-                return this.sharedError(e);
+            if (!allowRef && e.type.isShared() && !sharedFunc)
+                return sharedError(e);
 
-            // Allow to use `DotVarExp` within value types
-            if (e.e1.type.ty == Tsarray || e.e1.type.ty == Tstruct)
-                return e.e1.accept(this);
+            // Allow using `DotVarExp` within value types
+            if (e.e1.type.isTypeSArray() || e.e1.type.isTypeStruct())
+                return check(e.e1, allowRef);
 
             // If we end up with a single `VarExp`, it might be a `ref` param
             // `shared ref T` param == `shared(T)*`.
             if (auto ve = e.e1.isVarExp())
             {
-                this.allowRef = this.allowRef && (ve.var.storage_class & STC.ref_);
-                return e.e1.accept(this);
+                return check(e.e1, allowRef && (ve.var.storage_class & STC.ref_));
             }
 
-            this.allowRef = false;
-            return e.e1.accept(this);
+            return check(e.e1, false);
         }
 
-        override void visit(IndexExp e)
+        bool visitIndex(IndexExp e)
         {
-            if (!this.allowRef && e.type.isShared())
-                return this.sharedError(e);
+            if (!allowRef && e.type.isShared())
+                return sharedError(e);
 
             if (e.e1.type.isShared())
-                return this.sharedError(e);
+                return sharedError(e);
 
-            this.allowRef = false;
-            e.e1.accept(this);
+            return check(e.e1, false);
         }
 
-        override void visit(CommaExp e)
+        bool visitComma(CommaExp e)
         {
             // Cannot be `return ref` since we can't use the return,
             // but it's better to show that error than an unrelated `shared` one
-            this.allowRef = true;
-            e.e2.accept(this);
+            return check(e.e2, true);
+        }
+
+        switch (e.op)
+        {
+            default:              return visit(e);
+
+            // Those have no indirections / can be ignored
+            case EXP.call:
+            case EXP.error:
+            case EXP.complex80:
+            case EXP.int64:
+            case EXP.null_:       return false;
+
+            case EXP.variable:    return visitVar(e.isVarExp());
+            case EXP.new_:        return visitNew(e.isNewExp());
+            case EXP.address:     return visitAddr(e.isAddrExp());
+            case EXP.star:        return visitPtr(e.isPtrExp());
+            case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
+            case EXP.index:       return visitIndex(e.isIndexExp());
         }
     }
 
-    scope visitor = new SharedCheckVisitor(e, returnRef);
-    e.accept(visitor);
-    return visitor.result;
+    return check(e, returnRef);
 }
 
 
diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d
index b86c7995562..9fe40bf0603 100644
--- a/gcc/d/dmd/file_manager.d
+++ b/gcc/d/dmd/file_manager.d
@@ -11,7 +11,7 @@ 
 module dmd.file_manager;
 
 import dmd.root.stringtable : StringTable;
-import dmd.root.file : File, FileBuffer;
+import dmd.root.file : File, Buffer;
 import dmd.root.filename : FileName;
 import dmd.root.string : toDString;
 import dmd.globals;
@@ -22,7 +22,7 @@  enum package_di = "package." ~ hdr_ext;
 
 final class FileManager
 {
-    private StringTable!(FileBuffer*) files;
+    private StringTable!(const(ubyte)[]) files;
 
     ///
     public this () nothrow
@@ -146,7 +146,7 @@  nothrow:
      * Returns: the loaded source file if it was found in memory,
      *      otherwise `null`
      */
-    const(FileBuffer)* lookup(FileName filename)
+    const(ubyte)[] lookup(FileName filename)
     {
         const name = filename.toString;
         if (auto val = files.lookup(name))
@@ -154,7 +154,7 @@  nothrow:
 
         if (name == "__stdin.d")
         {
-            auto buffer = new FileBuffer(readFromStdin().extractSlice());
+            auto buffer = readFromStdin().extractSlice();
             if (this.files.insert(name, buffer) is null)
                 assert(0, "stdin: Insert after lookup failure should never return `null`");
             return buffer;
@@ -167,7 +167,7 @@  nothrow:
         if (!readResult.success)
             return null;
 
-        FileBuffer* fb = new FileBuffer(readResult.extractSlice());
+        auto fb = readResult.extractSlice();
         if (files.insert(name, fb) is null)
             assert(0, "Insert after lookup failure should never return `null`");
 
@@ -187,17 +187,17 @@  nothrow:
         const(char)[][] lines;
         if (const buffer = lookup(file))
         {
-            const slice = buffer.data[0 .. buffer.data.length];
+            const slice = buffer;
             size_t start, end;
-            ubyte c;
             for (auto i = 0; i < slice.length; i++)
             {
-                c = slice[i];
+                const c = slice[i];
                 if (c == '\n' || c == '\r')
                 {
                     if (i != 0)
                     {
                         end = i;
+                        // Appending lines one at a time will certainly be slow
                         lines ~= cast(const(char)[])slice[start .. end];
                     }
                     // Check for Windows-style CRLF newlines
@@ -234,18 +234,21 @@  nothrow:
     }
 
     /**
-     * Adds a FileBuffer to the table.
-     *
-     * Returns: The FileBuffer added, or null
+     * Adds the contents of a file to the table.
+     * Params:
+     *  filename = name of the file
+     *  buffer = contents of the file
+     * Returns:
+     *  the buffer added, or null
      */
-    FileBuffer* add(FileName filename, FileBuffer* filebuffer)
+    const(ubyte)[] add(FileName filename, const(ubyte)[] buffer)
     {
-        auto val = files.insert(filename.toString, filebuffer);
+        auto val = files.insert(filename.toString, buffer);
         return val == null ? null : val.value;
     }
 }
 
-private FileBuffer readFromStdin() nothrow
+private Buffer readFromStdin() nothrow
 {
     import core.stdc.stdio;
     import dmd.errors;
@@ -277,7 +280,7 @@  private FileBuffer readFromStdin() nothrow
                 // We're done
                 assert(pos < sz + 2);
                 buffer[pos .. pos + 4] = '\0';
-                return FileBuffer(buffer[0 .. pos]);
+                return Buffer(buffer[0 .. pos]);
             }
         } while (pos < sz);
 
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index a3afbe50796..69fdf27e1db 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -1538,6 +1538,13 @@  public:
             }
             else if (hgs.tpltMember == 0 && global.params.hdrStripPlainFunctions)
             {
+                if (!f.fbody)
+                {
+                    // this can happen on interfaces / abstract functions, see `allowsContractWithoutBody`
+                    if (f.fensures || f.frequires)
+                        buf.writenl();
+                    contractsToBuffer(f);
+                }
                 buf.writeByte(';');
                 buf.writenl();
             }
@@ -1548,19 +1555,9 @@  public:
             bodyToBuffer(f);
     }
 
-    void bodyToBuffer(FuncDeclaration f)
+    /// Returns: whether `do` is needed to write the function body
+    bool contractsToBuffer(FuncDeclaration f)
     {
-        if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember))
-        {
-            buf.writeByte(';');
-            buf.writenl();
-            return;
-        }
-        const savetlpt = hgs.tpltMember;
-        const saveauto = hgs.autoMember;
-        hgs.tpltMember = 0;
-        hgs.autoMember = 0;
-        buf.writenl();
         bool requireDo = false;
         // in{}
         if (f.frequires)
@@ -1619,6 +1616,29 @@  public:
                 }
             }
         }
+        return requireDo;
+    }
+
+    void bodyToBuffer(FuncDeclaration f)
+    {
+        if (!f.fbody || (hgs.hdrgen && global.params.hdrStripPlainFunctions && !hgs.autoMember && !hgs.tpltMember))
+        {
+            if (!f.fbody && (f.fensures || f.frequires))
+            {
+                buf.writenl();
+                contractsToBuffer(f);
+            }
+            buf.writeByte(';');
+            buf.writenl();
+            return;
+        }
+        const savetlpt = hgs.tpltMember;
+        const saveauto = hgs.autoMember;
+        hgs.tpltMember = 0;
+        hgs.autoMember = 0;
+        buf.writenl();
+        bool requireDo = contractsToBuffer(f);
+
         if (requireDo)
         {
             buf.writestring("do");
@@ -1788,26 +1808,17 @@  public:
     }
 }
 
-private extern (C++) final class ExpressionPrettyPrintVisitor : Visitor
+/*********************************************
+ * Print expression to buffer.
+ */
+private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hgs)
 {
-    alias visit = Visitor.visit;
-public:
-    OutBuffer* buf;
-    HdrGenState* hgs;
-
-    extern (D) this(OutBuffer* buf, HdrGenState* hgs)
-    {
-        this.buf = buf;
-        this.hgs = hgs;
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    override void visit(Expression e)
+    void visit(Expression e)
     {
         buf.writestring(EXPtoString(e.op));
     }
 
-    override void visit(IntegerExp e)
+    void visitInteger(IntegerExp e)
     {
         const dinteger_t v = e.toInteger();
         if (e.type)
@@ -1907,12 +1918,12 @@  public:
             buf.print(v);
     }
 
-    override void visit(ErrorExp e)
+    void visitError(ErrorExp e)
     {
         buf.writestring("__error");
     }
 
-    override void visit(VoidInitExp e)
+    void visitVoidInit(VoidInitExp e)
     {
         buf.writestring("__void");
     }
@@ -1922,12 +1933,12 @@  public:
         .floatToBuffer(type, value, buf, hgs.hdrgen);
     }
 
-    override void visit(RealExp e)
+    void visitReal(RealExp e)
     {
         floatToBuffer(e.type, e.value);
     }
 
-    override void visit(ComplexExp e)
+    void visitComplex(ComplexExp e)
     {
         /* Print as:
          *  (re+imi)
@@ -1939,7 +1950,7 @@  public:
         buf.writestring("i)");
     }
 
-    override void visit(IdentifierExp e)
+    void visitIdentifier(IdentifierExp e)
     {
         if (hgs.hdrgen || hgs.ddoc)
             buf.writestring(e.ident.toHChars2());
@@ -1947,27 +1958,27 @@  public:
             buf.writestring(e.ident.toString());
     }
 
-    override void visit(DsymbolExp e)
+    void visitDsymbol(DsymbolExp e)
     {
         buf.writestring(e.s.toChars());
     }
 
-    override void visit(ThisExp e)
+    void visitThis(ThisExp e)
     {
         buf.writestring("this");
     }
 
-    override void visit(SuperExp e)
+    void visitSuper(SuperExp e)
     {
         buf.writestring("super");
     }
 
-    override void visit(NullExp e)
+    void visitNull(NullExp e)
     {
         buf.writestring("null");
     }
 
-    override void visit(StringExp e)
+    void visitString(StringExp e)
     {
         buf.writeByte('"');
         const o = buf.length;
@@ -1982,14 +1993,14 @@  public:
             buf.writeByte(e.postfix);
     }
 
-    override void visit(ArrayLiteralExp e)
+    void visitArrayLiteral(ArrayLiteralExp e)
     {
         buf.writeByte('[');
         argsToBuffer(e.elements, buf, hgs, e.basis);
         buf.writeByte(']');
     }
 
-    override void visit(AssocArrayLiteralExp e)
+    void visitAssocArrayLiteral(AssocArrayLiteralExp e)
     {
         buf.writeByte('[');
         foreach (i, key; *e.keys)
@@ -2004,7 +2015,7 @@  public:
         buf.writeByte(']');
     }
 
-    override void visit(StructLiteralExp e)
+    void visitStructLiteral(StructLiteralExp e)
     {
         buf.writestring(e.sd.toChars());
         buf.writeByte('(');
@@ -2024,7 +2035,7 @@  public:
         buf.writeByte(')');
     }
 
-    override void visit(CompoundLiteralExp e)
+    void visitCompoundLiteral(CompoundLiteralExp e)
     {
         buf.writeByte('(');
         typeToBuffer(e.type, null, buf, hgs);
@@ -2032,12 +2043,12 @@  public:
         e.initializer.initializerToBuffer(buf, hgs);
     }
 
-    override void visit(TypeExp e)
+    void visitType(TypeExp e)
     {
         typeToBuffer(e.type, null, buf, hgs);
     }
 
-    override void visit(ScopeExp e)
+    void visitScope(ScopeExp e)
     {
         if (e.sds.isTemplateInstance())
         {
@@ -2059,12 +2070,12 @@  public:
         }
     }
 
-    override void visit(TemplateExp e)
+    void visitTemplate(TemplateExp e)
     {
         buf.writestring(e.td.toChars());
     }
 
-    override void visit(NewExp e)
+    void visitNew(NewExp e)
     {
         if (e.thisexp)
         {
@@ -2081,7 +2092,7 @@  public:
         }
     }
 
-    override void visit(NewAnonClassExp e)
+    void visitNewAnonClass(NewAnonClassExp e)
     {
         if (e.thisexp)
         {
@@ -2100,7 +2111,7 @@  public:
             e.cd.dsymbolToBuffer(buf, hgs);
     }
 
-    override void visit(SymOffExp e)
+    void visitSymOff(SymOffExp e)
     {
         if (e.offset)
             buf.printf("(& %s%+lld)", e.var.toChars(), e.offset);
@@ -2110,22 +2121,22 @@  public:
             buf.printf("& %s", e.var.toChars());
     }
 
-    override void visit(VarExp e)
+    void visitVar(VarExp e)
     {
         buf.writestring(e.var.toChars());
     }
 
-    override void visit(OverExp e)
+    void visitOver(OverExp e)
     {
         buf.writestring(e.vars.ident.toString());
     }
 
-    override void visit(TupleExp e)
+    void visitTuple(TupleExp e)
     {
         if (e.e0)
         {
             buf.writeByte('(');
-            e.e0.accept(this);
+            e.e0.expressionPrettyPrint(buf, hgs);
             buf.writestring(", tuple(");
             argsToBuffer(e.exps, buf, hgs);
             buf.writestring("))");
@@ -2138,13 +2149,13 @@  public:
         }
     }
 
-    override void visit(FuncExp e)
+    void visitFunc(FuncExp e)
     {
         e.fd.dsymbolToBuffer(buf, hgs);
         //buf.writestring(e.fd.toChars());
     }
 
-    override void visit(DeclarationExp e)
+    void visitDeclaration(DeclarationExp e)
     {
         /* Normal dmd execution won't reach here - regular variable declarations
          * are handled in visit(ExpStatement), so here would be used only when
@@ -2170,14 +2181,14 @@  public:
         }
     }
 
-    override void visit(TypeidExp e)
+    void visitTypeid(TypeidExp e)
     {
         buf.writestring("typeid(");
         objectToBuffer(e.obj, buf, hgs);
         buf.writeByte(')');
     }
 
-    override void visit(TraitsExp e)
+    void visitTraits(TraitsExp e)
     {
         buf.writestring("__traits(");
         if (e.ident)
@@ -2193,12 +2204,12 @@  public:
         buf.writeByte(')');
     }
 
-    override void visit(HaltExp e)
+    void visitHalt(HaltExp e)
     {
         buf.writestring("halt");
     }
 
-    override void visit(IsExp e)
+    void visitIs(IsExp e)
     {
         buf.writestring("is(");
         typeToBuffer(e.targ, e.id, buf, hgs);
@@ -2223,13 +2234,13 @@  public:
         buf.writeByte(')');
     }
 
-    override void visit(UnaExp e)
+    void visitUna(UnaExp e)
     {
         buf.writestring(EXPtoString(e.op));
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
-    override void visit(BinExp e)
+    void visitBin(BinExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
         buf.writeByte(' ');
@@ -2238,7 +2249,7 @@  public:
         expToBuffer(e.e2, cast(PREC)(precedence[e.op] + 1), buf, hgs);
     }
 
-    override void visit(CommaExp e)
+    void visitComma(CommaExp e)
     {
         // CommaExp is generated by the compiler so it shouldn't
         // appear in error messages or header files.
@@ -2251,7 +2262,7 @@  public:
         // the old path
         if (!ve || !(ve.var.storage_class & STC.temp))
         {
-            visit(cast(BinExp)e);
+            visitBin(cast(BinExp)e);
             return;
         }
 
@@ -2281,25 +2292,25 @@  public:
         }
 
         // not one of the known cases, go on the old path
-        visit(cast(BinExp)e);
+        visitBin(cast(BinExp)e);
         return;
     }
 
-    override void visit(MixinExp e)
+    void visitMixin(MixinExp e)
     {
         buf.writestring("mixin(");
         argsToBuffer(e.exps, buf, hgs, null);
         buf.writeByte(')');
     }
 
-    override void visit(ImportExp e)
+    void visitImport(ImportExp e)
     {
         buf.writestring("import(");
         expToBuffer(e.e1, PREC.assign, buf, hgs);
         buf.writeByte(')');
     }
 
-    override void visit(AssertExp e)
+    void visitAssert(AssertExp e)
     {
         buf.writestring("assert(");
         expToBuffer(e.e1, PREC.assign, buf, hgs);
@@ -2311,13 +2322,13 @@  public:
         buf.writeByte(')');
     }
 
-    override void visit(ThrowExp e)
+    void visitThrow(ThrowExp e)
     {
         buf.writestring("throw ");
         expToBuffer(e.e1, PREC.unary, buf, hgs);
     }
 
-    override void visit(DotIdExp e)
+    void visitDotId(DotIdExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         if (e.arrow)
@@ -2327,28 +2338,28 @@  public:
         buf.writestring(e.ident.toString());
     }
 
-    override void visit(DotTemplateExp e)
+    void visitDotTemplate(DotTemplateExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('.');
         buf.writestring(e.td.toChars());
     }
 
-    override void visit(DotVarExp e)
+    void visitDotVar(DotVarExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('.');
         buf.writestring(e.var.toChars());
     }
 
-    override void visit(DotTemplateInstanceExp e)
+    void visitDotTemplateInstance(DotTemplateInstanceExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('.');
         e.ti.dsymbolToBuffer(buf, hgs);
     }
 
-    override void visit(DelegateExp e)
+    void visitDelegate(DelegateExp e)
     {
         buf.writeByte('&');
         if (!e.func.isNested() || e.func.needThis())
@@ -2359,14 +2370,14 @@  public:
         buf.writestring(e.func.toChars());
     }
 
-    override void visit(DotTypeExp e)
+    void visitDotType(DotTypeExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('.');
         buf.writestring(e.sym.toChars());
     }
 
-    override void visit(CallExp e)
+    void visitCall(CallExp e)
     {
         if (e.e1.op == EXP.type)
         {
@@ -2375,7 +2386,7 @@  public:
              * This is ok since types in constructor calls
              * can never depend on parens anyway
              */
-            e.e1.accept(this);
+            e.e1.expressionPrettyPrint(buf, hgs);
         }
         else
             expToBuffer(e.e1, precedence[e.op], buf, hgs);
@@ -2384,19 +2395,19 @@  public:
         buf.writeByte(')');
     }
 
-    override void visit(PtrExp e)
+    void visitPtr(PtrExp e)
     {
         buf.writeByte('*');
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
-    override void visit(DeleteExp e)
+    void visitDelete(DeleteExp e)
     {
         buf.writestring("delete ");
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
-    override void visit(CastExp e)
+    void visitCast(CastExp e)
     {
         buf.writestring("cast(");
         if (e.to)
@@ -2409,7 +2420,7 @@  public:
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
-    override void visit(VectorExp e)
+    void visitVector(VectorExp e)
     {
         buf.writestring("cast(");
         typeToBuffer(e.to, null, buf, hgs);
@@ -2417,13 +2428,13 @@  public:
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
-    override void visit(VectorArrayExp e)
+    void visitVectorArray(VectorArrayExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writestring(".array");
     }
 
-    override void visit(SliceExp e)
+    void visitSlice(SliceExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
         buf.writeByte('[');
@@ -2442,32 +2453,32 @@  public:
         buf.writeByte(']');
     }
 
-    override void visit(ArrayLengthExp e)
+    void visitArrayLength(ArrayLengthExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writestring(".length");
     }
 
-    override void visit(IntervalExp e)
+    void visitInterval(IntervalExp e)
     {
         expToBuffer(e.lwr, PREC.assign, buf, hgs);
         buf.writestring("..");
         expToBuffer(e.upr, PREC.assign, buf, hgs);
     }
 
-    override void visit(DelegatePtrExp e)
+    void visitDelegatePtr(DelegatePtrExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writestring(".ptr");
     }
 
-    override void visit(DelegateFuncptrExp e)
+    void visitDelegateFuncptr(DelegateFuncptrExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writestring(".funcptr");
     }
 
-    override void visit(ArrayExp e)
+    void visitArray(ArrayExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('[');
@@ -2475,14 +2486,14 @@  public:
         buf.writeByte(']');
     }
 
-    override void visit(DotExp e)
+    void visitDot(DotExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('.');
         expToBuffer(e.e2, PREC.primary, buf, hgs);
     }
 
-    override void visit(IndexExp e)
+    void visitIndex(IndexExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writeByte('[');
@@ -2490,19 +2501,19 @@  public:
         buf.writeByte(']');
     }
 
-    override void visit(PostExp e)
+    void visitPost(PostExp e)
     {
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
         buf.writestring(EXPtoString(e.op));
     }
 
-    override void visit(PreExp e)
+    void visitPre(PreExp e)
     {
         buf.writestring(EXPtoString(e.op));
         expToBuffer(e.e1, precedence[e.op], buf, hgs);
     }
 
-    override void visit(RemoveExp e)
+    void visitRemove(RemoveExp e)
     {
         expToBuffer(e.e1, PREC.primary, buf, hgs);
         buf.writestring(".remove(");
@@ -2510,7 +2521,7 @@  public:
         buf.writeByte(')');
     }
 
-    override void visit(CondExp e)
+    void visitCond(CondExp e)
     {
         expToBuffer(e.econd, PREC.oror, buf, hgs);
         buf.writestring(" ? ");
@@ -2519,15 +2530,90 @@  public:
         expToBuffer(e.e2, PREC.cond, buf, hgs);
     }
 
-    override void visit(DefaultInitExp e)
+    void visitDefaultInit(DefaultInitExp e)
     {
         buf.writestring(EXPtoString(e.op));
     }
 
-    override void visit(ClassReferenceExp e)
+    void visitClassReference(ClassReferenceExp e)
     {
         buf.writestring(e.value.toChars());
     }
+
+    switch (e.op)
+    {
+        default:
+            if (auto be = e.isBinExp())
+                return visitBin(be);
+            else if (auto ue = e.isUnaExp())
+                return visitUna(ue);
+            else if (auto de = e.isDefaultInitExp())
+                return visitDefaultInit(e.isDefaultInitExp());
+            return visit(e);
+
+        case EXP.int64:         return visitInteger(e.isIntegerExp());
+        case EXP.error:         return visitError(e.isErrorExp());
+        case EXP.void_:         return visitVoidInit(e.isVoidInitExp());
+        case EXP.float64:       return visitReal(e.isRealExp());
+        case EXP.complex80:     return visitComplex(e.isComplexExp());
+        case EXP.identifier:    return visitIdentifier(e.isIdentifierExp());
+        case EXP.dSymbol:       return visitDsymbol(e.isDsymbolExp());
+        case EXP.this_:         return visitThis(e.isThisExp());
+        case EXP.super_:        return visitSuper(e.isSuperExp());
+        case EXP.null_:         return visitNull(e.isNullExp());
+        case EXP.string_:       return visitString(e.isStringExp());
+        case EXP.arrayLiteral:  return visitArrayLiteral(e.isArrayLiteralExp());
+        case EXP.assocArrayLiteral:     return visitAssocArrayLiteral(e.isAssocArrayLiteralExp());
+        case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
+        case EXP.compoundLiteral:       return visitCompoundLiteral(e.isCompoundLiteralExp());
+        case EXP.type:          return visitType(e.isTypeExp());
+        case EXP.scope_:        return visitScope(e.isScopeExp());
+        case EXP.template_:     return visitTemplate(e.isTemplateExp());
+        case EXP.new_:          return visitNew(e.isNewExp());
+        case EXP.newAnonymousClass:     return visitNewAnonClass(e.isNewAnonClassExp());
+        case EXP.symbolOffset:  return visitSymOff(e.isSymOffExp());
+        case EXP.variable:      return visitVar(e.isVarExp());
+        case EXP.overloadSet:   return visitOver(e.isOverExp());
+        case EXP.tuple:         return visitTuple(e.isTupleExp());
+        case EXP.function_:     return visitFunc(e.isFuncExp());
+        case EXP.declaration:   return visitDeclaration(e.isDeclarationExp());
+        case EXP.typeid_:       return visitTypeid(e.isTypeidExp());
+        case EXP.traits:        return visitTraits(e.isTraitsExp());
+        case EXP.halt:          return visitHalt(e.isHaltExp());
+        case EXP.is_:           return visitIs(e.isExp());
+        case EXP.comma:         return visitComma(e.isCommaExp());
+        case EXP.mixin_:        return visitMixin(e.isMixinExp());
+        case EXP.import_:       return visitImport(e.isImportExp());
+        case EXP.assert_:       return visitAssert(e.isAssertExp());
+        case EXP.throw_:        return visitThrow(e.isThrowExp());
+        case EXP.dotIdentifier: return visitDotId(e.isDotIdExp());
+        case EXP.dotTemplateDeclaration:        return visitDotTemplate(e.isDotTemplateExp());
+        case EXP.dotVariable:   return visitDotVar(e.isDotVarExp());
+        case EXP.dotTemplateInstance:   return visitDotTemplateInstance(e.isDotTemplateInstanceExp());
+        case EXP.delegate_:     return visitDelegate(e.isDelegateExp());
+        case EXP.dotType:       return visitDotType(e.isDotTypeExp());
+        case EXP.call:          return visitCall(e.isCallExp());
+        case EXP.star:          return visitPtr(e.isPtrExp());
+        case EXP.delete_:       return visitDelete(e.isDeleteExp());
+        case EXP.cast_:         return visitCast(e.isCastExp());
+        case EXP.vector:        return visitVector(e.isVectorExp());
+        case EXP.vectorArray:   return visitVectorArray(e.isVectorArrayExp());
+        case EXP.slice:         return visitSlice(e.isSliceExp());
+        case EXP.arrayLength:   return visitArrayLength(e.isArrayLengthExp());
+        case EXP.interval:      return visitInterval(e.isIntervalExp());
+        case EXP.delegatePointer:       return visitDelegatePtr(e.isDelegatePtrExp());
+        case EXP.delegateFunctionPointer:       return visitDelegateFuncptr(e.isDelegateFuncptrExp());
+        case EXP.array:         return visitArray(e.isArrayExp());
+        case EXP.dot:           return visitDot(e.isDotExp());
+        case EXP.index:         return visitIndex(e.isIndexExp());
+        case EXP.minusMinus:
+        case EXP.plusPlus:      return visitPost(e.isPostExp());
+        case EXP.preMinusMinus:
+        case EXP.prePlusPlus:   return visitPre(e.isPreExp());
+        case EXP.remove:        return visitRemove(e.isRemoveExp());
+        case EXP.question:      return visitCond(e.isCondExp());
+        case EXP.classReference:        return visitClassReference(e.isClassReferenceExp());
+    }
 }
 
 /**
@@ -2547,7 +2633,7 @@  void floatToBuffer(Type type, const real_t value, OutBuffer* buf, const bool all
         (ie, 8 chars more than mantissa). Plus one for trailing \0.
         Plus one for rounding. */
     const(size_t) BUFFER_LEN = value.sizeof * 3 + 8 + 1 + 1;
-    char[BUFFER_LEN] buffer;
+    char[BUFFER_LEN] buffer = void;
     CTFloat.sprint(buffer.ptr, 'g', value);
     assert(strlen(buffer.ptr) < BUFFER_LEN);
     if (allowHex)
@@ -2754,7 +2840,7 @@  bool stcToBuffer(OutBuffer* buf, StorageClass stc)
     }
     if (stc & STC.returninferred)
     {
-        //buf.writestring("return-inferred ");
+        //buf.writestring((stc & STC.returnScope) ? "return-scope-inferred " : "return-ref-inferred ");
         stc &= ~(STC.return_ | STC.returninferred);
     }
 
@@ -2958,8 +3044,7 @@  void functionToBufferWithIdent(TypeFunction tf, OutBuffer* buf, const(char)* ide
 
 void toCBuffer(const Expression e, OutBuffer* buf, HdrGenState* hgs)
 {
-    scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
-    (cast() e).accept(v);
+    expressionPrettyPrint(cast()e, buf, hgs);
 }
 
 /**************************************************
@@ -3221,8 +3306,7 @@  private void sizeToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
 
 private void expressionToBuffer(Expression e, OutBuffer* buf, HdrGenState* hgs)
 {
-    scope v = new ExpressionPrettyPrintVisitor(buf, hgs);
-    e.accept(v);
+    expressionPrettyPrint(e, buf, hgs);
 }
 
 /**************************************************
diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d
index 2ec75ab2516..11455afe263 100644
--- a/gcc/d/dmd/id.d
+++ b/gcc/d/dmd/id.d
@@ -491,6 +491,7 @@  immutable Msgtable[] msgtable =
     { "udaGNUAbiTag", "gnuAbiTag" },
     { "udaSelector", "selector" },
     { "udaOptional", "optional"},
+    { "udaMustUse", "mustuse" },
 
     // C names, for undefined identifier error messages
     { "NULL" },
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index 649d88e3d59..616712b463d 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -554,26 +554,39 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                     i.exp = e.optimize(WANTvalue);
             }
         }
+        {
         // Look for the case of statically initializing an array
         // with a single member.
-        if (tb.ty == Tsarray && !tb.nextOf().equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tb.nextOf()))
+        auto tba = tb.isTypeSArray();
+        if (tba && !tba.next.equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tba.next))
         {
             /* If the variable is not actually used in compile time, array creation is
              * redundant. So delay it until invocation of toExpression() or toDt().
              */
             t = tb.nextOf();
         }
+
+        auto tta = t.isTypeSArray();
         if (i.exp.implicitConvTo(t))
         {
             i.exp = i.exp.implicitCastTo(sc, t);
         }
+        else if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() &&
+            tta && (tta.next.ty == Tint8 || tta.next.ty == Tuns8) &&
+            ti.ty == Tpointer && ti.nextOf().ty == Tchar)
+        {
+            /* unsigned char bbb[1] = "";
+             *   signed char ccc[1] = "";
+             */
+            i.exp = i.exp.castTo(sc, t);
+        }
         else
         {
             // Look for mismatch of compile-time known length to emit
             // better diagnostic message, as same as AssignExp::semantic.
-            if (tb.ty == Tsarray && i.exp.implicitConvTo(tb.nextOf().arrayOf()) > MATCH.nomatch)
+            if (tba && i.exp.implicitConvTo(tba.next.arrayOf()) > MATCH.nomatch)
             {
-                uinteger_t dim1 = tb.isTypeSArray().dim.toInteger();
+                uinteger_t dim1 = tba.dim.toInteger();
                 uinteger_t dim2 = dim1;
                 if (auto ale = i.exp.isArrayLiteralExp())
                 {
@@ -596,6 +609,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             if (global.endGagging(errors))
                 currExp.error("cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toChars(), et.toChars(), t.toChars());
         }
+        }
     L1:
         if (i.exp.op == EXP.error)
         {
@@ -1059,7 +1073,7 @@  Initializer inferType(Initializer init, Scope* sc)
 
     Initializer visitC(CInitializer i)
     {
-        //printf(CInitializer::inferType()\n");
+        //printf("CInitializer.inferType()\n");
         error(i.loc, "TODO C inferType initializers not supported yet");
         return new ErrorInitializer();
     }
@@ -1331,6 +1345,10 @@  private bool hasNonConstPointers(Expression e)
     }
     if (auto ae = e.isAddrExp())
     {
+        if (ae.type.nextOf().isImmutable() || ae.type.nextOf().isConst())
+        {
+            return false;
+        }
         if (auto se = ae.e1.isStructLiteralExp())
         {
             if (!(se.stageflags & stageSearchPointers))
diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d
index 3183c8dbb79..fc270390fa4 100644
--- a/gcc/d/dmd/json.d
+++ b/gcc/d/dmd/json.d
@@ -319,10 +319,7 @@  public:
             // Should not be printed
             //property(name, "d");
             break;
-        case LINK.system:
-            // Should not be printed
-            //property(name, "system");
-            break;
+        case LINK.system:   return property(name, "system");
         case LINK.c:        return property(name, "c");
         case LINK.cpp:      return property(name, "cpp");
         case LINK.windows:  return property(name, "windows");
diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d
index 7cd4bfd5922..b778bc82d77 100644
--- a/gcc/d/dmd/lexer.d
+++ b/gcc/d/dmd/lexer.d
@@ -108,8 +108,8 @@  class Lexer
         size_t endoffset, bool doDocComment, bool commentToken) pure
     {
         scanloc = Loc(filename, 1, 1);
-        //printf("Lexer::Lexer(%p,%d)\n",base,length);
-        //printf("lexer.filename = %s\n", filename);
+        // debug printf("Lexer::Lexer(%p)\n", base);
+        // debug printf("lexer.filename = %s\n", filename);
         token = Token.init;
         this.base = base;
         this.end = base + endoffset;
@@ -2122,7 +2122,7 @@  class Lexer
                 // can't translate invalid octal value, just show a generic message
                 error("octal literals larger than 7 are no longer supported");
             else
-                error("octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!%llo%.*s` instead",
+                error("octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!\"%llo%.*s\"` instead",
                     n, cast(int)(p - psuffix), psuffix, n, cast(int)(p - psuffix), psuffix);
         }
         TOK result;
@@ -2926,7 +2926,7 @@  class Lexer
      */
     static const(char)* combineComments(const(char)[] c1, const(char)[] c2, bool newParagraph) pure
     {
-        //printf("Lexer::combineComments('%s', '%s', '%i')\n", c1, c2, newParagraph);
+        //debug printf("Lexer::combineComments('%*.s', '%*.s', '%i')\n", cast(int) c1.length, c1.ptr, cast(int) c2.length, c2.ptr, newParagraph);
         const(int) newParagraphSize = newParagraph ? 1 : 0; // Size of the combining '\n'
         if (!c1)
             return c2.ptr;
diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d
index b2c7aa461ca..50dd20b70b9 100644
--- a/gcc/d/dmd/mtype.d
+++ b/gcc/d/dmd/mtype.d
@@ -4200,27 +4200,28 @@  extern (C++) final class TypeFunction : TypeNext
 
     // These flags can be accessed like `bool` properties,
     // getters and setters are generated for them
-    private enum FunctionFlag : uint
-    {
-        none            = 0,
-        isnothrow       = 0x0001, // nothrow
-        isnogc          = 0x0002, // is @nogc
-        isproperty      = 0x0004, // can be called without parentheses
-        isref           = 0x0008, // returns a reference
-        isreturn        = 0x0010, // 'this' is returned by ref
-        isScopeQual     = 0x0020, // 'this' is scope
-        isreturninferred= 0x0040, // 'this' is return from inference
-        isscopeinferred = 0x0080, // 'this' is scope from inference
-        islive          = 0x0100, // is @live
-        incomplete      = 0x0200, // return type or default arguments removed
-        isInOutParam    = 0x0400, // inout on the parameters
-        isInOutQual     = 0x0800, // inout on the qualifier
-        isctor          = 0x1000, // the function is a constructor
-        isreturnscope   = 0x2000, // `this` is returned by value
-    }
+    private extern (D) static struct BitFields
+    {
+        bool isnothrow;        /// nothrow
+        bool isnogc;           /// is @nogc
+        bool isproperty;       /// can be called without parentheses
+        bool isref;            /// returns a reference
+        bool isreturn;         /// 'this' is returned by ref
+        bool isScopeQual;      /// 'this' is scope
+        bool isreturninferred; /// 'this' is return from inference
+        bool isscopeinferred;  /// 'this' is scope from inference
+        bool islive;           /// is @live
+        bool incomplete;       /// return type or default arguments removed
+        bool isInOutParam;     /// inout on the parameters
+        bool isInOutQual;      /// inout on the qualifier
+        bool isctor;           /// the function is a constructor
+        bool isreturnscope;    /// `this` is returned by value
+    }
+
+    import dmd.common.bitfields : generateBitFields;
+    mixin(generateBitFields!(BitFields, ushort));
 
     LINK linkage;               // calling convention
-    FunctionFlag funcFlags;
     TRUST trust;                // level of trust
     PURE purity = PURE.impure;
     byte inuse;
@@ -4684,6 +4685,16 @@  extern (C++) final class TypeFunction : TypeNext
             match = MATCH.convert; // match ... with a "conversion" match level
         }
 
+        // https://issues.dlang.org/show_bug.cgi?id=22997
+        if (parameterList.varargs == VarArg.none && nparams > nargs && !parameterList[nargs].defaultArg)
+        {
+            OutBuffer buf;
+            buf.printf("too few arguments, expected `%d`, got `%d`", cast(int)nparams, cast(int)nargs);
+            if (pMessage)
+                *pMessage = buf.extractChars();
+            goto Nomatch;
+        }
+
         foreach (u, p; parameterList)
         {
             if (u == nargs)
@@ -5086,41 +5097,21 @@  extern (C++) final class TypeFunction : TypeNext
         return false;
     }
 
-    // Generate getter / setter functions for `FunctionFlag` members so they can be
-    // treated like regular `bool` fields, instead of requiring bit twiddling to read/write
-    extern (D) mixin(() {
-        string result = "extern(C++) pure nothrow @safe @nogc {";
-        foreach (string mem; __traits(allMembers, FunctionFlag))
-        {
-            result ~= "
-            /// set or get if the function has the FunctionFlag attribute of the same name
-            bool "~mem~"() const { return (funcFlags & FunctionFlag."~mem~") != 0; }
-            /// ditto
-            void "~mem~"(bool v)
-            {
-                if (v) funcFlags |= FunctionFlag."~mem~";
-                else funcFlags &= ~FunctionFlag."~mem~";
-            }";
-        }
-        return result ~ "}\n";
-    }());
 
     /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise.
     bool iswild() const pure nothrow @safe @nogc
     {
-        return (funcFlags & (FunctionFlag.isInOutParam | FunctionFlag.isInOutQual)) != 0;
+        return isInOutParam || isInOutQual;
     }
 
     /// Returns: whether `this` function type has the same attributes (`@safe`,...) as `other`
     bool attributesEqual(const scope TypeFunction other) const pure nothrow @safe @nogc
     {
-        enum attributes = FunctionFlag.isnothrow
-                        | FunctionFlag.isnogc
-                        | FunctionFlag.islive;
-
         return this.trust == other.trust &&
                 this.purity == other.purity &&
-                (this.funcFlags & attributes) == (other.funcFlags & attributes);
+                this.isnothrow == other.isnothrow &&
+                this.isnogc == other.isnogc &&
+                this.islive == other.islive;
     }
 
     override void accept(Visitor v)
diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h
index 07c574d42b7..e0b6339bf23 100644
--- a/gcc/d/dmd/mtype.h
+++ b/gcc/d/dmd/mtype.h
@@ -595,8 +595,8 @@  public:
     // .next is the return type
 
     ParameterList parameterList; // function parameters
+    uint16_t bitFields;
     LINK linkage;                // calling convention
-    unsigned funcFlags;
     TRUST trust;                 // level of trust
     PURE purity;                 // PURExxxx
     char inuse;
diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d
new file mode 100644
index 00000000000..4eb422805db
--- /dev/null
+++ b/gcc/d/dmd/mustuse.d
@@ -0,0 +1,244 @@ 
+/**
+ * Compile-time checks associated with the @mustuse attribute.
+ *
+ * Copyright: Copyright (C) 2022 by The D Language Foundation, All Rights Reserved
+ * 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/mustuse.d, _mustuse.d)
+ * Documentation:  https://dlang.org/phobos/dmd_mustuse.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mustuse.d
+ */
+
+module dmd.mustuse;
+
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.globals;
+import dmd.identifier;
+
+// Used in isIncrementOrDecrement
+private static const StringExp plusPlus, minusMinus;
+
+// Loc.initial cannot be used in static initializers, so
+// these need a static constructor.
+static this()
+{
+    plusPlus = new StringExp(Loc.initial, "++");
+    minusMinus = new StringExp(Loc.initial, "--");
+}
+
+/**
+ * Check whether discarding an expression would violate the requirements of
+ * @mustuse. If so, emit an error.
+ *
+ * Params:
+ *   e = the expression to check
+ *   sc = scope in which `e` was semantically analyzed
+ *
+ * Returns: true on error, false on success.
+ */
+bool checkMustUse(Expression e, Scope* sc)
+{
+    import dmd.id : Id;
+
+    assert(e.type);
+    if (auto sym = e.type.toDsymbol(sc))
+    {
+        auto sd = sym.isStructDeclaration();
+        // isStructDeclaration returns non-null for both structs and unions
+        if (sd && hasMustUseAttribute(sd, sc) && !isAssignment(e) && !isIncrementOrDecrement(e))
+        {
+            e.error("ignored value of `@%s` type `%s`; prepend a `cast(void)` if intentional",
+                Id.udaMustUse.toChars(), e.type.toPrettyChars(true));
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Called from a symbol's semantic to check for reserved usage of @mustuse.
+ *
+ * If such usage is found, emits an errror.
+ *
+ * Params:
+ *   sym = symbol to check
+ */
+void checkMustUseReserved(Dsymbol sym)
+{
+    import dmd.attrib : foreachUdaNoSemantic;
+    import dmd.errors : error;
+    import dmd.id : Id;
+
+    // Can't use foreachUda (and by extension hasMustUseAttribute) while
+    // semantic analysis of `sym` is still in progress
+    foreachUdaNoSemantic(sym, (exp) {
+        if (isMustUseAttribute(exp))
+        {
+            if (sym.isFuncDeclaration())
+            {
+                error(sym.loc, "`@%s` on functions is reserved for future use",
+                    Id.udaMustUse.toChars());
+                sym.errors = true;
+            }
+            else if (sym.isClassDeclaration() || sym.isEnumDeclaration())
+            {
+                error(sym.loc, "`@%s` on `%s` types is reserved for future use",
+                    Id.udaMustUse.toChars(), sym.kind());
+                sym.errors = true;
+            }
+        }
+        return 0; // continue
+    });
+}
+
+/**
+ * Returns: true if the given expression is an assignment, either simple (a = b)
+ * or compound (a += b, etc).
+ */
+private bool isAssignment(Expression e)
+{
+    if (e.isAssignExp || e.isBinAssignExp)
+        return true;
+    if (auto ce = e.isCallExp())
+    {
+        if (auto fd = ce.f)
+        {
+            auto id = fd.ident;
+            if (id && isAssignmentOpId(id))
+                return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Returns: true if id is the identifier of an assignment operator overload.
+ */
+private bool isAssignmentOpId(Identifier id)
+{
+    import dmd.id : Id;
+
+    return id == Id.assign
+        || id == Id.addass
+        || id == Id.subass
+        || id == Id.mulass
+        || id == Id.divass
+        || id == Id.modass
+        || id == Id.andass
+        || id == Id.orass
+        || id == Id.xorass
+        || id == Id.shlass
+        || id == Id.shrass
+        || id == Id.ushrass
+        || id == Id.catass
+        || id == Id.indexass
+        || id == Id.slice
+        || id == Id.sliceass
+        || id == Id.opOpAssign
+        || id == Id.opIndexOpAssign
+        || id == Id.opSliceOpAssign
+        || id == Id.powass;
+}
+
+/**
+ * Returns: true if the given expression is an increment (++) or decrement (--).
+ */
+private bool isIncrementOrDecrement(Expression e)
+{
+    import dmd.dtemplate : isExpression;
+    import dmd.globals : Loc;
+    import dmd.id : Id;
+    import dmd.tokens : EXP;
+
+    if (e.op == EXP.plusPlus
+        || e.op == EXP.minusMinus
+        || e.op == EXP.prePlusPlus
+        || e.op == EXP.preMinusMinus)
+        return true;
+    if (auto call = e.isCallExp())
+    {
+        // Check for overloaded preincrement
+        // e.g., a.opUnary!"++"
+        if (auto fd = call.f)
+        {
+            if (fd.ident == Id.opUnary && fd.parent)
+            {
+                if (auto ti = fd.parent.isTemplateInstance())
+                {
+                    auto tiargs = ti.tiargs;
+                    if (tiargs && tiargs.length >= 1)
+                    {
+                        if (auto argExp = (*tiargs)[0].isExpression())
+                        {
+                            auto op = argExp.isStringExp();
+                            if (op && (op.compare(plusPlus) == 0 || op.compare(minusMinus) == 0))
+                                return true;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else if (auto comma = e.isCommaExp())
+    {
+        // Check for overloaded postincrement
+        // e.g., (auto tmp = a, ++a, tmp)
+        if (comma.e1)
+        {
+            if (auto left = comma.e1.isCommaExp())
+            {
+                if (auto middle = left.e2)
+                {
+                    if (middle && isIncrementOrDecrement(middle))
+                        return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+/**
+ * Returns: true if the given symbol has the @mustuse attribute.
+ */
+private bool hasMustUseAttribute(Dsymbol sym, Scope* sc)
+{
+    import dmd.attrib : foreachUda;
+
+    bool result = false;
+
+    foreachUda(sym, sc, (Expression uda) {
+        if (isMustUseAttribute(uda))
+        {
+            result = true;
+            return 1; // break
+        }
+        return 0; // continue
+    });
+
+    return result;
+}
+
+/**
+ * Returns: true if the given expression is core.attribute.mustuse.
+ */
+private bool isMustUseAttribute(Expression e)
+{
+    import dmd.attrib : isCoreUda;
+    import dmd.id : Id;
+
+    // Logic based on dmd.objc.Supported.declaredAsOptionalCount
+    auto typeExp = e.isTypeExp;
+    if (!typeExp)
+        return false;
+
+    auto typeEnum = typeExp.type.isTypeEnum();
+    if (!typeEnum)
+        return false;
+
+    if (isCoreUda(typeEnum.sym, Id.udaMustUse))
+        return true;
+
+    return false;
+}
diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d
index dbd761fed2f..4f6903cbab7 100644
--- a/gcc/d/dmd/opover.d
+++ b/gcc/d/dmd/opover.d
@@ -213,7 +213,7 @@  private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE
      */
     if (isRecursiveAliasThis(e.att1, e.e1.type))
         return null;
-    //printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
+    //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars());
     BinExp be = cast(BinExp)e.copy();
     // Resolve 'alias this' but in case of assigment don't resolve properties yet
     // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2'
@@ -241,7 +241,7 @@  private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE
      */
     if (isRecursiveAliasThis(e.att2, e.e2.type))
         return null;
-    //printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
+    //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars());
     BinExp be = cast(BinExp)e.copy();
     be.e2 = resolveAliasThis(sc, e.e2, true);
     if (!be.e2)
diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d
index 3745a15da42..3b8b1b6fa6c 100644
--- a/gcc/d/dmd/optimize.d
+++ b/gcc/d/dmd/optimize.d
@@ -271,7 +271,7 @@  package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
  */
 Expression Expression_optimize(Expression e, int result, bool keepLvalue)
 {
-    //printf("Expression_optimize() %s\n", e.toChars());
+    //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
     Expression ret = e;
 
     void error()
@@ -426,7 +426,7 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
 
     void visitAddr(AddrExp e)
     {
-        //printf("AddrExp::optimize(result = %d) %s\n", result, e.toChars());
+        //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars());
         /* Rewrite &(a,b) as (a,&b)
          */
         if (auto ce = e.e1.isCommaExp())
@@ -438,7 +438,8 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
         }
         // Keep lvalue-ness
         if (expOptimize(e.e1, result, true))
-            return;
+            return;                     // error return
+
         // Convert &*ex to ex
         if (auto pe = e.e1.isPtrExp())
         {
@@ -515,6 +516,23 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
                         }
                     }
                 }
+                else if (auto ei = e.isIndexExp())
+                {
+                    if (auto ve = ei.e1.isVarExp())
+                    {
+                        if (!ve.var.isReference() &&
+                            !ve.var.isImportedSymbol() &&
+                            ve.var.isDataseg() &&
+                            ve.var.isCsymbol())
+                        {
+                            if (auto ie = ei.e2.isIntegerExp())
+                            {
+                                var = ve.var.isVarDeclaration();
+                                offset += ie.toInteger() * ve.type.toBasetype().nextOf().size();
+                            }
+                        }
+                    }
+                }
                 return false;
             }
 
@@ -538,7 +556,7 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
                 return;
             }
         }
-        if (auto ae = e.e1.isIndexExp())
+        else if (auto ae = e.e1.isIndexExp())
         {
             // Convert &array[n] to &array+n
             if (ae.e2.isIntegerExp() && ae.e1.isVarExp())
@@ -551,9 +569,10 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
                     sinteger_t dim = ts.dim.toInteger();
                     if (index < 0 || index >= dim)
                     {
-                        /* 0 for C static arrays means size is unknown, no need to check
+                        /* 0 for C static arrays means size is unknown, no need to check,
+                         * and address one past the end is OK, too
                          */
-                        if (!(dim == 0 && ve.var.isCsymbol()))
+                        if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
                         {
                             e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
                             return error();
@@ -585,9 +604,10 @@  Expression Expression_optimize(Expression e, int result, bool keepLvalue)
                     sinteger_t dim = ts.dim.toInteger();
                     if (index < 0 || index >= dim)
                     {
-                        /* 0 for C static arrays means size is unknown, no need to check
+                        /* 0 for C static arrays means size is unknown, no need to check,
+                         * and address one past the end is OK, too
                          */
-                        if (!(dim == 0 && ve.var.isCsymbol()))
+                        if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
                         {
                             e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
                             return error();
diff --git a/gcc/d/dmd/root/aav.d b/gcc/d/dmd/root/aav.d
index ba5d3312d8a..beceb0f0707 100644
--- a/gcc/d/dmd/root/aav.d
+++ b/gcc/d/dmd/root/aav.d
@@ -14,6 +14,8 @@  module dmd.root.aav;
 import core.stdc.string;
 import dmd.root.rmem;
 
+nothrow:
+
 private size_t hash(size_t a) pure nothrow @nogc @safe
 {
     a ^= (a >> 20) ^ (a >> 12);
diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d
index cd2d7096d3d..c2eb3e15c2c 100644
--- a/gcc/d/dmd/root/array.d
+++ b/gcc/d/dmd/root/array.d
@@ -137,7 +137,7 @@  public:
 
     void reserve(size_t nentries) pure nothrow
     {
-        //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", (int)length, (int)data.length, (int)nentries);
+        //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", cast(int)length, cast(int)data.length, cast(int)nentries);
 
         // Cold path
         void enlarge(size_t nentries)
diff --git a/gcc/d/dmd/root/complex.d b/gcc/d/dmd/root/complex.d
index d9a396d9677..a7a74381ef9 100644
--- a/gcc/d/dmd/root/complex.d
+++ b/gcc/d/dmd/root/complex.d
@@ -13,11 +13,15 @@  module dmd.root.complex;
 
 import dmd.root.ctfloat;
 
+nothrow:
+
 extern (C++) struct complex_t
 {
     real_t re;
     real_t im;
 
+  nothrow:
+
     this() @disable;
 
     this(real_t re)
diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d
index a01cfdc2aac..b40413c9ef1 100644
--- a/gcc/d/dmd/root/file.d
+++ b/gcc/d/dmd/root/file.d
@@ -26,11 +26,15 @@  import dmd.root.string;
 import dmd.common.file;
 import dmd.common.string;
 
-/// Owns a (rmem-managed) file buffer.
-struct FileBuffer
+nothrow:
+
+/// Owns a (rmem-managed) buffer.
+struct Buffer
 {
     ubyte[] data;
 
+  nothrow:
+
     this(this) @disable;
 
     ~this() pure nothrow
@@ -54,7 +58,7 @@  struct File
     static struct ReadResult
     {
         bool success;
-        FileBuffer buffer;
+        Buffer buffer;
 
         /// Transfers ownership of the buffer to the caller.
         ubyte[] extractSlice() pure nothrow @nogc @safe
diff --git a/gcc/d/dmd/root/longdouble.d b/gcc/d/dmd/root/longdouble.d
index 27fcf9c849f..d6753aa8490 100644
--- a/gcc/d/dmd/root/longdouble.d
+++ b/gcc/d/dmd/root/longdouble.d
@@ -29,6 +29,8 @@  nothrow:
 // Type used by the front-end for compile-time reals
 struct longdouble
 {
+nothrow:
+@nogc:
     extern (D) this(T)(T r)
     {
         this.set(cast(SetType!T)r);
diff --git a/gcc/d/dmd/root/optional.d b/gcc/d/dmd/root/optional.d
index f2f7389efd2..266846bcb0b 100644
--- a/gcc/d/dmd/root/optional.d
+++ b/gcc/d/dmd/root/optional.d
@@ -10,6 +10,8 @@ 
  */
 module dmd.root.optional;
 
+nothrow:
+
 ///
 unittest
 {
@@ -45,6 +47,8 @@  extern (C++) struct Optional(T)
     /// whether `value` is set
     private bool present;
 
+  nothrow:
+
     /// Creates an `Optional` with the given value
     this(T value)
     {
diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d
index b9029a16ef8..cd65920e38b 100644
--- a/gcc/d/dmd/semantic2.d
+++ b/gcc/d/dmd/semantic2.d
@@ -478,7 +478,7 @@  private extern(C++) final class Semantic2Visitor : Visitor
         i.mod.semantic2(null);
         if (i.mod.needmoduleinfo)
         {
-            //printf("module5 %s because of %s\n", sc.module.toChars(), mod.toChars());
+            //printf("module5 %s because of %s\n", sc._module.toChars(), mod.toChars());
             if (sc)
                 sc._module.needmoduleinfo = 1;
         }
diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d
index c2967d65d5f..2916bbc38dd 100644
--- a/gcc/d/dmd/statementsem.d
+++ b/gcc/d/dmd/statementsem.d
@@ -51,6 +51,7 @@  import dmd.importc;
 import dmd.init;
 import dmd.intrange;
 import dmd.mtype;
+import dmd.mustuse;
 import dmd.nogc;
 import dmd.opover;
 import dmd.parse;
@@ -211,7 +212,8 @@  package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor
             if (f.checkForwardRef(s.exp.loc))
                 s.exp = ErrorExp.get();
         }
-
+        if (checkMustUse(s.exp, sc))
+            s.exp = ErrorExp.get();
         if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp))
             s.exp = ErrorExp.get();
 
diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d
index 43feab15f22..03e80245a74 100644
--- a/gcc/d/dmd/tokens.d
+++ b/gcc/d/dmd/tokens.d
@@ -591,7 +591,7 @@  shared static this() nothrow
     Identifier.initTable();
     foreach (kw; keywords)
     {
-        //printf("keyword[%d] = '%s'\n",kw, tochars[kw].ptr);
+        //printf("keyword[%d] = '%s'\n",kw, Token.tochars[kw].ptr);
         Identifier.idPool(Token.tochars[kw].ptr, Token.tochars[kw].length, cast(uint)kw);
     }
 }
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
index 4b15e8c4739..04e1c47d16e 100644
--- a/gcc/d/dmd/traits.d
+++ b/gcc/d/dmd/traits.d
@@ -1278,7 +1278,7 @@  Expression semanticTraits(TraitsExp e, Scope* sc)
             {
                 s = s.isImport().mod;
             }
-            //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s.scope);
+            //printf("getAttributes %s, attrs = %p, scope = %p\n", s.toChars(), s.userAttribDecl, s._scope);
             udad = s.userAttribDecl;
         }
         else
@@ -1514,7 +1514,9 @@  Expression semanticTraits(TraitsExp e, Scope* sc)
         TypeFunction tf = toTypeFunction(o, fd);
 
         if (tf)
-            link = tf.linkage;
+        {
+            link = fd ? fd.linkage : tf.linkage;
+        }
         else
         {
             auto s = getDsymbol(o);
@@ -1730,69 +1732,33 @@  Expression semanticTraits(TraitsExp e, Scope* sc)
             bool err = false;
 
             auto t = isType(o);
-            while (t)
+            auto ex = isExpression(o);
+            if (t)
             {
-                if (auto tm = t.isTypeMixin())
+                Dsymbol s;
+                t.resolve(e.loc, sc2, ex, t, s);
+                if (t)
                 {
-                    /* The mixin string could be a type or an expression.
-                     * Have to try compiling it to see.
-                     */
-                    OutBuffer buf;
-                    if (expressionsToString(buf, sc, tm.exps))
-                    {
+                    t.typeSemantic(e.loc, sc2);
+                    if (t.ty == Terror)
                         err = true;
-                        break;
-                    }
-                    const olderrors = global.errors;
-                    const len = buf.length;
-                    buf.writeByte(0);
-                    const str = buf.extractSlice()[0 .. len];
-                    scope p = new Parser!ASTCodegen(e.loc, sc._module, str, false);
-                    p.nextToken();
-                    //printf("p.loc.linnum = %d\n", p.loc.linnum);
-
-                    o = p.parseTypeOrAssignExp(TOK.endOfFile);
-                    if (olderrors != global.errors || p.token.value != TOK.endOfFile)
-                    {
-                        err = true;
-                        break;
-                    }
-                    t = o.isType();
                 }
-                else
-                    break;
+                else if (s && s.errors)
+                    err = true;
             }
-
-            if (!err)
+            if (ex)
             {
-                auto ex = t ? t.typeToExpression() : isExpression(o);
-                if (!ex && t)
-                {
-                    Dsymbol s;
-                    t.resolve(e.loc, sc2, ex, t, s);
-                    if (t)
-                    {
-                        t.typeSemantic(e.loc, sc2);
-                        if (t.ty == Terror)
-                            err = true;
-                    }
-                    else if (s && s.errors)
-                        err = true;
-                }
-                if (ex)
+                ex = ex.expressionSemantic(sc2);
+                ex = resolvePropertiesOnly(sc2, ex);
+                ex = ex.optimize(WANTvalue);
+                if (sc2.func && sc2.func.type.ty == Tfunction)
                 {
-                    ex = ex.expressionSemantic(sc2);
-                    ex = resolvePropertiesOnly(sc2, ex);
-                    ex = ex.optimize(WANTvalue);
-                    if (sc2.func && sc2.func.type.ty == Tfunction)
-                    {
-                        const tf = cast(TypeFunction)sc2.func.type;
-                        err |= tf.isnothrow && canThrow(ex, sc2.func, false);
-                    }
-                    ex = checkGC(sc2, ex);
-                    if (ex.op == EXP.error)
-                        err = true;
+                    const tf = cast(TypeFunction)sc2.func.type;
+                    err |= tf.isnothrow && canThrow(ex, sc2.func, false);
                 }
+                ex = checkGC(sc2, ex);
+                if (ex.op == EXP.error)
+                    err = true;
             }
 
             // Carefully detach the scope from the parent and throw it away as
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index bcdbec5fe2d..b26741242f9 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -1220,6 +1220,9 @@  extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
             tf.islive = true;
 
         tf.linkage = sc.linkage;
+        if (tf.linkage == LINK.system)
+            tf.linkage = target.systemLinkage();
+
         version (none)
         {
             /* If the parent is @safe, then this function defaults to safe
@@ -2281,10 +2284,7 @@  RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc)
         return null;
     }
 
-    Type t = o.isType();
-    Expression e = t ? t.typeToExpression() : o.isExpression();
-
-    return (!e && t) ? t : e;
+    return o;
 }
 
 
@@ -3619,12 +3619,31 @@  Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
                 e.error("`%s` is not an expression", e.toChars());
                 return ErrorExp.get();
             }
-            else if (checkUnsafeDotExp(sc, e, ident, flag))
+            else if (mt.dim.toUInteger() < 1 && checkUnsafeDotExp(sc, e, ident, flag))
             {
+                // .ptr on static array is @safe unless size is 0
+                // https://issues.dlang.org/show_bug.cgi?id=20853
                 return ErrorExp.get();
             }
             e = e.castTo(sc, e.type.nextOf().pointerTo());
         }
+        else if (ident == Id._tupleof)
+        {
+            if (e.isTypeExp())
+            {
+                e.error("`.tupleof` cannot be used on type `%s`", mt.toChars);
+                return ErrorExp.get();
+            }
+            else
+            {
+                const length = cast(size_t)mt.dim.toUInteger();
+                auto exps = new Expressions();
+                exps.reserve(length);
+                foreach (i; 0 .. length)
+                    exps.push(new IndexExp(e.loc, e, new IntegerExp(e.loc, i, Type.tsize_t)));
+                e = new TupleExp(e.loc, exps);
+            }
+        }
         else
         {
             e = visitArray(mt);
diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d
index 67e4d86a43e..cdfe3cb61aa 100644
--- a/gcc/d/dmd/utils.d
+++ b/gcc/d/dmd/utils.d
@@ -19,6 +19,7 @@  import dmd.root.filename;
 import dmd.common.outbuffer;
 import dmd.root.string;
 
+nothrow:
 
 /**
  * Normalize path by turning forward slashes into backslashes
@@ -52,13 +53,13 @@  const(char)* toWinPath(const(char)* src)
  *   loc = The line number information from where the call originates
  *   filename = Path to file
  */
-FileBuffer readFile(Loc loc, const(char)* filename)
+Buffer readFile(Loc loc, const(char)* filename)
 {
     return readFile(loc, filename.toDString());
 }
 
 /// Ditto
-FileBuffer readFile(Loc loc, const(char)[] filename)
+Buffer readFile(Loc loc, const(char)[] filename)
 {
     auto result = File.read(filename);
     if (!result.success)
@@ -66,7 +67,7 @@  FileBuffer readFile(Loc loc, const(char)[] filename)
         error(loc, "error reading file `%.*s`", cast(int)filename.length, filename.ptr);
         fatal();
     }
-    return FileBuffer(result.extractSlice());
+    return Buffer(result.extractSlice());
 }
 
 
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
index 61a2b50b149..c683d9da333 100644
--- a/gcc/d/expr.cc
+++ b/gcc/d/expr.cc
@@ -3006,6 +3006,16 @@  public:
     this->result_ = var;
   }
 
+  /* Build an uninitialized value, generated from void initializers.  */
+
+  void visit (VoidInitExp *e)
+  {
+    /* The front-end only generates these for the initializer of globals.
+       Represent `void' as zeroes, regardless of the type's default value.  */
+    gcc_assert (this->constp_);
+    this->result_ = build_zero_cst (build_ctype (e->type));
+  }
+
   /* These expressions are mainly just a placeholders in the frontend.
      We shouldn't see them here.  */
 
diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
index 3c2e90ab8ac..93f05a50c31 100644
--- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
+++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d
@@ -607,3 +607,13 @@  T throwStuff(T)(T t)
     if (false) test13x(1, throw new Exception(""), 2);
     return t ? t : throw new Exception("Bad stuff happens!");
 }
+
+class C12344
+{
+    abstract int c12344(int x) in(x > 0) out(result) {assert(result > 0);};
+}
+
+interface I12344
+{
+    int i12344(int x) in(x > 0) out(result) {assert(result > 0);};
+}
diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp17434a.d b/gcc/testsuite/gdc.test/compilable/imports/imp17434a.d
new file mode 100644
index 00000000000..5046cbb31ce
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/imp17434a.d
@@ -0,0 +1 @@ 
+module imp17434a.test1;
diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp17434b.d b/gcc/testsuite/gdc.test/compilable/imports/imp17434b.d
new file mode 100644
index 00000000000..88c37ab488a
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/imports/imp17434b.d
@@ -0,0 +1,6 @@ 
+module imp.imports17434b;
+
+void testing()
+{
+    return;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/interpret3.d b/gcc/testsuite/gdc.test/compilable/interpret3.d
index ff85856b62e..2c9a84eddfb 100644
--- a/gcc/testsuite/gdc.test/compilable/interpret3.d
+++ b/gcc/testsuite/gdc.test/compilable/interpret3.d
@@ -2473,12 +2473,12 @@  static assert(checkPass("foobar") == 1);
 
 struct Toq
 {
-    const(char)* m;
+    char* m;
 }
 
 Toq ptrRet(bool b)
 {
-    string x = "abc";
+    char[] x = "abc".dup;
     return Toq(b ? x[0 .. 1].ptr : null);
 }
 
@@ -7808,3 +7808,101 @@  int test9937()
 }
 
 static assert(test9937());
+
+/************************************************/
+// static array .tupleof
+
+struct SArrayTupleEquiv(T)
+{
+    T f1;
+    T f2;
+}
+
+// basic .tupleof invariants
+bool testSArrayTupleA()
+{
+    int[2] xs;
+    assert(xs.tupleof == TypeTuple!(0, 0));
+    assert(xs.tupleof == (cast(int[2])[0, 0]).tupleof);
+
+    xs.tupleof = TypeTuple!(1, 2);
+    assert(xs.tupleof == TypeTuple!(1, 2));
+
+    auto ys = SArrayTupleEquiv!int(1, 2);
+    assert(xs.tupleof == ys.tupleof);
+
+    return true;
+}
+static assert(testSArrayTupleA());
+
+// tuples with side effects
+bool testSArrayTupleB()
+{
+    // Counter records lifetime events in copies/dtors, as a cheap way to check that .tupleof for
+    // static arrays exhibit all the same side effects as an equivalent struct's .tupleof
+    int[int] copies;
+    int[int] dtors;
+    struct Counter
+    {
+        int id = -1;
+
+        this(this)
+        {
+            copies[id] = copies.get(id, 0) + 1;
+        }
+
+        ~this()
+        {
+            dtors[id] = dtors.get(id, 0) + 1;
+        }
+    }
+
+    void consume(Counter, Counter) {}
+    Counter[2] produce(int id1, int id2)
+    {
+        return [Counter(id1), Counter(id2)];
+    }
+
+    // first sample expected behavior from struct .tupleof
+    // braces create a subscope, shortening lifetimes
+    {
+        auto a = SArrayTupleEquiv!Counter(Counter(0), Counter(1));
+
+        typeof(a) b;
+        b.tupleof = a.tupleof;
+
+        Counter x, y;
+        TypeTuple!(x, y) = a.tupleof;
+
+        a.tupleof[0] = Counter(2);
+        a.tupleof[1] = Counter(3);
+        consume(a.tupleof);
+
+        a.tupleof = produce(4, 5).tupleof;
+    }
+    int[int][2] expected = [copies.dup, dtors.dup];
+    copies = null; // .clear is not CTFE friendly
+    dtors = null;
+
+    // the real test -- sample behavior of array .tupleof
+    {
+        Counter[2] a = [Counter(0), Counter(1)];
+
+        typeof(a) b;
+        b.tupleof = a.tupleof;
+
+        Counter x, y;
+        TypeTuple!(x, y) = a.tupleof;
+
+        a.tupleof[0] = Counter(2);
+        a.tupleof[1] = Counter(3);
+        consume(a.tupleof);
+
+        a.tupleof = produce(4, 5).tupleof;
+    }
+    assert(expected[0] == copies);
+    assert(expected[1] == dtors);
+
+    return true;
+}
+static assert(testSArrayTupleB());
diff --git a/gcc/testsuite/gdc.test/compilable/json.d b/gcc/testsuite/gdc.test/compilable/json.d
index 73d3101b835..f4d68e7dc8c 100644
--- a/gcc/testsuite/gdc.test/compilable/json.d
+++ b/gcc/testsuite/gdc.test/compilable/json.d
@@ -203,14 +203,14 @@  extern(C) int vlinakgeC;
 extern(C++) __gshared int vlinkageCpp;
 extern(Windows) int vlinkageWindows;
 extern(Objective-C) int vlinkageObjc;
-
+extern(System) int vlinkageSystem;
 extern int flinkageDefault();
 extern(D) int flinkageD();
 extern(C) int linakgeC();
 extern(C++) int flinkageCpp();
 extern(Windows) int flinkageWindows();
 extern(Objective-C) int flinkageObjc();
-
+extern(System) int flinkageSystem();
 mixin template test18211(int n)
 {
     static foreach (i; 0 .. n>10 ? 10 : n)
diff --git a/gcc/testsuite/gdc.test/compilable/mixintype2.d b/gcc/testsuite/gdc.test/compilable/mixintype2.d
index d160bd410ad..a61adc570fa 100644
--- a/gcc/testsuite/gdc.test/compilable/mixintype2.d
+++ b/gcc/testsuite/gdc.test/compilable/mixintype2.d
@@ -115,3 +115,9 @@  void test_statements_22356()
     mixin("int") y22356, z22356;
     static assert(is(typeof(y22356) == int) && is(typeof(z22356) == int));
 }
+
+/**************************************************/
+// https://issues.dlang.org/show_bug.cgi?id=22969
+
+enum e = 0;
+alias a = mixin("e");
diff --git a/gcc/testsuite/gdc.test/compilable/must_use_assign.d b/gcc/testsuite/gdc.test/compilable/must_use_assign.d
new file mode 100644
index 00000000000..bd1983a40e6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/must_use_assign.d
@@ -0,0 +1,9 @@ 
+import core.attribute;
+
+@mustuse struct S {}
+
+void test()
+{
+    S a, b;
+    a = b;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/must_use_not_reserved.d b/gcc/testsuite/gdc.test/compilable/must_use_not_reserved.d
new file mode 100644
index 00000000000..c6001198489
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/must_use_not_reserved.d
@@ -0,0 +1,5 @@ 
+import core.attribute;
+
+@mustuse int n;
+@mustuse alias A = int;
+@mustuse template tpl() {}
diff --git a/gcc/testsuite/gdc.test/compilable/must_use_opassign.d b/gcc/testsuite/gdc.test/compilable/must_use_opassign.d
new file mode 100644
index 00000000000..8e877dd69b6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/must_use_opassign.d
@@ -0,0 +1,15 @@ 
+import core.attribute;
+
+@mustuse struct S
+{
+    ref S opAssign(S rhs) return
+    {
+        return this;
+    }
+}
+
+void test()
+{
+    S a, b;
+    a = b;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/must_use_opopassign.d b/gcc/testsuite/gdc.test/compilable/must_use_opopassign.d
new file mode 100644
index 00000000000..0176354ed1d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/must_use_opopassign.d
@@ -0,0 +1,15 @@ 
+import core.attribute;
+
+@mustuse struct S
+{
+    ref S opOpAssign(string op)(S rhs) return
+    {
+        return this;
+    }
+}
+
+void test()
+{
+    S a, b;
+    a += b;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/must_use_opunary.d b/gcc/testsuite/gdc.test/compilable/must_use_opunary.d
new file mode 100644
index 00000000000..fc9650ebed5
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/must_use_opunary.d
@@ -0,0 +1,18 @@ 
+import core.attribute;
+
+@mustuse struct S
+{
+    ref S opUnary(string op)() return
+    {
+        return this;
+    }
+}
+
+void test()
+{
+    S s;
+    ++s;
+    --s;
+    s++;
+    s--;
+}
diff --git a/gcc/testsuite/gdc.test/compilable/must_use_suppress.d b/gcc/testsuite/gdc.test/compilable/must_use_suppress.d
new file mode 100644
index 00000000000..63ad75c2928
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/must_use_suppress.d
@@ -0,0 +1,10 @@ 
+import core.attribute;
+
+@mustuse struct S {}
+
+S fun() { return S(); }
+
+void test()
+{
+    cast(void) fun();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17419.d b/gcc/testsuite/gdc.test/compilable/test17419.d
index e34e7da5444..1a92a1c9112 100644
--- a/gcc/testsuite/gdc.test/compilable/test17419.d
+++ b/gcc/testsuite/gdc.test/compilable/test17419.d
@@ -17,10 +17,7 @@  static assert(__traits(getLinkage, food) == "D");
 static assert(__traits(getLinkage, foocpp) == "C++");
 static assert(__traits(getLinkage, foow) == "Windows");
 static assert(__traits(getLinkage, fooobjc) == "Objective-C");
-version (Windows)
-    static assert(__traits(getLinkage, foos) == "Windows");
-else
-    static assert(__traits(getLinkage, foos) == "C");
+static assert(__traits(getLinkage, foos) == "System");
 
 extern (C) int global;
 static assert(__traits(getLinkage, global) == "C");
diff --git a/gcc/testsuite/gdc.test/compilable/test17434.d b/gcc/testsuite/gdc.test/compilable/test17434.d
new file mode 100644
index 00000000000..82bfa570b5f
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17434.d
@@ -0,0 +1,11 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=17434
+
+// EXTRA_FILES: test17434a.d imports/imp17434a.d imports/imp17434b.d
+module test17434;
+
+import test17434a;
+
+void main()
+{
+    imports.imp17434b.testing();
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test17434a.d b/gcc/testsuite/gdc.test/compilable/test17434a.d
new file mode 100644
index 00000000000..c9175fe385b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test17434a.d
@@ -0,0 +1,5 @@ 
+// EXTRA_FILES: imports/imp17434a.d imports/imp17434b.d
+module test17434a;
+
+private import imports.imp17434a;
+public  import imports.imp17434b;
diff --git a/gcc/testsuite/gdc.test/compilable/test19097.d b/gcc/testsuite/gdc.test/compilable/test19097.d
index 19e189caf5d..a6d30024881 100644
--- a/gcc/testsuite/gdc.test/compilable/test19097.d
+++ b/gcc/testsuite/gdc.test/compilable/test19097.d
@@ -21,3 +21,43 @@  void foo(scope int* pf)
     betty(rf, pf);
     boop(rf, pf);
 }
+
+// https://issues.dlang.org/show_bug.cgi?id=22801
+struct Wrapper
+{
+    int* ptr;
+
+    this(return ref int var) @safe
+    {
+        this.ptr = &var;
+    }
+}
+
+void main() @safe
+{
+    int i;
+    auto w = Wrapper(i);
+    auto wt = WrapperT!()(i);
+}
+
+void assign(ref scope int* x, return ref int y) @safe
+{
+    x = &y;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=22967
+// inference of `return ref` when assigned to first parameter
+struct WrapperT()
+{
+    int* ptr;
+
+    this(ref int var) @safe
+    {
+        this.ptr = &var;
+    }
+
+    static void assignInferred(ref scope int* xi, ref int yi) @safe
+    {
+        xi = &yi;
+    }
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test22988.d b/gcc/testsuite/gdc.test/compilable/test22988.d
new file mode 100644
index 00000000000..6be3921e67b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test22988.d
@@ -0,0 +1,15 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=22988
+
+enum a1 = 0;
+enum b1 = a1 ? 1 << a1 - 1 : 0;
+
+enum l = 0;
+enum int[l] a2 = [];
+enum b2 = l ? a2[l - 1] : 0;
+
+enum a3 = 0 ? 1 << -1 : 0;
+
+enum int[0] a4 = [];
+enum b4 = 0 ? a4[0] : 0;
+
+enum b5 = false ? (1 << -1) : 0;
diff --git a/gcc/testsuite/gdc.test/compilable/test22997.d b/gcc/testsuite/gdc.test/compilable/test22997.d
new file mode 100644
index 00000000000..3cad527b4c2
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test22997.d
@@ -0,0 +1,14 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=22997
+
+struct Forward {}
+
+struct Foo
+{
+    this(ref typeof(this) rhs)
+    {
+        this(rhs, Forward.init);
+    }
+
+    this(ref typeof(this) rhs, Forward) {}
+    this(typeof(this) rhs, int i, double d, string s) {}
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag14235.d b/gcc/testsuite/gdc.test/fail_compilation/diag14235.d
index 3c8a98e6e35..29261b4ea1c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag14235.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag14235.d
@@ -2,8 +2,8 @@ 
 EXTRA_FILES: imports/a14235.d
 TEST_OUTPUT:
 ---
-fail_compilation/diag14235.d(12): Error: template identifier `Undefined` is not a member of module `imports.a14235`
-fail_compilation/diag14235.d(13): Error: template identifier `Something` is not a member of module `imports.a14235`, did you mean struct `SomeThing(T...)`?
+fail_compilation/diag14235.d(12): Error: undefined identifier `Undefined` in module `imports.a14235`
+fail_compilation/diag14235.d(13): Error: undefined identifier `Something` in module `imports.a14235`, did you mean struct `SomeThing(T...)`?
 fail_compilation/diag14235.d(14): Error: `imports.a14235.SomeClass` is not a template, it is a class
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8101.d b/gcc/testsuite/gdc.test/fail_compilation/diag8101.d
index 282665ff8d5..301472ca28a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag8101.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag8101.d
@@ -2,7 +2,7 @@ 
 TEST_OUTPUT:
 ---
 fail_compilation/diag8101.d(57): Error: function `diag8101.f_0(int)` is not callable using argument types `()`
-fail_compilation/diag8101.d(57):        missing argument for parameter #1: `int`
+fail_compilation/diag8101.d(57):        too few arguments, expected `1`, got `0`
 fail_compilation/diag8101.d(58): Error: none of the overloads of `f_1` are callable using argument types `()`
 fail_compilation/diag8101.d(33):        Candidates are: `diag8101.f_1(int)`
 fail_compilation/diag8101.d(34):                        `diag8101.f_1(int, int)`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d b/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d
index b46c562e87d..1279d7c2a2a 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diag_funclit.d
@@ -2,7 +2,7 @@ 
 TEST_OUTPUT:
 ---
 fail_compilation/diag_funclit.d(103): Error: function literal `__lambda1(x, y, z)` is not callable using argument types `()`
-fail_compilation/diag_funclit.d(103):        missing argument for parameter #1: `x`
+fail_compilation/diag_funclit.d(103):        too few arguments, expected `3`, got `0`
 fail_compilation/diag_funclit.d(106): Error: function literal `__lambda2(x, y, z)` is not callable using argument types `(int, string, int, int)`
 fail_compilation/diag_funclit.d(106):        too many arguments, expected `3`, got `4`
 fail_compilation/diag_funclit.d(108): Error: function literal `__lambda3(x, y, string z = "Hello")` is not callable using argument types `(int, int, string, string)`
diff --git a/gcc/testsuite/gdc.test/fail_compilation/diagin.d b/gcc/testsuite/gdc.test/fail_compilation/diagin.d
index eb88a23151a..1418ced43a2 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/diagin.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/diagin.d
@@ -3,7 +3,7 @@  PERMUTE_ARGS: -preview=in
 TEST_OUTPUT:
 ---
 fail_compilation/diagin.d(14): Error: function `diagin.foo(in int)` is not callable using argument types `()`
-fail_compilation/diagin.d(14):        missing argument for parameter #1: `in int`
+fail_compilation/diagin.d(14):        too few arguments, expected `1`, got `0`
 fail_compilation/diagin.d(16): Error: none of the overloads of template `diagin.foo1` are callable using argument types `!()(bool[])`
 fail_compilation/diagin.d(20):        Candidate is: `foo1(T)(in T v, string)`
 ---
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail2656.d b/gcc/testsuite/gdc.test/fail_compilation/fail2656.d
index ffd38de4502..520c1b14bfb 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail2656.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail2656.d
@@ -1,20 +1,20 @@ 
 /*
 TEST_OUTPUT:
 ---
-fail_compilation/fail2656.d(21): Error: octal literals `0123` are no longer supported, use `std.conv.octal!123` instead
-fail_compilation/fail2656.d(22): Error: octal literals `01000000000000000000000` are no longer supported, use `std.conv.octal!1000000000000000000000` instead
-fail_compilation/fail2656.d(23): Error: octal literals `0100000L` are no longer supported, use `std.conv.octal!100000L` instead
-fail_compilation/fail2656.d(24): Error: octal literals `01777777777777777777777u` are no longer supported, use `std.conv.octal!1777777777777777777777u` instead
-fail_compilation/fail2656.d(25): Error: octal literals `017777777777uL` are no longer supported, use `std.conv.octal!17777777777uL` instead
-fail_compilation/fail2656.d(26): Error: octal literals `0177777` are no longer supported, use `std.conv.octal!177777` instead
-fail_compilation/fail2656.d(27): Error: octal literals `020000000000L` are no longer supported, use `std.conv.octal!20000000000L` instead
-fail_compilation/fail2656.d(28): Error: octal literals `0200000u` are no longer supported, use `std.conv.octal!200000u` instead
-fail_compilation/fail2656.d(29): Error: octal literals `037777777777uL` are no longer supported, use `std.conv.octal!37777777777uL` instead
-fail_compilation/fail2656.d(30): Error: octal literals `040000000000` are no longer supported, use `std.conv.octal!40000000000` instead
-fail_compilation/fail2656.d(31): Error: octal literals `0777777777777777777777L` are no longer supported, use `std.conv.octal!777777777777777777777L` instead
-fail_compilation/fail2656.d(32): Error: octal literals `077777u` are no longer supported, use `std.conv.octal!77777u` instead
-fail_compilation/fail2656.d(33): Error: octal literals `077777uL` are no longer supported, use `std.conv.octal!77777uL` instead
-fail_compilation/fail2656.d(34): Error: octal literals `077777uL` are no longer supported, use `std.conv.octal!77777uL` instead
+fail_compilation/fail2656.d(21): Error: octal literals `0123` are no longer supported, use `std.conv.octal!"123"` instead
+fail_compilation/fail2656.d(22): Error: octal literals `01000000000000000000000` are no longer supported, use `std.conv.octal!"1000000000000000000000"` instead
+fail_compilation/fail2656.d(23): Error: octal literals `0100000L` are no longer supported, use `std.conv.octal!"100000L"` instead
+fail_compilation/fail2656.d(24): Error: octal literals `01777777777777777777777u` are no longer supported, use `std.conv.octal!"1777777777777777777777u"` instead
+fail_compilation/fail2656.d(25): Error: octal literals `017777777777uL` are no longer supported, use `std.conv.octal!"17777777777uL"` instead
+fail_compilation/fail2656.d(26): Error: octal literals `0177777` are no longer supported, use `std.conv.octal!"177777"` instead
+fail_compilation/fail2656.d(27): Error: octal literals `020000000000L` are no longer supported, use `std.conv.octal!"20000000000L"` instead
+fail_compilation/fail2656.d(28): Error: octal literals `0200000u` are no longer supported, use `std.conv.octal!"200000u"` instead
+fail_compilation/fail2656.d(29): Error: octal literals `037777777777uL` are no longer supported, use `std.conv.octal!"37777777777uL"` instead
+fail_compilation/fail2656.d(30): Error: octal literals `040000000000` are no longer supported, use `std.conv.octal!"40000000000"` instead
+fail_compilation/fail2656.d(31): Error: octal literals `0777777777777777777777L` are no longer supported, use `std.conv.octal!"777777777777777777777L"` instead
+fail_compilation/fail2656.d(32): Error: octal literals `077777u` are no longer supported, use `std.conv.octal!"77777u"` instead
+fail_compilation/fail2656.d(33): Error: octal literals `077777uL` are no longer supported, use `std.conv.octal!"77777uL"` instead
+fail_compilation/fail2656.d(34): Error: octal literals `077777uL` are no longer supported, use `std.conv.octal!"77777uL"` instead
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail99.d b/gcc/testsuite/gdc.test/fail_compilation/fail99.d
index c147b810ee6..e4cba9530bf 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fail99.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fail99.d
@@ -2,7 +2,7 @@ 
 TEST_OUTPUT:
 ---
 fail_compilation/fail99.d(13): Error: delegate `dg(int)` is not callable using argument types `()`
-fail_compilation/fail99.d(13):        missing argument for parameter #1: `int`
+fail_compilation/fail99.d(13):        too few arguments, expected `1`, got `0`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/fix19059.d b/gcc/testsuite/gdc.test/fail_compilation/fix19059.d
index 0be003a2f3b..b6bd9d33cf9 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/fix19059.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/fix19059.d
@@ -5,7 +5,7 @@  fail_compilation/fix19059.d(16): Error: octal digit expected, not `8`
 fail_compilation/fix19059.d(16): Error: octal literals larger than 7 are no longer supported
 fail_compilation/fix19059.d(17): Error: octal digit expected, not `9`
 fail_compilation/fix19059.d(17): Error: octal literals larger than 7 are no longer supported
-fail_compilation/fix19059.d(18): Error: octal literals `010` are no longer supported, use `std.conv.octal!10` instead
+fail_compilation/fix19059.d(18): Error: octal literals `010` are no longer supported, use `std.conv.octal!"10"` instead
 ---
  */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d
index c227ee55eb4..f3a7a57a689 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d
@@ -2,7 +2,7 @@ 
 TEST_OUTPUT:
 ---
 fail_compilation/ice10922.d(10): Error: function `ice10922.__lambda4(in uint n)` is not callable using argument types `()`
-fail_compilation/ice10922.d(10):        missing argument for parameter #1: `in uint n`
+fail_compilation/ice10922.d(10):        too few arguments, expected `1`, got `0`
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9540.d b/gcc/testsuite/gdc.test/fail_compilation/ice9540.d
index f95cb974c79..ce705078ac0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/ice9540.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/ice9540.d
@@ -2,7 +2,7 @@ 
 TEST_OUTPUT:
 ---
 fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int _param_0)` is not callable using argument types `()`
-fail_compilation/ice9540.d(35):        missing argument for parameter #1: `int _param_0`
+fail_compilation/ice9540.d(35):        too few arguments, expected `1`, got `0`
 fail_compilation/ice9540.d(26): Error: template instance `ice9540.A.test.AddFront!(this, f)` error instantiating
 ---
 */
diff --git a/gcc/testsuite/gdc.test/fail_compilation/must_use.d b/gcc/testsuite/gdc.test/fail_compilation/must_use.d
new file mode 100644
index 00000000000..e3b5fedfd4d
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/must_use.d
@@ -0,0 +1,16 @@ 
+/+
+TEST_OUTPUT:
+---
+fail_compilation/must_use.d(15): Error: ignored value of `@mustuse` type `must_use.S`; prepend a `cast(void)` if intentional
+---
++/
+import core.attribute;
+
+@mustuse struct S {}
+
+S fun() { return S(); }
+
+void test()
+{
+    fun();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/must_use_comma.d b/gcc/testsuite/gdc.test/fail_compilation/must_use_comma.d
new file mode 100644
index 00000000000..4621ab5ec29
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/must_use_comma.d
@@ -0,0 +1,17 @@ 
+/+
+TEST_OUTPUT:
+---
+fail_compilation/must_use_comma.d(16): Error: ignored value of `@mustuse` type `must_use_comma.S`; prepend a `cast(void)` if intentional
+---
++/
+import core.attribute;
+
+@mustuse struct S {}
+
+S fun() { return S(); }
+void sideEffect() {}
+
+void test()
+{
+    (fun(), sideEffect());
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/must_use_opunary.d b/gcc/testsuite/gdc.test/fail_compilation/must_use_opunary.d
new file mode 100644
index 00000000000..59248a969c4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/must_use_opunary.d
@@ -0,0 +1,21 @@ 
+/+
+TEST_OUTPUT:
+---
+fail_compilation/must_use_opunary.d(20): Error: ignored value of `@mustuse` type `must_use_opunary.S`; prepend a `cast(void)` if intentional
+---
++/
+import core.attribute;
+
+@mustuse struct S
+{
+    ref S opUnary(string op)() return
+    {
+        return this;
+    }
+}
+
+void test()
+{
+    S s;
+    -s;
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/must_use_reserved.d b/gcc/testsuite/gdc.test/fail_compilation/must_use_reserved.d
new file mode 100644
index 00000000000..96edbd3cbe4
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/must_use_reserved.d
@@ -0,0 +1,20 @@ 
+/+
+TEST_OUTPUT:
+---
+fail_compilation/must_use_reserved.d(14): Error: `@mustuse` on `class` types is reserved for future use
+fail_compilation/must_use_reserved.d(15): Error: `@mustuse` on `interface` types is reserved for future use
+fail_compilation/must_use_reserved.d(16): Error: `@mustuse` on `enum` types is reserved for future use
+fail_compilation/must_use_reserved.d(17): Error: `@mustuse` on functions is reserved for future use
+fail_compilation/must_use_reserved.d(19): Error: `@mustuse` on `class` types is reserved for future use
+fail_compilation/must_use_reserved.d(20): Error: template instance `must_use_reserved.CT!int` error instantiating
+---
++/
+import core.attribute;
+
+@mustuse class C {}
+@mustuse interface I {}
+@mustuse enum E { x }
+@mustuse int fun() { return 0; }
+
+@mustuse class CT(T) {}
+alias _ = CT!int;
diff --git a/gcc/testsuite/gdc.test/fail_compilation/must_use_template.d b/gcc/testsuite/gdc.test/fail_compilation/must_use_template.d
new file mode 100644
index 00000000000..eeaa774ea20
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/must_use_template.d
@@ -0,0 +1,16 @@ 
+/+
+TEST_OUTPUT:
+---
+fail_compilation/must_use_template.d(15): Error: ignored value of `@mustuse` type `must_use_template.S!int`; prepend a `cast(void)` if intentional
+---
++/
+import core.attribute;
+
+@mustuse struct S(T) {}
+
+S!int fun() { return S!int(); }
+
+void test()
+{
+    fun();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/must_use_union.d b/gcc/testsuite/gdc.test/fail_compilation/must_use_union.d
new file mode 100644
index 00000000000..d42b324ee9b
--- /dev/null
+++ b/gcc/testsuite/gdc.test/fail_compilation/must_use_union.d
@@ -0,0 +1,16 @@ 
+/+
+TEST_OUTPUT:
+---
+fail_compilation/must_use_union.d(15): Error: ignored value of `@mustuse` type `must_use_union.U`; prepend a `cast(void)` if intentional
+---
++/
+import core.attribute;
+
+@mustuse union U {}
+
+U fun() { return U(); }
+
+void test()
+{
+    fun();
+}
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test11176.d b/gcc/testsuite/gdc.test/fail_compilation/test11176.d
index cf60b80fd7a..5ef7324ce8c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test11176.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test11176.d
@@ -12,6 +12,10 @@  fail_compilation/test11176.d(16): Error: `b.ptr` cannot be used in `@safe` code,
     return *b.ptr;
 }
 
-@safe ubyte oops(ubyte[3] b) {
+@safe ubyte oops(ubyte[0] b) {
+    return *b.ptr;
+}
+
+@safe ubyte cool(ubyte[1] b) {
     return *b.ptr;
 }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17284.d b/gcc/testsuite/gdc.test/fail_compilation/test17284.d
index 713be42e595..5bb3c2cc9f6 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test17284.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test17284.d
@@ -1,8 +1,8 @@ 
+/*
 TEST_OUTPUT:
 ---
-fail_compilation/test17284.d(1): Error: no identifier for declarator `TEST_OUTPUT`
-fail_compilation/test17284.d(1): Error: declaration expected, not `:`
-fail_compilation/test17284.d(12): Error: unmatched closing brace
+fail_compilation/test17284.d(16): Error: field `U.c` cannot access pointers in `@safe` code that overlap other fields
+pure nothrow @safe void(U t)
 ---
 */
 
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test19097.d b/gcc/testsuite/gdc.test/fail_compilation/test19097.d
index 034813b5316..9c025a83ff0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test19097.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test19097.d
@@ -1,12 +1,19 @@ 
 /* REQUIRED_ARGS: -preview=dip1000
  * TEST_OUTPUT:
 ---
-fail_compilation/test19097.d(37): Error: scope variable `s` may not be returned
-fail_compilation/test19097.d(66): Error: scope variable `z` assigned to `refPtr` with longer lifetime
-fail_compilation/test19097.d(97): Error: scope variable `s` may not be returned
+fail_compilation/test19097.d(44): Error: scope variable `s` may not be returned
+fail_compilation/test19097.d(48): Error: scope variable `s1` may not be returned
+fail_compilation/test19097.d(77): Error: scope variable `z` assigned to `refPtr` with longer lifetime
+fail_compilation/test19097.d(108): Error: scope variable `s4` may not be returned
+fail_compilation/test19097.d(126): Error: scope variable `s5c` may not be returned
+fail_compilation/test19097.d(130): Error: scope variable `s5m` may not be returned
+fail_compilation/test19097.d(147): Error: scope variable `s6c` may not be returned
+fail_compilation/test19097.d(151): Error: scope variable `s6m` may not be returned
 ---
  */
 
+// Test extended return-scope / return-ref semantics, e.g. assigning to `this` or the first parameter
+
 // https://issues.dlang.org/show_bug.cgi?id=19097
 
 @safe:
@@ -35,6 +42,10 @@  S thorin()
     int i;
     S s = S(&i); // should infer scope for s
     return s;    // so this should error
+
+    S s1;
+    s1.mem(&i);
+    return s1;
 }
 
 /************************/
@@ -93,6 +104,49 @@  struct S4
 int* escape2()
 {
     int x;
-    auto s = S4(0, &x);
-    return s.p;
+    auto s4 = S4(0, &x);
+    return s4.p;
+}
+
+/************************/
+// https://issues.dlang.org/show_bug.cgi?id=22801
+struct S5
+{
+    int* a;
+    this(return ref int b) { a = &b; }
+
+    int* c;
+    void mem(return ref int d) scope { c = &d; }
+}
+
+S5 frerin()
+{
+    int i;
+    S5 s5c = S5(i); // should infer scope for s
+    return s5c;    // so this should error
+
+    S5 s5m;
+    s5m.mem(i);
+    return s5m;
+}
+
+
+struct S6
+{
+    int** a;
+    this(return ref int* b) { a = &b; }
+
+    int** c;
+    void mem(return ref int* d) scope { c = &d; }
+}
+
+S6 dis()
+{
+    int* i = null;
+    S6 s6c = S6(i); // should infer scope for s
+    return s6c;    // so this should error
+
+    S6 s6m;
+    s6m.mem(i);
+    return s6m;
 }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21008.d b/gcc/testsuite/gdc.test/fail_compilation/test21008.d
index d5cf96a6c3a..998cf1752d0 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/test21008.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/test21008.d
@@ -5,12 +5,12 @@  fail_compilation/test21008.d(110): Error: function `test21008.C.after` circular
 fail_compilation/test21008.d(117): Error: need `this` for `toString` of type `string()`
 fail_compilation/test21008.d(117): Error: need `this` for `toHash` of type `nothrow @trusted $?:32=uint|64=ulong$()`
 fail_compilation/test21008.d(117): Error: function `object.Object.opCmp(Object o)` is not callable using argument types `()`
-fail_compilation/test21008.d(117):        missing argument for parameter #1: `Object o`
+fail_compilation/test21008.d(117):        too few arguments, expected `1`, got `0`
 fail_compilation/test21008.d(117): Error: function `object.Object.opEquals(Object o)` is not callable using argument types `()`
-fail_compilation/test21008.d(117):        missing argument for parameter #1: `Object o`
+fail_compilation/test21008.d(117):        too few arguments, expected `1`, got `0`
 fail_compilation/test21008.d(117): Error: `Monitor` has no effect
 fail_compilation/test21008.d(117): Error: function `object.Object.factory(string classname)` is not callable using argument types `()`
-fail_compilation/test21008.d(117):        missing argument for parameter #1: `string classname`
+fail_compilation/test21008.d(117):        too few arguments, expected `1`, got `0`
 fail_compilation/test21008.d(105):        called from here: `handleMiddlewareAnnotation()`
 fail_compilation/test21008.d(108): Error: class `test21008.C` no size because of forward reference
 ---
diff --git a/gcc/testsuite/gdc.test/runnable/test20603.d b/gcc/testsuite/gdc.test/runnable/test20603.d
new file mode 100644
index 00000000000..47fd3985555
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test20603.d
@@ -0,0 +1,31 @@ 
+// https://issues.dlang.org/show_bug.cgi?id=20603
+
+enum immutable(int)* x = new int(3);
+enum const(int)* y = new int(5);
+
+struct Base {
+    union {
+        int overlap;
+        immutable(Sub)* sub;
+    }
+
+    this(Sub) {
+        sub = new Sub;
+    }
+}
+
+struct Sub {
+    Base base;
+}
+
+immutable c0 = Base(Sub.init);
+
+void main()
+{
+    enum const(int)* z = new int(9);
+
+    assert(*x == 3);
+    assert(*y == 5);
+    assert(*z == 9);
+    assert(c0.sub.base.sub == null);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/xtest46.d b/gcc/testsuite/gdc.test/runnable/xtest46.d
index eeaa1dbe72d..8ae08ad2e47 100644
--- a/gcc/testsuite/gdc.test/runnable/xtest46.d
+++ b/gcc/testsuite/gdc.test/runnable/xtest46.d
@@ -3816,8 +3816,8 @@  void test153()
 /***************************************************/
 // https://issues.dlang.org/show_bug.cgi?id=3632
 
-
-void test154() {
+void test154()
+{
     float f;
     assert(f is float.init);
     double d;
@@ -3832,6 +3832,87 @@  void test154() {
 
 /***************************************************/
 
+__gshared int global3632 = 1;
+
+void test3632()
+{
+    int test(T)()
+    {
+        static struct W
+        {
+            T f;
+            this(T g) { if (__ctfe || global3632) f = g; }
+        }
+        auto nan = W(T.nan);
+        auto nan2 = W(T.nan);
+        auto init = W(T.init);
+        auto init2 = W(T.init);
+        auto zero = W(cast(T)0);
+        auto zero2 = W(cast(T)0);
+        auto nzero2 = W(-cast(T)0);
+
+        // Struct equality
+        assert(!(nan == nan2));
+        assert(!(nan == init2));
+        assert(!(init == init2));
+        assert( (zero == zero2));
+        assert( (zero == nzero2));
+
+        // Float equality
+        assert(!(nan.f == nan2.f));
+        assert(!(nan.f == init2.f));
+        assert(!(init.f == init2.f));
+        assert( (zero.f == zero2.f));
+        assert( (zero.f == nzero2.f));
+
+        // Struct identity
+        assert( (nan is nan2));
+        assert( (nan is init2));
+        assert( (init is init2));
+        assert( (zero is zero2));
+        assert(!(zero is nzero2));
+
+        // Float identity
+        assert( (nan.f is nan2.f));
+        assert( (nan.f is init2.f));
+        assert( (init.f is init2.f));
+        assert( (zero.f is zero2.f));
+        assert(!(zero.f is nzero2.f));
+
+        // Struct !identity
+        assert(!(nan !is nan2));
+        assert( (nan  is init2));
+        assert(!(init !is init2));
+        assert(!(zero !is zero2));
+        assert( (zero !is nzero2));
+
+        // float !identity
+        assert(!(nan.f !is nan2.f));
+        assert( (nan.f is init2.f));
+        assert(!(init.f !is init2.f));
+        assert(!(zero.f !is zero2.f));
+        assert( (zero.f !is nzero2.f));
+
+        // .init identity
+        assert(W.init is W.init);
+
+        return 1;
+    }
+
+    auto rtF = test!float();
+    enum ctF = test!float();
+    auto rtD = test!double();
+    enum ctD = test!double();
+    auto rtR = test!real();
+    enum ctR = test!real();
+
+    assert(float.nan !is -float.nan);
+    assert(double.nan !is -double.nan);
+    assert(real.nan !is -real.nan);
+}
+
+/***************************************************/
+
 void test6545()
 {
     static int[] func()
@@ -8142,6 +8223,7 @@  int main()
     test155();
     test156();
     test658();
+    test3632();
     test4258();
     test4539();
     test4963();
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index b1da32e43e1..5e2566c9ceb 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@ 
-c52e28b723ccfbe845a95e8e7b528e3cc0b9d790
+9ba9a6ae2b8f6811cb85107cb56701df04f36ac6
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/druntime repository.
diff --git a/libphobos/libdruntime/core/int128.d b/libphobos/libdruntime/core/int128.d
index 2f628c03138..e4326fd54c3 100644
--- a/libphobos/libdruntime/core/int128.d
+++ b/libphobos/libdruntime/core/int128.d
@@ -18,15 +18,26 @@  alias I = long;
 alias U = ulong;
 enum Ubits = uint(U.sizeof * 8);
 
-align(16) struct Cent
+version (X86_64) private enum Cent_alignment = 16;
+else             private enum Cent_alignment = (size_t.sizeof * 2);
+
+align(Cent_alignment) struct Cent
 {
-    U lo;      // low 64 bits
-    U hi;      // high 64 bits
+    version (LittleEndian)
+    {
+        U lo;  // low 64 bits
+        U hi;  // high 64 bits
+    }
+    else
+    {
+        U hi;  // high 64 bits
+        U lo;  // low 64 bits
+    }
 }
 
-enum One = Cent(1);
-enum Zero = Cent();
-enum MinusOne = neg(One);
+enum Cent One = { lo:1 };
+enum Cent Zero = { lo:0 };
+enum Cent MinusOne = neg(One);
 
 /*****************************
  * Test against 0
@@ -320,7 +331,8 @@  Cent ror(Cent c, uint n)
 pure
 Cent and(Cent c1, Cent c2)
 {
-    return Cent(c1.lo & c2.lo, c1.hi & c2.hi);
+    const Cent ret = { lo:c1.lo & c2.lo, hi:c1.hi & c2.hi };
+    return ret;
 }
 
 /****************************
@@ -334,7 +346,8 @@  Cent and(Cent c1, Cent c2)
 pure
 Cent or(Cent c1, Cent c2)
 {
-    return Cent(c1.lo | c2.lo, c1.hi | c2.hi);
+    const Cent ret = { lo:c1.lo | c2.lo, hi:c1.hi | c2.hi };
+    return ret;
 }
 
 /****************************
@@ -348,7 +361,8 @@  Cent or(Cent c1, Cent c2)
 pure
 Cent xor(Cent c1, Cent c2)
 {
-    return Cent(c1.lo ^ c2.lo, c1.hi ^ c2.hi);
+    const Cent ret = { lo:c1.lo ^ c2.lo, hi:c1.hi ^ c2.hi };
+    return ret;
 }
 
 /****************************
@@ -363,7 +377,8 @@  pure
 Cent add(Cent c1, Cent c2)
 {
     U r = cast(U)(c1.lo + c2.lo);
-    return Cent(r, cast(U)(c1.hi + c2.hi + (r < c1.lo)));
+    const Cent ret = { lo:r, hi:cast(U)(c1.hi + c2.hi + (r < c1.lo)) };
+    return ret;
 }
 
 /****************************
@@ -419,9 +434,9 @@  Cent mul(Cent c1, Cent c2)
     const c1h1 = c1.hi >> mulshift;
     r3 = c1h1 * c2l0 + (r3 & mulmask);
 
-    return Cent((r0 & mulmask) + (r1 & mulmask) * (mulmask + 1),
-                (r2 & mulmask) + (r3 & mulmask) * (mulmask + 1));
-
+    const Cent ret = { lo:(r0 & mulmask) + (r1 & mulmask) * (mulmask + 1),
+                       hi:(r2 & mulmask) + (r3 & mulmask) * (mulmask + 1) };
+    return ret;
 }
 
 
@@ -523,8 +538,10 @@  Cent udivmod(Cent c1, Cent c2, out Cent modulus)
     if (c1.hi == 0 && c2.hi == 0)
     {
         // Single precision divide
-        modulus = Cent(c1.lo % c2.lo);
-        return Cent(c1.lo / c2.lo);
+        const Cent rem = { lo:c1.lo % c2.lo };
+        modulus = rem;
+        const Cent ret = { lo:c1.lo / c2.lo };
+        return ret;
     }
     if (c1.hi == 0)
     {
@@ -539,10 +556,11 @@  Cent udivmod(Cent c1, Cent c2, out Cent modulus)
         const q1 = (c1.hi < c2.lo) ? 0 : (c1.hi / c2.lo);
         if (q1)
             c1.hi = c1.hi % c2.lo;
-        U rem;
-        const q0 = udivmod128_64(c1, c2.lo, rem);
-        modulus = Cent(rem);
-        return Cent(q0, q1);
+        Cent rem;
+        const q0 = udivmod128_64(c1, c2.lo, rem.lo);
+        modulus = rem;
+        const Cent ret = { lo:q0, hi:q1 };
+        return ret;
     }
 
     // Full cent precision division.
@@ -560,10 +578,10 @@  Cent udivmod(Cent c1, Cent c2, out Cent modulus)
 
     // Get quotient from divide unsigned operation.
     U rem_ignored;
-    const q1 = udivmod128_64(u1, v1, rem_ignored);
+    const Cent q1 = { lo:udivmod128_64(u1, v1, rem_ignored) };
 
     // Undo normalization and division of c1 by 2.
-    Cent quotient = shr(shl(Cent(q1), shift), 63);
+    Cent quotient = shr(shl(q1, shift), 63);
 
     // Make quotient correct or too small by 1
     if (tst(quotient))
@@ -770,44 +788,44 @@  version (unittest)
 
 unittest
 {
-    const C0 = Zero;
-    const C1 = One;
-    const C2 = Cent(2);
-    const C3 = Cent(3);
-    const C5 = Cent(5);
-    const C10 = Cent(10);
-    const C20 = Cent(20);
-    const C30 = Cent(30);
-    const C100 = Cent(100);
-
-    const Cm1 =  neg(One);
-    const Cm3 =  neg(C3);
-    const Cm10 = neg(C10);
-
-    const C3_1 = Cent(1,3);
-    const C3_2 = Cent(2,3);
-    const C4_8  = Cent(8, 4);
-    const C5_0  = Cent(0, 5);
-    const C7_1 = Cent(1,7);
-    const C7_9 = Cent(9,7);
-    const C9_3 = Cent(3,9);
-    const C10_0 = Cent(0,10);
-    const C10_1 = Cent(1,10);
-    const C10_3 = Cent(3,10);
-    const C11_3 = Cent(3,11);
-    const C20_0 = Cent(0,20);
-    const C90_30 = Cent(30,90);
-
-    const Cm10_0 = inc(com(C10_0)); // Cent(0, -10);
-    const Cm10_1 = inc(com(C10_1)); // Cent(-1, -11);
-    const Cm10_3 = inc(com(C10_3)); // Cent(-3, -11);
-    const Cm20_0 = inc(com(C20_0)); // Cent(0, -20);
-
-    enum Cs_3 = Cent(3, I.min);
-
-    const Cbig_1 = Cent(0xa3ccac1832952398, 0xc3ac542864f652f8);
-    const Cbig_2 = Cent(0x5267b85f8a42fc20, 0);
-    const Cbig_3 = Cent(0xf0000000ffffffff, 0);
+    const Cent C0 = Zero;
+    const Cent C1 = One;
+    const Cent C2 = { lo:2 };
+    const Cent C3 = { lo:3 };
+    const Cent C5 = { lo:5 };
+    const Cent C10 = { lo:10 };
+    const Cent C20 = { lo:20 };
+    const Cent C30 = { lo:30 };
+    const Cent C100 = { lo:100 };
+
+    const Cent Cm1 =  neg(One);
+    const Cent Cm3 =  neg(C3);
+    const Cent Cm10 = neg(C10);
+
+    const Cent C3_1 = { lo:1, hi:3 };
+    const Cent C3_2 = { lo:2, hi:3 };
+    const Cent C4_8  = { lo:8, hi:4 };
+    const Cent C5_0  = { lo:0, hi:5 };
+    const Cent C7_1 = { lo:1, hi:7 };
+    const Cent C7_9 = { lo:9, hi:7 };
+    const Cent C9_3 = { lo:3, hi:9 };
+    const Cent C10_0 = { lo:0, hi:10 };
+    const Cent C10_1 = { lo:1, hi:10 };
+    const Cent C10_3 = { lo:3, hi:10 };
+    const Cent C11_3 = { lo:3, hi:11 };
+    const Cent C20_0 = { lo:0, hi:20 };
+    const Cent C90_30 = { lo:30, hi:90 };
+
+    const Cent Cm10_0 = inc(com(C10_0)); // Cent(lo=0,  hi=-10);
+    const Cent Cm10_1 = inc(com(C10_1)); // Cent(lo=-1, hi=-11);
+    const Cent Cm10_3 = inc(com(C10_3)); // Cent(lo=-3, hi=-11);
+    const Cent Cm20_0 = inc(com(C20_0)); // Cent(lo=0,  hi=-20);
+
+    enum Cent Cs_3 = { lo:3, hi:I.min };
+
+    const Cent Cbig_1 = { lo:0xa3ccac1832952398, hi:0xc3ac542864f652f8 };
+    const Cent Cbig_2 = { lo:0x5267b85f8a42fc20, hi:0 };
+    const Cent Cbig_3 = { lo:0xf0000000ffffffff, hi:0 };
 
     /************************/
 
@@ -893,12 +911,20 @@  unittest
     assert(div(mul(C90_30, C2), C2) == C90_30);
     assert(div(mul(C90_30, C2), C90_30) == C2);
 
-    assert(divmod(Cbig_1, Cbig_2, modulus) == Cent(0x4496aa309d4d4a2f, U.max));
-    assert(modulus == Cent(0xd83203d0fdc799b8, U.max));
-    assert(udivmod(Cbig_1, Cbig_2, modulus) == Cent(0x5fe0e9bace2bedad, 2));
-    assert(modulus == Cent(0x2c923125a68721f8, 0));
-    assert(div(Cbig_1, Cbig_3) == Cent(0xbfa6c02b5aff8b86, U.max));
-    assert(udiv(Cbig_1, Cbig_3) == Cent(0xd0b7d13b48cb350f, 0));
+    const Cent Cb1divb2 = { lo:0x4496aa309d4d4a2f, hi:U.max };
+    const Cent Cb1modb2 = { lo:0xd83203d0fdc799b8, hi:U.max };
+    assert(divmod(Cbig_1, Cbig_2, modulus) == Cb1divb2);
+    assert(modulus == Cb1modb2);
+
+    const Cent Cb1udivb2 = { lo:0x5fe0e9bace2bedad, hi:2 };
+    const Cent Cb1umodb2 = { lo:0x2c923125a68721f8, hi:0 };
+    assert(udivmod(Cbig_1, Cbig_2, modulus) == Cb1udivb2);
+    assert(modulus == Cb1umodb2);
+
+    const Cent Cb1divb3 = { lo:0xbfa6c02b5aff8b86, hi:U.max };
+    const Cent Cb1udivb3 = { lo:0xd0b7d13b48cb350f, hi:0 };
+    assert(div(Cbig_1, Cbig_3) == Cb1divb3);
+    assert(udiv(Cbig_1, Cbig_3) == Cb1udivb3);
 
     assert(mul(Cm10, C1) == Cm10);
     assert(mul(C1, Cm10) == Cm10);
diff --git a/libphobos/libdruntime/core/lifetime.d b/libphobos/libdruntime/core/lifetime.d
index 3a7c8e02b8a..47fed9dc514 100644
--- a/libphobos/libdruntime/core/lifetime.d
+++ b/libphobos/libdruntime/core/lifetime.d
@@ -1273,7 +1273,9 @@  void copyEmplace(S, T)(ref S source, ref T target) @system
         }
         else static if (__traits(hasCopyConstructor, T))
         {
-            emplace(cast(Unqual!(T)*) &target); // blit T.init
+            // https://issues.dlang.org/show_bug.cgi?id=22766
+            import core.internal.lifetime : emplaceInitializer;
+            emplaceInitializer(*(cast(Unqual!T*)&target));
             static if (__traits(isNested, T))
             {
                  // copy context pointer
@@ -1373,6 +1375,22 @@  void copyEmplace(S, T)(ref S source, ref T target) @system
     static assert(!__traits(compiles, copyEmplace(ss, t)));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=22766
+@system pure nothrow @nogc unittest
+{
+    static struct S
+    {
+        @disable this();
+        this(int) @safe pure nothrow @nogc{}
+        this(ref const(S) other) @safe pure nothrow @nogc {}
+    }
+
+    S s1 = S(1);
+    S s2 = void;
+    copyEmplace(s1, s2);
+    assert(s2 == S(1));
+}
+
 version (DigitalMars) version (X86) version (Posix) version = DMD_X86_Posix;
 
 // don't violate immutability for reference types
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 7306c1071a1..0feb0b01ef5 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@ 
-99e9c1b7741e0f4e6f2a8c14883c4828d092701d
+c0cc5e917db105301dd1199b4b3c854626526407
 
 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/etc/c/zlib.d b/libphobos/src/etc/c/zlib.d
index a1b20fedce9..73c6534d8da 100644
--- a/libphobos/src/etc/c/zlib.d
+++ b/libphobos/src/etc/c/zlib.d
@@ -2,15 +2,16 @@ 
 /* updated from 1.2.1 to 1.2.3 by Thomas Kuehne */
 /* updated from 1.2.3 to 1.2.8 by Dmitry Atamanov */
 /* updated from 1.2.8 to 1.2.11 by Iain Buclaw */
+/* updated from 1.2.11 to 1.2.12 by Brian Callahan */
 
 module etc.c.zlib;
 
 import core.stdc.config;
 
 /* zlib.h -- interface of the 'zlib' general purpose compression library
-  version 1.2.11, January 15th, 2017
+  version 1.2.12, March 11th, 2022
 
-  Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+  Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -42,8 +43,8 @@  nothrow:
 extern (C):
 
 // Those are extern(D) as they should be mangled
-extern(D) immutable string ZLIB_VERSION = "1.2.11";
-extern(D) immutable ZLIB_VERNUM = 0x12b0;
+extern(D) immutable string ZLIB_VERSION = "1.2.12";
+extern(D) immutable ZLIB_VERNUM = 0x12c0;
 
 /*
     The 'zlib' compression library provides in-memory compression and
@@ -566,8 +567,7 @@  int deflateInit2(z_streamp strm,
 }
 /*
      This is another version of deflateInit with more compression options.  The
-   fields next_in, zalloc, zfree and opaque must be initialized before by the
-   caller.
+   fields zalloc, zfree and opaque must be initialized before by the caller.
 
      The method parameter is the compression method.  It must be Z_DEFLATED in
    this version of the library.
@@ -586,7 +586,7 @@  int deflateInit2(z_streamp strm,
    with deflateInit2() with this initialization, or at least in that case use 9
    with inflateInit2().
 
-     windowBits can also be -8..-15 for raw deflate.  In this case, -windowBits
+     windowBits can also be -8 .. -15 for raw deflate.  In this case, -windowBits
    determines the window size.  deflate() will then generate raw deflate data
    with no zlib header or trailer, and will not compute a check value.
 
@@ -728,11 +728,12 @@  int deflateParams(z_streamp strm, int level, int strategy);
    used to switch between compression and straight copy of the input data, or
    to switch to a different kind of input data requiring a different strategy.
    If the compression approach (which is a function of the level) or the
-   strategy is changed, and if any input has been consumed in a previous
-   deflate() call, then the input available so far is compressed with the old
-   level and strategy using deflate(strm, Z_BLOCK).  There are three approaches
-   for the compression levels 0, 1 .. 3, and 4 .. 9 respectively.  The new level
-   and strategy will take effect at the next call of deflate().
+   strategy is changed, and if there have been any deflate() calls since the
+   state was initialized or reset, then the input available so far is
+   compressed with the old level and strategy using deflate(strm, Z_BLOCK).
+   There are three approaches for the compression levels 0, 1 .. 3, and 4 .. 9
+   respectively.  The new level and strategy will take effect at the next call
+   of deflate().
 
      If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does
    not have enough output space to complete, then the parameter change will not
@@ -856,7 +857,7 @@  int inflateInit2(z_streamp strm, int windowBits)
      windowBits can also be zero to request that inflate use the window size in
    the zlib header of the compressed stream.
 
-     windowBits can also be -8..-15 for raw inflate.  In this case, -windowBits
+     windowBits can also be -8 .. -15 for raw inflate.  In this case, -windowBits
    determines the window size.  inflate() will then process raw deflate data,
    not looking for a zlib or gzip header, not generating a check value, and not
    looking for any check values for comparison at the end of the stream.  This
@@ -873,9 +874,11 @@  int inflateInit2(z_streamp strm, int windowBits)
    detection, or add 16 to decode only the gzip format (the zlib format will
    return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a
    CRC-32 instead of an Adler-32.  Unlike the gunzip utility and gzread() (see
-   below), inflate() will not automatically decode concatenated gzip streams.
-   inflate() will return Z_STREAM_END at the end of the gzip stream.  The state
-   would need to be reset to continue decoding a subsequent gzip stream.
+   below), inflate() will *not* automatically decode concatenated gzip members.
+   inflate() will return Z_STREAM_END at the end of the gzip member.  The state
+   would need to be reset to continue decoding a subsequent gzip member.  This
+   *must* be done if there is more data after a gzip member, in order for the
+   decompression to be compliant with the gzip standard (RFC 1952).
 
      inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
    memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
@@ -1311,14 +1314,14 @@  alias z_size_t = size_t;
 
 gzFile gzopen(const(char)* path, const(char)* mode);
 /*
-     Opens a gzip (.gz) file for reading or writing.  The mode parameter is as
-   in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
-   a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
-   compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
-   for fixed code compression as in "wb9F".  (See the description of
-   deflateInit2 for more information about the strategy parameter.)  'T' will
-   request transparent writing or appending with no compression and not using
-   the gzip format.
+     Open the gzip (.gz) file at path for reading and decompressing, or
+   compressing and writing.  The mode parameter is as in fopen ("rb" or "wb")
+   but can also include a compression level ("wb9") or a strategy: 'f' for
+   filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h",
+   'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression
+   as in "wb9F".  (See the description of deflateInit2 for more information
+   about the strategy parameter.)  'T' will request transparent writing or
+   appending with no compression and not using the gzip format.
 
      "a" can be used instead of "w" to request that the gzip stream that will
    be written be appended to the file.  "+" will result in an error, since
@@ -1348,9 +1351,9 @@  gzFile gzopen(const(char)* path, const(char)* mode);
 
 gzFile gzdopen(int fd, const(char)* mode);
 /*
-     gzdopen associates a gzFile with the file descriptor fd.  File descriptors
-   are obtained from calls like open, dup, creat, pipe or fileno (if the file
-   has been previously opened with fopen).  The mode parameter is as in gzopen.
+     Associate a gzFile with the file descriptor fd.  File descriptors are
+   obtained from calls like open, dup, creat, pipe or fileno (if the file has
+   been previously opened with fopen).  The mode parameter is as in gzopen.
 
      The next call of gzclose on the returned gzFile will also close the file
    descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
@@ -1371,13 +1374,13 @@  gzFile gzdopen(int fd, const(char)* mode);
 
 int gzbuffer(gzFile file, uint size);
 /*
-     Set the internal buffer size used by this library's functions.  The
-   default buffer size is 8192 bytes.  This function must be called after
-   gzopen() or gzdopen(), and before any other calls that read or write the
-   file.  The buffer memory allocation is always deferred to the first read or
-   write.  Three times that size in buffer space is allocated.  A larger buffer
-   size of, for example, 64K or 128K bytes will noticeably increase the speed
-   of decompression (reading).
+     Set the internal buffer size used by this library's functions for file to
+   size.  The default buffer size is 8192 bytes.  This function must be called
+   after gzopen() or gzdopen(), and before any other calls that read or write
+   the file.  The buffer memory allocation is always deferred to the first read
+   or write.  Three times that size in buffer space is allocated.  A larger
+   buffer size of, for example, 64K or 128K bytes will noticeably increase the
+   speed of decompression (reading).
 
      The new buffer size also affects the maximum length for gzprintf().
 
@@ -1387,9 +1390,9 @@  int gzbuffer(gzFile file, uint size);
 
 int gzsetparams(gzFile file, int level, int strategy);
 /*
-     Dynamically update the compression level or strategy.  See the description
-   of deflateInit2 for the meaning of these parameters.  Previously provided
-   data is flushed before the parameter change.
+     Dynamically update the compression level and strategy for file.  See the
+   description of deflateInit2 for the meaning of these parameters. Previously
+   provided data is flushed before applying the parameter changes.
 
      gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not
    opened for writing, Z_ERRNO if there is an error writing the flushed data,
@@ -1398,7 +1401,7 @@  int gzsetparams(gzFile file, int level, int strategy);
 
 int gzread(gzFile file, void* buf, uint len);
 /*
-     Reads the given number of uncompressed bytes from the compressed file.  If
+     Read and decompress up to len uncompressed bytes from file into buf.  If
    the input file is not in gzip format, gzread copies the given number of
    bytes into the buffer directly from the file.
 
@@ -1428,11 +1431,11 @@  int gzread(gzFile file, void* buf, uint len);
 
 z_size_t gzfread(void* buf, z_size_t size, z_size_t nitems, gzFile file);
 /*
-     Read up to nitems items of size size from file to buf, otherwise operating
-   as gzread() does.  This duplicates the interface of stdio's fread(), with
-   size_t request and return types.  If the library defines size_t, then
-   z_size_t is identical to size_t.  If not, then z_size_t is an unsigned
-   integer type that can contain a pointer.
+     Read and decompress up to nitems items of size size from file into buf,
+   otherwise operating as gzread() does.  This duplicates the interface of
+   stdio's fread(), with size_t request and return types.  If the library
+   defines size_t, then z_size_t is identical to size_t.  If not, then z_size_t
+   is an unsigned integer type that can contain a pointer.
 
      gzfread() returns the number of full items read of size size, or zero if
    the end of the file was reached and a full item could not be read, or if
@@ -1453,14 +1456,13 @@  z_size_t gzfread(void* buf, z_size_t size, z_size_t nitems, gzFile file);
 
 int gzwrite(gzFile file, void* buf, uint len);
 /*
-     Writes the given number of uncompressed bytes into the compressed file.
-   gzwrite returns the number of uncompressed bytes written or 0 in case of
-   error.
+     Compress and write the len uncompressed bytes at buf to file. gzwrite
+   returns the number of uncompressed bytes written or 0 in case of error.
 */
 
 z_size_t gzfwrite(void* buf, z_size_t size, z_size_t nitems, gzFile file);
 /*
-     gzfwrite() writes nitems items of size size from buf to file, duplicating
+     Compress and write nitems items of size size from buf to file, duplicating
    the interface of stdio's fwrite(), with size_t request and return types.  If
    the library defines size_t, then z_size_t is identical to size_t.  If not,
    then z_size_t is an unsigned integer type that can contain a pointer.
@@ -1473,22 +1475,22 @@  z_size_t gzfwrite(void* buf, z_size_t size, z_size_t nitems, gzFile file);
 
 int gzprintf(gzFile file, const(char)* format, ...);
 /*
-     Converts, formats, and writes the arguments to the compressed file under
-   control of the format string, as in fprintf.  gzprintf returns the number of
+     Convert, format, compress, and write the arguments (...) to file under
+   control of the string format, as in fprintf.  gzprintf returns the number of
    uncompressed bytes actually written, or a negative zlib error code in case
    of error.  The number of uncompressed bytes written is limited to 8191, or
    one less than the buffer size given to gzbuffer().  The caller should assure
    that this limit is not exceeded.  If it is exceeded, then gzprintf() will
    return an error (0) with nothing written.  In this case, there may also be a
    buffer overflow with unpredictable consequences, which is possible only if
-   zlib was compiled with the insecure functions sprintf() or vsprintf()
+   zlib was compiled with the insecure functions sprintf() or vsprintf(),
    because the secure snprintf() or vsnprintf() functions were not available.
    This can be determined using zlibCompileFlags().
 */
 
 int gzputs(gzFile file, const(char)* s);
 /*
-     Writes the given null-terminated string to the compressed file, excluding
+     Compress and write the given null-terminated string s to file, excluding
    the terminating null character.
 
      gzputs returns the number of characters written, or -1 in case of error.
@@ -1496,11 +1498,12 @@  int gzputs(gzFile file, const(char)* s);
 
 const(char)* gzgets(gzFile file, const(char)* buf, int len);
 /*
-     Reads bytes from the compressed file until len-1 characters are read, or a
-   newline character is read and transferred to buf, or an end-of-file
-   condition is encountered.  If any characters are read or if len == 1, the
-   string is terminated with a null character.  If no characters are read due
-   to an end-of-file or len < 1, then the buffer is left untouched.
+     Read and decompress bytes from file into buf, until len-1 characters are
+   read, or until a newline character is read and transferred to buf, or an
+   end-of-file condition is encountered.  If any characters are read or if len
+   is one, the string is terminated with a null character.  If no characters
+   are read due to an end-of-file or len is less than one, then the buffer is
+   left untouched.
 
      gzgets returns buf which is a null-terminated string, or it returns NULL
    for end-of-file or in case of error.  If there was an error, the contents at
@@ -1509,13 +1512,13 @@  const(char)* gzgets(gzFile file, const(char)* buf, int len);
 
 int gzputc(gzFile file, int c);
 /*
-     Writes c, converted to an unsigned char, into the compressed file.  gzputc
+     Compress and write c, converted to an unsigned char, into file.  gzputc
    returns the value that was written, or -1 in case of error.
 */
 
 int gzgetc(gzFile file);
 /*
-     Reads one byte from the compressed file.  gzgetc returns this byte or -1
+     Read and decompress one byte from file.  gzgetc returns this byte or -1
    in case of end of file or error.  This is implemented as a macro for speed.
    As such, it does not do all of the checking the other functions do.  I.e.
    it does not check to see if file is NULL, nor whether the structure file
@@ -1524,8 +1527,8 @@  int gzgetc(gzFile file);
 
 int gzungetc(int c, gzFile file);
 /*
-     Push one character back onto the stream to be read as the first character
-   on the next read.  At least one character of push-back is allowed.
+     Push c back onto the stream for file to be read as the first character on
+   the next read.  At least one character of push-back is always allowed.
    gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will
    fail if c is -1, and may fail if a character has been pushed but not read
    yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the
@@ -1536,9 +1539,9 @@  int gzungetc(int c, gzFile file);
 
 int gzflush(gzFile file, int flush);
 /*
-     Flushes all pending output into the compressed file.  The parameter flush
-   is as in the deflate() function.  The return value is the zlib error number
-   (see function gzerror below).  gzflush is only permitted when writing.
+     Flush all pending output to file.  The parameter flush is as in the
+   deflate() function.  The return value is the zlib error number (see function
+   gzerror below).  gzflush is only permitted when writing.
 
      If the flush parameter is Z_FINISH, the remaining data is written and the
    gzip stream is completed in the output.  If gzwrite() is called again, a new
@@ -1551,8 +1554,8 @@  int gzflush(gzFile file, int flush);
 
 z_off_t gzseek(gzFile file, z_off_t offset, int whence);
 /*
-     Sets the starting position for the next gzread or gzwrite on the given
-   compressed file.  The offset represents a number of bytes in the
+     Set the starting position to offset relative to whence for the next gzread
+   or gzwrite on file.  The offset represents a number of bytes in the
    uncompressed data stream.  The whence parameter is defined as in lseek(2);
    the value SEEK_END is not supported.
 
@@ -1569,39 +1572,39 @@  z_off_t gzseek(gzFile file, z_off_t offset, int whence);
 
 int gzrewind(gzFile file);
 /*
-     Rewinds the given file. This function is supported only for reading.
+     Rewind file. This function is supported only for reading.
 
-     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET).
 */
 
 z_off_t gztell(gzFile file);
 /*
-     Returns the starting position for the next gzread or gzwrite on the given
-   compressed file.  This position represents a number of bytes in the
-   uncompressed data stream, and is zero when starting, even if appending or
-   reading a gzip stream from the middle of a file using gzdopen().
+     Return the starting position for the next gzread or gzwrite on file.
+   This position represents a number of bytes in the uncompressed data stream,
+   and is zero when starting, even if appending or reading a gzip stream from
+   the middle of a file using gzdopen().
 
      gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
 */
 
 z_off_t gzoffset(gzFile file);
 /*
-     Returns the current offset in the file being read or written.  This offset
-   includes the count of bytes that precede the gzip stream, for example when
-   appending or when using gzdopen() for reading.  When reading, the offset
-   does not include as yet unused buffered input.  This information can be used
-   for a progress indicator.  On error, gzoffset() returns -1.
+     Return the current compressed (actual) read or write offset of file.  This
+   offset includes the count of bytes that precede the gzip stream, for example
+   when appending or when using gzdopen() for reading.  When reading, the
+   offset does not include as yet unused buffered input.  This information can
+   be used for a progress indicator.  On error, gzoffset() returns -1.
 */
 
 int gzeof(gzFile file);
 /*
-     Returns true (1) if the end-of-file indicator has been set while reading,
-   false (0) otherwise.  Note that the end-of-file indicator is set only if the
-   read tried to go past the end of the input, but came up short.  Therefore,
-   just like feof(), gzeof() may return false even if there is no more data to
-   read, in the event that the last read request was for the exact number of
-   bytes remaining in the input file.  This will happen if the input file size
-   is an exact multiple of the buffer size.
+     Return true (1) if the end-of-file indicator for file has been set while
+   reading, false (0) otherwise.  Note that the end-of-file indicator is set
+   only if the read tried to go past the end of the input, but came up short.
+   Therefore, just like feof(), gzeof() may return false even if there is no
+   more data to read, in the event that the last read request was for the exact
+   number of bytes remaining in the input file.  This will happen if the input
+   file size is an exact multiple of the buffer size.
 
      If gzeof() returns true, then the read functions will return no more data,
    unless the end-of-file indicator is reset by gzclearerr() and the input file
@@ -1610,7 +1613,7 @@  int gzeof(gzFile file);
 
 int gzdirect(gzFile file);
 /*
-     Returns true (1) if file is being copied directly while reading, or false
+     Return true (1) if file is being copied directly while reading, or false
    (0) if file is a gzip stream being decompressed.
 
      If the input file is empty, gzdirect() will return true, since the input
@@ -1631,8 +1634,8 @@  int gzdirect(gzFile file);
 
 int gzclose(gzFile file);
 /*
-     Flushes all pending output if necessary, closes the compressed file and
-   deallocates the (de)compression state.  Note that once file is closed, you
+     Flush all pending output for file, if necessary, close file and
+   deallocate the (de)compression state.  Note that once file is closed, you
    cannot call gzerror with file, since its structures have been deallocated.
    gzclose must not be called more than once on the same file, just as free
    must not be called more than once on the same allocation.
@@ -1656,10 +1659,10 @@  int gzclose_w(gzFile file);
 
 const(char)* gzerror(gzFile file, int* errnum);
 /*
-     Returns the error message for the last error which occurred on the given
-   compressed file.  errnum is set to zlib error number.  If an error occurred
-   in the file system and not in the compression library, errnum is set to
-   Z_ERRNO and the application may consult errno to get the exact error code.
+     Return the error message for the last error which occurred on file.
+   errnum is set to zlib error number.  If an error occurred in the file system
+   and not in the compression library, errnum is set to Z_ERRNO and the
+   application may consult errno to get the exact error code.
 
      The application must not modify the returned string.  Future calls to
    this function may invalidate the previously returned string.  If file is
@@ -1672,7 +1675,7 @@  const(char)* gzerror(gzFile file, int* errnum);
 
 void gzclearerr(gzFile file);
 /*
-     Clears the error and end-of-file flags for file.  This is analogous to the
+     Clear the error and end-of-file flags for file.  This is analogous to the
    clearerr() function in stdio.  This is useful for continuing to read a gzip
    file that is being written concurrently.
 */
@@ -1688,8 +1691,9 @@  void gzclearerr(gzFile file);
 uint adler32(uint adler, const(ubyte)* buf, uint len);
 /*
      Update a running Adler-32 checksum with the bytes buf[0 .. len-1] and
-   return the updated checksum.  If buf is Z_NULL, this function returns the
-   required initial value for the checksum.
+   return the updated checksum. An Adler-32 value is in the range of a 32-bit
+   unsigned integer. If buf is Z_NULL, this function returns the required
+   initial value for the checksum.
 
      An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed
    much faster.
@@ -1722,9 +1726,10 @@  uint adler32_combine(uint adler1, uint adler2, z_off_t len2);
 uint crc32(uint crc, const(ubyte)* buf, uint len);
 /*
      Update a running CRC-32 with the bytes buf[0 .. len-1] and return the
-   updated CRC-32.  If buf is Z_NULL, this function returns the required
-   initial value for the crc.  Pre- and post-conditioning (one's complement) is
-   performed within this function so it shouldn't be done by the application.
+   updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer.
+   If buf is Z_NULL, this function returns the required initial value for the
+   crc. Pre- and post-conditioning (one's complement) is performed within this
+   function so it shouldn't be done by the application.
 
    Usage example:
 
@@ -1736,7 +1741,7 @@  uint crc32(uint crc, const(ubyte)* buf, uint len);
      if (crc != original_crc) error();
 */
 
-uint crc32_z(uint adler, const(ubyte)* buf, z_size_t len);
+uint crc32_z(uint crc, const(ubyte)* buf, z_size_t len);
 /*
      Same as crc32(), but with a size_t length.
 */
@@ -1751,6 +1756,18 @@  uint crc32_combine(uint crc1, uint crc2, z_off_t len2);
    len2.
 */
 
+uint crc32_combine_gen(z_off_t len2);
+/*
+     Return the operator corresponding to length len2, to be used with
+   crc32_combine_op().
+*/
+
+uint crc32_combine_op(uint crc1, uint crc2, uint op);
+/*
+     Give the same result as crc32_combine(), using op in place of len2. op is
+   is generated from len2 by crc32_combine_gen(). This will be faster than
+   crc32_combine() if the generated op is used more than once.
+*/
 
                         /* various hacks, don't look :) */
 
diff --git a/libphobos/src/std/regex/internal/parser.d b/libphobos/src/std/regex/internal/parser.d
index 41ca6872a7a..448bb99a9a1 100644
--- a/libphobos/src/std/regex/internal/parser.d
+++ b/libphobos/src/std/regex/internal/parser.d
@@ -841,7 +841,7 @@  if (isForwardRange!R && is(ElementType!R : dchar))
         switch (front)
         {
         case '*', '?', '+', '|', '{', '}':
-            error("'*', '+', '?', '{', '}' not allowed in atom");
+            return error("'*', '+', '?', '{', '}' not allowed in atom");
         case '.':
             if (re_flags & RegexOption.singleline)
                 g.put(Bytecode(IR.Any, 0));