https://gcc.gnu.org/g:c6b1b62f11ed65b68bc1cebefe686a2fa75eb724

commit r14-11425-gc6b1b62f11ed65b68bc1cebefe686a2fa75eb724
Author: Iain Buclaw <ibuc...@gdcproject.org>
Date:   Thu Mar 20 01:09:13 2025 +0100

    d: Merge upstream dmd, druntime af92b68a81, phobos c970ca67f
    
    D front-end changes:
    
            - Import dmd v2.108.1.
    
    D runtime changes:
    
            - Import druntime v2.108.1.
    
    Phobos changes:
    
            - Import phobos v2.108.1.
    
    gcc/d/ChangeLog:
    
            * dmd/MERGE: Merge upstream dmd af92b68a81.
            * dmd/VERSION: Bump version to v2.108.1.
    
    libphobos/ChangeLog:
    
            * libdruntime/MERGE: Merge upstream druntime af92b68a81.
            * src/MERGE: Merge upstream phobos c970ca67f.

Diff:
---
 gcc/d/dmd/MERGE                                    |   2 +-
 gcc/d/dmd/VERSION                                  |   2 +-
 gcc/d/dmd/cparse.d                                 |  70 ++++--
 gcc/d/dmd/escape.d                                 |  16 +-
 gcc/d/dmd/expressionsem.d                          |   3 +-
 gcc/d/dmd/initsem.d                                |  17 +-
 gcc/d/dmd/traits.d                                 |   2 +-
 .../gdc.test/compilable/returnscope_without_safe.d |  16 ++
 gcc/testsuite/gdc.test/compilable/test24479.d      |  35 +++
 .../gdc.test/runnable/imports/issue18919b.d        |   6 +
 gcc/testsuite/gdc.test/runnable/issue18919.d       |   4 +
 gcc/testsuite/gdc.test/runnable/test24498.d        |  21 ++
 libphobos/libdruntime/MERGE                        |   2 +-
 .../libdruntime/core/internal/array/construction.d |  17 +-
 libphobos/src/MERGE                                |   2 +-
 libphobos/src/std/internal/test/range.d            |  91 +++++++
 libphobos/src/std/logger/core.d                    |   6 +-
 libphobos/src/std/range/package.d                  | 262 ++++++++++++++++++---
 18 files changed, 508 insertions(+), 66 deletions(-)

diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index dc47db87a80c..4041ed9ea765 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-b65767825f365dbc153457fc86e1054b03196c6d
+af92b68a81888702896620db1d10ee477b6b31e8
 
 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 5868b8749552..99582f5e05fa 100644
--- a/gcc/d/dmd/VERSION
+++ b/gcc/d/dmd/VERSION
@@ -1 +1 @@
-v2.108.0
+v2.108.1
diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d
index aeedb493efc3..bfd28c475202 100644
--- a/gcc/d/dmd/cparse.d
+++ b/gcc/d/dmd/cparse.d
@@ -1919,6 +1919,14 @@ final class CParser(AST) : Parser!AST
                 auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), 
specifier);
                 typedefTab.setDim(typedefTabLengthSave);
                 symbols = symbolsSave;
+                if (specifier.mod & MOD.x__stdcall)
+                {
+                    // If this function is __stdcall, wrap it in a 
LinkDeclaration so that
+                    // it's extern(Windows) when imported in D.
+                    auto decls = new AST.Dsymbols(1);
+                    (*decls)[0] = s;
+                    s = new AST.LinkDeclaration(s.loc, LINK.windows, decls);
+                }
                 symbols.push(s);
                 return;
             }
@@ -2071,13 +2079,14 @@ final class CParser(AST) : Parser!AST
                     }
                 }
                 s = applySpecifier(s, specifier);
