Skip to content

Commit 3760942

Browse files
authored
Merge pull request #31543 from DougGregor/function-builders-buildarray
[Function builders] Add for...in loop support via buildArray().
2 parents 9e0d50f + 19e234f commit 3760942

File tree

3 files changed

+204
-2
lines changed

3 files changed

+204
-2
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ IDENTIFIER(Any)
3131
IDENTIFIER(ArrayLiteralElement)
3232
IDENTIFIER(atIndexedSubscript)
3333
IDENTIFIER_(bridgeToObjectiveC)
34+
IDENTIFIER(buildArray)
3435
IDENTIFIER(buildBlock)
3536
IDENTIFIER(buildDo)
3637
IDENTIFIER(buildEither)

lib/Sema/BuilderTransform.cpp

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,11 +741,124 @@ class BuilderClosureVisitor
741741
return visit(caseStmt->getBody());
742742
}
743743

744+
VarDecl *visitForEachStmt(ForEachStmt *forEachStmt) {
745+
// for...in statements are handled via buildArray(_:); bail out if the
746+
// builder does not support it.
747+
if (!builderSupports(ctx.Id_buildArray)) {
748+
if (!unhandledNode)
749+
unhandledNode = forEachStmt;
750+
return nullptr;
751+
}
752+
753+
// For-each statements require the Sequence protocol. If we don't have
754+
// it (which generally means the standard library isn't loaded), fall
755+
// out of the function-builder path entirely to let normal type checking
756+
// take care of this.
757+
auto sequenceProto = TypeChecker::getProtocol(
758+
dc->getASTContext(), forEachStmt->getForLoc(),
759+
KnownProtocolKind::Sequence);
760+
if (!sequenceProto) {
761+
if (!unhandledNode)
762+
unhandledNode = forEachStmt;
763+
return nullptr;
764+
}
765+
766+
// Generate constraints for the loop header. This also wires up the
767+
// types for the patterns.
768+
auto target = SolutionApplicationTarget::forForEachStmt(
769+
forEachStmt, sequenceProto, dc, /*bindPatternVarsOneWay=*/true);
770+
if (cs) {
771+
if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow)) {
772+
hadError = true;
773+
return nullptr;
774+
}
775+
776+
cs->setSolutionApplicationTarget(forEachStmt, target);
777+
}
778+
779+
// Visit the loop body itself.
780+
VarDecl *bodyVar = visit(forEachStmt->getBody());
781+
if (!bodyVar)
782+
return nullptr;
783+
784+
// If there's no constraint system, there is nothing left to visit.
785+
if (!cs)
786+
return nullptr;
787+
788+
// Form a variable of array type that will capture the result of each
789+
// iteration of the loop. We need a fresh type variable to remove the
790+
// lvalue-ness of the array variable.
791+
SourceLoc loc = forEachStmt->getForLoc();
792+
VarDecl *arrayVar = buildVar(loc);
793+
Type arrayElementType = cs->createTypeVariable(
794+
cs->getConstraintLocator(forEachStmt), 0);
795+
cs->addConstraint(
796+
ConstraintKind::Equal, cs->getType(bodyVar), arrayElementType,
797+
cs->getConstraintLocator(
798+
forEachStmt, ConstraintLocator::RValueAdjustment));
799+
Type arrayType = ArraySliceType::get(arrayElementType);
800+
cs->setType(arrayVar, arrayType);
801+
802+
// Form an initialization of the array to an empty array literal.
803+
Expr *arrayInitExpr = ArrayExpr::create(ctx, loc, { }, { }, loc);
804+
cs->setContextualType(
805+
arrayInitExpr, TypeLoc::withoutLoc(arrayType), CTP_CannotFail);
806+
arrayInitExpr = cs->generateConstraints(arrayInitExpr, dc);
807+
if (!arrayInitExpr) {
808+
hadError = true;
809+
return nullptr;
810+
}
811+
cs->addConstraint(
812+
ConstraintKind::Equal, cs->getType(arrayInitExpr), arrayType,
813+
cs->getConstraintLocator(arrayInitExpr));
814+
815+
// Form a call to Array.append(_:) to add the result of executing each
816+
// iteration of the loop body to the array formed above.
817+
SourceLoc endLoc = forEachStmt->getEndLoc();
818+
auto arrayVarRef = buildVarRef(arrayVar, endLoc);
819+
auto arrayAppendRef = new (ctx) UnresolvedDotExpr(
820+
arrayVarRef, endLoc, DeclNameRef(ctx.getIdentifier("append")),
821+
DeclNameLoc(endLoc), /*implicit=*/true);
822+
arrayAppendRef->setFunctionRefKind(FunctionRefKind::SingleApply);
823+
auto bodyVarRef = buildVarRef(bodyVar, endLoc);
824+
Expr *arrayAppendCall = CallExpr::create(
825+
ctx, arrayAppendRef, endLoc, { bodyVarRef } , { Identifier() },
826+
{ endLoc }, endLoc, /*trailingClosure=*/nullptr, /*implicit=*/true);
827+
arrayAppendCall = cs->generateConstraints(arrayAppendCall, dc);
828+
if (!arrayAppendCall) {
829+
hadError = true;
830+
return nullptr;
831+
}
832+
833+
// Form the final call to buildArray(arrayVar) to allow the function
834+
// builder to reshape the array into whatever it wants as the result of
835+
// the for-each loop.
836+
auto finalArrayVarRef = buildVarRef(arrayVar, endLoc);
837+
auto buildArrayCall = buildCallIfWanted(
838+
endLoc, ctx.Id_buildArray, { finalArrayVarRef }, { Identifier() });
839+
assert(buildArrayCall);
840+
buildArrayCall = cs->generateConstraints(buildArrayCall, dc);
841+
if (!buildArrayCall) {
842+
hadError = true;
843+
return nullptr;
844+
}
845+
846+
// Form a final variable for the for-each expression itself, which will
847+
// be initialized with the call to the function builder's buildArray(_:).
848+
auto finalForEachVar = buildVar(loc);
849+
cs->setType(finalForEachVar, cs->getType(buildArrayCall));
850+
applied.capturedStmts.insert(
851+
{forEachStmt, {
852+
finalForEachVar,
853+
{ arrayVarRef, arrayInitExpr, arrayAppendCall, buildArrayCall }}});
854+
855+
return finalForEachVar;
856+
}
857+
744858
CONTROL_FLOW_STMT(Guard)
745859
CONTROL_FLOW_STMT(While)
746860
CONTROL_FLOW_STMT(DoCatch)
747861
CONTROL_FLOW_STMT(RepeatWhile)
748-
CONTROL_FLOW_STMT(ForEach)
749862
CONTROL_FLOW_STMT(Case)
750863
CONTROL_FLOW_STMT(Break)
751864
CONTROL_FLOW_STMT(Continue)
@@ -765,6 +878,9 @@ struct FunctionBuilderTarget {
765878
ReturnValue,
766879
/// The temporary variable into which the result should be assigned.
767880
TemporaryVar,
881+
/// An expression to evaluate at the end of the block, allowing the update
882+
/// of some state from an outer scope.
883+
Expression,
768884
} kind;
769885

