@@ -71,7 +71,7 @@ class BuilderClosureVisitor
71
71
Type builderType;
72
72
NominalTypeDecl *builder = nullptr ;
73
73
Identifier buildOptionalId;
74
- llvm::SmallDenseMap<Identifier , bool > supportedOps;
74
+ llvm::SmallDenseMap<DeclName , bool > supportedOps;
75
75
76
76
SkipUnhandledConstructInResultBuilder::UnhandledNode unhandledNode;
77
77
@@ -141,15 +141,18 @@ class BuilderClosureVisitor
141
141
}
142
142
143
143
// / 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);
147
149
if (known != supportedOps.end ()) {
148
150
return known->second ;
149
151
}
150
152
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);
153
156
}
154
157
155
158
// / Build an implicit variable in this context.
@@ -368,11 +371,39 @@ class BuilderClosureVisitor
368
371
return nullptr ;
369
372
370
373
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.
373
377
if (ctx.LangOpts .EnableExperimentalPairwiseBuildBlock &&
374
378
!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 })) {
376
407
// NOTE: The current implementation uses one-way constraints in between
377
408
// subexpressions. It's functionally equivalent to the following:
378
409
// let v0 = Builder.buildBlock(arg_0)
@@ -387,6 +418,17 @@ class BuilderClosureVisitor
387
418
{ctx.Id_combining , ctx.Id_into });
388
419
}
389
420
}
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
+ }
390
432
// Otherwise, call `buildBlock` on all subexpressions.
391
433
else {
392
434
// Call Builder.buildBlock(... args ...)
@@ -2016,7 +2058,8 @@ std::vector<ReturnStmt *> TypeChecker::findReturnStatements(AnyFunctionRef fn) {
2016
2058
2017
2059
bool TypeChecker::typeSupportsBuilderOp (
2018
2060
Type builderType, DeclContext *dc, Identifier fnName,
2019
- ArrayRef<Identifier> argLabels, SmallVectorImpl<ValueDecl *> *allResults) {
2061
+ ArrayRef<Identifier> argLabels, SmallVectorImpl<ValueDecl *> *allResults,
2062
+ bool checkAvailability) {
2020
2063
bool foundMatch = false ;
2021
2064
SmallVector<ValueDecl *, 4 > foundDecls;
2022
2065
dc->lookupQualified (
@@ -2036,6 +2079,17 @@ bool TypeChecker::typeSupportsBuilderOp(
2036
2079
continue ;
2037
2080
}
2038
2081
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
+
2039
2093
foundMatch = true ;
2040
2094
break ;
2041
2095
}
0 commit comments