1313#include " Initialization.h"
1414#include " Scope.h"
1515#include " SILGenFunction.h"
16+ #include " swift/AST/ASTWalker.h"
1617#include " swift/AST/GenericEnvironment.h"
1718
1819using namespace swift ;
@@ -165,8 +166,52 @@ class PartialDestroyRemainingTupleCleanup : public Cleanup {
165166#endif
166167 }
167168};
169+
170+ // / An ASTWalker to emit tuple values in `MaterializePackExpr` nodes.
171+ // /
172+ // / Materialized packs are emitted inside a pack expansion context before
173+ // / entering the dynamic pack loop so that the values are only evaluated
174+ // / once, rather than at each pack element iteration.
175+ struct MaterializePackEmitter : public ASTWalker {
176+ SILGenFunction &SGF;
177+
178+ MaterializePackEmitter (SILGenFunction &SGF) : SGF(SGF) {}
179+
180+ ASTWalker::PreWalkResult<Expr *> walkToExprPre (Expr *expr) override {
181+ using Action = ASTWalker::Action;
182+
183+ // Don't walk into nested pack expansions.
184+ if (isa<PackExpansionExpr>(expr))
185+ return Action::SkipChildren (expr);
186+
187+ if (auto *packExpr = dyn_cast<MaterializePackExpr>(expr)) {
188+ auto *fromExpr = packExpr->getFromExpr ();
189+ assert (fromExpr->getType ()->is <TupleType>());
190+
191+ auto &lowering = SGF.getTypeLowering (fromExpr->getType ());
192+ auto loweredTy = lowering.getLoweredType ();
193+ auto tupleAddr = SGF.emitTemporaryAllocation (fromExpr, loweredTy);
194+ auto init = SGF.useBufferAsTemporary (tupleAddr, lowering);
195+ SGF.emitExprInto (fromExpr, init.get ());
196+
197+ // Write the tuple value to a side table in the active pack expansion
198+ // to be projected later within the dynamic pack loop.
199+ auto *activeExpansion = SGF.getInnermostPackExpansion ();
200+ activeExpansion->MaterializedPacks [packExpr] = tupleAddr;
201+ }
202+
203+ return Action::Continue (expr);
204+ }
205+ };
206+
168207} // end anonymous namespace
169208
209+ void
210+ SILGenFunction::prepareToEmitPackExpansionExpr (PackExpansionExpr *E) {
211+ MaterializePackEmitter tempPackEmission (*this );
212+ E->getPatternExpr ()->walk (tempPackEmission);
213+ }
214+
170215CleanupHandle SILGenFunction::enterDeallocPackCleanup (SILValue temp) {
171216 assert (temp->getType ().isAddress () && " dealloc must have an address type" );
172217 assert (temp->getType ().is <SILPackType>());
@@ -390,6 +435,17 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc,
390435 " cannot reverse with a starting index" );
391436 ASTContext &ctx = SGM.getASTContext ();
392437
438+ // Save and restore the innermost pack expansion.
439+ ActivePackExpansion activeExpansionRecord = {
440+ openedElementEnv
441+ };
442+
443+ llvm::SaveAndRestore<ActivePackExpansion*>
444+ packExpansionScope (InnermostPackExpansion, &activeExpansionRecord);
445+
446+ if (auto *expansion = loc.getAsASTNode <PackExpansionExpr>())
447+ prepareToEmitPackExpansionExpr (expansion);
448+
393449 auto wordTy = SILType::getBuiltinWordType (ctx);
394450 auto boolTy = SILType::getBuiltinIntegerType (1 , ctx);
395451
@@ -461,6 +517,7 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc,
461517 // Construct the dynamic pack index into the component.
462518 SILValue packExpansionIndex =
463519 B.createDynamicPackIndex (loc, curIndex, formalDynamicPackType);
520+ getInnermostPackExpansion ()->ExpansionIndex = packExpansionIndex;
464521
465522 // If there's an opened element environment, open it here.
466523 if (openedElementEnv) {
@@ -478,14 +535,6 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc,
478535 // Emit the loop body in a scope as a convenience, since it's necessary
479536 // to avoid dominance problems anyway.
480537 {
481- // Save and restore the innermost pack expansion.
482- ActivePackExpansion activeExpansionRecord = {
483- packExpansionIndex,
484- openedElementEnv
485- };
486- llvm::SaveAndRestore<ActivePackExpansion*>
487- packExpansionScope (InnermostPackExpansion, &activeExpansionRecord);
488-
489538 FullExpr scope (Cleanups, CleanupLocation (loc));
490539 emitBody (curIndex, packExpansionIndex, packIndex);
491540 }
0 commit comments