770886
/// Captured variable information.
@@ -778,6 +894,10 @@ struct FunctionBuilderTarget {
778894
llvm::TinyPtrVector<Expr *> exprs) {
779895
return FunctionBuilderTarget{TemporaryVar, {temporaryVar, exprs}};
780896
}
897+
898+
static FunctionBuilderTarget forExpression(Expr *expr) {
899+
return FunctionBuilderTarget{Expression, { nullptr, { expr }}};
900+
}
781901
};
782902

783903
/// Handles the rewrite of the body of a closure to which a function builder
@@ -882,6 +1002,10 @@ class BuilderClosureRewriter
8821002
assign->setType(TupleType::getEmpty(ctx));
8831003
return assign;
8841004
}
1005+
1006+
case FunctionBuilderTarget::Expression:
1007+
// Execute the expression.
1008+
return rewriteExpr(capturedExpr);
8851009
}
8861010
}
8871011

@@ -1181,6 +1305,59 @@ class BuilderClosureRewriter
11811305
return caseStmt;
11821306
}
11831307

1308+
Stmt *visitForEachStmt(
1309+
ForEachStmt *forEachStmt, FunctionBuilderTarget target) {
1310+
// Translate the for-each loop header.
1311+
ConstraintSystem &cs = solution.getConstraintSystem();
1312+
auto forEachTarget =
1313+
rewriteTarget(*cs.getSolutionApplicationTarget(forEachStmt));
1314+
if (!forEachTarget)
1315+
return nullptr;
1316+
1317+
const auto &captured = target.captured;
1318+
auto finalForEachVar = captured.first;
1319+
auto arrayVarRef = captured.second[0];
1320+
auto arrayVar = cast<VarDecl>(cast<DeclRefExpr>(arrayVarRef)->getDecl());
1321+
auto arrayInitExpr = captured.second[1];
1322+
auto arrayAppendCall = captured.second[2];
1323+
auto buildArrayCall = captured.second[3];
1324+
1325+
// Collect the three steps to initialize the array variable to an
1326+
// empty array, execute the loop to collect the results of each iteration,
1327+
// then form the buildArray() call to the write the result.
1328+
std::vector<ASTNode> outerBodySteps;
1329+
1330+
// Step 1: Declare and initialize the array variable.
1331+
arrayVar->setInterfaceType(solution.simplifyType(cs.getType(arrayVar)));
1332+
arrayInitExpr = rewriteExpr(arrayInitExpr);
1333+
declareTemporaryVariable(arrayVar, outerBodySteps, arrayInitExpr);
1334+
1335+
// Step 2. Transform the body of the for-each statement. Each iteration
1336+
// will append the result of executing the loop body to the array.
1337+
auto body = forEachStmt->getBody();
1338+
auto capturedBody = takeCapturedStmt(body);
1339+
auto newBody = cast<BraceStmt>(
1340+
visitBraceStmt(
1341+
body,
1342+
FunctionBuilderTarget::forExpression(arrayAppendCall),
1343+
FunctionBuilderTarget::forAssign(
1344+
capturedBody.first, {capturedBody.second.front()})));
1345+
forEachStmt->setBody(newBody);
1346+
outerBodySteps.push_back(forEachStmt);
1347+
1348+
// Step 3. Perform the buildArray() call to turn the array of results
1349+
// collected from the iterations into a single value under the control of
1350+
// the function builder.
1351+
outerBodySteps.push_back(
1352+
initializeTarget(
1353+
FunctionBuilderTarget::forAssign(finalForEachVar, {buildArrayCall})));
1354+
1355+
// Form a brace statement to put together the three main steps for the
1356+
// for-each loop translation outlined above.
1357+
return BraceStmt::create(
1358+
ctx, forEachStmt->getStartLoc(), outerBodySteps, newBody->getEndLoc());
1359+
}
1360+
11841361
#define UNHANDLED_FUNCTION_BUILDER_STMT(STMT) \
11851362
Stmt *visit##STMT##Stmt(STMT##Stmt *stmt, FunctionBuilderTarget target) { \
11861363
llvm_unreachable("Function builders do not allow statement of kind " \
@@ -1194,7 +1371,6 @@ class BuilderClosureRewriter
11941371
UNHANDLED_FUNCTION_BUILDER_STMT(Defer)
11951372
UNHANDLED_FUNCTION_BUILDER_STMT(DoCatch)
11961373
UNHANDLED_FUNCTION_BUILDER_STMT(RepeatWhile)
1197-
UNHANDLED_FUNCTION_BUILDER_STMT(ForEach)
11981374
UNHANDLED_FUNCTION_BUILDER_STMT(Break)
11991375
UNHANDLED_FUNCTION_BUILDER_STMT(Continue)
12001376
UNHANDLED_FUNCTION_BUILDER_STMT(Fallthrough)

test/Constraints/function_builder.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ struct TupleBuilder {
4141
static func buildEither<T,U>(second value: U) -> Either<T,U> {
4242
return .second(value)
4343
}
44+
45+
static func buildArray<T>(_ array: [T]) -> [T] { return array }
4446
}
4547

4648
func tuplify<T>(_ cond: Bool, @TupleBuilder body: (Bool) -> T) {
@@ -664,3 +666,26 @@ tuplifyWithOpt(true) { c in
664666
"1"
665667
3.14159
666668
}
669+
670+
// Test for-each loops with buildArray.
671+
// CHECK: testForEach
672+
// CHECK-SAME: (1, "separator")
673+
// CHECK-SAME: (2, "separator")
674+
// CHECK-SAME: (3, "separator")
675+
// CHECK-SAME: (4, "separator")
676+
// CHECK-SAME: (5, "separator")
677+
// CHECK-SAME: (6, "separator")
678+
// CHECK-SAME: (7, "separator")
679+
// CHECK-SAME: (8, "separator")
680+
// CHECK-SAME: (9, "separator")
681+
// CHECK-SAME: (10, "separator")
682+
tuplify(true) { c in
683+
"testForEach"
684+
for i in 0 ..< (c ? 10 : 5) {
685+
i + 1
686+
"separator"
687+
}
688+
}
689+
690+
691+

0 commit comments

Comments
 (0)