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;}