diff mbox series

[committed] d: Merge upstream dmd, druntime 2bbf64907c, phobos b64bfbf91

Message ID 20231211100710.348988-1-ibuclaw@gdcproject.org
State New
Headers show
Series [committed] d: Merge upstream dmd, druntime 2bbf64907c, phobos b64bfbf91 | expand

Commit Message

Iain Buclaw Dec. 11, 2023, 10:07 a.m. UTC
Hi,

This patch merges the D front-end and runtime library with upstream dmd
2bbf64907c, and the standard library with phobos b64bfbf91.

Synchronizing with the upstream release of v2.106.0.

D front-end changes:

    - Import dmd v2.106.0.

D runtime changes:

    - Import druntime v2.106.0.

Phobos changes:

    - Import phobos v2.106.0.

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

Regards,
Iain.

---
gcc/d/ChangeLog:

	* Make-lang.in (D_FRONTEND_OBJS): Rename d/common-string.o to
	d/common-smallbuffer.o.
	* dmd/MERGE: Merge upstream dmd 2bbf64907c.
	* dmd/VERSION: Bump version to v2.106.0.
	* modules.cc (layout_moduleinfo_fields): Update for new front-end
	interface.
	(layout_moduleinfo): Likewise.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime 2bbf64907c.
	* src/MERGE: Merge upstream phobos b64bfbf91.
---
 gcc/d/Make-lang.in                            |   2 +-
 gcc/d/dmd/MERGE                               |   2 +-
 gcc/d/dmd/VERSION                             |   2 +-
 gcc/d/dmd/aggregate.d                         |  10 -
 gcc/d/dmd/aggregate.h                         |   1 -
 gcc/d/dmd/attrib.d                            |  67 ------
 gcc/d/dmd/attrib.h                            |   9 -
 gcc/d/dmd/canthrow.d                          |   2 +-
 gcc/d/dmd/common/README.md                    |   2 +-
 gcc/d/dmd/common/file.d                       |  15 +-
 gcc/d/dmd/common/{string.d => smallbuffer.d}  |  49 ++--
 gcc/d/dmd/cparse.d                            |   8 +
 gcc/d/dmd/dcast.d                             |  12 +-
 gcc/d/dmd/denum.d                             |   7 -
 gcc/d/dmd/dimport.d                           |  16 --
 gcc/d/dmd/dmodule.d                           |  36 ++-
 gcc/d/dmd/dsymbol.d                           | 172 --------------
 gcc/d/dmd/dsymbol.h                           |   5 +-
 gcc/d/dmd/dsymbolsem.d                        | 214 +++++++++++++++++
 gcc/d/dmd/dtemplate.d                         |   7 +-
 gcc/d/dmd/enum.h                              |   1 -
 gcc/d/dmd/escape.d                            |   2 +-
 gcc/d/dmd/expressionsem.d                     |   2 +-
 gcc/d/dmd/hdrgen.d                            |  27 +++
 gcc/d/dmd/import.h                            |   1 -
 gcc/d/dmd/initsem.d                           |  20 +-
 gcc/d/dmd/module.h                            |   1 +
 gcc/d/dmd/nspace.d                            |  14 --
 gcc/d/dmd/nspace.h                            |   1 -
 gcc/d/dmd/parse.d                             |  12 +-
 gcc/d/dmd/root/file.d                         |   2 +-
 gcc/d/dmd/root/filename.d                     |   4 +-
 gcc/d/dmd/root/speller.d                      |   2 +-
 gcc/d/dmd/root/string.d                       |   2 +-
 gcc/d/dmd/typesem.d                           |  58 +++++
 gcc/d/modules.cc                              |   4 +-
 .../fail_compilation/misc_parser_err_cov1.d   |   2 +-
 gcc/testsuite/gdc.test/runnable/dbitfields.d  |  34 +++
 libphobos/libdruntime/MERGE                   |   2 +-
 libphobos/libdruntime/core/cpuid.d            |   7 +-
 libphobos/src/MERGE                           |   2 +-
 libphobos/src/std/algorithm/searching.d       | 218 +++++++-----------
 libphobos/src/std/conv.d                      |   5 +-
 libphobos/src/std/range/package.d             |  24 +-
 libphobos/src/std/uni/package.d               |  12 +
 45 files changed, 579 insertions(+), 518 deletions(-)
 rename gcc/d/dmd/common/{string.d => smallbuffer.d} (82%)

Comments

Iain Buclaw Dec. 11, 2023, 10:13 a.m. UTC | #1
Excerpts from Iain Buclaw's message of Dezember 11, 2023 11:07 am:
> Hi,
> 
> This patch merges the D front-end and runtime library with upstream dmd
> 2bbf64907c, and the standard library with phobos b64bfbf91.
> 
> Synchronizing with the upstream release of v2.106.0.
> 

...

> diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
> index 67305922df6..5a608a9986d 100644
> --- a/gcc/d/dmd/canthrow.d
> +++ b/gcc/d/dmd/canthrow.d
> @@ -22,7 +22,6 @@ import dmd.declaration;
>  import dmd.dsymbol;
>  import dmd.errorsink;
>  import dmd.expression;
> -import dmd.expressionsem;
>  import dmd.func;
>  import dmd.globals;
>  import dmd.init;
> @@ -81,6 +80,7 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink)
>                      if (!f.isDtorDeclaration())
>                          errorSupplementalInferredAttr(f, 10, false, STC.nothrow_);
>  
> +                    import dmd.expressionsem : checkOverriddenDtor;
>                      f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
>                  }
>                  else if (func)
> 

Hi Rainer,

This specific change that moves an import from toplevel to local should
fix that linker problem when using gdc-9 for bootstrapping.

Iain.
Thomas Schwinge Dec. 12, 2023, 8:25 a.m. UTC | #2
Hi Iain!

On 2023-12-11T11:13:28+0100, Iain Buclaw <ibuclaw@gdcproject.org> wrote:
> Excerpts from Iain Buclaw's message of Dezember 11, 2023 11:07 am:
>> This patch merges the D front-end and runtime library with upstream dmd
>> 2bbf64907c, and the standard library with phobos b64bfbf91.
>>
>> Synchronizing with the upstream release of v2.106.0.

> [...] should
> fix that linker problem when using gdc-9 for bootstrapping.

Thanks, confirmed for x86_64-pc-linux-gnu bootstrap with GCC 9.


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955
Rainer Orth Dec. 14, 2023, 3:09 p.m. UTC | #3
Thomas Schwinge <thomas@codesourcery.com> writes:

> On 2023-12-11T11:13:28+0100, Iain Buclaw <ibuclaw@gdcproject.org> wrote:
>> Excerpts from Iain Buclaw's message of Dezember 11, 2023 11:07 am:
>>> This patch merges the D front-end and runtime library with upstream dmd
>>> 2bbf64907c, and the standard library with phobos b64bfbf91.
>>>
>>> Synchronizing with the upstream release of v2.106.0.
>
>> [...] should
>> fix that linker problem when using gdc-9 for bootstrapping.
>
> Thanks, confirmed for x86_64-pc-linux-gnu bootstrap with GCC 9.

Also confirmed on i386-pc-solaris2.11 and sparc-sun-solaris2.11.

Thanks.
        Rainer
diff mbox series

Patch

diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in
index b3007a96bd0..a0d4d7cbeb4 100644
--- a/gcc/d/Make-lang.in
+++ b/gcc/d/Make-lang.in
@@ -95,7 +95,7 @@  D_FRONTEND_OBJS = \
 	d/common-bitfields.o \
 	d/common-file.o \
 	d/common-outbuffer.o \
-	d/common-string.o \
+	d/common-smallbuffer.o \
 	d/compiler.o \
 	d/cond.o \
 	d/constfold.o \
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index aa0062c10eb..5edcee1c84d 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@ 
-ff57fec51558013b25cadb7e83da9f4675915d56
+2bbf64907cbbb483d003e0a8fcf8b502e4883799
 
 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/VERSION b/gcc/d/dmd/VERSION
index 41fdc654b14..8c95cd04f80 100644
--- a/gcc/d/dmd/VERSION
+++ b/gcc/d/dmd/VERSION
@@ -1 +1 @@ 
-v2.106.0-rc.1
+v2.106.0
diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d
index 307bb0171c4..352ca88f470 100644
--- a/gcc/d/dmd/aggregate.d
+++ b/gcc/d/dmd/aggregate.d
@@ -178,16 +178,6 @@  extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
         return sc2;
     }
 
