Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions compiler/src/dmd/ctfeexpr.d
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,33 @@ bool needToCopyLiteral(const Expression expr) nothrow
}
}

private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null)
// oldelems can have null elements when the ArrayLiteralExp uses 'basis'.
// This preserves the sparse encoding; callers that need dense arrays
// should use copyLiteralArrayExpand.
private Expressions* copyLiteralArray(Expressions* oldelems)
{
if (!oldelems)
return oldelems;
incArrayAllocs();
auto newelems = new Expressions(oldelems.length);
foreach (i, el; *oldelems)
{
(*newelems)[i] = copyLiteral(el ? el : basis).copy();
(*newelems)[i] = el ? copyLiteral(el).copy() : null;
}
return newelems;
}

// Expand null elements using the given basis, for callers that need
// a dense array (e.g. concatenation, AA operations).
private Expressions* copyLiteralArrayExpand(Expressions* oldelems, Expression basis)
{
auto newelems = copyLiteralArray(oldelems);
if (basis && newelems)
{
foreach (ref e; *newelems)
{
if (!e) e = copyLiteral(basis).copy();
}
}
return newelems;
}
Expand All @@ -226,9 +244,10 @@ UnionExp copyLiteral(Expression e)
}
if (auto ale = e.isArrayLiteralExp())
{
auto elements = copyLiteralArray(ale.elements, ale.basis);
auto elements = copyLiteralArray(ale.elements);
auto basis = ale.basis ? copyLiteral(ale.basis).copy() : null;

emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements);
emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, basis, elements);

ArrayLiteralExp r = ue.exp().isArrayLiteralExp();
r.ownedByCtfe = OwnedBy.ctfe;
Expand Down Expand Up @@ -1094,7 +1113,9 @@ private int ctfeCmpArrays(Loc loc, Expression e1, Expression e2, uinteger_t len)
foreach (size_t i; 0 .. cast(size_t)len)
{
Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)];
if (!ee1) ee1 = ae1.basis;
Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)];
if (!ee2) ee2 = ae2.basis;
if (needCmp)
{
const sinteger_t c = ee1.toInteger() - ee2.toInteger();
Expand Down Expand Up @@ -1386,7 +1407,8 @@ UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2)
foreach (size_t i; 0 .. es2.elements.length)
{
Expression es2e = (*es2.elements)[i];
if (es2e.op != EXP.int64)
if (!es2e) es2e = es2.basis;
if (!es2e || es2e.op != EXP.int64)
{
emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
return ue;
Expand Down Expand Up @@ -1416,7 +1438,8 @@ UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2)
foreach (size_t i; 0 .. es2.elements.length)
{
Expression es2e = (*es2.elements)[i];
if (es2e.op != EXP.int64)
if (!es2e) es2e = es2.basis;
if (!es2e || es2e.op != EXP.int64)
{
emplaceExp!(CTFEExp)(&ue, EXP.cantExpression);
return ue;
Expand All @@ -1438,9 +1461,9 @@ UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2)
// [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
ArrayLiteralExp es1 = e1.isArrayLiteralExp();
ArrayLiteralExp es2 = e2.isArrayLiteralExp();
emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements));
emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArrayExpand(es1.elements, es1.basis));
es1 = ue.exp().isArrayLiteralExp();
es1.elements.insert(es1.elements.length, copyLiteralArray(es2.elements));
es1.elements.insert(es1.elements.length, copyLiteralArrayExpand(es2.elements, es2.basis));
return ue;
}
if (e1.op == EXP.arrayLiteral && e2.op == EXP.null_ && t1.nextOf().equals(t2.nextOf()))
Expand Down Expand Up @@ -1508,6 +1531,7 @@ Expression ctfeIndex(UnionExp* pue, Loc loc, Type type, Expression e1, uinteger_
return CTFEExp.cantexp;
}
Expression e = (*ale.elements)[cast(size_t)indx];
if (!e) e = ale.basis;
return paintTypeOntoLiteral(pue, type, e);
}

