Moved Staticforeach.lowerNonArrayAggregate and Staticforeach.prepare to expressionsem (#20944)

This commit is contained in:
Matthew Qiu 2025-03-04 04:05:02 -05:00 committed by GitHub
parent 5cd254f9e0
commit 58d3b59dc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 205 additions and 200 deletions

View file

@ -24,7 +24,7 @@ import dmd.dscope;
import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
import dmd.expressionsem : expressionSemantic, evalStaticCondition, resolveProperties;
import dmd.expressionsem : expressionSemantic, evalStaticCondition;
import dmd.globals;
import dmd.identifier;
import dmd.location;
@ -151,7 +151,7 @@ extern (C++) final class StaticForeach : RootObject
* to
* static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... }
*/
private extern(D) void lowerArrayAggregate(Scope* sc)
extern(D) void lowerArrayAggregate(Scope* sc)
{
auto aggr = aggrfe.aggr;
Expression el = new ArrayLengthExp(aggr.loc, aggr);
@ -198,7 +198,7 @@ extern (C++) final class StaticForeach : RootObject
* Returns:
* AST of the expression `(){ s; }()` with location loc.
*/
private extern(D) Expression wrapAndCall(Loc loc, Statement s)
extern(D) Expression wrapAndCall(Loc loc, Statement s)
{
auto tf = new TypeFunction(ParameterList(), null, LINK.default_, STC.none);
auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null);
@ -221,7 +221,7 @@ extern (C++) final class StaticForeach : RootObject
* `foreach (parameters; lower .. upper) s;`
* Where aggregate/lower, upper are as for the current StaticForeach.
*/
private extern(D) Statement createForeach(Loc loc, Parameters* parameters, Statement s)
extern(D) Statement createForeach(Loc loc, Parameters* parameters, Statement s)
{
if (aggrfe)
{
@ -254,7 +254,7 @@ extern (C++) final class StaticForeach : RootObject
* }
*/
private extern(D) TypeStruct createTupleType(Loc loc, Expressions* e, Scope* sc)
extern(D) TypeStruct createTupleType(Loc loc, Expressions* e, Scope* sc)
{ // TODO: move to druntime?
auto sid = Identifier.generateId("Tuple");
auto sdecl = new StructDeclaration(loc, sid, false);
@ -280,205 +280,11 @@ extern (C++) final class StaticForeach : RootObject
* An AST for the expression `Tuple(e)`.
*/
private extern(D) Expression createTuple(Loc loc, TypeStruct type, Expressions* e) @safe
extern(D) Expression createTuple(Loc loc, TypeStruct type, Expressions* e) @safe
{ // TODO: move to druntime?
return new CallExp(loc, new TypeExp(loc, type), e);
}
/*****************************************
* Lower any aggregate that is not an array to an array using a
* regular foreach loop within CTFE. If there are multiple
* `static foreach` loop variables, an array of tuples is
* generated. In thise case, the field `needExpansion` is set to
* true to indicate that the static foreach loop expansion will
* need to expand the tuples into multiple variables.
*
* For example, `static foreach (x; range) { ... }` is lowered to:
*
* static foreach (x; {
* typeof({
* foreach (x; range) return x;
* }())[] __res;
* foreach (x; range) __res ~= x;
* return __res;
* }()) { ... }
*
* Finally, call `lowerArrayAggregate` to turn the produced
* array into an expression tuple.
*
* Params:
* sc = The current scope.
*/
private void lowerNonArrayAggregate(Scope* sc)
{
auto nvars = aggrfe ? aggrfe.parameters.length : 1;
auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc;
// We need three sets of foreach loop variables because the
// lowering contains three foreach loops.
Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()];
foreach (i; 0 .. nvars)
{
foreach (params; pparams)
{
auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.param;
params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null));
}
}
Expression[2] res;
TypeStruct tplty = null;
if (nvars == 1) // only one `static foreach` variable, generate identifiers.
{
foreach (i; 0 .. 2)
{
res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident);
}
}
else // multiple `static foreach` variables, generate tuples.
{
foreach (i; 0 .. 2)
{
auto e = new Expressions(pparams[0].length);
foreach (j, ref elem; *e)
{
auto p = (*pparams[i])[j];
elem = new IdentifierExp(aloc, p.ident);
}
if (!tplty)
{
tplty = createTupleType(aloc, e, sc);
}
res[i] = createTuple(aloc, tplty, e);
}
needExpansion = true; // need to expand the tuples later
}
// generate remaining code for the new aggregate which is an
// array (see documentation comment).
if (rangefe)
{
sc = sc.startCTFE();
rangefe.lwr = rangefe.lwr.expressionSemantic(sc);
rangefe.lwr = resolveProperties(sc, rangefe.lwr);
rangefe.upr = rangefe.upr.expressionSemantic(sc);
rangefe.upr = resolveProperties(sc, rangefe.upr);
sc = sc.endCTFE();
rangefe.lwr = rangefe.lwr.optimize(WANTvalue);
rangefe.lwr = rangefe.lwr.ctfeInterpret();
rangefe.upr = rangefe.upr.optimize(WANTvalue);
rangefe.upr = rangefe.upr.ctfeInterpret();
}
auto s1 = new Statements();
auto sfe = new Statements();
if (tplty) sfe.push(new ExpStatement(loc, tplty.sym));
sfe.push(new ReturnStatement(aloc, res[0]));
s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe)));
s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
Type ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
auto aty = ety.arrayOf();
auto idres = Identifier.generateId("__res");
auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp);
auto s2 = new Statements();
// Run 'typeof' gagged to avoid duplicate errors and if it fails just create
// an empty foreach to expose them.
const olderrors = global.startGagging();
ety = ety.typeSemantic(aloc, sc);
if (global.endGagging(olderrors))
s2.push(createForeach(aloc, pparams[1], null));
else
{
s2.push(new ExpStatement(aloc, vard));
auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass)));
s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
}
Expression aggr = void;
Type indexty = void;
if (rangefe && (indexty = ety).isIntegral())
{
rangefe.lwr.type = indexty;
rangefe.upr.type = indexty;
auto lwrRange = getIntRange(rangefe.lwr);
auto uprRange = getIntRange(rangefe.upr);
const lwr = rangefe.lwr.toInteger();
auto upr = rangefe.upr.toInteger();
size_t length = 0;
if (lwrRange.imin <= uprRange.imax)
length = cast(size_t) (upr - lwr);
auto exps = new Expressions(length);
if (rangefe.op == TOK.foreach_)
{
foreach (i; 0 .. length)
(*exps)[i] = new IntegerExp(aloc, lwr + i, indexty);
}
else
{
--upr;
foreach (i; 0 .. length)
(*exps)[i] = new IntegerExp(aloc, upr - i, indexty);
}
aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps);
}
else
{
aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2));
sc = sc.startCTFE();
aggr = aggr.expressionSemantic(sc);
aggr = resolveProperties(sc, aggr);
sc = sc.endCTFE();
aggr = aggr.optimize(WANTvalue);
aggr = aggr.ctfeInterpret();
}
assert(!!aggrfe ^ !!rangefe);
aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr,
aggrfe ? aggrfe._body : rangefe._body,
aggrfe ? aggrfe.endloc : rangefe.endloc);
rangefe = null;
lowerArrayAggregate(sc); // finally, turn generated array into expression tuple
}
/*****************************************
* Perform `static foreach` lowerings that are necessary in order
* to finally expand the `static foreach` using
* `dmd.statementsem.makeTupleForeach`.
*/
extern(D) void prepare(Scope* sc)
{
assert(sc);
if (aggrfe)
{
sc = sc.startCTFE();
aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
sc = sc.endCTFE();
}
if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror)
{
return;
}
if (!ready())
{
if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray)
{
lowerArrayAggregate(sc);
}
else
{
lowerNonArrayAggregate(sc);
}
}
}
/*****************************************
* Returns:
* `true` iff ready to call `dmd.statementsem.makeTupleForeach`.

View file

@ -50,6 +50,7 @@ namespace dmd
{
// in expressionsem.d
Expression *expressionSemantic(Expression *e, Scope *sc);
void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc);
// in typesem.d
Expression *defaultInit(Type *mt, Loc loc, const bool isCfile = false);

View file

@ -25,6 +25,7 @@ import dmd.astcodegen;
import dmd.astenums;
import dmd.canthrow;
import dmd.chkformat;
import dmd.cond;
import dmd.ctorflow;
import dmd.dscope;
import dmd.dsymbol;
@ -17406,3 +17407,198 @@ bool fill(StructDeclaration sd, Loc loc, ref Expressions elements, bool ctorinit
return !errors;
}
/*****************************************
* Lower any aggregate that is not an array to an array using a
* regular foreach loop within CTFE. If there are multiple
* `static foreach` loop variables, an array of tuples is
* generated. In thise case, the field `needExpansion` is set to
* true to indicate that the static foreach loop expansion will
* need to expand the tuples into multiple variables.
*
* For example, `static foreach (x; range) { ... }` is lowered to:
*
* static foreach (x; {
* typeof({
* foreach (x; range) return x;
* }())[] __res;
* foreach (x; range) __res ~= x;
* return __res;
* }()) { ... }
*
* Finally, call `lowerArrayAggregate` to turn the produced
* array into an expression tuple.
*
* Params:
* sfe = The 'static foreach'.
* sc = The current scope.
*/
extern (C++) void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc)
{
import dmd.statement;
auto nvars = sfe.aggrfe ? sfe.aggrfe.parameters.length : 1;
auto aloc = sfe.aggrfe ? sfe.aggrfe.aggr.loc : sfe.rangefe.lwr.loc;
// We need three sets of foreach loop variables because the
// lowering contains three foreach loops.
Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()];
foreach (i; 0 .. nvars)
{
foreach (params; pparams)
{
auto p = sfe.aggrfe ? (*sfe.aggrfe.parameters)[i] : sfe.rangefe.param;
params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null));
}
}
Expression[2] res;
TypeStruct tplty = null;
if (nvars == 1) // only one `static foreach` variable, generate identifiers.
{
foreach (i; 0 .. 2)
{
res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident);
}
}
else // multiple `static foreach` variables, generate tuples.
{
foreach (i; 0 .. 2)
{
auto e = new Expressions(pparams[0].length);
foreach (j, ref elem; *e)
{
auto p = (*pparams[i])[j];
elem = new IdentifierExp(aloc, p.ident);
}
if (!tplty)
{
tplty = sfe.createTupleType(aloc, e, sc);
}
res[i] = sfe.createTuple(aloc, tplty, e);
}
sfe.needExpansion = true; // need to expand the tuples later
}
// generate remaining code for the new aggregate which is an
// array (see documentation comment).
if (sfe.rangefe)
{
sc = sc.startCTFE();
sfe.rangefe.lwr = sfe.rangefe.lwr.expressionSemantic(sc);
sfe.rangefe.lwr = resolveProperties(sc, sfe.rangefe.lwr);
sfe.rangefe.upr = sfe.rangefe.upr.expressionSemantic(sc);
sfe.rangefe.upr = resolveProperties(sc, sfe.rangefe.upr);
sc = sc.endCTFE();
sfe.rangefe.lwr = sfe.rangefe.lwr.optimize(WANTvalue);
sfe.rangefe.lwr = sfe.rangefe.lwr.ctfeInterpret();
sfe.rangefe.upr = sfe.rangefe.upr.optimize(WANTvalue);
sfe.rangefe.upr = sfe.rangefe.upr.ctfeInterpret();
}
auto s1 = new Statements();
auto stmts = new Statements();
if (tplty) stmts.push(new ExpStatement(sfe.loc, tplty.sym));
stmts.push(new ReturnStatement(aloc, res[0]));
s1.push(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts)));
s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
Type ety = new TypeTypeof(aloc, sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
auto aty = ety.arrayOf();
auto idres = Identifier.generateId("__res");
auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp);
auto s2 = new Statements();
// Run 'typeof' gagged to avoid duplicate errors and if it fails just create
// an empty foreach to expose them.
const olderrors = global.startGagging();
ety = ety.typeSemantic(aloc, sc);
if (global.endGagging(olderrors))
s2.push(sfe.createForeach(aloc, pparams[1], null));
else
{
s2.push(new ExpStatement(aloc, vard));
auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
s2.push(sfe.createForeach(aloc, pparams[1], new ExpStatement(aloc, catass)));
s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
}
Expression aggr = void;
Type indexty = void;
if (sfe.rangefe && (indexty = ety).isIntegral())
{
sfe.rangefe.lwr.type = indexty;
sfe.rangefe.upr.type = indexty;
auto lwrRange = getIntRange(sfe.rangefe.lwr);
auto uprRange = getIntRange(sfe.rangefe.upr);
const lwr = sfe.rangefe.lwr.toInteger();
auto upr = sfe.rangefe.upr.toInteger();
size_t length = 0;
if (lwrRange.imin <= uprRange.imax)
length = cast(size_t) (upr - lwr);
auto exps = new Expressions(length);
if (sfe.rangefe.op == TOK.foreach_)
{
foreach (i; 0 .. length)
(*exps)[i] = new IntegerExp(aloc, lwr + i, indexty);
}
else
{
--upr;
foreach (i; 0 .. length)
(*exps)[i] = new IntegerExp(aloc, upr - i, indexty);
}
aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps);
}
else
{
aggr = sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s2));
sc = sc.startCTFE();
aggr = aggr.expressionSemantic(sc);
aggr = resolveProperties(sc, aggr);
sc = sc.endCTFE();
aggr = aggr.optimize(WANTvalue);
aggr = aggr.ctfeInterpret();
}
assert(!!sfe.aggrfe ^ !!sfe.rangefe);
sfe.aggrfe = new ForeachStatement(sfe.loc, TOK.foreach_, pparams[2], aggr,
sfe.aggrfe ? sfe.aggrfe._body : sfe.rangefe._body,
sfe.aggrfe ? sfe.aggrfe.endloc : sfe.rangefe.endloc);
sfe.rangefe = null;
sfe.lowerArrayAggregate(sc); // finally, turn generated array into expression tuple
}
/*****************************************
* Perform `static foreach` lowerings that are necessary in order
* to finally expand the `static foreach` using
* `dmd.statementsem.makeTupleForeach`.
*/
extern(D) void prepare(StaticForeach sfe, Scope* sc)
{
assert(sc);
if (sfe.aggrfe)
{
sc = sc.startCTFE();
sfe.aggrfe.aggr = sfe.aggrfe.aggr.expressionSemantic(sc);
sc = sc.endCTFE();
}
if (sfe.aggrfe && sfe.aggrfe.aggr.type.toBasetype().ty == Terror)
{
return;
}
if (!sfe.ready())
{
if (sfe.aggrfe && sfe.aggrfe.aggr.type.toBasetype().ty == Tarray)
{
sfe.lowerArrayAggregate(sc);
}
else
{
sfe.lowerNonArrayAggregate(sc);
}
}
}

View file

@ -7550,6 +7550,8 @@ public:
void visit(StaticForeachDeclaration* sfd) override;
};
extern void lowerNonArrayAggregate(StaticForeach* sfe, Scope* sc);
class NrvoWalker final : public StatementRewriteWalker
{
public: