Skip to content

Commit a3979da

Browse files
authored
Merge pull request #30322 from DougGregor/function-builders-extras
[Function builders] Additional entry points
2 parents e976aa9 + f0530d0 commit a3979da

File tree

4 files changed

+131
-4
lines changed

4 files changed

+131
-4
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ IDENTIFIER(buildBlock)
3535
IDENTIFIER(buildDo)
3636
IDENTIFIER(buildEither)
3737
IDENTIFIER(buildExpression)
38+
IDENTIFIER(buildFinalResult)
3839
IDENTIFIER(buildIf)
40+
IDENTIFIER(buildOptional)
3941
IDENTIFIER(callAsFunction)
4042
IDENTIFIER(Change)
4143
IDENTIFIER_WITH_NAME(code_, "_code")

lib/Sema/BuilderTransform.cpp

Lines changed: 22 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.
@@ -210,6 +219,15 @@ class BuilderClosureVisitor
210219
return None;
211220

212221
applied.returnExpr = buildVarRef(bodyVar, stmt->getEndLoc());
222+
223+
// If there is a buildFinalResult(_:), call it.
224+
ASTContext &ctx = cs->getASTContext();
225+
if (builderSupports(ctx.Id_buildFinalResult, { Identifier() })) {
226+
applied.returnExpr = buildCallIfWanted(
227+
applied.returnExpr->getLoc(), ctx.Id_buildFinalResult,
228+
{ applied.returnExpr }, { Identifier() });
229+
}
230+
213231
applied.returnExpr = cs->buildTypeErasedExpr(applied.returnExpr,
214232
dc, applied.bodyResultType,
215233
CTP_ReturnStmt);
@@ -407,8 +425,8 @@ class BuilderClosureVisitor
407425
if (!isBuildableIfChainRecursive(ifStmt, numPayloads, isOptional))
408426
return false;
409427

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

414432
// If there are multiple clauses, we need 'buildEither(first:)' and
@@ -514,9 +532,9 @@ class BuilderClosureVisitor
514532
// The operand should have optional type if we had optional results,
515533
// so we just need to call `buildIf` now, since we're at the top level.
516534
if (isOptional && isTopLevel) {
517-
thenExpr = buildCallIfWanted(ifStmt->getEndLoc(), ctx.Id_buildIf,
535+
thenExpr = buildCallIfWanted(ifStmt->getEndLoc(), buildOptionalId,
518536
thenExpr, /*argLabels=*/{ });
519-
elseExpr = buildCallIfWanted(ifStmt->getEndLoc(), ctx.Id_buildIf,
537+
elseExpr = buildCallIfWanted(ifStmt->getEndLoc(), buildOptionalId,
520538
elseExpr, /*argLabels=*/{ });
521539
}
522540

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+
}

test/Constraints/function_builder_diags.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,63 @@ func testCaseVarTypes(e: E3) {
480480
}
481481
}
482482
}
483+
484+
// Test for buildFinalResult.
485+
@_functionBuilder
486+
struct WrapperBuilder {
487+
static func buildBlock() -> () { }
488+
489+
static func buildBlock<T1>(_ t1: T1) -> T1 {
490+
return t1
491+
}
492+
493+
static func buildBlock<T1, T2>(_ t1: T1, _ t2: T2) -> (T1, T2) {
494+
return (t1, t2)
495+
}
496+
497+
static func buildBlock<T1, T2, T3>(_ t1: T1, _ t2: T2, _ t3: T3)
498+
-> (T1, T2, T3) {
499+
return (t1, t2, t3)
500+
}
501+
502+
static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)
503+
-> (T1, T2, T3, T4) {
504+
return (t1, t2, t3, t4)
505+
}
506+
507+
static func buildBlock<T1, T2, T3, T4, T5>(
508+
_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4, _ t5: T5
509+
) -> (T1, T2, T3, T4, T5) {
510+
return (t1, t2, t3, t4, t5)
511+
}
512+
513+
static func buildDo<T>(_ value: T) -> T { return value }
514+
static func buildIf<T>(_ value: T?) -> T? { return value }
515+
516+
static func buildEither<T,U>(first value: T) -> Either<T,U> {
517+
return .first(value)
518+
}
519+
static func buildEither<T,U>(second value: U) -> Either<T,U> {
520+
return .second(value)
521+
}
522+
static func buildFinalResult<T>(_ value: T) -> Wrapper<T> {
523+
return Wrapper(value: value)
524+
}
525+
}
526+
527+
struct Wrapper<T> {
528+
var value: T
529+
}
530+
531+
func wrapperify<T>(_ cond: Bool, @WrapperBuilder body: (Bool) -> T) -> T{
532+
return body(cond)
533+
}
534+
535+
func testWrapperBuilder() {
536+
let x = wrapperify(true) { c in
537+
3.14159
538+
"hello"
539+
}
540+
541+
let _: Int = x // expected-error{{cannot convert value of type 'Wrapper<(Double, String)>' to specified type 'Int'}}
542+
}

0 commit comments

Comments
 (0)