-    override final void setScope(Scope* sc)
-    {
-        // Might need a scope to resolve forward references. The check for
-        // semanticRun prevents unnecessary setting of _scope during deferred
-        // setScope phases for aggregates which already finished semantic().
-        // See https://issues.dlang.org/show_bug.cgi?id=16607
-        if (semanticRun < PASS.semanticdone)
-            ScopeDsymbol.setScope(sc);
-    }
-
     /***************************************
      * Returns:
      *      The total number of fields minus the number of hidden fields.
diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h
index cd8f1a15fbd..98fa6bd1bb0 100644
--- a/gcc/d/dmd/aggregate.h
+++ b/gcc/d/dmd/aggregate.h
@@ -113,7 +113,6 @@  public:
     Sizeok sizeok;              // set when structsize contains valid data
 
     virtual Scope *newScope(Scope *sc);
-    void setScope(Scope *sc) override final;
     virtual void finalizeSize() = 0;
     uinteger_t size(const Loc &loc) override final;
     bool fill(const Loc &loc, Expressions &elements, bool ctorinit);
diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d
index 251e2e88ca5..faf04890e8e 100644
--- a/gcc/d/dmd/attrib.d
+++ b/gcc/d/dmd/attrib.d
@@ -123,19 +123,6 @@  extern (C++) abstract class AttribDeclaration : Dsymbol
         return sc;
     }
 
-    override void setScope(Scope* sc)
-    {
-        Dsymbols* d = include(sc);
-        //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
-        if (d)
-        {
-            Scope* sc2 = newScope(sc);
-            d.foreachDsymbol( s => s.setScope(sc2) );
-            if (sc2 != sc)
-                sc2.pop();
-        }
-    }
-
     override void importAll(Scope* sc)
     {
         Dsymbols* d = include(sc);
@@ -338,14 +325,6 @@  extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration
         return scx;
     }
 
-    override void setScope(Scope* sc)
-    {
-        //printf("DeprecatedDeclaration::setScope() %p\n", this);
-        if (decl)
-            Dsymbol.setScope(sc); // for forward reference
-        return AttribDeclaration.setScope(sc);
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -433,13 +412,6 @@  extern (C++) final class CPPMangleDeclaration : AttribDeclaration
             sc.aligndecl, sc.inlining);
     }
 
-    override void setScope(Scope* sc)
-    {
-        if (decl)
-            Dsymbol.setScope(sc); // for forward reference
-        return AttribDeclaration.setScope(sc);
-    }
-
     override const(char)* toChars() const
     {
         return toString().ptr;
@@ -703,13 +675,6 @@  extern (C++) final class AnonDeclaration : AttribDeclaration
         return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl));
     }
 
-    override void setScope(Scope* sc)
-    {
-        if (decl)
-            Dsymbol.setScope(sc);
-        return AttribDeclaration.setScope(sc);
-    }
-
     override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
     {
         //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this);
@@ -913,11 +878,6 @@  extern (C++) class ConditionalDeclaration : AttribDeclaration
         }
     }
 
-    override void setScope(Scope* sc)
-    {
-        include(sc).foreachDsymbol( s => s.setScope(sc) );
-    }
-
     override void accept(Visitor v)
     {
         v.visit(this);
@@ -983,13 +943,6 @@  extern (C++) final class StaticIfDeclaration : ConditionalDeclaration
         }
     }
 
-    override void setScope(Scope* sc)
-    {
-        // do not evaluate condition before semantic pass
-        // But do set the scope, in case we need it for forward referencing
-        Dsymbol.setScope(sc);
-    }
-
     override void importAll(Scope* sc)
     {
         // do not evaluate condition before semantic pass
@@ -1104,13 +1057,6 @@  extern (C++) final class StaticForeachDeclaration : AttribDeclaration
         // change this to give semantics to documentation comments on static foreach declarations
     }
 
-    override void setScope(Scope* sc)
-    {
-        // do not evaluate condition before semantic pass
-        // But do set the scope, in case we need it for forward referencing
-        Dsymbol.setScope(sc);
-    }
-
     override void importAll(Scope* sc)
     {
         // do not evaluate aggregate before semantic pass
@@ -1209,11 +1155,6 @@  extern (C++) final class MixinDeclaration : AttribDeclaration
         return new MixinDeclaration(loc, Expression.arraySyntaxCopy(exps));
     }
 
-    override void setScope(Scope* sc)
-    {
-        Dsymbol.setScope(sc);
-    }
-
     override const(char)* kind() const
     {
         return "mixin";
@@ -1264,14 +1205,6 @@  extern (C++) final class UserAttributeDeclaration : AttribDeclaration
         return sc2;
     }
 
-    override void setScope(Scope* sc)
-    {
-        //printf("UserAttributeDeclaration::setScope() %p\n", this);
-        if (decl)
-            Dsymbol.setScope(sc); // for forward reference of UDAs
-        return AttribDeclaration.setScope(sc);
-    }
-
     extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2)
     {
         Expressions* udas;
diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h
index efea9af950c..98c5e521977 100644
--- a/gcc/d/dmd/attrib.h
+++ b/gcc/d/dmd/attrib.h
@@ -26,7 +26,6 @@  public:
 
     virtual Dsymbols *include(Scope *sc);
     virtual Scope *newScope(Scope *sc);
-    void setScope(Scope *sc) override;
     void importAll(Scope *sc) override;
     void addComment(const utf8_t *comment) override;
     const char *kind() const override;
@@ -61,7 +60,6 @@  public:
 
     DeprecatedDeclaration *syntaxCopy(Dsymbol *s) override;
     Scope *newScope(Scope *sc) override;
-    void setScope(Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -84,7 +82,6 @@  public:
 
     CPPMangleDeclaration *syntaxCopy(Dsymbol *s) override;
     Scope *newScope(Scope *sc) override;
-    void setScope(Scope *sc) override;
     const char *toChars() const override;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -135,7 +132,6 @@  public:
     unsigned anonalignsize;     // size of anonymous struct for alignment purposes
 
     AnonDeclaration *syntaxCopy(Dsymbol *s) override;
-    void setScope(Scope *sc) override;
     void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override;
     const char *kind() const override;
     AnonDeclaration *isAnonDeclaration() override { return this; }
@@ -163,7 +159,6 @@  public:
     bool oneMember(Dsymbol **ps, Identifier *ident) override final;
     Dsymbols *include(Scope *sc) override;
     void addComment(const utf8_t *comment) override final;
-    void setScope(Scope *sc) override;
     void accept(Visitor *v) override { v->visit(this); }
 };
 
@@ -176,7 +171,6 @@  public:
 
     StaticIfDeclaration *syntaxCopy(Dsymbol *s) override;
     Dsymbols *include(Scope *sc) override;
-    void setScope(Scope *sc) override;
     void importAll(Scope *sc) override;
     StaticIfDeclaration *isStaticIfDeclaration() override { return this; }
     const char *kind() const override;
@@ -196,7 +190,6 @@  public:
     bool oneMember(Dsymbol **ps, Identifier *ident) override;
     Dsymbols *include(Scope *sc) override;
     void addComment(const utf8_t *comment) override;
-    void setScope(Scope *sc) override;
     void importAll(Scope *sc) override;
     const char *kind() const override;
     void accept(Visitor *v) override { v->visit(this); }
@@ -223,7 +216,6 @@  public:
     d_bool compiled;
 
     MixinDeclaration *syntaxCopy(Dsymbol *s) override;
-    void setScope(Scope *sc) override;
     const char *kind() const override;
     void accept(Visitor *v) override { v->visit(this); }
 };
@@ -239,7 +231,6 @@  public:
 
     UserAttributeDeclaration *syntaxCopy(Dsymbol *s) override;
     Scope *newScope(Scope *sc) override;
-    void setScope(Scope *sc) override;
     Expressions *getAttributes();
     const char *kind() const override;
     void accept(Visitor *v) override { v->visit(this); }
diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d
index 67305922df6..5a608a9986d 100644
--- a/gcc/d/dmd/canthrow.d
+++ b/gcc/d/dmd/canthrow.d
@@ -22,7 +22,6 @@  import dmd.declaration;
 import dmd.dsymbol;
 import dmd.errorsink;
 import dmd.expression;
-import dmd.expressionsem;
 import dmd.func;
 import dmd.globals;
 import dmd.init;
@@ -81,6 +80,7 @@  CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink)
                     if (!f.isDtorDeclaration())
                         errorSupplementalInferredAttr(f, 10, false, STC.nothrow_);
 
+                    import dmd.expressionsem : checkOverriddenDtor;
                     f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
                 }
                 else if (func)
diff --git a/gcc/d/dmd/common/README.md b/gcc/d/dmd/common/README.md
index 853fd4ff502..ad507c75bd4 100644
--- a/gcc/d/dmd/common/README.md
+++ b/gcc/d/dmd/common/README.md
@@ -5,4 +5,4 @@ 
 | [bitfields.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/bitfields.d) | Pack multiple boolean fields into bit fields                    |
 | [file.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/file.d)           | Functions and objects dedicated to file I/O and management      |
 | [outbuffer.d](https://github.com/dlang/dmd/blob/master/compiler/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/compiler/src/dmd/common/string.d)       | Common string functions including filename manipulation         |
+| [string.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/smallbuffer.d)  | Common string functions including filename manipulation         |
diff --git a/gcc/d/dmd/common/file.d b/gcc/d/dmd/common/file.d
index 076f357e50b..704110f50bd 100644
--- a/gcc/d/dmd/common/file.d
+++ b/gcc/d/dmd/common/file.d
@@ -17,13 +17,13 @@  module dmd.common.file;
 import core.stdc.errno : errno;
 import core.stdc.stdio : fprintf, remove, rename, stderr;
 import core.stdc.stdlib : exit;
-import core.stdc.string : strerror;
+import core.stdc.string : strerror, strlen;
 import core.sys.windows.winbase;
 import core.sys.windows.winnt;
 import core.sys.posix.fcntl;
 import core.sys.posix.unistd;
 
-import dmd.common.string;
+import dmd.common.smallbuffer;
 
 nothrow:
 
@@ -129,7 +129,8 @@  struct FileMapping(Datum)
                 enum openFlags = CREATE_ALWAYS;
             }
 
-            handle = filename.asDString.extendedPathThen!(p => CreateFileW(p.ptr, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null));
+            handle = filename[0 .. strlen(filename)].
+                extendedPathThen!(p => CreateFileW(p.ptr, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null));
             if (handle == invalidHandle)
             {
                 static if (is(Datum == const))
@@ -312,7 +313,7 @@  struct FileMapping(Datum)
         else version(Windows)
         {
             import core.sys.windows.winbase;
-            if (deleteme.asDString.extendedPathThen!(p => DeleteFileW(p.ptr)) == 0)
+            if (deleteme[0 .. strlen(deleteme)].extendedPathThen!(p => DeleteFileW(p.ptr)) == 0)
             {
                 fprintf(stderr, "DeleteFileW error %d\n", GetLastError());
                 return false;
@@ -447,8 +448,8 @@  struct FileMapping(Datum)
         else version(Windows)
         {
             import core.sys.windows.winbase;
-            auto r = oldname.asDString.extendedPathThen!(
-                p1 => filename.asDString.extendedPathThen!(p2 => MoveFileExW(p1.ptr, p2.ptr, MOVEFILE_REPLACE_EXISTING))
+            auto r = oldname[0 .. strlen(oldname)].extendedPathThen!(
+                p1 => filename[0 .. strlen(filename)].extendedPathThen!(p2 => MoveFileExW(p1.ptr, p2.ptr, MOVEFILE_REPLACE_EXISTING))
             );
             if (r == 0)
             {
@@ -483,7 +484,7 @@  extern(D) static bool writeFile(const(char)* name, const void[] data) nothrow
     else version (Windows)
     {
         DWORD numwritten; // here because of the gotos
-        const nameStr = name.asDString;
+        const nameStr = name[0 .. strlen(name)];
         // work around Windows file path length limitation
         // (see documentation for extendedPathThen).
         HANDLE h = nameStr.extendedPathThen!
diff --git a/gcc/d/dmd/common/string.d b/gcc/d/dmd/common/smallbuffer.d
similarity index 82%
rename from gcc/d/dmd/common/string.d
rename to gcc/d/dmd/common/smallbuffer.d
index 9453a3474da..ec0eaae647f 100644
--- a/gcc/d/dmd/common/string.d
+++ b/gcc/d/dmd/common/smallbuffer.d
@@ -4,11 +4,11 @@ 
  * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
  * Authors:   Walter Bright, https://www.digitalmars.com
  * 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/string.d, common/_string.d)
- * Documentation: https://dlang.org/phobos/dmd_common_string.html
- * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/string.d
+ * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/smallbuffer.d, common/_smallbuffer.d)
+ * Documentation: https://dlang.org/phobos/dmd_common_smallbuffer.html
+ * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/smallbuffer
  */
