Skip to content

Commit f0530d0

Browse files
committed
[Function builders] Support buildOptional(_:) in lieu of buildIf(_:).
Line up with the function builders pitch, which uses buildOptional(_:) to build optional values.
1 parent ce97d22 commit f0530d0

File tree

3 files changed

+61
-4
lines changed

3 files changed

+61
-4
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ IDENTIFIER(buildEither)
3737
IDENTIFIER(buildExpression)
3838
IDENTIFIER(buildFinalResult)
3939
IDENTIFIER(buildIf)
40+
IDENTIFIER(buildOptional)
4041
IDENTIFIER(callAsFunction)
4142
IDENTIFIER(Change)
4243
IDENTIFIER_WITH_NAME(code_, "_code")

lib/Sema/BuilderTransform.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class BuilderClosureVisitor
4949
ASTContext &ctx;
5050
Type builderType;
5151
NominalTypeDecl *builder = nullptr;
52+
Identifier buildOptionalId;
5253
llvm::SmallDenseMap<Identifier, bool> supportedOps;
5354

5455
SkipUnhandledConstructInFunctionBuilder::UnhandledNode unhandledNode;
@@ -201,6 +202,14 @@ class BuilderClosureVisitor
201202
builder = builderType->getAnyNominal();
202203
applied.builderType = builderType;
203204
applied.bodyResultType = bodyResultType;
205+
206+
// Use buildOptional(_:) if available, otherwise fall back to buildIf
207+
// when available.
208+
if (builderSupports(ctx.Id_buildOptional) ||
209+
!builderSupports(ctx.Id_buildIf))
210+
buildOptionalId = ctx.Id_buildOptional;
211+
else
212+
buildOptionalId = ctx.Id_buildIf;
204213
}
205214

206215
/// Apply the builder transform to the given statement.
@@ -416,8 +425,8 @@ class BuilderClosureVisitor
416425
if (!isBuildableIfChainRecursive(ifStmt, numPayloads, isOptional))
417426
return false;
418427

419-
// If there's a missing 'else', we need 'buildIf' to exist.
420-
if (isOptional && !builderSupports(ctx.Id_buildIf))
428+
// If there's a missing 'else', we need 'buildOptional' to exist.
429+
if (isOptional && !builderSupports(buildOptionalId))
421430
return false;
422431

423432
// If there are multiple clauses, we need 'buildEither(first:)' and
@@ -523,9 +532,9 @@ class BuilderClosureVisitor
523532
// The operand should have optional type if we had optional results,
524533
// so we just need to call `buildIf` now, since we're at the top level.
525534
if (isOptional && isTopLevel) {
526-
thenExpr = buildCallIfWanted(ifStmt->getEndLoc(), ctx.Id_buildIf,
535+
thenExpr = buildCallIfWanted(ifStmt->getEndLoc(), buildOptionalId,
527536
thenExpr, /*argLabels=*/{ });
528-
elseExpr = buildCallIfWanted(ifStmt->getEndLoc(), ctx.Id_buildIf,
537+
elseExpr = buildCallIfWanted(ifStmt->getEndLoc(), buildOptionalId,
529538
elseExpr, /*argLabels=*/{ });
530539
}
531540

test/Constraints/function_builder.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,50 @@ testSwitchCombined(getE(1))
617617
// CHECK: testSwitchCombined
618618
// CHECK-SAME: second("just 42")
619619
testSwitchCombined(getE(2))
620+
621+
622+
// Test buildOptional(_:) as an alternative to buildIf(_:).
623+
@_functionBuilder
624+
struct TupleBuilderWithOpt {
625+
static func buildBlock<T1>(_ t1: T1) -> (T1) {
626+
return (t1)
627+
}
628+
629+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
630+
return (t1, t2)
631+
}
632+
633+
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
634+
-> (T1, T2, T3) {
635+
return (t1, t2, t3)
636+
}
637+
638+
static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)
639+
-> (T1, T2, T3, T4) {
640+
return (t1, t2, t3, t4)
641+
}
642+
643+
static func buildBlock<T1, T2, T3, T4, T5>(
644+
_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5
645+
) -> (T1, T2, T3, T4, T5) {
646+
return (t1, t2, t3, t4, t5)
647+
}
648+
649+
static func buildDo<T>(_ value: T) -> T { return value }
650+
static func buildOptional<T>(_ value: T?) -> T? { return value }
651+
652+
static func buildEither<T,U>(first value: T) -> Either<T,U> {
653+
return .first(value)
654+
}
655+
static func buildEither<T,U>(second value: U) -> Either<T,U> {
656+
return .second(value)
657+
}
658+
}
659+
660+
func tuplifyWithOpt<T>(_ cond: Bool, @TupleBuilderWithOpt body: (Bool) -> T) {
661+
print(body(cond))
662+
}
663+
tuplifyWithOpt(true) { c in
664+
"1"
665+
3.14159
666+
}

0 commit comments

Comments
 (0)