Skip to content

Commit 95d9362

Browse files
committed
[BuilderTransform] Extract support checks and builder info into ResultBuilder type
1 parent a97879d commit 95d9362

File tree

2 files changed

+76
-49
lines changed

2 files changed

+76
-49
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,42 @@ enum class FreeTypeVariableBinding {
105105

106106
namespace constraints {
107107

108+
struct ResultBuilder {
109+
private:
110+
DeclContext *DC;
111+
Type BuilderType;
112+
llvm::SmallDenseMap<DeclName, bool> SupportedOps;
113+
114+
Identifier BuildOptionalId;
115+
116+
public:
117+
ResultBuilder(DeclContext *DC, Type builderType)
118+
: DC(DC), BuilderType(builderType) {
119+
auto &ctx = DC->getASTContext();
120+
// Use buildOptional(_:) if available, otherwise fall back to buildIf
121+
// when available.
122+
BuildOptionalId =
123+
(supports(ctx.Id_buildOptional) || !supports(ctx.Id_buildIf))
124+
? ctx.Id_buildOptional
125+
: ctx.Id_buildIf;
126+
}
127+
128+
DeclContext *getDeclContext() const { return DC; }
129+
130+
Type getType() const { return BuilderType; }
131+
132+
NominalTypeDecl *getBuilderDecl() const {
133+
return BuilderType->getAnyNominal();
134+
}
135+
136+
Identifier getBuildOptionalId() const { return BuildOptionalId; }
137+
138+
bool supports(Identifier fnBaseName, ArrayRef<Identifier> argLabels = {},
139+
bool checkAvailability = false);
140+
141+
bool supportsOptional() { return supports(getBuildOptionalId()); }
142+
};
143+
108144
/// Describes the algorithm to use for trailing closure matching.
109145
enum class TrailingClosureMatching {
110146
/// Match a trailing closure to the first parameter that appears to work.

lib/Sema/BuilderTransform.cpp

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,8 @@ class BuilderClosureVisitor
6868
ConstraintSystem *cs;
6969
DeclContext *dc;
7070
ASTContext &ctx;
71-
Type builderType;
72-
NominalTypeDecl *builder = nullptr;
73-
Identifier buildOptionalId;
74-
llvm::SmallDenseMap<DeclName, bool> supportedOps;
71+
72+
ResultBuilder builder;
7573

7674
/// The variable used as a base for all `build*` operations added
7775
/// by this transform.
@@ -121,21 +119,6 @@ class BuilderClosureVisitor
121119
return CallExpr::createImplicit(ctx, memberRef, argList);
122120
}
123121

124-
/// Check whether the builder supports the given operation.
125-
bool builderSupports(Identifier fnBaseName,
126-
ArrayRef<Identifier> argLabels = {},
127-
bool checkAvailability = false) {
128-
DeclName name(dc->getASTContext(), fnBaseName, argLabels);
129-
auto known = supportedOps.find(name);
130-
if (known != supportedOps.end()) {
131-
return known->second;
132-
}
133-
134-
return supportedOps[name] = TypeChecker::typeSupportsBuilderOp(
135-
builderType, dc, fnBaseName, argLabels, /*allResults*/ {},
136-
checkAvailability);
137-
}
138-
139122
/// Build an implicit variable in this context.
140123
VarDecl *buildVar(SourceLoc loc) {
141124
// Create the implicit variable.
@@ -192,27 +175,19 @@ class BuilderClosureVisitor
192175
public:
193176
BuilderClosureVisitor(ASTContext &ctx, ConstraintSystem *cs, DeclContext *dc,
194177
Type builderType, Type bodyResultType)
195-
: cs(cs), dc(dc), ctx(ctx), builderType(builderType) {
196-
builder = builderType->getAnyNominal();
197-
applied.builderType = builderType;
178+
: cs(cs), dc(dc), ctx(ctx),
179+
builder(dc, cs ? cs->simplifyType(builderType) : builderType) {
180+
applied.builderType = builder.getType();
198181
applied.bodyResultType = bodyResultType;
199182

200-
// Use buildOptional(_:) if available, otherwise fall back to buildIf
201-
// when available.
202-
if (builderSupports(ctx.Id_buildOptional) ||
203-
!builderSupports(ctx.Id_buildIf))
204-
buildOptionalId = ctx.Id_buildOptional;
205-
else
206-
buildOptionalId = ctx.Id_buildIf;
207-
208183
// If we are about to generate constraints, let's establish builder
209184
// variable for the base of `build*` calls.
210185
if (cs) {
211186
builderVar = new (ctx) VarDecl(
212187
/*isStatic=*/false, VarDecl::Introducer::Let,
213188
/*nameLoc=*/SourceLoc(), ctx.Id_builderSelf, dc);
214189
builderVar->setImplicit();
215-
cs->setType(builderVar, MetatypeType::get(cs->simplifyType(builderType)));
190+
cs->setType(builderVar, MetatypeType::get(builder.getType()));
216191
}
217192
}
218193

@@ -226,7 +201,7 @@ class BuilderClosureVisitor
226201

227202
// If there is a buildFinalResult(_:), call it.
228203
ASTContext &ctx = cs->getASTContext();
229-
if (builderSupports(ctx.Id_buildFinalResult, { Identifier() })) {
204+
if (builder.supports(ctx.Id_buildFinalResult, {Identifier()})) {
230205
applied.returnExpr = buildCallIfWanted(
231206
applied.returnExpr->getLoc(), ctx.Id_buildFinalResult,
232207
{ applied.returnExpr }, { Identifier() });
@@ -350,7 +325,7 @@ class BuilderClosureVisitor
350325
}
351326

352327
auto expr = node.get<Expr *>();
353-
if (cs && builderSupports(ctx.Id_buildExpression)) {
328+
if (cs && builder.supports(ctx.Id_buildExpression)) {
354329
expr = buildCallIfWanted(expr->getLoc(), ctx.Id_buildExpression,
355330
{ expr }, { Identifier() });
356331
}
@@ -366,11 +341,11 @@ class BuilderClosureVisitor
366341
// `buildPartialBlock(accumulated:next:)`, use this to combine
367342
// subexpressions pairwise.
368343
if (!expressions.empty() &&
369-
builderSupports(ctx.Id_buildPartialBlock, {ctx.Id_first},
370-
/*checkAvailability*/ true) &&
371-
builderSupports(ctx.Id_buildPartialBlock,
372-
{ctx.Id_accumulated, ctx.Id_next},
373-
/*checkAvailability*/ true)) {
344+
builder.supports(ctx.Id_buildPartialBlock, {ctx.Id_first},
345+
/*checkAvailability*/ true) &&
346+
builder.supports(ctx.Id_buildPartialBlock,
347+
{ctx.Id_accumulated, ctx.Id_next},
348+
/*checkAvailability*/ true)) {
374349
// NOTE: The current implementation uses one-way constraints in between
375350
// subexpressions. It's functionally equivalent to the following:
376351
// let v0 = Builder.buildPartialBlock(first: arg_0)
@@ -391,11 +366,11 @@ class BuilderClosureVisitor
391366
// If `buildBlock` does not exist at this point, it could be the case that
392367
// `buildPartialBlock` did not have the sufficient availability for this
393368
// call site. Diagnose it.
394-
else if (!builderSupports(ctx.Id_buildBlock)) {
369+
else if (!builder.supports(ctx.Id_buildBlock)) {
395370
ctx.Diags.diagnose(
396371
braceStmt->getStartLoc(),
397372
diag::result_builder_missing_available_buildpartialblock,
398-
builderType);
373+
builder.getType());
399374
return nullptr;
400375
}
401376
// Otherwise, call `buildBlock` on all subexpressions.
@@ -462,14 +437,14 @@ class BuilderClosureVisitor
462437
return false;
463438

464439
// If there's a missing 'else', we need 'buildOptional' to exist.
465-
if (isOptional && !builderSupports(buildOptionalId))
440+
if (isOptional && !builder.supportsOptional())
466441
return false;
467442

468443
// If there are multiple clauses, we need 'buildEither(first:)' and
469444
// 'buildEither(second:)' to both exist.
470445
if (numPayloads > 1) {
471-
if (!builderSupports(ctx.Id_buildEither, {ctx.Id_first}) ||
472-
!builderSupports(ctx.Id_buildEither, {ctx.Id_second}))
446+
if (!builder.supports(ctx.Id_buildEither, {ctx.Id_first}) ||
447+
!builder.supports(ctx.Id_buildEither, {ctx.Id_second}))
473448
return false;
474449
}
475450

@@ -543,7 +518,7 @@ class BuilderClosureVisitor
543518
// buildLimitedAvailability(_:).
544519
auto availabilityCond = findAvailabilityCondition(ifStmt->getCond());
545520
bool supportsAvailability =
546-
availabilityCond && builderSupports(ctx.Id_buildLimitedAvailability);
521+
availabilityCond && builder.supports(ctx.Id_buildLimitedAvailability);
547522
if (supportsAvailability &&
548523
!availabilityCond->getAvailability()->isUnavailability()) {
549524
thenVarRefExpr = buildCallIfWanted(ifStmt->getThenStmt()->getEndLoc(),
@@ -592,10 +567,12 @@ class BuilderClosureVisitor
592567
// The operand should have optional type if we had optional results,
593568
// so we just need to call `buildIf` now, since we're at the top level.
594569
if (isOptional && isTopLevel) {
595-
thenExpr = buildCallIfWanted(ifStmt->getEndLoc(), buildOptionalId,
596-
thenExpr, /*argLabels=*/{ });
597-
elseExpr = buildCallIfWanted(ifStmt->getEndLoc(), buildOptionalId,
598-
elseExpr, /*argLabels=*/{ });
570+
thenExpr =
571+
buildCallIfWanted(ifStmt->getEndLoc(), builder.getBuildOptionalId(),
572+
thenExpr, /*argLabels=*/{});
573+
elseExpr =
574+
buildCallIfWanted(ifStmt->getEndLoc(), builder.getBuildOptionalId(),
575+
elseExpr, /*argLabels=*/{});
599576
}
600577

601578
thenExpr = cs->generateConstraints(thenExpr, dc);
@@ -832,7 +809,7 @@ class BuilderClosureVisitor
832809
VarDecl *visitForEachStmt(ForEachStmt *forEachStmt) {
833810
// for...in statements are handled via buildArray(_:); bail out if the
834811
// builder does not support it.
835-
if (!builderSupports(ctx.Id_buildArray)) {
812+
if (!builder.supports(ctx.Id_buildArray)) {
836813
if (!unhandledNode)
837814
unhandledNode = forEachStmt;
838815
return nullptr;
@@ -2220,3 +2197,17 @@ void swift::printResultBuilderBuildFunction(
22202197
printer << "}";
22212198
}
22222199
}
2200+
2201+
bool ResultBuilder::supports(Identifier fnBaseName,
2202+
ArrayRef<Identifier> argLabels,
2203+
bool checkAvailability) {
2204+
DeclName name(DC->getASTContext(), fnBaseName, argLabels);
2205+
auto known = SupportedOps.find(name);
2206+
if (known != SupportedOps.end()) {
2207+
return known->second;
2208+
}
2209+
2210+
return SupportedOps[name] = TypeChecker::typeSupportsBuilderOp(
2211+
BuilderType, DC, fnBaseName, argLabels, /*allResults*/ {},
2212+
checkAvailability);
2213+
}

0 commit comments

Comments
 (0)