-module dmd.common.string;
+module dmd.common.smallbuffer;
 
 nothrow:
 
@@ -106,34 +106,12 @@  unittest
     assert(b[] !is buf[]);
 }
 
-/**
-Converts a zero-terminated C string to a D slice. Takes linear time and allocates no memory.
-
-Params:
-stringz = the C string to be converted
-
-Returns:
-a slice comprehending the string. The terminating 0 is not part of the slice.
-*/
-auto asDString(C)(C* stringz) pure @nogc nothrow
-{
-    import core.stdc.string : strlen;
-    return stringz[0 .. strlen(stringz)];
-}
-
-///
-unittest
-{
-    const char* p = "123".ptr;
-    assert(p.asDString == "123");
-}
-
 /**
 (Windows only) Converts a narrow string to a wide string using `buffer` as strorage. Returns a slice managed by
 `buffer` containing the converted string. The terminating zero is not part of the returned slice,
 but is guaranteed to follow it.
 */
-version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow
+version(Windows) wchar[] toWStringz(scope const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow
 {
     import core.sys.windows.winnls : MultiByteToWideChar;
     import dmd.common.file : CodePage;
@@ -141,16 +119,17 @@  version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar
     if (narrow is null)
         return null;
 
-    const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
-    if (requiredLength < cast(int) buffer.length)
+    size_t length;
+    int i;
+    while (1)
     {
-        buffer[requiredLength] = 0;
-        return buffer[0 .. requiredLength];
+        // https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
+        length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
+        if (length < buffer.length)
+            break;
+        buffer.create(length + 1);
+        assert(++i == 1);   // ensure loop should only execute once or twice
     }
-
-    buffer.create(requiredLength + 1);
-    const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, requiredLength);
-    assert(length == requiredLength);
     buffer[length] = 0;
     return buffer[0 .. length];
 }
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index ed5f1f8b9a2..89a594823ae 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -1890,6 +1890,14 @@  final class CParser(AST) : Parser!AST
                 if (specifier.alignExps)
                     error("no alignment-specifier for typedef declaration"); // C11 6.7.5-2
 
+                if (specifier.vector_size)
+                {
+                    auto length = new AST.IntegerExp(token.loc, specifier.vector_size / dt.size(), AST.Type.tuns32);
+                    auto tsa = new AST.TypeSArray(dt, length);
+                    dt = new AST.TypeVector(tsa);
+                    specifier.vector_size = 0;          // used it up
+                }
+
                 bool isalias = true;
                 if (auto ts = dt.isTypeStruct())
                 {
diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d
index 14c67f062a3..bb86b080be6 100644
--- a/gcc/d/dmd/dcast.d
+++ b/gcc/d/dmd/dcast.d
@@ -68,7 +68,6 @@  Expression implicitCastTo(Expression e, Scope* sc, Type t)
     Expression visit(Expression e)
     {
         // printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars());
-
         if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t))
         {
             // no need for an extra cast when matching is exact
@@ -802,8 +801,8 @@  extern(C++) MATCH implicitConvTo(Expression e, Type t)
 
             return result;
         }
-        else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray))
-        {
+        else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray || typeb.ty == Tpointer))
+        {   // Tpointer because ImportC eagerly converts Tsarray to Tpointer
             result = MATCH.exact;
             // Convert array literal to vector type
             TypeVector tv = tb.isTypeVector();
@@ -1487,6 +1486,10 @@  MATCH cimplicitConvTo(Expression e, Type t)
 
     if (tb.equals(typeb))
         return MATCH.exact;
+
+    if (tb.isTypeVector() || typeb.isTypeVector())
+        return implicitConvTo(e, t);    // permissive checking doesn't apply to vectors
+
     if ((typeb.isintegral() || typeb.isfloating()) &&
         (tb.isintegral() || tb.isfloating()))
         return MATCH.convert;
@@ -2298,9 +2301,10 @@  Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
                 ae.type = tp;
             }
         }
-        else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray))
+        else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray || typeb.ty == Tpointer))
         {
             // Convert array literal to vector type
+            // The Tpointer case comes from C eagerly converting Tsarray to Tpointer
             TypeVector tv = tb.isTypeVector();
             TypeSArray tbase = tv.basetype.isTypeSArray();
             assert(tbase.ty == Tsarray);
diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d
index 797f6ee0a2d..5713be1e99e 100644
--- a/gcc/d/dmd/denum.d
+++ b/gcc/d/dmd/denum.d
@@ -83,13 +83,6 @@  extern (C++) final class EnumDeclaration : ScopeDsymbol
         return ed;
     }
 
-    override void setScope(Scope* sc)
-    {
-        if (semanticRun > PASS.initial)
-            return;
-        ScopeDsymbol.setScope(sc);
-    }
-
     override bool oneMember(Dsymbol* ps, Identifier ident)
     {
         if (isAnonymous())
diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d
index 0132e49cbed..5c01a9f5889 100644
--- a/gcc/d/dmd/dimport.d
+++ b/gcc/d/dmd/dimport.d
@@ -305,22 +305,6 @@  extern (C++) final class Import : Dsymbol
         return this;
     }
 
-    override void setScope(Scope* sc)
-    {
-        Dsymbol.setScope(sc);
-        if (aliasdecls.length)
-        {
-            if (!mod)
-                importAll(sc);
-
-            sc = sc.push(mod);
-            sc.visibility = visibility;
-            foreach (ad; aliasdecls)
-                ad.setScope(sc);
-            sc = sc.pop();
-        }
-    }
-
     override bool overloadInsert(Dsymbol s)
     {
         /* Allow multiple imports with the same package base, but disallow
diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d
index 5f5de6390fb..d096e437cf9 100644
--- a/gcc/d/dmd/dmodule.d
+++ b/gcc/d/dmd/dmodule.d
@@ -33,6 +33,7 @@  import dmd.errorsink;
 import dmd.expression;
 import dmd.expressionsem;
 import dmd.file_manager;
+import dmd.func;
 import dmd.globals;
 import dmd.id;
 import dmd.identifier;
@@ -969,7 +970,7 @@  extern (C++) final class Module : Package
          * If this works out well, it can be extended to all modules
          * before any semantic() on any of them.
          */
-        setScope(sc); // remember module scope for semantic
+        this.setScope(sc); // remember module scope for semantic
         for (size_t i = 0; i < members.length; i++)
         {
             Dsymbol s = (*members)[i];
@@ -1576,3 +1577,36 @@  private const(char)[] processSource (const(ubyte)[] src, Module mod)
 
     return buf;
 }
+
+/*******************************************
+ * Look for member of the form:
+ *      const(MemberInfo)[] getMembers(string);
+ * Returns NULL if not found
+ */
+extern(C++) FuncDeclaration findGetMembers(ScopeDsymbol dsym)
+{
+    import dmd.opover : search_function;
+    Dsymbol s = search_function(dsym, Id.getmembers);
+    FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
+    version (none)
+    {
+        // Finish
+        __gshared TypeFunction tfgetmembers;
+        if (!tfgetmembers)
+        {
+            Scope sc;
+            sc.eSink = global.errorSink;
+            auto parameters = new Parameters();
+            Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
+            parameters.push(p);
+            Type tret = null;
+            TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
+            tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
+        }
+        if (fdx)
+            fdx = fdx.overloadExactMatch(tfgetmembers);
+    }
+    if (fdx && fdx.isVirtual())
+        fdx = null;
+    return fdx;
+}
diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d
index a52745fcc0e..8f5a292a284 100644
--- a/gcc/d/dmd/dsymbol.d
+++ b/gcc/d/dmd/dsymbol.d
@@ -31,7 +31,6 @@  import dmd.dmodule;
 import dmd.dversion;
 import dmd.dscope;
 import dmd.dstruct;
-import dmd.dsymbolsem;
 import dmd.dtemplate;
 import dmd.errors;
 import dmd.expression;
@@ -44,11 +43,9 @@  import dmd.lexer;
 import dmd.location;
 import dmd.mtype;
 import dmd.nspace;
-import dmd.opover;
 import dmd.root.aav;
 import dmd.root.rmem;
 import dmd.rootobject;
-import dmd.root.speller;
 import dmd.root.string;
 import dmd.statement;
 import dmd.staticassert;
@@ -386,40 +383,6 @@  extern (C++) class Dsymbol : ASTNode
         return '`' ~ cstr.toDString() ~ "`\0";
     }
 
