Skip to content

Commit f9b2395

Browse files
authored
Merge pull request #41576 from rxwei/buildpartialblock
[ResultBuilders] `buildPartialBlock` support
2 parents 3dedfda + b1f020b commit f9b2395

10 files changed

+428
-15
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6112,6 +6112,15 @@ WARNING(result_builder_missing_limited_availability, none,
61126112
ERROR(result_builder_static_buildblock, none,
61136113
"result builder must provide at least one static 'buildBlock' "
61146114
"method", ())
6115+
ERROR(result_builder_static_buildblock_or_buildpartialblock, none,
6116+
"result builder must provide at least one static 'buildBlock' "
6117+
"method, or both 'buildPartialBlock(first:)' and "
6118+
"'buildPartialBlock(accumulated:next:)'", ())
6119+
ERROR(result_builder_missing_available_buildpartialblock, none,
6120+
"result builder %0 does not implement any 'buildBlock' or a combination "
6121+
"of 'buildPartialBlock(first:)' and "
6122+
"'buildPartialBlock(accumulated:next:)' with sufficient availability for "
6123+
"this call site", (Type))
61156124
NOTE(result_builder_non_static_buildblock, none,
61166125
"did you mean to make instance method 'buildBlock' static?", ())
61176126
NOTE(result_builder_buildblock_enum_case, none,

include/swift/AST/KnownIdentifiers.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ IDENTIFIER(allKeys)
2828
IDENTIFIER(alloc)
2929
IDENTIFIER(allocWithZone)
3030
IDENTIFIER(allZeros)
31+
IDENTIFIER(accumulated)
3132
IDENTIFIER(ActorType)
3233
IDENTIFIER(Any)
3334
IDENTIFIER(ArrayLiteralElement)
@@ -41,6 +42,7 @@ IDENTIFIER(buildFinalResult)
4142
IDENTIFIER(buildIf)
4243
IDENTIFIER(buildLimitedAvailability)
4344
IDENTIFIER(buildOptional)
45+
IDENTIFIER(buildPartialBlock)
4446
IDENTIFIER(callAsFunction)
4547
IDENTIFIER(Change)
4648
IDENTIFIER_WITH_NAME(code_, "_code")

lib/Sema/BuilderTransform.cpp

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class BuilderClosureVisitor
7171
Type builderType;
7272
NominalTypeDecl *builder = nullptr;
7373
Identifier buildOptionalId;
74-
llvm::SmallDenseMap<Identifier, bool> supportedOps;
74+
llvm::SmallDenseMap<DeclName, bool> supportedOps;
7575

7676
SkipUnhandledConstructInResultBuilder::UnhandledNode unhandledNode;
7777

@@ -141,15 +141,18 @@ class BuilderClosureVisitor
141141
}
142142

143143
/// Check whether the builder supports the given operation.
144-
bool builderSupports(Identifier fnName,
145-
ArrayRef<Identifier> argLabels = {}) {
146-
auto known = supportedOps.find(fnName);
144+
bool builderSupports(Identifier fnBaseName,
145+
ArrayRef<Identifier> argLabels = {},
146+
bool checkAvailability = false) {
147+
DeclName name(dc->getASTContext(), fnBaseName, argLabels);
148+
auto known = supportedOps.find(name);
147149
if (known != supportedOps.end()) {
148150
return known->second;
149151
}
150152

151-
return supportedOps[fnName] = TypeChecker::typeSupportsBuilderOp(
152-
builderType, dc, fnName, argLabels);
153+
return supportedOps[name] = TypeChecker::typeSupportsBuilderOp(
154+
builderType, dc, fnBaseName, argLabels, /*allResults*/ {},
155+
checkAvailability);
153156
}
154157

155158
/// Build an implicit variable in this context.
@@ -368,11 +371,39 @@ class BuilderClosureVisitor
368371
return nullptr;
369372

370373
Expr *call = nullptr;
371-
// If the builder supports `buildBlock(combining:into:)`, use this to
372-
// combine subexpressions pairwise.
374+
// If the builder supports `buildPartialBlock(first:)` and
375+
// `buildPartialBlock(accumulated:next:)`, use this to combine
376+
// subexpressions pairwise.
373377
if (ctx.LangOpts.EnableExperimentalPairwiseBuildBlock &&
374378
!expressions.empty() &&
375-
builderSupports(ctx.Id_buildBlock, {ctx.Id_combining, ctx.Id_into})) {
379+
builderSupports(ctx.Id_buildPartialBlock, {ctx.Id_first},
380+
/*checkAvailability*/ true) &&
381+
builderSupports(ctx.Id_buildPartialBlock,
382+
{ctx.Id_accumulated, ctx.Id_next},
383+
/*checkAvailability*/ true)) {
384+
// NOTE: The current implementation uses one-way constraints in between
385+
// subexpressions. It's functionally equivalent to the following:
386+
// let v0 = Builder.buildPartialBlock(first: arg_0)
387+
// let v1 = Builder.buildPartialBlock(accumulated: arg_1, next: v0)
388+
// ...
389+
// return Builder.buildPartialBlock(accumulated: arg_n, next: ...)
390+
call = buildCallIfWanted(braceStmt->getStartLoc(),
391+
ctx.Id_buildPartialBlock,
392+
{expressions.front()},
393+
/*argLabels=*/{ctx.Id_first});
394+
for (auto *expr : llvm::drop_begin(expressions)) {
395+
call = buildCallIfWanted(braceStmt->getStartLoc(),
396+
ctx.Id_buildPartialBlock,
397+
{new (ctx) OneWayExpr(call), expr},
398+
{ctx.Id_accumulated, ctx.Id_next});
399+
}
400+
}
401+
// TODO: Remove support for the old method name,
402+
// `buildBlock(combining:into:)`.
403+
else if (ctx.LangOpts.EnableExperimentalPairwiseBuildBlock &&
404+
!expressions.empty() &&
405+
builderSupports(ctx.Id_buildBlock,
406+
{ctx.Id_combining, ctx.Id_into})) {
376407
// NOTE: The current implementation uses one-way constraints in between
377408
// subexpressions. It's functionally equivalent to the following:
378409
// let v0 = Builder.buildBlock(arg_0)
@@ -387,6 +418,17 @@ class BuilderClosureVisitor
387418
{ctx.Id_combining, ctx.Id_into});
388419
}
389420
}
421+
// If `buildBlock` does not exist at this point, it could be the case that
422+
// `buildPartialBlock` did not have the sufficient availability for this
423+
// call site. Diagnose it.
424+
else if (ctx.LangOpts.EnableExperimentalPairwiseBuildBlock &&
425+
!builderSupports(ctx.Id_buildBlock)) {
426+
ctx.Diags.diagnose(
427+
braceStmt->getStartLoc(),
428+
diag::result_builder_missing_available_buildpartialblock,
429+
builderType);
430+
return nullptr;
431+
}
390432
// Otherwise, call `buildBlock` on all subexpressions.
391433
else {
392434
// Call Builder.buildBlock(... args ...)
@@ -2016,7 +2058,8 @@ std::vector<ReturnStmt *> TypeChecker::findReturnStatements(AnyFunctionRef fn) {
20162058

20172059
bool TypeChecker::typeSupportsBuilderOp(
20182060
Type builderType, DeclContext *dc, Identifier fnName,
2019-
ArrayRef<Identifier> argLabels, SmallVectorImpl<ValueDecl *> *allResults) {
2061+
ArrayRef<Identifier> argLabels, SmallVectorImpl<ValueDecl *> *allResults,
2062+
bool checkAvailability) {
20202063
bool foundMatch = false;
20212064
SmallVector<ValueDecl *, 4> foundDecls;
20222065
dc->lookupQualified(
@@ -2036,6 +2079,17 @@ bool TypeChecker::typeSupportsBuilderOp(
20362079
continue;
20372080
}
20382081

2082+
// If we are checking availability, the candidate must have enough
2083+
// availability in the calling context.
2084+
if (checkAvailability) {
2085+
if (AvailableAttr::isUnavailable(func))
2086+
continue;
2087+
if (TypeChecker::checkDeclarationAvailability(
2088+
func, ExportContext::forFunctionBody(
2089+
dc, extractNearestSourceLoc(dc))))
2090+
continue;
2091+
}
2092+
20392093
foundMatch = true;
20402094
break;
20412095
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3266,15 +3266,30 @@ void AttributeChecker::visitPropertyWrapperAttr(PropertyWrapperAttr *attr) {
32663266

32673267
void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) {
32683268
auto *nominal = dyn_cast<NominalTypeDecl>(D);
3269+
auto &ctx = D->getASTContext();
32693270
SmallVector<ValueDecl *, 4> potentialMatches;
32703271
bool supportsBuildBlock = TypeChecker::typeSupportsBuilderOp(
3271-
nominal->getDeclaredType(), nominal, D->getASTContext().Id_buildBlock,
3272+
nominal->getDeclaredType(), nominal, ctx.Id_buildBlock,
32723273
/*argLabels=*/{}, &potentialMatches);
3273-
3274-
if (!supportsBuildBlock) {
3274+
bool isBuildPartialBlockFeatureEnabled =
3275+
ctx.LangOpts.EnableExperimentalPairwiseBuildBlock;
3276+
bool supportsBuildPartialBlock = isBuildPartialBlockFeatureEnabled &&
3277+
TypeChecker::typeSupportsBuilderOp(
3278+
nominal->getDeclaredType(), nominal,
3279+
ctx.Id_buildPartialBlock,
3280+
/*argLabels=*/{ctx.Id_first}, &potentialMatches) &&
3281+
TypeChecker::typeSupportsBuilderOp(
3282+
nominal->getDeclaredType(), nominal,
3283+
ctx.Id_buildPartialBlock,
3284+
/*argLabels=*/{ctx.Id_accumulated, ctx.Id_next}, &potentialMatches);
3285+
3286+
if (!supportsBuildBlock && !supportsBuildPartialBlock) {
32753287
{
32763288
auto diag = diagnose(
3277-
nominal->getLoc(), diag::result_builder_static_buildblock);
3289+
nominal->getLoc(),
3290+
isBuildPartialBlockFeatureEnabled
3291+
? diag::result_builder_static_buildblock_or_buildpartialblock
3292+
: diag::result_builder_static_buildblock);
32783293

32793294
// If there were no close matches, propose adding a stub.
32803295
SourceLoc buildInsertionLoc;

lib/Sema/TypeChecker.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,8 @@ UnresolvedMemberExpr *getUnresolvedMemberChainBase(Expr *expr);
11851185
/// are verified against any candidates.
11861186
bool typeSupportsBuilderOp(Type builderType, DeclContext *dc, Identifier fnName,
11871187
ArrayRef<Identifier> argLabels = {},
1188-
SmallVectorImpl<ValueDecl *> *allResults = nullptr);
1188+
SmallVectorImpl<ValueDecl *> *allResults = nullptr,
1189+
bool checkAvailability = false);
11891190

11901191
/// Forces all changes specified by the module's access notes file to be
11911192
/// applied to this declaration. It is safe to call this function more than

0 commit comments

Comments
 (0)