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.
This commit is contained in:
Iain Buclaw 2025-03-20 01:09:13 +01:00
parent bc8569984e
commit c6b1b62f11
18 changed files with 508 additions and 66 deletions

View File

@ -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.

View File

@ -1 +1 @@
v2.108.0
v2.108.1

View File

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

View File

@ -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)
{

View File

@ -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_);

View File

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

View File

@ -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");

View File

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

View File

@ -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]);
}

View File

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

View File

@ -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();
}

View File

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

View File

@ -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.

View File

@ -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)
{
/**

View File

@ -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.

View File

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

View File

@ -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

View File

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