-    final bool checkDeprecated(const ref Loc loc, Scope* sc)
-    {
-        if (global.params.useDeprecated == DiagnosticReporting.off)
-            return false;
-        if (!this.isDeprecated())
-            return false;
-        // Don't complain if we're inside a deprecated symbol's scope
-        if (sc.isDeprecated())
-            return false;
-        // Don't complain if we're inside a template constraint
-        // https://issues.dlang.org/show_bug.cgi?id=21831
-        if (sc.flags & SCOPE.constraint)
-            return false;
-
-        const(char)* message = null;
-        for (Dsymbol p = this; p; p = p.parent)
-        {
-            message = p.depdecl ? p.depdecl.getMessage() : null;
-            if (message)
-                break;
-        }
-        if (message)
-            deprecation(loc, "%s `%s` is deprecated - %s", kind, toPrettyChars, message);
-        else
-            deprecation(loc, "%s `%s` is deprecated", kind, toPrettyChars);
-
-        if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
-            ti.printInstantiationTrace(Classification.deprecation);
-        else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null)
-            ti.printInstantiationTrace(Classification.deprecation);
-
-        return true;
-    }
-
     /**********************************
      * Determine which Module a Dsymbol is in.
      */
@@ -749,113 +712,10 @@  extern (C++) class Dsymbol : ASTNode
         return toAlias();
     }
 
-    /*************************************
-     * Set scope for future semantic analysis so we can
-     * deal better with forward references.
-     */
-    void setScope(Scope* sc)
-    {
-        //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", this, toChars(), sc, sc.stc);
-        if (!sc.nofree)
-            sc.setNoFree(); // may need it even after semantic() finishes
-        _scope = sc;
-        if (sc.depdecl)
-            depdecl = sc.depdecl;
-        if (!userAttribDecl)
-            userAttribDecl = sc.userAttribDecl;
-    }
-
     void importAll(Scope* sc)
     {
     }
 
-    extern (D) final Dsymbol search_correct(Identifier ident)
-    {
-        /***************************************************
-         * Search for symbol with correct spelling.
-         */
-        extern (D) Dsymbol symbol_search_fp(const(char)[] seed, out int cost)
-        {
-            /* If not in the lexer's string table, it certainly isn't in the symbol table.
-             * Doing this first is a lot faster.
-             */
-            if (!seed.length)
-                return null;
-            Identifier id = Identifier.lookup(seed);
-            if (!id)
-                return null;
-            cost = 0;   // all the same cost
-            Dsymbol s = this;
-            Module.clearCache();
-            return s.search(Loc.initial, id, IgnoreErrors);
-        }
-
-        if (global.gag)
-            return null; // don't do it for speculative compiles; too time consuming
-        // search for exact name first
-        if (auto s = this.search(Loc.initial, ident, IgnoreErrors))
-            return s;
-        return speller!symbol_search_fp(ident.toString());
-    }
-
-    /***************************************
-     * Search for identifier id as a member of `this`.
-     * `id` may be a template instance.
-     *
-     * Params:
-     *  loc = location to print the error messages
-     *  sc = the scope where the symbol is located
-     *  id = the id of the symbol
-     *  flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports`
-     *
-     * Returns:
-     *      symbol found, NULL if not
-     */
-    extern (D) final Dsymbol searchX(const ref Loc loc, Scope* sc, RootObject id, int flags)
-    {
-        //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars());
-        Dsymbol s = toAlias();
-        Dsymbol sm;
-        if (Declaration d = s.isDeclaration())
-        {
-            if (d.inuse)
-            {
-                .error(loc, "circular reference to `%s`", d.toPrettyChars());
-                return null;
-            }
-        }
-        switch (id.dyncast())
-        {
-        case DYNCAST.identifier:
-            sm = s.search(loc, cast(Identifier)id, flags);
-            break;
-        case DYNCAST.dsymbol:
-            {
-                // It's a template instance
-                //printf("\ttemplate instance id\n");
-                Dsymbol st = cast(Dsymbol)id;
-                TemplateInstance ti = st.isTemplateInstance();
-                sm = s.search(loc, ti.name);
-                if (!sm)
-                    return null;
-                sm = sm.toAlias();
-                TemplateDeclaration td = sm.isTemplateDeclaration();
-                if (!td)
-                    return null; // error but handled later
-                ti.tempdecl = td;
-                if (!ti.semanticRun)
-                    ti.dsymbolSemantic(sc);
-                sm = ti.toAlias();
-                break;
-            }
-        case DYNCAST.type:
-        case DYNCAST.expression:
-        default:
-            assert(0);
-        }
-        return sm;
-    }
-
     bool overloadInsert(Dsymbol s)
     {
         //printf("Dsymbol::overloadInsert('%s')\n", s.toChars());
@@ -1468,38 +1328,6 @@  public:
         return "ScopeDsymbol";
     }
 
-    /*******************************************
-     * Look for member of the form:
-     *      const(MemberInfo)[] getMembers(string);
-     * Returns NULL if not found
-     */
-    final FuncDeclaration findGetMembers()
-    {
-        Dsymbol s = search_function(this, Id.getmembers);
-        FuncDeclaration fdx = s ? s.isFuncDeclaration() : null;
-        version (none)
-        {
-            // Finish
-            __gshared TypeFunction tfgetmembers;
-            if (!tfgetmembers)
-            {
-                Scope sc;
-                sc.eSink = global.errorSink;
-                auto parameters = new Parameters();
-                Parameters* p = new Parameter(STC.in_, Type.tchar.constOf().arrayOf(), null, null);
-                parameters.push(p);
-                Type tret = null;
-                TypeFunction tf = new TypeFunction(parameters, tret, VarArg.none, LINK.d);
-                tfgetmembers = tf.dsymbolSemantic(Loc.initial, &sc).isTypeFunction();
-            }
-            if (fdx)
-                fdx = fdx.overloadExactMatch(tfgetmembers);
-        }
-        if (fdx && fdx.isVirtual())
-            fdx = null;
-        return fdx;
-    }
-
     /********************************
      * Insert Dsymbol in table.
      * Params:
diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h
index e0c2046bf90..15c997027da 100644
--- a/gcc/d/dmd/dsymbol.h
+++ b/gcc/d/dmd/dsymbol.h
@@ -205,7 +205,6 @@  public:
     const char *locToChars();
     bool equals(const RootObject * const o) const override;
     bool isAnonymous() const;
-    bool checkDeprecated(const Loc &loc, Scope *sc);
     Module *getModule();
     bool isCsymbol();
     Module *getAccessModule();
@@ -228,7 +227,6 @@  public:
     virtual const char *kind() const;
     virtual Dsymbol *toAlias();                 // resolve real symbol
     virtual Dsymbol *toAlias2();
-    virtual void setScope(Scope *sc);
     virtual void importAll(Scope *sc);
     virtual bool overloadInsert(Dsymbol *s);
     virtual uinteger_t size(const Loc &loc);
@@ -342,7 +340,6 @@  public:
     bool isforwardRef() override final;
     static void multiplyDefined(const Loc &loc, Dsymbol *s1, Dsymbol *s2);
     const char *kind() const override;
-    FuncDeclaration *findGetMembers();
     virtual Dsymbol *symtabInsert(Dsymbol *s);
     virtual Dsymbol *symtabLookup(Dsymbol *s, Identifier *id);
     bool hasStaticCtorOrDtor() override;
@@ -431,3 +428,5 @@  public:
 
 void addMember(Dsymbol *dsym, Scope *sc, ScopeDsymbol *sds);
 Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
+bool checkDeprecated(Dsymbol *d, const Loc &loc, Scope *sc);
+void setScope(Dsymbol *d, Scope *sc);
diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d
index 430377ff874..060abfe1896 100644
--- a/gcc/d/dmd/dsymbolsem.d
+++ b/gcc/d/dmd/dsymbolsem.d
@@ -212,6 +212,39 @@  const(char)* getMessage(DeprecatedDeclaration dd)
     return dd.msgstr;
 }
 
+bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc)
+{
+    if (global.params.useDeprecated == DiagnosticReporting.off)
+        return false;
+    if (!d.isDeprecated())
+        return false;
+    // Don't complain if we're inside a deprecated symbol's scope
+    if (sc.isDeprecated())
+        return false;
+    // Don't complain if we're inside a template constraint
+    // https://issues.dlang.org/show_bug.cgi?id=21831
+    if (sc.flags & SCOPE.constraint)
+        return false;
+
+    const(char)* message = null;
+    for (Dsymbol p = d; p; p = p.parent)
+    {
+        message = p.depdecl ? p.depdecl.getMessage() : null;
+        if (message)
+            break;
+    }
+    if (message)
+        deprecation(loc, "%s `%s` is deprecated - %s", d.kind, d.toPrettyChars, message);
+    else
+        deprecation(loc, "%s `%s` is deprecated", d.kind, d.toPrettyChars);
+
+    if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
+        ti.printInstantiationTrace(Classification.deprecation);
+    else if (auto ti = sc.parent ? sc.parent.isTemplateInstance() : null)
+        ti.printInstantiationTrace(Classification.deprecation);
+
+    return true;
+}
 
 // Returns true if a contract can appear without a function body.
 package bool allowsContractWithoutBody(FuncDeclaration funcdecl)
@@ -7811,6 +7844,37 @@  extern(C++) Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, int f
     return v.result;
 }
 
+Dsymbol search_correct(Dsymbol d, Identifier ident)
+{
+    /***************************************************
+     * Search for symbol with correct spelling.
+     */
+    Dsymbol symbol_search_fp(const(char)[] seed, out int cost)
+    {
+        /* If not in the lexer's string table, it certainly isn't in the symbol table.
+         * Doing this first is a lot faster.
+         */
+        if (!seed.length)
+            return null;
+        Identifier id = Identifier.lookup(seed);
+        if (!id)
+            return null;
+        cost = 0;   // all the same cost
+        Dsymbol s = d;
+        Module.clearCache();
+        return s.search(Loc.initial, id, IgnoreErrors);
+    }
+
+    if (global.gag)
+        return null; // don't do it for speculative compiles; too time consuming
+    // search for exact name first
+    if (auto s = d.search(Loc.initial, ident, IgnoreErrors))
+        return s;
+
+    import dmd.root.speller : speller;
+    return speller!symbol_search_fp(ident.toString());
+}
+
 private extern(C++) class SearchVisitor : Visitor
 {
     alias visit = Visitor.visit;
@@ -8407,3 +8471,153 @@  private extern(C++) class SearchVisitor : Visitor
         return setResult(s);
     }
 }
