-b65767825f365dbc153457fc86e1054b03196c6d
+af92b68a81888702896620db1d10ee477b6b31e8
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
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;
}
}
}
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);
}
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
*/
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)
*/
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;
}
*/
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;
}
*/
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;
}
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;
}
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;
}
}
}
+ 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;
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;
* 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)
}
}
else
- escapeByValue(arg, er, live, retRefTransition);
+ escapeByValue(arg, er, live, true);
}
else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
{
* 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)
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
* 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;
* 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)
{
* 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)
{
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_);
* 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
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;
// @@@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");
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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]);
+}
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);
+}
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();
func10();
func11();
func12();
+ func13();
}
--- /dev/null
+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);
+ }
+}
-b65767825f365dbc153457fc86e1054b03196c6d
+af92b68a81888702896620db1d10ee477b6b31e8
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
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);
}
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)
}
}
+// 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)
{
/**
-92dc5a4e98591a0e6b0af4ff0f84f096fea09016
+c970ca67f25eab9f975da285d6cb4a56902c525a
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
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);
+ }
+}
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
{
@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;
}
}
{
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;
}
}
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,
{
@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;
}
}
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
@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;
}
{
@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;
}
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)
{
}
}
- 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;
}
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.
/// 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)
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
{
@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;
}
}
}
}}
}
+// 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
/// 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;
}
}
/// 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;
}
}
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);
/**
{
@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;
}
}
{
@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;
}
}
}
{
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;
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
}
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;}