Skip to content

Commit 2e0cc04

Browse files
authored
Merge pull request #62227 from xedin/result-builder-ast-transform-direct-buildBlock
[ResultBuilder] ASTTransform: Don't capture buildBlock into a separat…
2 parents c17d35d + 8060fa0 commit 2e0cc04

File tree

2 files changed

+106
-66
lines changed

2 files changed

+106
-66
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ class ResultBuilderTransform
10661066
return None;
10671067
}
10681068

1069-
std::pair<NullablePtr<VarDecl>, Optional<UnsupportedElt>>
1069+
std::pair<NullablePtr<Expr>, Optional<UnsupportedElt>>
10701070
transform(BraceStmt *braceStmt, SmallVectorImpl<ASTNode> &newBody) {
10711071
SmallVector<Expr *, 4> buildBlockArguments;
10721072

@@ -1082,7 +1082,6 @@ class ResultBuilderTransform
10821082
}
10831083

10841084
// Synthesize `buildBlock` or `buildPartial` based on captured arguments.
1085-
NullablePtr<VarDecl> buildBlockVar;
10861085
{
10871086
// If the builder supports `buildPartialBlock(first:)` and
10881087
// `buildPartialBlock(accumulated:next:)`, use this to combine
@@ -1097,16 +1096,19 @@ class ResultBuilderTransform
10971096
{buildBlockArguments.front()},
10981097
/*argLabels=*/{ctx.Id_first});
10991098

1100-
buildBlockVar = captureExpr(buildPartialFirst, newBody);
1099+
auto *buildBlockVar = captureExpr(buildPartialFirst, newBody);
11011100

11021101
for (auto *argExpr : llvm::drop_begin(buildBlockArguments)) {
11031102
auto *accumPartialBlock = builder.buildCall(
11041103
braceStmt->getStartLoc(), ctx.Id_buildPartialBlock,
1105-
{builder.buildVarRef(buildBlockVar.get(), argExpr->getStartLoc()),
1104+
{builder.buildVarRef(buildBlockVar, argExpr->getStartLoc()),
11061105
argExpr},
11071106
{ctx.Id_accumulated, ctx.Id_next});
11081107
buildBlockVar = captureExpr(accumPartialBlock, newBody);
11091108
}
1109+
1110+
return std::make_pair(
1111+
builder.buildVarRef(buildBlockVar, braceStmt->getStartLoc()), None);
11101112
}
11111113
// If `buildBlock` does not exist at this point, it could be the case that
11121114
// `buildPartialBlock` did not have the sufficient availability for this
@@ -1118,17 +1120,14 @@ class ResultBuilderTransform
11181120
builder.getType());
11191121
return failTransform(braceStmt);
11201122
}
1123+
11211124
// Otherwise, call `buildBlock` on all subexpressions.
1122-
else {
1123-
// Call Builder.buildBlock(... args ...)
1124-
auto *buildBlock = builder.buildCall(
1125-
braceStmt->getStartLoc(), ctx.Id_buildBlock, buildBlockArguments,
1126-
/*argLabels=*/{});
1127-
buildBlockVar = captureExpr(buildBlock, newBody);
1128-
}
1125+
// Call Builder.buildBlock(... args ...)
1126+
auto *buildBlock = builder.buildCall(
1127+
braceStmt->getStartLoc(), ctx.Id_buildBlock, buildBlockArguments,
1128+
/*argLabels=*/{});
1129+
return std::make_pair(buildBlock, None);
11291130
}
1130-
1131-
return std::make_pair(buildBlockVar.get(), None);
11321131
}
11331132

11341133
std::pair<bool, UnsupportedElt>
@@ -1141,10 +1140,10 @@ class ResultBuilderTransform
11411140
return std::make_pair(true, element);
11421141
};
11431142

1144-
NullablePtr<VarDecl> buildBlockVar;
1143+
NullablePtr<Expr> buildBlockVarRef;
11451144
Optional<UnsupportedElt> unsupported;
11461145