+/*************************************
+ * Set scope for future semantic analysis so we can
+ * deal better with forward references.
+ *
+ * Params:
+ *   d = dsymbol for which the scope is set
+ *   sc = scope that is used to set the value
+ */
+extern(C++) void setScope(Dsymbol d, Scope* sc)
+{
+    scope setScopeVisitor = new SetScopeVisitor(sc);
+    d.accept(setScopeVisitor);
+}
+
+private extern(C++) class SetScopeVisitor : Visitor
+{
+    alias visit = typeof(super).visit;
+    Scope* sc;
+
+    this(Scope* sc)
+    {
+        this.sc = sc;
+    }
+
+    override void visit(Dsymbol d)
+    {
+        //printf("Dsymbol::setScope() %p %s, %p stc = %llx\n", d, d.toChars(), sc, sc.stc);
+        if (!sc.nofree)
+            sc.setNoFree(); // may need it even after semantic() finishes
+        d._scope = sc;
+        if (sc.depdecl)
+            d.depdecl = sc.depdecl;
+        if (!d.userAttribDecl)
+            d.userAttribDecl = sc.userAttribDecl;
+    }
+
+    override void visit(Import i)
+    {
+        visit(cast(Dsymbol)i);
+        if (i.aliasdecls.length)
+        {
+            if (!i.mod)
+                i.importAll(sc);
+
+            sc = sc.push(i.mod);
+            sc.visibility = i.visibility;
+            foreach (ad; i.aliasdecls)
+                ad.setScope(sc);
+            sc = sc.pop();
+        }
+    }
+
+    override void visit(Nspace ns)
+    {
+        visit(cast(Dsymbol)ns);
+        if (ns.members)
+        {
+            assert(sc);
+            sc = sc.push(ns);
+            sc.linkage = LINK.cpp; // namespaces default to C++ linkage
+            sc.parent = ns;
+            ns.members.foreachDsymbol(s => s.setScope(sc));
+            sc.pop();
+        }
+    }
+
+    override void visit(EnumDeclaration ed)
+    {
+        if (ed.semanticRun > PASS.initial)
+            return;
+        visit(cast(Dsymbol)ed);
+    }
+
+    override void visit(AggregateDeclaration ad)
+    {
+        // Might need a scope to resolve forward references. The check for
+        // semanticRun prevents unnecessary setting of _scope during deferred
+        // setScope phases for aggregates which already finished semantic().
+        // See https://issues.dlang.org/show_bug.cgi?id=16607
+        if (ad.semanticRun < PASS.semanticdone)
+            visit(cast(Dsymbol)ad);
+    }
+
+    override void visit(AttribDeclaration atr)
+    {
+        Dsymbols* d = atr.include(sc);
+        //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d);
+        if (d)
+        {
+            Scope* sc2 = atr.newScope(sc);
+            d.foreachDsymbol( s => s.setScope(sc2) );
+            if (sc2 != sc)
+                sc2.pop();
+        }
+    }
+
+    override void visit(DeprecatedDeclaration dd)
+    {
+        //printf("DeprecatedDeclaration::setScope() %p\n", this);
+        if (dd.decl)
+            visit(cast(Dsymbol)dd); // for forward reference
+        visit(cast(AttribDeclaration)dd);
+    }
+
+    override void visit(CPPMangleDeclaration cppmd)
+    {
+        if (cppmd.decl)
+            visit(cast(Dsymbol)cppmd); // for forward reference
+        visit(cast(AttribDeclaration)cppmd);
+    }
+
+    override void visit(AnonDeclaration anond)
+    {
+        if (anond.decl)
+            visit(cast(Dsymbol)anond); // for forward reference
+        visit(cast(AttribDeclaration)anond);
+    }
+
+    override void visit(ConditionalDeclaration condd)
+    {
+        condd.include(sc).foreachDsymbol( s => s.setScope(sc) );
+    }
+
+    override void visit(StaticIfDeclaration sid)
+    {
+        // do not evaluate condition before semantic pass
+        // But do set the scope, in case we need it for forward referencing
+        visit(cast(Dsymbol)sid); // for forward reference
+    }
+
+    override void visit(StaticForeachDeclaration sfd)
+    {
+        // do not evaluate condition before semantic pass
+        // But do set the scope, in case we need it for forward referencing
+        visit(cast(Dsymbol)sfd); // for forward reference
+    }
+
+    override void visit(MixinDeclaration md)
+    {
+        visit(cast(Dsymbol)md);
+    }
+
+    override void visit(UserAttributeDeclaration uad)
+    {
+        //printf("UserAttributeDeclaration::setScope() %p\n", this);
+        if (uad.decl)
+            visit(cast(Dsymbol)uad);
+        visit(cast(AttribDeclaration)uad);
+    }
+}
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
index 037e0d01196..326d66364b8 100644
--- a/gcc/d/dmd/dtemplate.d
+++ b/gcc/d/dmd/dtemplate.d
@@ -7515,7 +7515,12 @@  extern (C++) class TemplateInstance : ScopeDsymbol
         }
         //printf("\t-. mi = %s\n", mi.toPrettyChars());
 
-        assert(!memberOf || (!memberOf.isRoot() && mi.isRoot()), "can only re-append from non-root to root module");
+        if (memberOf) // already appended to some module
+        {
+            assert(mi.isRoot(), "can only re-append to a root module");
+            if (memberOf.isRoot())
+                return null; // no need to move to another root module
+        }
 
         Dsymbols* a = mi.members;
         a.push(this);
diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h
index e17e8cf5b0a..5f91ead9e17 100644
--- a/gcc/d/dmd/enum.h
+++ b/gcc/d/dmd/enum.h
@@ -46,7 +46,6 @@  public:
     bool inuse(bool v);
 
     EnumDeclaration *syntaxCopy(Dsymbol *s) override;
-    void setScope(Scope *sc) override;
     bool oneMember(Dsymbol **ps, Identifier *ident) override;
     Type *getType() override;
     const char *kind() const override;
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index e25fc84234e..f928b08503a 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -2343,7 +2343,7 @@  void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
         VarDeclaration[10] tmp = void;
         size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0);
 
-        import dmd.common.string : SmallBuffer;
+        import dmd.common.smallbuffer : SmallBuffer;
         auto sb = SmallBuffer!VarDeclaration(dim, tmp[]);
         VarDeclaration[] array = sb[];
 
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index e6b90183b51..1664bf22dca 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -4387,7 +4387,7 @@  private extern (C++) final class ExpressionSemanticVisitor : Visitor
         auto e = initializerToExpression(init, t, (sc.flags & SCOPE.Cfile) != 0);
         if (!e)
         {
-            error(cle.loc, "cannot convert initializer `%s` to expression", init.toChars());
+            error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init));
             return setError();
         }
         result = e;
diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d
index ac2dda3e89f..0944ade4c28 100644
--- a/gcc/d/dmd/hdrgen.d
+++ b/gcc/d/dmd/hdrgen.d
@@ -1968,6 +1968,10 @@  private void visitVarDecl(VarDeclaration v, bool anywritten, ref OutBuffer buf,
             v._init.initializerToBuffer(buf, &hgs);
     }
 
+    const commentIt = hgs.importcHdr && isSpecialCName(v.ident);
+    if (commentIt)
+        buf.writestring("/+");
+
     if (anywritten)
     {
         buf.writestring(", ");
@@ -2000,8 +2004,31 @@  private void visitVarDecl(VarDeclaration v, bool anywritten, ref OutBuffer buf,
         buf.writestring(" = ");
         vinit(v);
     }
+    if (commentIt)
+        buf.writestring("+/");
 }
 