Expand Down Expand Up @@ -1744,7 +1768,11 @@ Expression changeArrayLiteralLength(UnionExp* pue, Loc loc, TypeArray arrayType,
assert(oldval.op == EXP.arrayLiteral);
ArrayLiteralExp ae = oldval.isArrayLiteralExp();
foreach (size_t i; 0 .. copylen)
(*elements)[i] = (*ae.elements)[indxlo + i];
{
Expression e = (*ae.elements)[indxlo + i];
if (!e) e = ae.basis;
(*elements)[i] = e;
}
}
if (elemType.ty == Tstruct || elemType.ty == Tsarray)
{
Expand Down
21 changes: 18 additions & 3 deletions compiler/src/dmd/dinterpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta
assert(ne);
auto ale = thisarg.isAddrExp().e1.isArrayLiteralExp();
e = (*ale.elements)[cast(size_t)ne.getInteger()];
if (!e) e = ale.basis;
if (auto ae = e.isAddrExp())
{
e = ae.e1;
Expand Down Expand Up @@ -1771,7 +1772,9 @@ public:
assert(result.op == EXP.address);
result = result.isAddrExp().e1;
assert(result.op == EXP.arrayLiteral);
result = (*result.isArrayLiteralExp().elements)[0];
auto rale = result.isArrayLiteralExp();
result = (*rale.elements)[0];
if (!result) result = rale.basis;
if (e.type.ty == Tstruct)
{
result = result.isAddrExp().e1;
Expand Down Expand Up @@ -5541,6 +5544,7 @@ public:
// https://issues.dlang.org/show_bug.cgi?id=14686
foreach (elem; *ale.elements)
{
if (!elem) continue;
Expression ex = evaluatePostblit(istate, elem);
if (exceptionOrCant(ex))
return;
Expand Down Expand Up @@ -5709,7 +5713,9 @@ public:
const indx = cast(size_t)ie.e2.toInteger();
if (indx < ale.elements.length)
{
if (Expression xx = (*ale.elements)[indx])
Expression xx = (*ale.elements)[indx];
if (!xx) xx = ale.basis;
if (xx)
{
if (auto iex = xx.isIndexExp())
origType = iex.e1.type.nextOf();
Expand Down Expand Up @@ -5998,6 +6004,7 @@ public:
if (ae.elements.length == 1)
{
result = (*ae.elements)[0];
if (!result) result = ae.basis;
return;
}
}
Expand Down Expand Up @@ -7161,7 +7168,11 @@ StringExp arrayLiteralToString(ArrayLiteralExp ale)
{
T[] result = new T[len];
foreach (i; 0 .. len)
result[i] = cast(T) (*ale.elements)[i].isIntegerExp().getInteger();
{
auto el = (*ale.elements)[i];
if (!el) el = ale.basis;
result[i] = cast(T) el.isIntegerExp().getInteger();
}
return new StringExp(ale.loc, result[], len, cast(ubyte) size);
}

Expand Down Expand Up @@ -7499,6 +7510,7 @@ private Expression evaluatePostblit(InterState* istate, Expression e)
{
foreach (elem; *ale.elements)
{
if (!elem) continue;
if (auto ex = evaluatePostblit(istate, elem))
return ex;
}
Expand Down Expand Up @@ -7531,7 +7543,10 @@ private Expression evaluateDtor(InterState* istate, Expression e)
if (auto ale = e.isArrayLiteralExp())
{
foreach_reverse (elem; *ale.elements)
{
if (!elem) continue;
e = evaluateDtor(istate, elem);
}
}
else if (e.op == EXP.structLiteral)
{
Expand Down
50 changes: 50 additions & 0 deletions compiler/test/compilable/test_issue23367.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// https://github.com/dlang/dmd/issues/23367
// Tests for sparse array literal optimization: copyLiteral now preserves
// CONSTANT encoding (null elements + basis) instead of expanding to dense.
// Most sparse-array code paths are already covered by existing CTFE tests;
// this test covers the novel paths specific to the implementation.

// ============================================================
// Dynamic array resize filling with array-typed default values.
// The resize copies old elements (which may be null in sparse arrays)
// and fills new slots with the default element.
// ============================================================
alias f = {
int[1][] pieces = [];
pieces.length = 2;
return pieces;
};
static assert(f() == [[0], [0]]);

alias g = {
int[1][] pieces = [];
pieces.length = 3;
pieces[1] = [42];
return pieces;
};
static assert(g() == [[0], [42], [0]]);

// Larger resize: grow from empty to many elements
static int testLargeGrow()
{
int[2][] pieces = [];
pieces.length = 1000;
pieces[0] = [1, 2];
pieces[999] = [3, 4];
return pieces[0][0] + pieces[0][1] +
pieces[999][0] + pieces[999][1] +
pieces[500][0];
}
static assert(testLargeGrow() == 1 + 2 + 3 + 4 + 0);

// ============================================================
// Concatenation of two sparse arrays that each have a basis.
// ============================================================
static int testCatSparse()
{
int[500] a = 1;
int[500] b = 2;
auto c = a ~ b;
return c[0] + c[250] + c[499] + c[500] + c[750] + c[999];
}
static assert(testCatSparse() == 1 + 1 + 1 + 2 + 2 + 2);
Loading