1147-
std::tie(buildBlockVar, unsupported) = transform(braceStmt, elements);
1146+
std::tie(buildBlockVarRef, unsupported) = transform(braceStmt, elements);
11481147
if (unsupported)
11491148
return failure(*unsupported);
11501149

@@ -1156,14 +1155,12 @@ class ResultBuilderTransform
11561155
// are attached to the beginning of the brace instead of its end.
11571156
auto resultLoc = braceStmt->getStartLoc();
11581157
if (bodyVar) {
1159-
elements.push_back(new (ctx) AssignExpr(
1160-
builder.buildVarRef(bodyVar.get(), resultLoc),
1161-
/*EqualLoc=*/SourceLoc(),
1162-
builder.buildVarRef(buildBlockVar.get(), resultLoc),
1163-
/*Implicit=*/true));
1158+
elements.push_back(
1159+
new (ctx) AssignExpr(builder.buildVarRef(bodyVar.get(), resultLoc),
1160+
/*EqualLoc=*/SourceLoc(), buildBlockVarRef.get(),
1161+
/*Implicit=*/true));
11641162
} else {
1165-
Expr *buildBlockResult =
1166-
builder.buildVarRef(buildBlockVar.get(), resultLoc);
1163+
Expr *buildBlockResult = buildBlockVarRef.get();
11671164
// Otherwise, it's a top-level brace and we need to synthesize
11681165
// a call to `buildFialBlock` if supported.
11691166
if (builder.supports(ctx.Id_buildFinalResult, {Identifier()})) {
@@ -1220,9 +1217,9 @@ class ResultBuilderTransform
12201217
if (!isBuildableIfChain(ifStmt, numPayloads, isOptional))
12211218
return failTransform(ifStmt);
12221219

1223-
SmallVector<std::pair<VarDecl *, Stmt *>, 4> branchVars;
1220+
SmallVector<std::pair<Expr *, Stmt *>, 4> branchVarRefs;
12241221

1225-
auto transformed = transformIf(ifStmt, branchVars);
1222+
auto transformed = transformIf(ifStmt, branchVarRefs);
12261223
if (!transformed)
12271224
return failTransform(ifStmt);
12281225

@@ -1236,17 +1233,15 @@ class ResultBuilderTransform
12361233
// `if` goes first.
12371234
doBody.push_back(ifStmt);
12381235

1239-
assert(numPayloads == branchVars.size());
1236+
assert(numPayloads == branchVarRefs.size());
12401237

12411238
SmallVector<Expr *, 4> buildEitherCalls;
12421239
for (unsigned i = 0; i != numPayloads; i++) {
1243-
VarDecl *branchVar;
1240+
Expr *branchVarRef;
12441241
Stmt *anchor;
12451242

1246-
std::tie(branchVar, anchor) = branchVars[i];
1243+
std::tie(branchVarRef, anchor) = branchVarRefs[i];
12471244

1248-
auto *branchVarRef =
1249-
builder.buildVarRef(branchVar, ifStmt->getEndLoc());
12501245
auto *builderCall =
12511246
buildWrappedChainPayload(branchVarRef, i, numPayloads, isOptional);
12521247

@@ -1308,7 +1303,7 @@ class ResultBuilderTransform
13081303

13091304
NullablePtr<IfStmt>
13101305
transformIf(IfStmt *ifStmt,
1311-
SmallVectorImpl<std::pair<VarDecl *, Stmt *>> &branchVars) {
1306+
SmallVectorImpl<std::pair<Expr *, Stmt *>> &branchVarRefs) {
13121307
Optional<UnsupportedElt> unsupported;
13131308

13141309
// If there is a #available in the condition, wrap the 'then' or 'else'
@@ -1317,42 +1312,40 @@ class ResultBuilderTransform
13171312
bool supportsAvailability =
13181313
availabilityCond && builder.supports(ctx.Id_buildLimitedAvailability);
13191314

1320-
NullablePtr<VarDecl> thenVar;
1315+
NullablePtr<Expr> thenVarRef;
13211316
NullablePtr<Stmt> thenBranch;
13221317
{
13231318
SmallVector<ASTNode, 4> thenBody;
13241319

13251320
auto *ifBraceStmt = cast<BraceStmt>(ifStmt->getThenStmt());
13261321

1327-
std::tie(thenVar, unsupported) = transform(ifBraceStmt, thenBody);
1322+
std::tie(thenVarRef, unsupported) = transform(ifBraceStmt, thenBody);
13281323
if (unsupported) {
13291324
recordUnsupported(*unsupported);
13301325
return nullptr;
13311326
}
13321327

13331328
if (supportsAvailability &&
13341329
!availabilityCond->getAvailability()->isUnavailability()) {
1335-
auto *thenVarRef =
1336-
builder.buildVarRef(thenVar.get(), ifBraceStmt->getEndLoc());
1337-
13381330
auto *builderCall = buildCallIfWanted(
1339-
ifStmt->getThenStmt()->getEndLoc(), ctx.Id_buildLimitedAvailability,
1340-
{thenVarRef}, {Identifier()});
1331+
ifBraceStmt->getStartLoc(), ctx.Id_buildLimitedAvailability,
1332+
{thenVarRef.get()}, {Identifier()});
13411333

1342-
thenVar = captureExpr(builderCall, thenBody);
1334+
thenVarRef = builder.buildVarRef(captureExpr(builderCall, thenBody),
1335+
ifBraceStmt->getStartLoc());
13431336
}
13441337

13451338
thenBranch = cloneBraceWith(ifBraceStmt, thenBody);
1346-
branchVars.push_back({thenVar.get(), thenBranch.get()});
1339+
branchVarRefs.push_back({thenVarRef.get(), thenBranch.get()});
13471340
}
13481341

13491342
NullablePtr<Stmt> elseBranch;
13501343

13511344
if (auto *elseStmt = ifStmt->getElseStmt()) {
1352-
NullablePtr<VarDecl> elseVar;
1345+
NullablePtr<Expr> elseVarRef;
13531346

13541347
if (auto *innerIfStmt = getAsStmt<IfStmt>(elseStmt)) {
1355-
elseBranch = transformIf(innerIfStmt, branchVars);
1348+
elseBranch = transformIf(innerIfStmt, branchVarRefs);
13561349
if (!elseBranch) {
13571350
recordUnsupported(innerIfStmt);
13581351
return nullptr;
@@ -1361,7 +1354,7 @@ class ResultBuilderTransform
13611354
auto *elseBraceStmt = cast<BraceStmt>(elseStmt);
13621355
SmallVector<ASTNode> elseBody;
13631356

1364-
std::tie(elseVar, unsupported) = transform(elseBraceStmt, elseBody);
1357+
std::tie(elseVarRef, unsupported) = transform(elseBraceStmt, elseBody);
13651358
if (unsupported) {
13661359
recordUnsupported(*unsupported);
13671360
return nullptr;
@@ -1371,18 +1364,16 @@ class ResultBuilderTransform
13711364
// call to buildLimitedAvailability(_:).
13721365
if (supportsAvailability &&
13731366
availabilityCond->getAvailability()->isUnavailability()) {
1374-
auto *elseVarRef =
1375-
builder.buildVarRef(elseVar.get(), elseBraceStmt->getEndLoc());
1376-
1377-
auto *builderCall = buildCallIfWanted(elseBraceStmt->getStartLoc(),
1378-
ctx.Id_buildLimitedAvailability,
1379-
{elseVarRef}, {Identifier()});
1367+
auto *builderCall = buildCallIfWanted(
1368+
elseBraceStmt->getStartLoc(), ctx.Id_buildLimitedAvailability,
1369+
{elseVarRef.get()}, {Identifier()});
13801370

1381-
elseVar = captureExpr(builderCall, elseBody);
1371+
elseVarRef = builder.buildVarRef(captureExpr(builderCall, elseBody),
1372+
elseBraceStmt->getStartLoc());
13821373
}
13831374

13841375
elseBranch = cloneBraceWith(elseBraceStmt, elseBody);
1385-
branchVars.push_back({elseVar.get(), elseBranch.get()});
1376+
branchVarRefs.push_back({elseVarRef.get(), elseBranch.get()});
13861377
}
13871378
}
13881379

@@ -1404,23 +1395,23 @@ class ResultBuilderTransform
14041395
SmallVector<ASTNode, 4> doBody;
14051396

14061397
SmallVector<ASTNode, 4> cases;
1407-
SmallVector<VarDecl *, 4> caseVars;
1398+
SmallVector<Expr *, 4> caseVarRefs;
14081399

14091400
for (auto *caseStmt : switchStmt->getCases()) {
14101401
auto transformed = transformCase(caseStmt);
14111402
if (!transformed)
14121403
return failTransform(caseStmt);
14131404

14141405
cases.push_back(transformed->second);
1415-
caseVars.push_back(transformed->first);
1406+
caseVarRefs.push_back(transformed->first);
14161407
}
14171408

14181409
// If there are no 'case' statements in the body let's try
14191410
// to diagnose this situation via limited exhaustiveness check
14201411
// before failing a builder transform, otherwise type-checker
14211412
// might end up without any diagnostics which leads to crashes
14221413
// in SILGen.
1423-
if (caseVars.empty()) {
1414+
if (caseVarRefs.empty()) {
14241415
TypeChecker::checkSwitchExhaustiveness(switchStmt, dc,
14251416
/*limitChecking=*/true);
14261417
return failTransform(switchStmt);
@@ -1434,15 +1425,13 @@ class ResultBuilderTransform
14341425
doBody.push_back(transformedSwitch);
14351426

14361427
SmallVector<Expr *, 4> injectedExprs;
1437-
for (auto idx : indices(caseVars)) {
1438-
auto caseStmt = cases[idx];
1439-
auto *caseVar = caseVars[idx];
1428+
for (auto idx : indices(caseVarRefs)) {
1429+
auto *caseVarRef = caseVarRefs[idx];
14401430

14411431
// Build the expression that injects the case variable into appropriate
14421432
// buildEither(first:)/buildEither(second:) chain.
1443-
Expr *caseVarRef = builder.buildVarRef(caseVar, caseStmt.getEndLoc());
14441433
Expr *injectedCaseExpr = buildWrappedChainPayload(
1445-
caseVarRef, idx, caseVars.size(), /*isOptional=*/false);
1434+
caseVarRef, idx, caseVarRefs.size(), /*isOptional=*/false);
14461435

14471436
injectedExprs.push_back(injectedCaseExpr);
14481437
}
@@ -1454,7 +1443,7 @@ class ResultBuilderTransform
14541443
return DoStmt::createImplicit(ctx, LabeledStmtInfo(), doBody);
14551444
}
14561445

1457-
Optional<std::pair<VarDecl *, CaseStmt *>> transformCase(CaseStmt *caseStmt) {
1446+
Optional<std::pair<Expr *, CaseStmt *>> transformCase(CaseStmt *caseStmt) {
14581447
auto *body = caseStmt->getBody();
14591448

14601449
// Explicitly disallow `case` statements with empty bodies
@@ -1470,11 +1459,11 @@ class ResultBuilderTransform
14701459
}
14711460
}
14721461

1473-
NullablePtr<VarDecl> caseVar;
1462+
NullablePtr<Expr> caseVarRef;
14741463
Optional<UnsupportedElt> unsupported;
14751464
SmallVector<ASTNode, 4> newBody;
14761465

1477-
std::tie(caseVar, unsupported) = transform(body, newBody);
1466+
std::tie(caseVarRef, unsupported) = transform(body, newBody);
14781467

14791468
if (unsupported) {
14801469
recordUnsupported(*unsupported);
@@ -1489,7 +1478,7 @@ class ResultBuilderTransform
14891478
caseStmt->getCaseBodyVariablesOrEmptyArray(), caseStmt->isImplicit(),
14901479
caseStmt->getFallthroughStmt());
14911480

1492-
return std::make_pair(caseVar.get(), newCase);
1481+
return std::make_pair(caseVarRef.get(), newCase);
14931482
}
14941483

14951484
/// do {
@@ -1533,12 +1522,12 @@ class ResultBuilderTransform
15331522
ArrayExpr::create(ctx, /*LBrace=*/endLoc, /*Elements=*/{},
15341523
/*Commas=*/{}, /*RBrace=*/endLoc));
15351524

1536-
NullablePtr<VarDecl> bodyVar;
1525+
NullablePtr<Expr> bodyVarRef;
15371526
Optional<UnsupportedElt> unsupported;
15381527

15391528
SmallVector<ASTNode, 4> newBody;
15401529
{
1541-
std::tie(bodyVar, unsupported) =
1530+
std::tie(bodyVarRef, unsupported) =
15421531
transform(forEachStmt->getBody(), newBody);
15431532
if (unsupported)
15441533
return failTransform(*unsupported);
@@ -1552,9 +1541,8 @@ class ResultBuilderTransform
15521541
DeclNameLoc(endLoc), /*implicit=*/true);
15531542
arrayAppendRef->setFunctionRefKind(FunctionRefKind::SingleApply);
15541543

1555-
auto bodyVarRef = builder.buildVarRef(bodyVar.get(), endLoc);
15561544
auto *argList = ArgumentList::createImplicit(
1557-
ctx, endLoc, {Argument::unlabeled(bodyVarRef)}, endLoc);
1545+
ctx, endLoc, {Argument::unlabeled(bodyVarRef.get())}, endLoc);
15581546

15591547
newBody.push_back(
15601548
CallExpr::createImplicit(ctx, arrayAppendRef, argList));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-typecheck-verify-swift -I %t -enable-experimental-feature ResultBuilderASTTransform
3+
4+
// This test verifies that `buildBlock` is type-checked together with enclosing context,
5+
// which means that it's not captured into separate variable but rather used directly and
6+
// contextual information can impact overload resolution.
7+
8+
protocol ActionIdentifier: Hashable {
9+
}
10+
11+
struct ActionLookup<Identifier: ActionIdentifier> {
12+
init(_: Identifier...) {}
13+
}
14+
15+
@resultBuilder
16+
enum ActionLookupBuilder<Identifier: ActionIdentifier> {
17+
static func buildBlock<Identifier: ActionIdentifier>(_ components: [ActionLookup<Identifier>]...) -> ActionLookup<Identifier> {
18+
fatalError()
19+
}
20+
21+
static func buildBlock<Identifier: ActionIdentifier>(_ components: [ActionLookup<Identifier>]...) -> [ActionLookup<Identifier>] {
22+
[]
23+
}
24+
25+
static func buildExpression(_ expression: ActionLookup<Identifier>) -> [ActionLookup<Identifier>] {
26+
[]
27+
}
28+
29+
static func buildOptional<Identifier: ActionIdentifier>(_ component: [ActionLookup<Identifier>]?) -> [ActionLookup<Identifier>] {
30+
[]
31+
}
32+
}
33+
34+
enum ActionType: String, ActionIdentifier, CaseIterable {
35+
case download
36+
case upload
37+
38+
public typealias ActionTypeLookup = ActionLookup<Self>
39+
public typealias ActionTypeLookupBuilder = ActionLookupBuilder<Self>
40+
41+
@ActionTypeLookupBuilder
42+
static var test: ActionTypeLookup {
43+
ActionTypeLookup(
44+
.download
45+
)
46+
if true { // If condition is needed to make sure that `buildOptional` affects `buildBlock` resolution.
47+
ActionTypeLookup(
48+
.upload
49+
)
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)