+/*************************************
+ * The names __DATE__, __TIME__,__EOF__, __VENDOR__, __TIMESTAMP__, __VERSION__
+ * are special to the D lexer and cannot be used as D source variable names.
+ * Params:
+ *      id = name to check
+ * Returns:
+ *      true if special C name
+ */
+private bool isSpecialCName(Identifier id)
+{
+    auto s = id.toString();
+    if (s.length >= 7 && s[0] == '_' && s[1] == '_' &&
+        (id == Id.DATE ||
+         id == Id.TIME ||
+         id == Id.EOFX ||
+         id == Id.VENDOR ||
+         id == Id.TIMESTAMP ||
+         id == Id.VERSIONX))
+        return true;
+    return false;
+}
 
 /*********************************************
  * Print expression to buffer.
diff --git a/gcc/d/dmd/import.h b/gcc/d/dmd/import.h
index aeb3621f1eb..624cd7406a3 100644
--- a/gcc/d/dmd/import.h
+++ b/gcc/d/dmd/import.h
@@ -43,7 +43,6 @@  public:
     Import *syntaxCopy(Dsymbol *s) override; // copy only syntax trees
     void importAll(Scope *sc) override;
     Dsymbol *toAlias() override;
-    void setScope(Scope* sc) override;
     bool overloadInsert(Dsymbol *s) override;
 
     Import *isImport() override { return this; }
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index 76c2d8916b0..6d31f956c8e 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -199,7 +199,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
         uint length;
         const(uint) amax = 0x80000000;
         bool errors = false;
-        //printf("ArrayInitializer::semantic(%s), ai: %s %p\n", t.toChars(), i.toChars(), i);
+        //printf("ArrayInitializer::semantic(%s), ai: %s\n", t.toChars(), toChars(i));
         if (i.sem) // if semantic() already run
         {
             return i;
@@ -600,7 +600,17 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
 
     Initializer visitC(CInitializer ci)
     {
-        //printf("CInitializer::semantic() tx: %s t: %s ci: %s\n", (tx ? tx.toChars() : "".ptr), t.toChars(), ci.toChars());
+        //printf("CInitializer::semantic() tx: %s t: %s ci: %s\n", (tx ? tx.toChars() : "".ptr), t.toChars(), toChars(ci));
+        static if (0)
+            if (auto ts = tx.isTypeStruct())
+            {
+                import dmd.common.outbuffer;
+                OutBuffer buf;
+                HdrGenStage hgs;
+                toCBuffer(ts.sym, buf, hgs);
+                printf("%s\n", buf.peekChars());
+            }
+
         /* Rewrite CInitializer into ExpInitializer, ArrayInitializer, or StructInitializer
          */
         t = t.toBasetype();
@@ -794,6 +804,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
             for (size_t index = 0; index < ci.initializerList.length; )
             {
                 CInitializer cprev;
+                size_t indexprev;
              L1:
                 DesigInit di = ci.initializerList[index];
                 Designators* dlist = di.designatorList;
@@ -827,6 +838,7 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                         /* The peeling didn't work, so unpeel it
                          */
                         ci = cprev;
+                        index = indexprev;
                         di = ci.initializerList[index];
                         goto L2;
                     }
@@ -837,12 +849,14 @@  extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
                 {
                     if (fieldi == nfields)
                         break;
-                    if (index == 0 && ci.initializerList.length == 1 && di.initializer.isCInitializer())
+                    if (/*index == 0 && ci.initializerList.length == 1 &&*/ di.initializer.isCInitializer())
                     {
                         /* Try peeling off this set of { } and see if it works
                          */
                         cprev = ci;
                         ci = di.initializer.isCInitializer();
+                        indexprev = index;
+                        index = 0;
                         goto L1;
                     }
 
diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h
index 92efc1656da..cab0b0a4c1b 100644
--- a/gcc/d/dmd/module.h
+++ b/gcc/d/dmd/module.h
@@ -169,3 +169,4 @@  struct ModuleDeclaration
 };
 
 extern void getLocalClasses(Module* mod, Array<ClassDeclaration* >& aclasses);
+FuncDeclaration *findGetMembers(ScopeDsymbol *dsym);
diff --git a/gcc/d/dmd/nspace.d b/gcc/d/dmd/nspace.d
index a49e0bf0cc2..22c6e63a465 100644
--- a/gcc/d/dmd/nspace.d
+++ b/gcc/d/dmd/nspace.d
@@ -85,20 +85,6 @@  extern (C++) final class Nspace : ScopeDsymbol
         return ns;
     }
 
-    override void setScope(Scope* sc)
-    {
-        ScopeDsymbol.setScope(sc);
-        if (members)
-        {
-            assert(sc);
-            sc = sc.push(this);
-            sc.linkage = LINK.cpp; // namespaces default to C++ linkage
-            sc.parent = this;
-            members.foreachDsymbol(s => s.setScope(sc));
-            sc.pop();
-        }
-    }
-
     override bool hasPointers()
     {
         //printf("Nspace::hasPointers() %s\n", toChars());
diff --git a/gcc/d/dmd/nspace.h b/gcc/d/dmd/nspace.h
index 7d30402c595..701cc935eb5 100644
--- a/gcc/d/dmd/nspace.h
+++ b/gcc/d/dmd/nspace.h
@@ -21,7 +21,6 @@  class Nspace final : public ScopeDsymbol
   public:
     Expression *identExp;
     Nspace *syntaxCopy(Dsymbol *s) override;
-    void setScope(Scope *sc) override;
     bool hasPointers() override;
     void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion) override;
     const char *kind() const override;
diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d
index f9d174ab14f..b6f30b93f47 100644
--- a/gcc/d/dmd/parse.d
+++ b/gcc/d/dmd/parse.d
@@ -8428,7 +8428,12 @@  class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                 AST.TemplateParameters* tpl = null;
 
                 nextToken();
-                if (token.value == TOK.leftParenthesis)
+                if (token.value != TOK.leftParenthesis)
+                {
+                    error("expected `(` following `is`, not `%s`", token.toChars());
+                    goto Lerr;
+                }
+                else
                 {
                     nextToken();
                     if (token.value == TOK.identifier && peekNext() == TOK.leftParenthesis)
@@ -8476,11 +8481,6 @@  class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
                     else
                         check(TOK.rightParenthesis);
                 }
-                else
-                {
-                    error("`type identifier : specialization` expected following `is`");
-                    goto Lerr;
-                }
                 e = new AST.IsExp(loc, targ, ident, tok, tspec, tok2, tpl);
                 break;
             }
diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d
index 1fb105682ea..fdf13d4e8b0 100644
--- a/gcc/d/dmd/root/file.d
+++ b/gcc/d/dmd/root/file.d
@@ -24,7 +24,7 @@  import dmd.root.rmem;
 import dmd.root.string;
 
 import dmd.common.file;
-import dmd.common.string;
+import dmd.common.smallbuffer;
 
 nothrow:
 
diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d
index 631c08c44d2..8f31f212048 100644
--- a/gcc/d/dmd/root/filename.d
+++ b/gcc/d/dmd/root/filename.d
@@ -37,7 +37,7 @@  version (Windows)
     import core.sys.windows.windef;
     import core.sys.windows.winnls;
 
-    import dmd.common.string : extendedPathThen;
+    import dmd.common.smallbuffer : extendedPathThen;
 
     extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc;
     extern (Windows) void SetLastError(DWORD) nothrow @nogc;