-                if (level == LVL.local)
+                if (level == LVL.local || (specifier.mod & MOD.x__stdcall))
                 {
-                    // Wrap the declaration in `extern (C) { declaration }`
+                    // Wrap the declaration in `extern (C/Windows) { 
declaration }`
                     // Necessary for function pointers, but harmless to apply 
to all.
                     auto decls = new AST.Dsymbols(1);
                     (*decls)[0] = s;
-                    s = new AST.LinkDeclaration(s.loc, linkage, decls);
+                    const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows 
: linkage;
+                    s = new AST.LinkDeclaration(s.loc, lkg, decls);
                 }
                 symbols.push(s);
             }
@@ -5860,13 +5869,15 @@ final class CParser(AST) : Parser!AST
 
         const(char)* endp = &slice[length - 7];
 
+        AST.Dsymbols newSymbols;
+
         size_t[void*] defineTab;    // hash table of #define's turned into 
Symbol's
-                                    // indexed by Identifier, returns index 
into symbols[]
+                                    // indexed by Identifier, returns index 
into newSymbols[]
                                     // The memory for this is leaked
 
-        void addVar(AST.Dsymbol s)
+        void addSym(AST.Dsymbol s)
         {
-            //printf("addVar() %s\n", s.toChars());
+            //printf("addSym() %s\n", s.toChars());
             if (auto v = s.isVarDeclaration())
                 v.isCmacro(true);       // mark it as coming from a C #define
             /* If it's already defined, replace the earlier
@@ -5874,13 +5885,22 @@ final class CParser(AST) : Parser!AST
              */
             if (size_t* pd = cast(void*)s.ident in defineTab)
             {
-                //printf("replacing %s\n", v.toChars());
-                (*symbols)[*pd] = s;
+                //printf("replacing %s\n", s.toChars());
+                newSymbols[*pd] = s;
                 return;
             }
-            assert(symbols, "symbols is null");
-            defineTab[cast(void*)s.ident] = symbols.length;
-            symbols.push(s);
+            defineTab[cast(void*)s.ident] = newSymbols.length;
+            newSymbols.push(s);
+        }
+
+        void removeSym(Identifier ident)
+        {
+            //printf("removeSym() %s\n", ident.toChars());
+            if (size_t* pd = cast(void*)ident in defineTab)
+            {
+                //printf("removing %s\n", ident.toChars());
+                newSymbols[*pd] = null;
+            }
         }
 
         while (p < endp)
@@ -5924,7 +5944,7 @@ final class CParser(AST) : Parser!AST
                                  */
                                 AST.Expression e = new AST.IntegerExp(scanloc, 
intvalue, t);
                                 auto v = new AST.VarDeclaration(scanloc, t, 
id, new AST.ExpInitializer(scanloc, e), STC.manifest);
-                                addVar(v);
+                                addSym(v);
                                 ++p;
                                 continue;
                             }
@@ -5947,7 +5967,7 @@ final class CParser(AST) : Parser!AST
                                  */
                                 AST.Expression e = new AST.RealExp(scanloc, 
floatvalue, t);
                                 auto v = new AST.VarDeclaration(scanloc, t, 
id, new AST.ExpInitializer(scanloc, e), STC.manifest);
-                                addVar(v);
+                                addSym(v);
                                 ++p;
                                 continue;
                             }
@@ -5965,7 +5985,7 @@ final class CParser(AST) : Parser!AST
                                  */
                                 AST.Expression e = new AST.StringExp(scanloc, 
str[0 .. len], len, 1, postfix);
                                 auto v = new AST.VarDeclaration(scanloc, null, 
id, new AST.ExpInitializer(scanloc, e), STC.manifest);
-                                addVar(v);
+                                addSym(v);
                                 ++p;
                                 continue;
                             }
@@ -6001,7 +6021,7 @@ final class CParser(AST) : Parser!AST
                             AST.TemplateParameters* tpl = new 
AST.TemplateParameters();
                             AST.Expression constraint = null;
                             auto tempdecl = new 
AST.TemplateDeclaration(exp.loc, id, tpl, constraint, decldefs, false);
-                            addVar(tempdecl);
+                            addSym(tempdecl);
                             ++p;
                             continue;
                         }
@@ -6092,7 +6112,7 @@ final class CParser(AST) : Parser!AST
                             AST.Dsymbols* decldefs = new AST.Dsymbols();
                             decldefs.push(fd);
                             auto tempdecl = new 
AST.TemplateDeclaration(exp.loc, id, tpl, null, decldefs, false);
-                            addVar(tempdecl);
+                            addSym(tempdecl);
 
                             ++p;
                             continue;
@@ -6103,6 +6123,14 @@ final class CParser(AST) : Parser!AST
                     }
                 }
             }
+            else if (p[0 .. 6] == "#undef")
+            {
+                p += 6;
+                nextToken();
+                //printf("undef %s\n", token.toChars());
+                if (token.value == TOK.identifier)
+                    removeSym(token.ident);
+            }
             // scan to end of line
             while (*p)
                 ++p;
@@ -6110,6 +6138,16 @@ final class CParser(AST) : Parser!AST
             scanloc.linnum = scanloc.linnum + 1;
         }
 
+        if (newSymbols.length)
+        {
+            assert(symbols, "symbols is null");
+            symbols.reserve(newSymbols.length);
+
+            foreach (sym; newSymbols)
+                if (sym) // undefined entries are null
+                    symbols.push(sym);
+        }
+
         scanloc = scanlocSave;
         eSink = save;
         defines = buf;
diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d
index 3e17ff4736d3..1057b9a46bf7 100644
--- a/gcc/d/dmd/escape.d
+++ b/gcc/d/dmd/escape.d
@@ -1542,7 +1542,7 @@ private bool inferReturn(FuncDeclaration fd, 
VarDeclaration v, bool returnScope)
  *      e = expression to be returned by value
  *      er = where to place collected data
  *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, 
etc., all return `p`.
-  *     retRefTransition = if `e` is returned through a `return ref scope` 
function call
+  *     retRefTransition = if `e` is returned through a `return (ref) scope` 
function call
  */
 public
 void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool 
retRefTransition = false)
@@ -1786,7 +1786,7 @@ void escapeByValue(Expression e, EscapeByResults* er, 
bool live = false, bool re
                                     }
                         }
                         else
-                            escapeByValue(arg, er, live, retRefTransition);
+                            escapeByValue(arg, er, live, true);
                     }
                     else if (psr == ScopeRef.ReturnRef || psr == 
ScopeRef.ReturnRef_Scope)
                     {
@@ -1941,7 +1941,7 @@ void escapeByValue(Expression e, EscapeByResults* er, 
bool live = false, bool re
  *      e = expression to be returned by 'ref'
  *      er = where to place collected data
  *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, 
etc., all return `p`.
- *      retRefTransition = if `e` is returned through a `return ref scope` 
function call
+ *      retRefTransition = if `e` is returned through a `return (ref) scope` 
function call
  */
 private
 void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool 
retRefTransition = false)
@@ -2189,7 +2189,7 @@ struct EscapeByResults
     import dmd.root.array: Array;
 
     /**
-     * Whether the variable / expression went through a `return ref scope` 
function call
+     * Whether the variable / expression went through a `return (ref) scope` 
function call
      *
      * This is needed for the dip1000 by default transition, since the rules 
for
      * disambiguating `return scope ref` have changed. Therefore, functions in 
legacy code
@@ -2197,6 +2197,10 @@ struct EscapeByResults
      * are being escaped, which is an error even in `@system` code. By keeping 
track of this
      * information, variables escaped through `return ref` can be treated as a 
deprecation instead
      * of error, see test/fail_compilation/dip1000_deprecation.d
+     *
+     * Additionally, return scope can be inferred wrongly instead of scope, in 
which
+     * case the code could give false positives even without @safe or dip1000:
+     * https://issues.dlang.org/show_bug.cgi?id=23657
      */
     private Array!bool refRetRefTransition;
     private Array!bool expRetRefTransition;
@@ -2218,7 +2222,7 @@ struct EscapeByResults
      * Escape variable `v` by reference
      * Params:
      *   v = variable to escape
-     *   retRefTransition = `v` is escaped through a `return ref scope` 
function call
+     *   retRefTransition = `v` is escaped through a `return (ref) scope` 
function call
      */
     void pushRef(VarDeclaration v, bool retRefTransition)
     {
@@ -2230,7 +2234,7 @@ struct EscapeByResults
      * Escape a reference to expression `e`
      * Params:
      *   e = expression to escape
-     *   retRefTransition = `e` is escaped through a `return ref scope` 
function call
+     *   retRefTransition = `e` is escaped through a `return (ref) scope` 
function call
      */
     void pushExp(Expression e, bool retRefTransition)
     {
diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d
index a6425d311439..4c1067832105 100644
--- a/gcc/d/dmd/expressionsem.d
+++ b/gcc/d/dmd/expressionsem.d
@@ -5335,7 +5335,8 @@ private extern (C++) final class 
ExpressionSemanticVisitor : Visitor
                 lowering = new DotIdExp(exp.loc, lowering, Id.object);
 
                 auto tbn = exp.type.nextOf();
-                while (tbn.ty == Tarray)
+                size_t i = nargs;
+                while (tbn.ty == Tarray && --i)
                     tbn = tbn.nextOf();
                 auto unqualTbn = tbn.unqualify(MODFlags.wild | MODFlags.const_ 
|
                     MODFlags.immutable_ | MODFlags.shared_);
diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d
index b07699e19fe2..8faad30f5391 100644
--- a/gcc/d/dmd/initsem.d
+++ b/gcc/d/dmd/initsem.d
@@ -868,11 +868,13 @@ Initializer initializerSemantic(Initializer init, Scope* 
sc, ref Type tx, NeedIn
                          * by the initializer syntax. if a CInitializer has a 
Designator, it is probably
                          * a nested anonymous struct
                          */
-                        if (cix.initializerList.length)
+                        int found;
+                        foreach (dix; cix.initializerList)
                         {
-                            DesigInit dix = cix.initializerList[0];
                             Designators* dlistx = dix.designatorList;
-                            if (dlistx && (*dlistx).length == 1 && 
(*dlistx)[0].ident)
+                            if (!dlistx)
+                                continue;
+                            if ((*dlistx).length == 1 && (*dlistx)[0].ident)
                             {
                                 auto id = (*dlistx)[0].ident;
                                 foreach (k, f; sd.fields[])         // linear 
search for now
@@ -883,11 +885,18 @@ Initializer initializerSemantic(Initializer init, Scope* 
sc, ref Type tx, NeedIn
                                         si.addInit(id, dix.initializer);
                                         ++fieldi;
                                         ++index;
-                                        continue Loop1;
+                                        ++found;
+                                        break;
                                     }
                                 }
                             }
+                            else {
+                                error(ci.loc, "only 1 designator currently 
allowed for C struct field initializer `%s`", toChars(ci));
+                            }
                         }
+
+                        if (found == cix.initializerList.length)
+                            continue Loop1;
                     }
 
                     VarDeclaration field;
diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d
index be7aa9923c03..6fb46afac21c 100644
--- a/gcc/d/dmd/traits.d
+++ b/gcc/d/dmd/traits.d
@@ -1245,7 +1245,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
             // @@@DEPRECATION 2.100.2
             if (auto td = s.isTemplateDeclaration())
             {
-                if (td.overnext || td.overroot)
+                if (td.overnext)
                 {
                     deprecation(e.loc, "`__traits(getAttributes)` may only be 
used for individual functions, not the overload set `%s`", td.ident.toChars());
                     deprecationSupplemental(e.loc, "the result of 
`__traits(getOverloads)` may be used to select the desired function to extract 
attributes from");
diff --git a/gcc/testsuite/gdc.test/compilable/returnscope_without_safe.d 
b/gcc/testsuite/gdc.test/compilable/returnscope_without_safe.d
new file mode 100644
index 000000000000..7a84e65b67c6
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/returnscope_without_safe.d
@@ -0,0 +1,16 @@
+// Stack pointers are being escaped here, but without
+// @safe and dip1000, it should still be allowed
+// because return scope could have been inferred incorrectly,
+// and it breaks existing code:
+// https://issues.dlang.org/show_bug.cgi?id=23657
+
+int* identity(return scope int* x);
+
+auto identityAuto(int* x) => x;
+
+int* f()
+{
+    int x;
+    return identity(&x);
+    return identityAuto(&x);
+}
diff --git a/gcc/testsuite/gdc.test/compilable/test24479.d 
b/gcc/testsuite/gdc.test/compilable/test24479.d
new file mode 100644
index 000000000000..7865c1b60741
--- /dev/null
+++ b/gcc/testsuite/gdc.test/compilable/test24479.d
@@ -0,0 +1,35 @@
+// https://issues.dlang.org/show_bug.cgi?id=24479
+
+/*
+TEST_OUTPUT:
+---
+1
+2
+---
+*/
+
+struct S
+{
+    @1
+    S opBinary(string op: "-")(S rhs) const pure nothrow @nogc
+    {
+        return rhs;
+    }
+    @2
+    S opBinary(string op: "*")(S dur) const pure nothrow @nogc
+    {
+        return dur;
+    }
+}
+
+private enum hasExternalUDA(alias A) = is(A == External) || is(typeof(A) == 
External);
+
+void foo()
+{
+    static foreach (t; __traits(getOverloads, S, "opBinary", true))
+        static foreach(attr; __traits(getAttributes, t))
+            pragma(msg, attr);
+
+    static assert(__traits(getOverloads, S, "opBinary", true).length == 2);
+    alias A = __traits(getAttributes, __traits(getOverloads, S, "opBinary", 
true)[1]);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/imports/issue18919b.d 
b/gcc/testsuite/gdc.test/runnable/imports/issue18919b.d
index 4278f7f5cdee..b009298e66f4 100644
--- a/gcc/testsuite/gdc.test/runnable/imports/issue18919b.d
+++ b/gcc/testsuite/gdc.test/runnable/imports/issue18919b.d
@@ -248,3 +248,9 @@ void func12(const(char)*[] args = [baseName(__FILE__.ptr),
         printf(" %s", arg);
     printf("\n");
 }
+
+// https://issues.dlang.org/show_bug.cgi?id=24519
+void func13(string file = __FILE__[])
+{
+    printf("%s: %s\n", __FUNCTION__.ptr, file.ptr);
+}
diff --git a/gcc/testsuite/gdc.test/runnable/issue18919.d 
b/gcc/testsuite/gdc.test/runnable/issue18919.d
index 815e018d649c..9f3df62a7be3 100644
--- a/gcc/testsuite/gdc.test/runnable/issue18919.d
+++ b/gcc/testsuite/gdc.test/runnable/issue18919.d
@@ -20,10 +20,13 @@ imports.issue18919b.func9.fp: issue18919b.d:216 
imports.issue18919b
 imports.issue18919b.func10: expr1=imports.issue18919b, 
expr2=imports.issue18919b
 imports.issue18919b.func11: issue18919b.d:233   imports.issue18919b
 imports.issue18919b.func12: issue18919.d issue18919.main void 
issue18919.main() issue18919
+imports.issue18919b.func13: runnable/issue18919.d
 ---
 */
 import imports.issue18919b;
 
+#line 26
+
 void main()
 {
     func1();
@@ -44,4 +47,5 @@ void main()
     func10();
     func11();
     func12();
+    func13();
 }
diff --git a/gcc/testsuite/gdc.test/runnable/test24498.d 
b/gcc/testsuite/gdc.test/runnable/test24498.d
new file mode 100644
index 000000000000..6c48e269f171
--- /dev/null
+++ b/gcc/testsuite/gdc.test/runnable/test24498.d
@@ -0,0 +1,21 @@
+import core.memory;
+
+void main()
+{
+    {
+        int[][] a = new int[][](2, 2);
+        assert(!(GC.getAttr(a.ptr) & GC.BlkAttr.NO_SCAN));
+        assert(GC.getAttr(a[0].ptr) & GC.BlkAttr.NO_SCAN);
+    }
+    {
+        void*[][] a = new void*[][](2, 2);
+        assert(!(GC.getAttr(a.ptr) & GC.BlkAttr.NO_SCAN));
+        assert(!(GC.getAttr(a[0].ptr) & GC.BlkAttr.NO_SCAN));
+    }
+    {
+        int[][][] a = new int[][][](2, 2);
+        assert(!(GC.getAttr(a.ptr) & GC.BlkAttr.NO_SCAN));
+        assert(!(GC.getAttr(a[0].ptr) & GC.BlkAttr.NO_SCAN));
+        assert(a[0][0].ptr is null);
+    }
+}
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index dc47db87a80c..4041ed9ea765 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
-b65767825f365dbc153457fc86e1054b03196c6d
+af92b68a81888702896620db1d10ee477b6b31e8
 
 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/internal/array/construction.d 
b/libphobos/libdruntime/core/internal/array/construction.d
index 0950b5a5aa90..8f0323a054b4 100644
--- a/libphobos/libdruntime/core/internal/array/construction.d
+++ b/libphobos/libdruntime/core/internal/array/construction.d
@@ -526,7 +526,7 @@ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool 
isShared=false) @trust
 
         auto dim = dims[0];
 
-        debug(PRINTF) printf("__allocateInnerArray(ti = %p, ti.next = %p, dim 
= %d, ndims = %d\n", ti, ti.next, dim, dims.length);
+        debug(PRINTF) printf("__allocateInnerArray(UnqT = %s, dim = %lu, ndims 
= %lu\n", UnqT.stringof.ptr, dim, dims.length);
         if (dims.length == 1)
         {
             auto r = _d_newarrayT!UnqT(dim, isShared);
@@ -534,8 +534,9 @@ Tarr _d_newarraymTX(Tarr : U[], T, U)(size_t[] dims, bool 
isShared=false) @trust
         }
 
         auto allocSize = (void[]).sizeof * dim;
-        auto info = __arrayAlloc!UnqT(allocSize);
-        __setArrayAllocLength!UnqT(info, allocSize, isShared);
+        // the array-of-arrays holds pointers! Don't use UnqT here!
+        auto info = __arrayAlloc!(void[])(allocSize);
+        __setArrayAllocLength!(void[])(info, allocSize, isShared);
         auto p = __arrayStart(info)[0 .. dim];
 
         foreach (i; 0..dim)
@@ -579,6 +580,16 @@ unittest
     }
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24436
+@system unittest
+{
+    import core.memory : GC;
+
+    int[][] a = _d_newarraymTX!(int[][], int)([2, 2]);
+
+    assert(!(GC.getAttr(a.ptr) & GC.BlkAttr.NO_SCAN));
+}
+
 version (D_ProfileGC)
 {
     /**
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index a4f25db810ed..1766bd84795a 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-92dc5a4e98591a0e6b0af4ff0f84f096fea09016
+c970ca67f25eab9f975da285d6cb4a56902c525a
 
 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/internal/test/range.d 
b/libphobos/src/std/internal/test/range.d
index 6aa9676abf1f..4a5ff721d190 100644
--- a/libphobos/src/std/internal/test/range.d
+++ b/libphobos/src/std/internal/test/range.d
@@ -23,3 +23,94 @@ module std.internal.test.range;
     auto r = R().chunks(3);
     assert(r.equal!equal([[ 0, 1, 2 ], [ 3, 4 ]]));
 }
+
+// https://issues.dlang.org/show_bug.cgi?id=24415
+@safe unittest
+{
+    import std.range : only;
+
+    static struct S(T)
+    {
+        T i;
+
+        this(ref return scope inout(S) rhs) scope @safe inout pure nothrow
+        {
+            i = rhs.i;
+        }
+    }
+    {
+        auto a = only(S!int(42));
+        auto b = a;
+        assert(!b.empty);
+        assert(b.front == S!int(42));
+
+        a.popFront();
+        auto c = a;
+        assert(c.empty);
+    }
+    {
+        auto a = only(S!(const int)(42));
+        auto b = a;
+        assert(!b.empty);
+        assert(b.front == S!(const int)(42));
+
+        a.popFront();
+        auto c = a;
+        assert(c.empty);
+    }
+    {
+        auto a = only(S!(immutable int)(42));
+        auto b = a;
+        assert(!b.empty);
+        assert(b.front == S!(immutable int)(42));
+
+        a.popFront();
+        auto c = a;
+        assert(c.empty);
+    }
+    {
+        auto a = only(S!int(42), S!int(192));
+        auto b = a;
+        assert(!b.empty);
+        assert(b.front == S!int(42));
+
+        a.popFront();
+        auto c = a;
+        assert(!c.empty);
+        assert(c.front == S!int(192));
+
+        a.popFront();
+        auto d = a;
+        assert(d.empty);
+    }
+    {
+        auto a = only(S!(const int)(42), S!(const int)(192));
+        auto b = a;
+        assert(!b.empty);
+        assert(b.front == S!(const int)(42));
+
+        a.popFront();
+        auto c = a;
+        assert(!c.empty);
+        assert(c.front == S!(const int)(192));
+
+        a.popFront();
+        auto d = a;
+        assert(d.empty);
+    }
+    {
+        auto a = only(S!(immutable int)(42), S!(immutable int)(192));
+        auto b = a;
+        assert(!b.empty);
+        assert(b.front == S!(immutable int)(42));
+
+        a.popFront();
+        auto c = a;
+        assert(!c.empty);
+        assert(c.front == S!(immutable int)(192));
+
+        a.popFront();
+        auto d = a;
+        assert(d.empty);
+    }
+}
diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d
index 846f6ee214ab..0633bddee81a 100644
--- a/libphobos/src/std/logger/core.d
+++ b/libphobos/src/std/logger/core.d
@@ -1473,15 +1473,15 @@ if (sharedLog !is myLogger)
     atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger));
 }
 
-/** This methods get and set the global `LogLevel`.
+/** These methods get and set the global `LogLevel`.
 
-Every log message with a `LogLevel` lower as the global `LogLevel`
+Every log message with a `LogLevel` lower than the global `LogLevel`
 will be discarded before it reaches `writeLogMessage` method of any
 `Logger`.
 */
 /* Implementation note:
 For any public logging call, the global log level shall only be queried once on
-entry. Otherwise when another threads changes the level, we would work with
+entry. Otherwise when another thread changes the level, we would work with
 different levels at different spots in the code.
 */
 @property LogLevel globalLogLevel() @safe @nogc
diff --git a/libphobos/src/std/range/package.d 
b/libphobos/src/std/range/package.d
index 30f6ffb9f44e..9f530418a85f 100644
--- a/libphobos/src/std/range/package.d
+++ b/libphobos/src/std/range/package.d
@@ -313,16 +313,18 @@ if (isBidirectionalRange!(Unqual!Range))
             {
                 @property void front(ElementType!R val)
                 {
-                    import std.algorithm.mutation : move;
+                    import core.lifetime : forward;
 
-                    source.back = move(val);
+                    // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                    source.back = __ctfe ? val : forward!val;
                 }
 
                 @property void back(ElementType!R val)
                 {
-                    import std.algorithm.mutation : move;
+                    import core.lifetime : forward;
 
-                    source.front = move(val);
+                    // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                    source.front = __ctfe ? val : forward!val;
                 }
             }
 
@@ -334,9 +336,10 @@ if (isBidirectionalRange!(Unqual!Range))
                 {
                     void opIndexAssign(ElementType!R val, size_t n)
                     {
-                        import std.algorithm.mutation : move;
+                        import core.lifetime : forward;
 
-                        source[retroIndex(n)] = move(val);
+                        // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                        source[retroIndex(n)] = __ctfe ? val : forward!val;
                     }
                 }
 
@@ -494,6 +497,32 @@ pure @safe nothrow unittest
     assert(equal(r, [S(3), S(2), S(1)]));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), 
Handle(4)];
+    auto range = arr[].retro();
+
+    called = false;
+    range.front = Handle(42);
+    assert(called);
+
+    called = false;
+    range.back = Handle(42);
+    assert(called);
+
+    called = false;
+    range[2] = Handle(42);
+    assert(called);
+}
+
 /**
 Iterates range `r` with stride `n`. If the range is a
 random-access range, moves by indexing into the range; otherwise,
@@ -604,9 +633,10 @@ do
             {
                 @property void front(ElementType!R val)
                 {
-                    import std.algorithm.mutation : move;
+                    import core.lifetime : forward;
 
-                    source.front = move(val);
+                    // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                    source.front = __ctfe ? val : forward!val;
                 }
             }
 
@@ -899,6 +929,24 @@ pure @safe nothrow unittest
     assert(equal(r, [S(1), S(4)]));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), 
Handle(4)];
+    auto range = arr[].stride(2);
+
+    called = false;
+    range.front = Handle(42);
+    assert(called);
+}
+
 /**
 Spans multiple ranges in sequence. The function `chain` takes any
 number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
@@ -1120,14 +1168,15 @@ if (Ranges.length > 0 &&
 
                 @property void front(RvalueElementType v)
                 {
-                    import std.algorithm.mutation : move;
+                    import core.lifetime : forward;
 
                     sw: switch (frontIndex)
                     {
                         static foreach (i; 0 .. R.length)
                         {
                         case i:
-                            source[i].front = move(v);
+                            // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                            source[i].front = __ctfe ? v : forward!v;
                             break sw;
                         }
 
@@ -1246,14 +1295,15 @@ if (Ranges.length > 0 &&
                 {
                     @property void back(RvalueElementType v)
                     {
-                        import std.algorithm.mutation : move;
+                        import core.lifetime : forward;
 
                         sw: switch (backIndex)
                         {
                             static foreach_reverse (i; 1 .. R.length + 1)
                             {
                             case i:
-                                source[i-1].back = move(v);
+                                // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                                source[i - 1].back = __ctfe ? v : forward!v;
                                 break sw;
                             }
 
@@ -1359,7 +1409,7 @@ if (Ranges.length > 0 &&
                 static if (allSameType && allSatisfy!(hasAssignableElements, 
R))
                     void opIndexAssign(ElementType v, size_t index)
                     {
-                        import std.algorithm.mutation : move;
+                        import core.lifetime : forward;
 
                         sw: switch (frontIndex)
                         {
@@ -1376,7 +1426,8 @@ if (Ranges.length > 0 &&
                                     }
                                 }
 
-                                source[i][index] = move(v);
+                                // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                                source[i][index] = __ctfe ? v : forward!v;
                                 break sw;
                             }
 
@@ -1727,6 +1778,32 @@ pure @safe nothrow unittest
     assert(typeof(range).init.empty);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), 
Handle(4)];
+    auto range = arr[0 .. 2].chain(arr[4 .. 5]);
+
+    called = false;
+    range.front = Handle(42);
+    assert(called);
+
+    called = false;
+    range.back = Handle(42);
+    assert(called);
+
+    called = false;
+    range[2] = Handle(42);
+    assert(called);
+}
+
 /**
 Choose one of two ranges at runtime depending on a Boolean condition.
 
@@ -2694,12 +2771,14 @@ if (isInputRange!(Unqual!Range) &&
         /// ditto
         @property void front(ElementType!R v)
         {
-            import std.algorithm.mutation : move;
+            import core.lifetime : forward;
 
             assert(!empty,
                 "Attempting to assign to the front of an empty "
                 ~ Take.stringof);
-            source.front = move(v);
+
+            // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+            source.front = __ctfe ? v : forward!v;
         }
 
     static if (hasMobileElements!R)
@@ -2996,6 +3075,25 @@ pure @safe nothrow @nogc unittest
     assert(r.take(2).equal(repeat(1, 2)));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    import std.algorithm.iteration : filter;
+
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), 
Handle(4)];
+    auto range = arr[].filter!(a => true)().take(3);
+
+    called = false;
+    range.front = Handle(42);
+    assert(called);
+}
 
 /**
 Similar to $(LREF take), but assumes that `range` has at least $(D
@@ -3075,12 +3173,14 @@ if (isInputRange!R)
             {
                 @property auto ref front(ElementType!R v)
                 {
-                    import std.algorithm.mutation : move;
+                    import core.lifetime : forward;
 
                     assert(!empty,
                         "Attempting to assign to the front of an empty "
                         ~ typeof(this).stringof);
-                    return _input.front = move(v);
+
+                    // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                    return _input.front = __ctfe ? v : forward!v;
                 }
             }
         }
@@ -3217,6 +3317,26 @@ pure @safe nothrow unittest
     }}
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    import std.algorithm.iteration : filter;
+
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[5] arr = [Handle(0), Handle(1), Handle(2), Handle(3), 
Handle(4)];
+    auto range = arr[].filter!(a => true)().takeExactly(3);
+
+    called = false;
+    range.front = Handle(42);
+    assert(called);
+}
+
 /**
 Returns a range with at most one element; for example, $(D
 takeOne([42, 43, 44])) returns a range consisting of the integer $(D
@@ -4310,9 +4430,10 @@ if (isForwardRange!R && !isInfinite!R)
             /// ditto
             @property void front(ElementType!R val)
             {
-                import std.algorithm.mutation : move;
+                import core.lifetime : forward;
 
-                _original[_index] = move(val);
+                // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                _original[_index] = __ctfe ? val : forward!val;
             }
         }
 
@@ -4422,9 +4543,10 @@ if (isForwardRange!R && !isInfinite!R)
             /// ditto
             @property auto front(ElementType!R val)
             {
-                import std.algorithm.mutation : move;
+                import core.lifetime : forward;
 
-                return _current.front = move(val);
+                // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                return _current.front = __ctfe ? val : forward!val;
             }
         }
 
@@ -4767,6 +4889,35 @@ pure @safe unittest
     assert(equal(r.save, "foof"));
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    import std.algorithm.iteration : filter;
+
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[3] arr = [Handle(0), Handle(1), Handle(2)];
+    {
+        auto range = arr[].cycle().take(5);
+
+        called = false;
+        range.front = Handle(42);
+        assert(called);
+    }
+    {
+        auto range = arr[].filter!(a => true)().cycle().take(5);
+
+        called = false;
+        range.front = Handle(42);
+        assert(called);
+    }
+}
+
 private alias lengthType(R) = typeof(R.init.length.init);
 
 /**
@@ -7438,9 +7589,10 @@ struct FrontTransversal(Ror,
     {
         @property void front(ElementType val)
         {
-            import std.algorithm.mutation : move;
+            import core.lifetime : forward;
 
-            _input.front.front = move(val);
+            // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+            _input.front.front = __ctfe ? val : forward!val;
         }
     }
 
@@ -7497,9 +7649,10 @@ struct FrontTransversal(Ror,
         {
             @property void back(ElementType val)
             {
-                import std.algorithm.mutation : move;
+                import core.lifetime : forward;
 
-                _input.back.front = move(val);
+                // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                _input.back.front = __ctfe ? val : forward!val;
             }
         }
     }
@@ -7532,9 +7685,10 @@ struct FrontTransversal(Ror,
         {
             void opIndexAssign(ElementType val, size_t n)
             {
-                import std.algorithm.mutation : move;
+                import core.lifetime : forward;
 
-                _input[n].front = move(val);
+                // __ctfe check is workaround for 
https://issues.dlang.org/show_bug.cgi?id=21542
+                _input[n].front = __ctfe ? val : forward!val;
             }
         }
         mixin ImplementLength!_input;
@@ -7675,6 +7829,50 @@ pure @safe unittest
     assert(ft.empty);
 }
 
+// https://issues.dlang.org/show_bug.cgi?id=24481
+@safe unittest
+{
+    bool called;
+    struct Handle
+    {
+        int entry;
+        void opAssign()(auto ref const(typeof(this)) that) const { called = 
true; }
+    }
+
+    const(Handle)[][] arr = [[Handle(0), Handle(10)],
+                             [Handle(1), Handle(11)],
+                             [Handle(2), Handle(12)],
+                             [Handle(3), Handle(13)],
+                             [Handle(4), Handle(14)]];
+
+    {
+        auto range = arr.frontTransversal();
+
+        called = false;
+        range.front = Handle(42);
+        assert(called == true);
+
+        called = false;
+        range.back = Handle(42);
+        assert(called == true);
+    }
+    {
+        auto range = 
arr.frontTransversal!(TransverseOptions.assumeNotJagged)();
+
+        called = false;
+        range.front = Handle(42);
+        assert(called == true);
+
+        called = false;
+        range.back = Handle(42);
+        assert(called == true);
+
+        called = false;
+        range[0] = Handle(42);
+        assert(called == true);
+    }
+}
+
 /**
     Given a range of ranges, iterate transversally through the
     `n`th element of each of the enclosed ranges. This function
@@ -10375,6 +10573,14 @@ private struct OnlyResult(T)
     }
     alias opDollar = length;
 
+    // FIXME Workaround for https://issues.dlang.org/show_bug.cgi?id=24415
+    import std.traits : hasElaborateCopyConstructor;
+    static if (hasElaborateCopyConstructor!T)
+    {
+        private static struct WorkaroundBugzilla24415 {}
+        public this()(WorkaroundBugzilla24415) {}
+    }
+
     private this()(return scope auto ref T value)
     {
         ref @trusted unqual(ref T x){return cast() x;}

Reply via email to