Skip to content

Commit 186bf0a

Browse files
committed
[BuilderTransform] Cache previously applied transforms
Since result builder is just an AST transformation, the result of a successful transform could be cache and reused with a different `$__builderSelf` type.
1 parent 0202785 commit 186bf0a

File tree

2 files changed

+81
-33
lines changed

2 files changed

+81
-33
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ struct ResultBuilder {
134134
return BuilderType->getAnyNominal();
135135
}
136136

137+
VarDecl *getBuilderSelf() const { return BuilderSelf; }
138+
137139
Identifier getBuildOptionalId() const { return BuildOptionalId; }
138140

139141
bool supports(Identifier fnBaseName, ArrayRef<Identifier> argLabels = {},
@@ -2734,6 +2736,14 @@ class ConstraintSystem {
27342736
/// diagnostics when result builder has multiple overloads.
27352737
llvm::SmallDenseSet<AnyFunctionRef> InvalidResultBuilderBodies;
27362738

2739+
/// The *global* set of all functions that have a particular result builder
2740+
/// applied.
2741+
///
2742+
/// The value here is `$__builderSelf` variable and a transformed body.
2743+
llvm::DenseMap<std::pair<AnyFunctionRef, NominalTypeDecl *>,
2744+
std::pair<VarDecl *, BraceStmt *>>
2745+
BuilderTransformedBodies;
2746+
27372747
/// Arguments after the code completion token that were thus ignored (i.e.
27382748
/// assigned fresh type variables) for type checking.
27392749
llvm::SetVector<Expr *> IgnoredArguments;
@@ -3746,6 +3756,27 @@ class ConstraintSystem {
37463756
return None;
37473757
}
37483758

3759+
void setBuilderTransformedBody(AnyFunctionRef fn, NominalTypeDecl *builder,
3760+
NullablePtr<VarDecl> builderSelf,
3761+
NullablePtr<BraceStmt> body) {
3762+
assert(builder->getAttrs().hasAttribute<ResultBuilderAttr>());
3763+
assert(body);
3764+
assert(builderSelf);
3765+
3766+
auto existing = BuilderTransformedBodies.insert(
3767+
{{fn, builder}, {builderSelf.get(), body.get()}});
3768+
assert(existing.second && "Duplicate result builder transform");
3769+
(void)existing;
3770+
}
3771+
3772+
Optional<std::pair<VarDecl *, BraceStmt *>>
3773+
getBuilderTransformedBody(AnyFunctionRef fn, NominalTypeDecl *builder) const {
3774+
auto result = BuilderTransformedBodies.find({fn, builder});
3775+
if (result == BuilderTransformedBodies.end())
3776+
return None;
3777+
return result->second;
3778+
}
3779+
37493780
void setCaseLabelItemInfo(const CaseLabelItem *item, CaseLabelItemInfo info) {
37503781
assert(item != nullptr);
37513782
assert(caseLabelItems.count(item) == 0);

lib/Sema/BuilderTransform.cpp

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,8 @@ class ResultBuilderTransform
942942
return castToStmt<BraceStmt>(newBody.get());
943943
}
944944

945+
VarDecl *getBuilderSelf() const { return builder.getBuilderSelf(); }
946+
945947
protected:
946948
NullablePtr<Stmt> failTransform(UnsupportedElt unsupported) {
947949
recordUnsupported(unsupported);
@@ -2426,6 +2428,7 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
24262428
Type bodyResultType,
24272429
ConstraintKind bodyResultConstraintKind,
24282430
ConstraintLocatorBuilder locator) {
2431+
builderType = simplifyType(builderType);
24292432
auto builder = builderType->getAnyNominal();
24302433
assert(builder && "Bad result builder type");
24312434
assert(builder->getAttrs().hasAttribute<ResultBuilderAttr>());
@@ -2477,58 +2480,72 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
24772480
return None;
24782481
}
24792482

2480-
if (Context.TypeCheckerOpts.ResultBuilderASTTransform) {
2481-
ResultBuilderTransform transform(*this, fn.getAsDeclContext(), builderType,
2482-
bodyResultType);
2483-
auto *body = transform.apply(fn.getBody());
2483+
if (Context.LangOpts.hasFeature(Feature::ResultBuilderASTTransform)) {
2484+
auto transformedBody = getBuilderTransformedBody(fn, builder);
2485+
// If this builder transform has not yet been applied to this function,
2486+
// let's do it and cache the result.
2487+
if (!transformedBody) {
2488+
ResultBuilderTransform transform(*this, fn.getAsDeclContext(),
2489+
builderType, bodyResultType);
2490+
auto *body = transform.apply(fn.getBody());
24842491

2485-
if (auto unsupported = transform.getUnsupportedElement()) {
2486-
assert(!body);
2492+
if (auto unsupported = transform.getUnsupportedElement()) {
2493+
assert(!body);
24872494

2488-
// If we aren't supposed to attempt fixes, fail.
2489-
if (!shouldAttemptFixes()) {
2490-
return getTypeMatchFailure(locator);
2491-
}
2495+
// If we aren't supposed to attempt fixes, fail.
2496+
if (!shouldAttemptFixes()) {
2497+
return getTypeMatchFailure(locator);
2498+
}
24922499

2493-
// If we're solving for code completion and the body contains the code
2494-
// completion location, skipping it won't get us to a useful solution so
2495-
// just bail.
2496-
if (isForCodeCompletion() && containsCodeCompletionLoc(fn.getBody())) {
2497-
return getTypeMatchFailure(locator);
2498-
}
2500+
// If we're solving for code completion and the body contains the code
2501+
// completion location, skipping it won't get us to a useful solution so
2502+
// just bail.
2503+
if (isForCodeCompletion() && containsCodeCompletionLoc(fn.getBody())) {
2504+
return getTypeMatchFailure(locator);
2505+
}
24992506

2500-
// Record the first unhandled construct as a fix.
2501-
if (recordFix(SkipUnhandledConstructInResultBuilder::create(
2502-
*this, unsupported, builder, getConstraintLocator(locator)))) {
2503-
return getTypeMatchFailure(locator);
2504-
}
2507+
// Record the first unhandled construct as a fix.
2508+
if (recordFix(SkipUnhandledConstructInResultBuilder::create(
2509+
*this, unsupported, builder, getConstraintLocator(locator)))) {
2510+
return getTypeMatchFailure(locator);
2511+
}
25052512

2506-
if (auto *closure = getAsExpr<ClosureExpr>(fn.getAbstractClosureExpr())) {
2507-
auto closureTy = getClosureType(closure);
2508-
simplifyType(closureTy).visit([&](Type componentTy) {
2509-
if (auto *typeVar = componentTy->getAs<TypeVariableType>()) {
2510-
assignFixedType(typeVar,
2511-
PlaceholderType::get(getASTContext(), typeVar));
2512-
}
2513-
});
2513+
if (auto *closure =
2514+
getAsExpr<ClosureExpr>(fn.getAbstractClosureExpr())) {
2515+
auto closureTy = getClosureType(closure);
2516+
simplifyType(closureTy).visit([&](Type componentTy) {
2517+
if (auto *typeVar = componentTy->getAs<TypeVariableType>()) {
2518+
assignFixedType(typeVar,
2519+
PlaceholderType::get(getASTContext(), typeVar));
2520+
}
2521+
});
2522+
}
2523+
2524+
return getTypeMatchSuccess();
25142525
}
25152526

2516-
return getTypeMatchSuccess();
2527+
transformedBody = std::make_pair(transform.getBuilderSelf(), body);
2528+
// Record the transformation so it could be re-used if needed.
2529+
setBuilderTransformedBody(fn, builder, transformedBody->first,
2530+
transformedBody->second);
25172531
}
25182532

2533+
// Set the type of `$__builderSelf` variable before constraint generation.
2534+
setType(transformedBody->first, MetatypeType::get(builderType));
2535+
25192536
if (isDebugMode()) {
25202537
auto &log = llvm::errs();
25212538
auto indent = solverState ? solverState->depth * 2 : 0;
25222539
log.indent(indent) << "------- Transfomed Body -------\n";
2523-
body->dump(log);
2540+
transformedBody->second->dump(log);
25242541
log << '\n';
25252542
}
25262543

25272544
AppliedBuilderTransform transformInfo;
25282545

25292546
transformInfo.builderType = builderType;
25302547
transformInfo.bodyResultType = bodyResultType;
2531-
transformInfo.transformedBody = body;
2548+
transformInfo.transformedBody = transformedBody->second;
25322549

25332550
// Record the transformation.
25342551
assert(
@@ -2541,7 +2558,7 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
25412558
resultBuilderTransformed.insert(
25422559
std::make_pair(fn, std::move(transformInfo)));
25432560

2544-
if (generateConstraints(fn, body))
2561+
if (generateConstraints(fn, transformInfo.transformedBody.get()))
25452562
return getTypeMatchFailure(locator);
25462563

25472564
return getTypeMatchSuccess();

0 commit comments

Comments
 (0)