@@ -1177,7 +1177,7 @@  version(Windows)
      */
     private auto toWStringzThen(alias F)(const(char)[] str) nothrow
     {
-        import dmd.common.string : SmallBuffer, toWStringz;
+        import dmd.common.smallbuffer : SmallBuffer, toWStringz;
 
         if (!str.length) return F(""w.ptr);
 
diff --git a/gcc/d/dmd/root/speller.d b/gcc/d/dmd/root/speller.d
index b646bdda0cc..7ad08b7216e 100644
--- a/gcc/d/dmd/root/speller.d
+++ b/gcc/d/dmd/root/speller.d
@@ -42,7 +42,7 @@  private:
 
 import core.stdc.stdlib;
 import core.stdc.string;
-import dmd.common.string : SmallBuffer;
+import dmd.common.smallbuffer : SmallBuffer;
 
 enum isSearchFunction(alias fun) = is(searchFunctionType!fun);
 alias searchFunctionType(alias fun) = typeof(() {int x; return fun("", x);}());
diff --git a/gcc/d/dmd/root/string.d b/gcc/d/dmd/root/string.d
index 8b204ab4cad..5ee81a9b63d 100644
--- a/gcc/d/dmd/root/string.d
+++ b/gcc/d/dmd/root/string.d
@@ -69,7 +69,7 @@  The return value of `T`
 auto toCStringThen(alias dg)(const(char)[] src) nothrow
 {
     import dmd.root.rmem : mem;
-    import dmd.common.string : SmallBuffer;
+    import dmd.common.smallbuffer : SmallBuffer;
 
     const len = src.length + 1;
     char[512] small = void;
diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d
index 8795002cd15..2063a954b99 100644
--- a/gcc/d/dmd/typesem.d
+++ b/gcc/d/dmd/typesem.d
@@ -372,6 +372,64 @@  private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb
         pt = t.merge();
 }
 
+/***************************************
+ * Search for identifier id as a member of `this`.
+ * `id` may be a template instance.
+ *
+ * Params:
+ *  loc = location to print the error messages
+ *  sc = the scope where the symbol is located
+ *  id = the id of the symbol
+ *  flags = the search flags which can be `SearchLocalsOnly` or `IgnorePrivateImports`
+ *
+ * Returns:
+ *      symbol found, NULL if not
+ */
+private Dsymbol searchX(Dsymbol dsym, const ref Loc loc, Scope* sc, RootObject id, int flags)
+{
+    //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars());
+    Dsymbol s = dsym.toAlias();
+    Dsymbol sm;
+    if (Declaration d = s.isDeclaration())
+    {
+        if (d.inuse)
+        {
+            .error(loc, "circular reference to `%s`", d.toPrettyChars());
+            return null;
+        }
+    }
+    switch (id.dyncast())
+    {
+    case DYNCAST.identifier:
+        sm = s.search(loc, cast(Identifier)id, flags);
+        break;
+    case DYNCAST.dsymbol:
+        {
+            // It's a template instance
+            //printf("\ttemplate instance id\n");
+            Dsymbol st = cast(Dsymbol)id;
+            TemplateInstance ti = st.isTemplateInstance();
+            sm = s.search(loc, ti.name);
+            if (!sm)
+                return null;
+            sm = sm.toAlias();
+            TemplateDeclaration td = sm.isTemplateDeclaration();
+            if (!td)
+                return null; // error but handled later
+            ti.tempdecl = td;
+            if (!ti.semanticRun)
+                ti.dsymbolSemantic(sc);
+            sm = ti.toAlias();
+            break;
+        }
+    case DYNCAST.type:
+    case DYNCAST.expression:
+    default:
+        assert(0);
+    }
+    return sm;
+}
+
 /******************************************
  * We've mistakenly parsed `t` as a type.
  * Redo `t` as an Expression only if there are no type modifiers.
diff --git a/gcc/d/modules.cc b/gcc/d/modules.cc
index e3c1ef9f82e..250743e8dbe 100644
--- a/gcc/d/modules.cc
+++ b/gcc/d/modules.cc
@@ -503,7 +503,7 @@  layout_moduleinfo_fields (Module *decl, tree type)
   if (decl->sshareddtor)
     layout_moduleinfo_field (ptr_type_node, type, offset);
 
-  if (decl->findGetMembers ())
+  if (findGetMembers (decl))
     layout_moduleinfo_field (ptr_type_node, type, offset);
 
   if (decl->sictor)
@@ -571,7 +571,7 @@  layout_moduleinfo (Module *decl)
 	aimports_dim--;
     }
 
-  sgetmembers = decl->findGetMembers ();
+  sgetmembers = findGetMembers (decl);
 
   size_t flags = 0;
   if (decl->sctor)
diff --git a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
index 9de436b0119..a170b77b88c 100644
--- a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
+++ b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d
@@ -7,7 +7,7 @@  fail_compilation/misc_parser_err_cov1.d(30): Error: basic type expected, not `)`
 fail_compilation/misc_parser_err_cov1.d(31): Error: `__traits(identifier, args...)` expected
 fail_compilation/misc_parser_err_cov1.d(31): Error: semicolon expected following auto declaration, not `o`
 fail_compilation/misc_parser_err_cov1.d(31): Error: expression expected, not `)`
-fail_compilation/misc_parser_err_cov1.d(32): Error: `type identifier : specialization` expected following `is`
+fail_compilation/misc_parser_err_cov1.d(32): Error: expected `(` following `is`, not `;`
 fail_compilation/misc_parser_err_cov1.d(33): Error: semicolon expected following auto declaration, not `auto`
 fail_compilation/misc_parser_err_cov1.d(33): Error: found `+` when expecting `(` following `mixin`
 fail_compilation/misc_parser_err_cov1.d(35): Error: `key:value` expected for associative array literal
diff --git a/gcc/testsuite/gdc.test/runnable/dbitfields.d b/gcc/testsuite/gdc.test/runnable/dbitfields.d
index 0d1877a7bfd..aa154c7bbb2 100644
--- a/gcc/testsuite/gdc.test/runnable/dbitfields.d
+++ b/gcc/testsuite/gdc.test/runnable/dbitfields.d
@@ -173,6 +173,39 @@  static assert(test7u() ==  1);
 static assert(test7s() == -1);
 static assert(test7s2() == -2);
 
+/******************************************/
+// https://issues.dlang.org/show_bug.cgi?id=24257
+
+struct S24257
+{
+    uint : 15;
+    bool done : 1;
+}
+
+bool advance()
+{
+    S24257 n;
+    n.done = false;
+    n.done = true;
+    return n.done;
+}
+
+bool retard()
+{
+    S24257 n;
+    n.done = true;
+    n.done = false;
+    return n.done;
+}
+
+static assert(advance() == true);
+
+void test24257()
+{
+    assert(advance() == true);
+    assert(retard() == false);
+}
+
 /******************************************/
 
 int main()
@@ -184,6 +217,7 @@  int main()
     test5();
     test6();
     test7();
+    test24257();
 
     return 0;
 }
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index aa0062c10eb..5edcee1c84d 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@ 
-ff57fec51558013b25cadb7e83da9f4675915d56
+2bbf64907cbbb483d003e0a8fcf8b502e4883799
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/dmd repository.
diff --git a/libphobos/libdruntime/core/cpuid.d b/libphobos/libdruntime/core/cpuid.d
index 9c5735728b5..62edbac34f3 100644
--- a/libphobos/libdruntime/core/cpuid.d
+++ b/libphobos/libdruntime/core/cpuid.d
@@ -628,16 +628,17 @@  void getAMDcacheinfo()
 
     if (max_extended_cpuid >= 0x8000_0006) {
         // AMD K6-III or K6-2+ or later.
-        ubyte numcores = 1;
+        uint numcores = 1;
         if (max_extended_cpuid >= 0x8000_0008) {
+            // read the number of physical cores (minus 1) from the 8 lowest ECX bits
             version (GNU_OR_LDC) asm pure nothrow @nogc {
                 "cpuid" : "=a" (dummy), "=c" (numcores) : "a" (0x8000_0008) : "ebx", "edx";
             } else asm pure nothrow @nogc {
                 mov EAX, 0x8000_0008;
                 cpuid;
-                mov numcores, CL;
+                mov numcores, ECX;
             }
-            ++numcores;
+            numcores = (numcores & 0xFF) + 1;
             if (numcores>cpuFeatures.maxCores) cpuFeatures.maxCores = numcores;
         }
 
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 1b20d58c1e2..3c0e1b28d31 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@ 
-17bafda797296e04f40f16a9660e5a9685392db4
+b64bfbf911fcd1675ae9792545649c9d45bb907e
 
 The first line of this file holds the git revision number of the last
 merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index 68979057f54..4526aa22bc0 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -2895,94 +2895,100 @@  if (isForwardRange!R1 && ifTestable!(typeof(haystack.front), unaryFun!pred))
     assert(findSkip!isWhite(s) == 2);
 }
 
+private struct FindSplitResult(ubyte emptyRangeIndex, Types...)
+{
+    this(Types vals)
+    {
+        asTuple = typeof(asTuple)(vals);
+    }
+    void opAssign(typeof(asTuple) rhs)
+    {
+        asTuple = rhs;
+    }
+    Tuple!Types asTuple;
+    alias asTuple this;
+
+    static if (hasConstEmptyMember!(typeof(asTuple[emptyRangeIndex])))
+    {
+        bool opCast(T : bool)() const => !asTuple[emptyRangeIndex].empty;
+    }
+    else
+    {
+        bool opCast(T : bool)() => !asTuple[emptyRangeIndex].empty;
+    }
+}
+
 /**
 These functions find the first occurrence of `needle` in `haystack` and then
 split `haystack` as follows.
 
-`findSplit` returns a tuple `result` containing $(I three) ranges. `result[0]`
-is the portion of `haystack` before `needle`, `result[1]` is the portion of
-`haystack` that matches `needle`, and `result[2]` is the portion of `haystack`
-after the match. If `needle` was not found, `result[0]` comprehends `haystack`
+$(PANEL
+`findSplit` returns a tuple `result` containing $(I three) ranges.
+$(UL
+$(LI `result[0]` is the portion of `haystack` before `needle`)
+$(LI `result[1]` is the portion of
+`haystack` that matches `needle`)
+$(LI `result[2]` is the portion of `haystack`
+after the match.)
+)
+If `needle` was not found, `result[0]` comprehends `haystack`
 entirely and `result[1]` and `result[2]` are empty.
 
-`findSplitBefore` returns a tuple `result` containing two ranges. `result[0]` is
-the portion of `haystack` before `needle`, and `result[1]` is the balance of
-`haystack` starting with the match. If `needle` was not found, `result[0]`
+`findSplitBefore` returns a tuple `result` containing two ranges.
+$(UL
+$(LI `result[0]` is the portion of `haystack` before `needle`)
+$(LI `result[1]` is the balance of `haystack` starting with the match.)
+)
+If `needle` was not found, `result[0]`
 comprehends `haystack` entirely and `result[1]` is empty.
 
 `findSplitAfter` returns a tuple `result` containing two ranges.
-`result[0]` is the portion of `haystack` up to and including the
-match, and `result[1]` is the balance of `haystack` starting
-after the match. If `needle` was not found, `result[0]` is empty
+$(UL
+$(LI `result[0]` is the portion of `haystack` up to and including the
+match)
+$(LI `result[1]` is the balance of `haystack` starting
+after the match.)
+)
+If `needle` was not found, `result[0]` is empty
 and `result[1]` is `haystack`.
-
+)
+$(P
 In all cases, the concatenation of the returned ranges spans the
 entire `haystack`.
 
 If `haystack` is a random-access range, all three components of the tuple have
 the same type as `haystack`. Otherwise, `haystack` must be a
 $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and
-the type of `result[0]` and `result[1]` is the same as $(REF takeExactly,
-std,range).
+the type of `result[0]` (and `result[1]` for `findSplit`) is the same as
+the result of $(REF takeExactly, std,range).
 
 For more information about `pred` see $(LREF find).
-
+)
 Params:
-    pred = Predicate to use for comparing needle against haystack.
-    haystack = The range to search.
-    needle = What to look for.
+    pred = Predicate to compare 2 elements.
+    haystack = The forward range to search.
+    needle = The forward range to look for.
 
 Returns:
 
-A sub-type of `Tuple!()` of the split portions of `haystack` (see above for
-details).  This sub-type of `Tuple!()` has `opCast` defined for `bool`.  This
-`opCast` returns `true` when the separating `needle` was found
-and `false` otherwise.
+A sub-type of $(REF Tuple, std, typecons) of the split portions of `haystack` (see above for
+details). This sub-type of `Tuple` defines `opCast!bool`, which
+returns `true` when the separating `needle` was found and `false` otherwise.
 
 See_Also: $(LREF find)
  */
 auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
 if (isForwardRange!R1 && isForwardRange!R2)
 {
-    static struct Result(S1, S2) if (isForwardRange!S1 &&
-                                     isForwardRange!S2)
-    {
-        this(S1 pre, S1 separator, S2 post)
-        {
-            asTuple = typeof(asTuple)(pre, separator, post);
-        }
-        void opAssign(typeof(asTuple) rhs)
-        {
-            asTuple = rhs;
-        }
-        Tuple!(S1, S1, S2) asTuple;
-        static if (hasConstEmptyMember!(typeof(asTuple[1])))
-        {
-            bool opCast(T : bool)() const
-            {
-                return !asTuple[1].empty;
-            }
-        }
-        else
-        {
-            bool opCast(T : bool)()
-            {
-                return !asTuple[1].empty;
-            }
-        }
-        alias asTuple this;
-    }
-
     static if (isSomeString!R1 && isSomeString!R2
             || (isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && hasLength!R2))
     {
         auto balance = find!pred(haystack, needle);
         immutable pos1 = haystack.length - balance.length;
         immutable pos2 = balance.empty ? pos1 : pos1 + needle.length;
-        return Result!(typeof(haystack[0 .. pos1]),
-                       typeof(haystack[pos2 .. haystack.length]))(haystack[0 .. pos1],
-                                                                  haystack[pos1 .. pos2],
-                                                                  haystack[pos2 .. haystack.length]);
+        alias Slice = typeof(haystack[0 .. pos1]);
+        return FindSplitResult!(1, Slice, Slice, Slice)(
+            haystack[0 .. pos1], haystack[pos1 .. pos2], haystack[pos2 .. haystack.length]);
     }
     else
     {
@@ -3011,10 +3017,11 @@  if (isForwardRange!R1 && isForwardRange!R2)
         {
             pos1 = pos2;
         }
-        return Result!(typeof(takeExactly(original, pos1)),
-                       typeof(h))(takeExactly(original, pos1),
-                                  takeExactly(haystack, pos2 - pos1),
-                                  h);
+        return FindSplitResult!(1,
+            typeof(takeExactly(original, pos1)),
+            typeof(takeExactly(original, pos1)), typeof(h))(
+            takeExactly(original, pos1),
+            takeExactly(haystack, pos2 - pos1), h);
     }
 }
 
