@@ -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
20172059bool 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 }
0 commit comments