*
* Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE))
*
- * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
+ * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d, _dinterpret.d)
import dmd.identifier;
import dmd.init;
import dmd.initsem;
+import dmd.location;
import dmd.mtype;
import dmd.printast;
import dmd.root.rmem;
return e;
}
-public extern (C++) Expression getValue(VarDeclaration vd)
+public Expression getValue(VarDeclaration vd)
{
return ctfeGlobals.stack.getValue(vd);
}
Expression localThis; // value of 'this', or NULL if none
public:
- extern (C++) size_t stackPointer()
+ size_t stackPointer()
{
- return values.dim;
+ return values.length;
}
// The current value of 'this', or NULL if none
- extern (C++) Expression getThis()
+ Expression getThis()
{
return localThis;
}
// Largest number of stack positions we've used
- extern (C++) size_t maxStackUsage()
+ size_t maxStackUsage()
{
return maxStackPointer;
}
// Start a new stack frame, using the provided 'this'.
- extern (C++) void startFrame(Expression thisexp)
+ void startFrame(Expression thisexp)
{
frames.push(cast(void*)cast(size_t)framepointer);
savedThis.push(localThis);
localThis = thisexp;
}
- extern (C++) void endFrame()
+ void endFrame()
{
- size_t oldframe = cast(size_t)frames[frames.dim - 1];
- localThis = savedThis[savedThis.dim - 1];
+ size_t oldframe = cast(size_t)frames[frames.length - 1];
+ localThis = savedThis[savedThis.length - 1];
popAll(framepointer);
framepointer = oldframe;
- frames.setDim(frames.dim - 1);
- savedThis.setDim(savedThis.dim - 1);
+ frames.setDim(frames.length - 1);
+ savedThis.setDim(savedThis.length - 1);
}
- extern (C++) bool isInCurrentFrame(VarDeclaration v)
+ bool isInCurrentFrame(VarDeclaration v)
{
if (v.isDataseg() && !v.isCTFE())
return false; // It's a global
return v.ctfeAdrOnStack >= framepointer;
}
- extern (C++) Expression getValue(VarDeclaration v)
+ Expression getValue(VarDeclaration v)
{
//printf("getValue() %s\n", v.toChars());
if ((v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE())
{
- assert(v.ctfeAdrOnStack < globalValues.dim);
+ assert(v.ctfeAdrOnStack < globalValues.length);
return globalValues[v.ctfeAdrOnStack];
}
assert(v.ctfeAdrOnStack < stackPointer());
return values[v.ctfeAdrOnStack];
}
- extern (C++) void setValue(VarDeclaration v, Expression e)
+ void setValue(VarDeclaration v, Expression e)
{
//printf("setValue() %s : %s\n", v.toChars(), e.toChars());
assert(!v.isDataseg() || v.isCTFE());
values[v.ctfeAdrOnStack] = e;
}
- extern (C++) void push(VarDeclaration v)
+ void push(VarDeclaration v)
{
//printf("push() %s\n", v.toChars());
assert(!v.isDataseg() || v.isCTFE());
return;
}
savedId.push(cast(void*)cast(size_t)v.ctfeAdrOnStack);
- v.ctfeAdrOnStack = cast(uint)values.dim;
+ v.ctfeAdrOnStack = cast(uint)values.length;
vars.push(v);
values.push(null);
}
- extern (C++) void pop(VarDeclaration v)
+ void pop(VarDeclaration v)
{
assert(!v.isDataseg() || v.isCTFE());
assert(!v.isReference());
const oldid = v.ctfeAdrOnStack;
v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[oldid];
- if (v.ctfeAdrOnStack == values.dim - 1)
+ if (v.ctfeAdrOnStack == values.length - 1)
{
values.pop();
vars.pop();
}
}
- extern (C++) void popAll(size_t stackpointer)
+ void popAll(size_t stackpointer)
{
if (stackPointer() > maxStackPointer)
maxStackPointer = stackPointer();
- assert(values.dim >= stackpointer);
- for (size_t i = stackpointer; i < values.dim; ++i)
+ assert(values.length >= stackpointer);
+ for (size_t i = stackpointer; i < values.length; ++i)
{
VarDeclaration v = vars[i];
v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[i];
savedId.setDim(stackpointer);
}
- extern (C++) void saveGlobalConstant(VarDeclaration v, Expression e)
+ void saveGlobalConstant(VarDeclaration v, Expression e)
{
assert(v._init && (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !v.isCTFE());
- v.ctfeAdrOnStack = cast(uint)globalValues.dim;
+ v.ctfeAdrOnStack = cast(uint)globalValues.length;
globalValues.push(copyRegionExp(e));
}
}
auto tf = fd.type.toBasetype().isTypeFunction();
if (tf.parameterList.varargs != VarArg.none && arguments &&
- ((fd.parameters && arguments.dim != fd.parameters.dim) || (!fd.parameters && arguments.dim)))
+ ((fd.parameters && arguments.length != fd.parameters.length) || (!fd.parameters && arguments.length)))
{
fd.error("C-style variadic functions are not yet implemented in CTFE");
return CTFEExp.cantexp;
// Place to hold all the arguments to the function while
// we are evaluating them.
- size_t dim = arguments ? arguments.dim : 0;
- assert((fd.parameters ? fd.parameters.dim : 0) == dim);
+ size_t dim = arguments ? arguments.length : 0;
+ assert((fd.parameters ? fd.parameters.length : 0) == dim);
/* Evaluate all the arguments to the function,
* store the results in eargs[]
if (istate.start == s)
istate.start = null;
- const dim = s.statements ? s.statements.dim : 0;
+ const dim = s.statements ? s.statements.length : 0;
foreach (i; 0 .. dim)
{
Statement sx = (*s.statements)[i];
if (istate.start == s)
istate.start = null;
- const dim = s.statements ? s.statements.dim : 0;
+ const dim = s.statements ? s.statements.length : 0;
foreach (i; 0 .. dim)
{
Statement sx = (*s.statements)[i];
result = interpret(pue, s.exp, istate, CTFEGoal.LValue);
return;
}
- if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.dim > 0)
+ if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.length > 0)
{
// To support this, we need to copy all the closure vars
// into the delegate literal.
result = e;
}
- static bool isAnErrorException(ClassDeclaration cd)
- {
- return cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null);
- }
-
static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest)
{
debug (LOG)
}
// Little sanity check to make sure it's really a Throwable
ClassReferenceExp boss = oldest.thrown;
- const next = 4; // index of Throwable.next
+ const next = 5; // index of Throwable.next
assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next
ClassReferenceExp collateral = newest.thrown;
- if (isAnErrorException(collateral.originalClass()) && !isAnErrorException(boss.originalClass()))
+ if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException())
{
/* Find the index of the Error.bypassException field
*/
if (exceptionOrCant(e))
return;
- assert(e.op == EXP.classReference);
- result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp());
+ if (e.op == EXP.classReference)
+ {
+ result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp());
+ }
+ else
+ {
+ exp.error("to be thrown `%s` must be non-null", exp.toChars());
+ result = ErrorExp.get();
+ }
}
override void visit(ScopeGuardStatement s)
{
printf("%s StringExp::interpret() %s\n", e.loc.toChars(), e.toChars());
}
- /* Attempts to modify string literals are prevented
- * in BinExp::interpretAssignCommon.
- */
- result = e;
+ if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted the string
+ {
+ result = e;
+ return;
+ }
+
+ if (e.type.ty != Tsarray ||
+ (cast(TypeNext)e.type).next.mod & (MODFlags.const_ | MODFlags.immutable_))
+ {
+ // If it's immutable, we don't need to dup it. Attempts to modify
+ // string literals are prevented in BinExp::interpretAssignCommon.
+ result = e;
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20811
+ // Create a copy of mutable string literals, so that any change in
+ // value via an index or slice will not survive CTFE.
+ *pue = copyLiteral(e);
+ result = pue.exp();
+ }
}
override void visit(FuncExp e)
while (s.isAttribDeclaration())
{
auto ad = cast(AttribDeclaration)s;
- assert(ad.decl && ad.decl.dim == 1); // Currently, only one allowed when parsing
+ assert(ad.decl && ad.decl.length == 1); // Currently, only one allowed when parsing
s = (*ad.decl)[0];
}
if (VarDeclaration v = s.isVarDeclaration())
return;
auto expsx = e.elements;
- size_t dim = expsx ? expsx.dim : 0;
+ size_t dim = expsx ? expsx.length : 0;
for (size_t i = 0; i < dim; i++)
{
Expression exp = (*expsx)[i];
{
// todo: all tuple expansions should go in semantic phase.
expandTuples(expsx);
- if (expsx.dim != dim)
+ if (expsx.length != dim)
{
e.error("CTFE internal error: invalid array literal");
result = CTFEExp.cantexp;
expandTuples(keysx);
if (valuesx !is e.values)
expandTuples(valuesx);
- if (keysx.dim != valuesx.dim)
+ if (keysx.length != valuesx.length)
{
e.error("CTFE internal error: invalid AA");
result = CTFEExp.cantexp;
/* Remove duplicate keys
*/
- for (size_t i = 1; i < keysx.dim; i++)
+ for (size_t i = 1; i < keysx.length; i++)
{
auto ekey = (*keysx)[i - 1];
- for (size_t j = i; j < keysx.dim; j++)
+ for (size_t j = i; j < keysx.length; j++)
{
auto ekey2 = (*keysx)[j];
if (!ctfeEqual(e.loc, EXP.equal, ekey, ekey2))
return;
}
- size_t dim = e.elements ? e.elements.dim : 0;
+ size_t dim = e.elements ? e.elements.length : 0;
auto expsx = e.elements;
- if (dim != e.sd.fields.dim)
+ if (dim != e.sd.fields.length)
{
// guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
- const nvthis = e.sd.fields.dim - e.sd.nonHiddenFields();
- assert(e.sd.fields.dim - dim == nvthis);
+ const nvthis = e.sd.fields.length - e.sd.nonHiddenFields();
+ assert(e.sd.fields.length - dim == nvthis);
/* If a nested struct has no initialized hidden pointer,
* set it to null to match the runtime behaviour.
++dim;
}
}
- assert(dim == e.sd.fields.dim);
+ assert(dim == e.sd.fields.length);
foreach (i; 0 .. dim)
{
if (expsx !is e.elements)
{
expandTuples(expsx);
- if (expsx.dim != e.sd.fields.dim)
+ if (expsx.length != e.sd.fields.length)
{
e.error("CTFE internal error: invalid struct literal");
result = CTFEExp.cantexp;
return lenExpr;
size_t len = cast(size_t)lenExpr.toInteger();
Type elemType = (cast(TypeArray)newtype).next;
- if (elemType.ty == Tarray && argnum < arguments.dim - 1)
+ if (elemType.ty == Tarray && argnum < arguments.length - 1)
{
Expression elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate, arguments, argnum + 1);
if (exceptionOrCantInterpret(elem))
ae.ownedByCtfe = OwnedBy.ctfe;
return ae;
}
- assert(argnum == arguments.dim - 1);
+ assert(argnum == arguments.length - 1);
if (elemType.ty.isSomeChar)
{
const ch = cast(dchar)elemType.defaultInitLiteral(loc).toInteger();
{
StructDeclaration sd = ts.sym;
auto exps = new Expressions();
- exps.reserve(sd.fields.dim);
+ exps.reserve(sd.fields.length);
if (e.arguments)
{
- exps.setDim(e.arguments.dim);
+ exps.setDim(e.arguments.length);
foreach (i, ex; *e.arguments)
{
ex = interpretRegion(ex, istate);
ClassDeclaration cd = tc.sym;
size_t totalFieldCount = 0;
for (ClassDeclaration c = cd; c; c = c.baseClass)
- totalFieldCount += c.fields.dim;
+ totalFieldCount += c.fields.length;
auto elems = new Expressions(totalFieldCount);
size_t fieldsSoFar = totalFieldCount;
for (ClassDeclaration c = cd; c; c = c.baseClass)
{
- fieldsSoFar -= c.fields.dim;
+ fieldsSoFar -= c.fields.length;
foreach (i, v; c.fields)
{
if (v.inuse)
else
m = v.getConstInitializer(true);
}
+ else if (v.type.isTypeNoreturn())
+ {
+ // Noreturn field with default initializer
+ (*elems)[fieldsSoFar + i] = null;
+ continue;
+ }
else
m = v.type.defaultInitLiteral(e.loc);
if (exceptionOrCant(m))
if (e.newtype.toBasetype().isscalar())
{
Expression newval;
- if (e.arguments && e.arguments.dim)
+ if (e.arguments && e.arguments.length)
newval = (*e.arguments)[0];
else
newval = e.newtype.defaultInitLiteral(e.loc);
e.error("CTFE internal error: cannot find field `%s` in `%s`", v.toChars(), ex.toChars());
return CTFEExp.cantexp;
}
- assert(0 <= fieldi && fieldi < sle.elements.dim);
+ assert(0 <= fieldi && fieldi < sle.elements.length);
// If it's a union, set all other members of this union to void
stompOverlappedFields(sle, v);
Expressions* oldelems = oldval.isArrayLiteralExp().elements;
Expressions* newelems = newval.isArrayLiteralExp().elements;
- assert(oldelems.dim == newelems.dim);
+ assert(oldelems.length == newelems.length);
Type elemtype = oldval.type.nextOf();
foreach (i, ref oldelem; *oldelems)
if (auto ale = e1.isArrayLiteralExp())
{
lowerbound = 0;
- upperbound = ale.elements.dim;
+ upperbound = ale.elements.length;
}
else if (auto se = e1.isStringExp())
{
bool needsPostblit;
bool needsDtor;
- extern (C++) Expression assignTo(ArrayLiteralExp ae)
+ Expression assignTo(ArrayLiteralExp ae)
{
- return assignTo(ae, 0, ae.elements.dim);
+ return assignTo(ae, 0, ae.elements.length);
}
- extern (C++) Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr)
+ Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr)
{
Expressions* w = ae.elements;
assert(ae.type.ty == Tsarray || ae.type.ty == Tarray);
if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor)
{
- assert(e.arguments.dim == 1);
+ assert(e.arguments.length == 1);
Expression ea = (*e.arguments)[0];
// printf("1 ea = %s %s\n", ea.type.toChars(), ea.toChars());
if (auto se = ea.isSliceExp())
{
// In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`.
// The following code will rewrite it back to `ea.length = eb` and then interpret that expression.
- assert(e.arguments.dim == 2);
+ assert(e.arguments.length == 2);
Expression ea = (*e.arguments)[0];
Expression eb = (*e.arguments)[1];
// then interpret that expression.
if (fd.ident == Id._d_arrayctor)
- assert(e.arguments.dim == 3);
+ assert(e.arguments.length == 3);
else
- assert(e.arguments.dim == 2);
+ assert(e.arguments.length == 2);
Expression ea = (*e.arguments)[0];
if (ea.isCastExp)
if (fd.ident == Id._d_arrayappendT)
{
- assert(e.arguments.dim == 2);
+ assert(e.arguments.length == 2);
lhs = (*e.arguments)[0];
rhs = (*e.arguments)[1];
}
else
{
- assert(e.arguments.dim == 5);
+ assert(e.arguments.length == 5);
lhs = (*e.arguments)[3];
rhs = (*e.arguments)[4];
}
// `_d_arrayappendcTX(arr, elem), arr[arr.length - 1] = elem, elem;`.
// The following code will rewrite it back to `arr ~= elem`
// and then interpret that expression.
- assert(ce.arguments.dim == 2);
+ assert(ce.arguments.length == 2);
auto arr = (*ce.arguments)[0];
auto elem = e.e2.isConstructExp().e2;
{
ArrayLiteralExp ale = ie.e1.isArrayLiteralExp();
const indx = cast(size_t)ie.e2.toInteger();
- if (indx < ale.elements.dim)
+ if (indx < ale.elements.length)
{
if (Expression xx = (*ale.elements)[indx])
{
{
debug (LOG)
{
- printf("%s ThrowExpression::interpret()\n", e.loc.toChars());
+ printf("%s ThrowExpression::interpret()\n", te.loc.toChars());
}
interpretThrow(te.e1, te.loc);
}
(*valuesx)[j - removed] = evalue;
}
}
- valuesx.dim = valuesx.dim - removed;
- keysx.dim = keysx.dim - removed;
+ valuesx.length = valuesx.length - removed;
+ keysx.length = keysx.length - removed;
result = IntegerExp.createBool(removed != 0);
}
return earg;
dinteger_t len = 0;
if (auto aae = earg.isAssocArrayLiteralExp())
- len = aae.keys.dim;
+ len = aae.keys.length;
else
assert(earg.op == EXP.null_);
emplaceExp!(IntegerExp)(pue, earg.loc, len, Type.tsize_t);
if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray)
return null;
auto aae = copyLiteral(earg).copy().isAssocArrayLiteralExp();
- for (size_t i = 0; i < aae.keys.dim; i++)
+ for (size_t i = 0; i < aae.keys.length; i++)
{
if (Expression e = evaluatePostblit(istate, (*aae.keys)[i]))
return e;
assert(fd && fd.fbody);
assert(fd.parameters);
- size_t numParams = fd.parameters.dim;
+ size_t numParams = fd.parameters.length;
assert(numParams == 1 || numParams == 2);
Parameter fparam = fd.type.isTypeFunction().parameterList[numParams - 1];
Expressions args = Expressions(numParams);
AssocArrayLiteralExp ae = aa.isAssocArrayLiteralExp();
- if (!ae.keys || ae.keys.dim == 0)
+ if (!ae.keys || ae.keys.length == 0)
return ctfeEmplaceExp!IntegerExp(deleg.loc, 0, Type.tsize_t);
Expression eresult;
- for (size_t i = 0; i < ae.keys.dim; ++i)
+ for (size_t i = 0; i < ae.keys.length; ++i)
{
Expression ekey = (*ae.keys)[i];
Expression evalue = (*ae.values)[i];
assert(fd && fd.fbody);
assert(fd.parameters);
- size_t numParams = fd.parameters.dim;
+ size_t numParams = fd.parameters.length;
assert(numParams == 1 || numParams == 2);
Type charType = (*fd.parameters)[numParams - 1].type;
Type indexType = numParams == 2 ? (*fd.parameters)[0].type : Type.tsize_t;
private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const ref Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis)
{
Expression e = null;
- size_t nargs = arguments ? arguments.dim : 0;
+ size_t nargs = arguments ? arguments.length : 0;
if (!pthis)
{
if (isBuiltin(fd) != BUILTIN.unimp)
// At present, the constructors just copy their arguments into the struct.
// But we might need some magic if stack tracing gets added to druntime.
StructLiteralExp se = pthis.isClassReferenceExp().value;
- assert(arguments.dim <= se.elements.dim);
+ assert(arguments.length <= se.elements.length);
foreach (i, arg; *arguments)
{
auto elem = interpret(arg, istate);
// Remove the first three trace parameters
auto arguments = new Expressions();
- arguments.reserve(ce.arguments.dim - 3);
+ arguments.reserve(ce.arguments.length - 3);
arguments.pushSlice((*ce.arguments)[3 .. $]);
ce = ctfeEmplaceExp!CallExp(ce.loc, ctfeEmplaceExp!VarExp(ce.loc, fd, false), arguments);