@@ -3022,43 +3029,14 @@  if (isForwardRange!R1 && isForwardRange!R2)
 auto findSplitBefore(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
 if (isForwardRange!R1 && isForwardRange!R2)
 {
-    static struct Result(S1, S2) if (isForwardRange!S1 &&
-                                     isForwardRange!S2)
-    {
-        this(S1 pre, S2 post)
-        {
-            asTuple = typeof(asTuple)(pre, post);
-        }
-        void opAssign(typeof(asTuple) rhs)
-        {
-            asTuple = rhs;
-        }
-        Tuple!(S1, S2) asTuple;
-        static if (hasConstEmptyMember!(typeof(asTuple[1])))
-        {
-            bool opCast(T : bool)() const
-            {
-                return !asTuple[1].empty;
-            }
-        }
-        else
-        {
-            bool opCast(T : bool)()
-            {
-                return !asTuple[1].empty;
-            }
-        }
-        alias asTuple this;
-    }
-
     static if (isSomeString!R1 && isSomeString!R2
             || (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2))
     {
         auto balance = find!pred(haystack, needle);
         immutable pos = haystack.length - balance.length;
-        return Result!(typeof(haystack[0 .. pos]),
-                       typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos],
-                                                                 haystack[pos .. haystack.length]);
+        return FindSplitResult!(1,
+            typeof(haystack[0 .. pos]), typeof(haystack[0 .. pos]))(
+            haystack[0 .. pos], haystack[pos .. haystack.length]);
     }
     else
     {
@@ -3088,9 +3066,9 @@  if (isForwardRange!R1 && isForwardRange!R2)
             pos1 = pos2;
             haystack = h;
         }
-        return Result!(typeof(takeExactly(original, pos1)),
-                       typeof(haystack))(takeExactly(original, pos1),
-                                         haystack);
+        return FindSplitResult!(1,
+            typeof(takeExactly(original, pos1)), typeof(haystack))(
+            takeExactly(original, pos1), haystack);
     }
 }
 
@@ -3098,47 +3076,19 @@  if (isForwardRange!R1 && isForwardRange!R2)
 auto findSplitAfter(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
 if (isForwardRange!R1 && isForwardRange!R2)
 {
-    static struct Result(S1, S2) if (isForwardRange!S1 &&
-                                     isForwardRange!S2)
-    {
-        this(S1 pre, S2 post)
-        {
-            asTuple = typeof(asTuple)(pre, post);
-        }
-        void opAssign(typeof(asTuple) rhs)
-        {
-            asTuple = rhs;
-        }
-        Tuple!(S1, S2) asTuple;
-        static if (hasConstEmptyMember!(typeof(asTuple[1])))
-        {
-            bool opCast(T : bool)() const
-            {
-                return !asTuple[0].empty;
-            }
-        }
-        else
-        {
-            bool opCast(T : bool)()
-            {
-                return !asTuple[0].empty;
-            }
-        }
-        alias asTuple this;
-    }
-
     static if (isSomeString!R1 && isSomeString!R2
             || isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2)
     {
         auto balance = find!pred(haystack, needle);
         immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length;
-        return Result!(typeof(haystack[0 .. pos]),
-                       typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos],
-                                                                 haystack[pos .. haystack.length]);
+        return FindSplitResult!(0,
+            typeof(haystack[0 .. pos]), typeof(haystack[0 .. pos]))(
+            haystack[0 .. pos], haystack[pos .. haystack.length]);
     }
     else
     {
         import std.range : takeExactly;
+        alias Res = FindSplitResult!(0, typeof(takeExactly(haystack, 0)), typeof(haystack));
         auto original = haystack.save;
         auto h = haystack.save;
         auto n = needle.save;
@@ -3148,9 +3098,7 @@  if (isForwardRange!R1 && isForwardRange!R2)
             if (h.empty)
             {
                 // Failed search
-                return Result!(typeof(takeExactly(original, 0)),
-                               typeof(original))(takeExactly(original, 0),
-                                                 original);
+                return Res(takeExactly(original, 0), original);
             }
             if (binaryFun!pred(h.front, n.front))
             {
@@ -3166,9 +3114,7 @@  if (isForwardRange!R1 && isForwardRange!R2)
                 pos2 = ++pos1;
             }
         }
-        return Result!(typeof(takeExactly(original, pos2)),
-                       typeof(h))(takeExactly(original, pos2),
-                                  h);
+        return Res(takeExactly(original, pos2), h);
     }
 }
 
@@ -3185,12 +3131,12 @@  if (isForwardRange!R1 && isForwardRange!R2)
     }
     else assert(0);
 
-    // works with const aswell
-    if (const split = "dlang-rocks".findSplit("-"))
+    // findSplitBefore returns 2 ranges
+    if (const split = [2, 3, 2, 3, 4, 1].findSplitBefore!"a > b"([2, 2]))
     {
-        assert(split[0] == "dlang");
-        assert(split[1] == "-");
-        assert(split[2] == "rocks");
+        assert(split[0] == [2, 3, 2]);
+        // [3, 4] each greater than [2, 2]
+        assert(split[1] == [3, 4, 1]);
     }
     else assert(0);
 }
diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d
index 4248e4b9d44..3a533814894 100644
--- a/libphobos/src/std/conv.d
+++ b/libphobos/src/std/conv.d
@@ -4848,8 +4848,9 @@  private S textImpl(S, U...)(U args)
         static foreach (arg; args)
         {
             static if (
-                isSomeChar!(typeof(arg)) || isSomeString!(typeof(arg)) ||
-                ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) )
+                isSomeChar!(typeof(arg))
+                || isSomeString!(typeof(arg))
+                || ( isInputRange!(typeof(arg)) && isSomeChar!(ElementType!(typeof(arg))) )
             )
                 app.put(arg);
             else static if (
diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d
index 1b4f233d94a..c985015a7af 100644
--- a/libphobos/src/std/range/package.d
+++ b/libphobos/src/std/range/package.d
@@ -1025,7 +1025,18 @@  if (Ranges.length > 0 &&
             }
             else
             {
-                @property bool empty() => frontIndex >= backIndex;
+                @property bool empty()
+                {
+                    if (frontIndex == 0)
+                    {
+                        // special handling: we might be in Range.init state!
+                        // For instance, `format!"%s"` uses Range.init to ensure
+                        // that formatting is possible.
+                        // In that case, we must still behave in an internally consistent way.
+                        return source[0].empty;
+                    }
+                    return frontIndex >= backIndex;
+                }
             }
 
             static if (allSatisfy!(isForwardRange, R))
@@ -1705,6 +1716,17 @@  pure @safe nothrow @nogc unittest
     }
 }
 
+/// https://issues.dlang.org/show_bug.cgi?id=24243
+pure @safe nothrow unittest
+{
+    import std.algorithm.iteration : filter;
+
+    auto range = chain([2], [3].filter!"a");
+
+    // This might happen in format!"%s"(range), for instance.
+    assert(typeof(range).init.empty);
+}
+
 /**
 Choose one of two ranges at runtime depending on a Boolean condition.
 
diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d
index 9903d6c28c3..fec7e5f78ac 100644
--- a/libphobos/src/std/uni/package.d
+++ b/libphobos/src/std/uni/package.d
@@ -7706,6 +7706,12 @@  public:
         return this.tupleof == other.tupleof;
     }
 
+    // Define a default toHash to allow AA usage
+    size_t toHash() const @trusted
+    {
+        return hashOf(slen_, hashOf(small_));
+    }
+
     /++
         True if this object contains valid extended grapheme cluster.
         Decoding primitives of this module always return a valid `Grapheme`.
@@ -7907,6 +7913,12 @@  static assert(Grapheme.sizeof == size_t.sizeof*4);
     assert(equal(h[], iota(cast(int)'A', cast(int)'Z'+1)));
 }
 
+// ensure Grapheme can be used as an AA key.
+@safe unittest
+{
+    int[Grapheme] aa;
+}
+
 /++
     $(P Does basic case-insensitive comparison of `r1` and `r2`.
     This function uses simpler comparison rule thus achieving better performance