Merge pull request #79617 from xedin/isolate-buildBlock-in-if-and-case-statements

[BuilderTransform] Type-check `buildBlock` of each condition body separately from "join" operation
This commit is contained in:
Pavel Yaskevich
2025-02-27 16:45:22 -08:00
committed by GitHub
4 changed files with 70 additions and 11 deletions

View File

@@ -261,7 +261,8 @@ protected:
}
std::pair<NullablePtr<Expr>, std::optional<UnsupportedElt>>
transform(BraceStmt *braceStmt, SmallVectorImpl<ASTNode> &newBody) {
transform(BraceStmt *braceStmt, SmallVectorImpl<ASTNode> &newBody,
bool isolateBuildBlock = false) {
SmallVector<Expr *, 4> buildBlockArguments;
auto failTransform = [&](UnsupportedElt unsupported) {
@@ -326,6 +327,14 @@ protected:
auto *buildBlock = builder.buildCall(
braceStmt->getStartLoc(), ctx.Id_buildBlock, buildBlockArguments,
/*argLabels=*/{});
if (isolateBuildBlock) {
auto *buildBlockVar = captureExpr(buildBlock, newBody);
return std::make_pair(
builder.buildVarRef(buildBlockVar, braceStmt->getStartLoc()),
std::nullopt);
}
return std::make_pair(buildBlock, std::nullopt);
}
}
@@ -502,7 +511,8 @@ protected:
auto *ifBraceStmt = cast<BraceStmt>(ifStmt->getThenStmt());
std::tie(thenVarRef, unsupported) = transform(ifBraceStmt, thenBody);
std::tie(thenVarRef, unsupported) =
transform(ifBraceStmt, thenBody, /*isolateBuildBlock=*/true);
if (unsupported) {
recordUnsupported(*unsupported);
return nullptr;
@@ -537,7 +547,8 @@ protected:
auto *elseBraceStmt = cast<BraceStmt>(elseStmt);
SmallVector<ASTNode> elseBody;
std::tie(elseVarRef, unsupported) = transform(elseBraceStmt, elseBody);
std::tie(elseVarRef, unsupported) = transform(
elseBraceStmt, elseBody, /*isolateBuildBlock=*/true);
if (unsupported) {
recordUnsupported(*unsupported);
return nullptr;
@@ -634,7 +645,8 @@ protected:
std::optional<UnsupportedElt> unsupported;
SmallVector<ASTNode, 4> newBody;
std::tie(caseVarRef, unsupported) = transform(body, newBody);
std::tie(caseVarRef, unsupported) =
transform(body, newBody, /*isolateBuildBlock=*/true);
if (unsupported) {
recordUnsupported(*unsupported);

View File

@@ -57,7 +57,7 @@ public enum FooBuilder {
public static func buildBlock(_ components: Component...) -> Component {
return components.flatMap { $0 }
}
public static func buildLimitedAvailability(_ component: Component) -> Component {
return component
}
@@ -359,6 +359,9 @@ public struct MyFooProviderInferredWithArrayReturn: FooProvider {
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "elseElements": [
@@ -379,6 +382,9 @@ public struct MyFooProviderInferredWithArrayReturn: FooProvider {
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "elseElements": [
@@ -397,6 +403,9 @@ public struct MyFooProviderInferredWithArrayReturn: FooProvider {
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
@@ -455,6 +464,9 @@ public struct MyFooProviderInferredWithArrayReturn: FooProvider {
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "elseElements": []
@@ -518,6 +530,9 @@ public struct MyFooProviderInferredWithArrayReturn: FooProvider {
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "elseElements": [
@@ -536,6 +551,9 @@ public struct MyFooProviderInferredWithArrayReturn: FooProvider {
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "element": {}
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }

View File

@@ -90,3 +90,33 @@ tuplify {
value.a
}
}
tuplify { _ in
switch true {
// CHECK: (case_stmt {{.*}}
// CHECK: (pattern_named implicit type="Int" "$__builder2")
// CHECK: (declref_expr implicit type="(TupleBuilder.Type) -> (Int) -> Int" location={{.*}} range={{.*}} decl="{{.*}}buildBlock@{{.*}}" function_ref=single apply)
// CHECK: (call_expr implicit type="Either<Int, Int>" {{.*}}
// CHECK-NEXT: (dot_syntax_call_expr implicit type="(Int) -> Either<Int, Int>" {{.*}}
// CHECK-NEXT: (declref_expr implicit type="(TupleBuilder.Type) -> (Int) -> Either<Int, Int>" location={{.*}} range={{.*}} decl="{{.*}}buildEither(first:)@{{.*}}" function_ref=single apply)
// CHECK: (argument_list implicit labels="first:"
// CHECK-NEXT: (argument label="first"
// CHECK-NEXT: (load_expr implicit type="Int" {{.*}}
// CHECK-NEXT: (declref_expr implicit type="@lvalue Int" location={{.*}} range={{.*}} decl="{{.*}}.$__builder2@{{.*}}" function_ref=unapplied))))))))
case true: 0
// CHECK: (case_stmt {{.*}}
// CHECK: (pattern_named implicit type="Int" "$__builder4")
// CHECK: (declref_expr implicit type="(TupleBuilder.Type) -> (Int) -> Int" location={{.*}} range={{.*}} decl="{{.*}}buildBlock@{{.*}}" function_ref=single apply)
// CHECK: (call_expr implicit type="Either<Int, Int>" {{.*}}
// CHECK-NEXT: (dot_syntax_call_expr implicit type="(Int) -> Either<Int, Int>" {{.*}}
// CHECK-NEXT: (declref_expr implicit type="(TupleBuilder.Type) -> (Int) -> Either<Int, Int>" location={{.*}} range={{.*}} decl="{{.*}}buildEither(second:)@{{.*}}" function_ref=single apply)
// CHECK: (argument_list implicit labels="second:"
// CHECK-NEXT: (argument label="second"
// CHECK-NEXT: (load_expr implicit type="Int" {{.*}}
// CHECK-NEXT: (declref_expr implicit type="@lvalue Int" location={{.*}} range={{.*}} decl="{{.*}}.$__builder4@{{.*}}" function_ref=unapplied))))))))
case false: 1
}
}

View File

@@ -1,9 +1,7 @@
// RUN: %target-typecheck-verify-swift
// RUN: %target-typecheck-verify-swift -I %t
// This test verifies that `buildBlock` is type-checked together with enclosing context,
// which means that it's not captured into separate variable but rather used directly and
// contextual information can impact overload resolution.
// ! - Based on the result builders proposal `buildBlock` shouldn't be type-checked together with `buildOptional`.
protocol ActionIdentifier: Hashable {
}
@@ -14,11 +12,11 @@ struct ActionLookup<Identifier: ActionIdentifier> {
@resultBuilder
enum ActionLookupBuilder<Identifier: ActionIdentifier> { // expected-note 3{{'Identifier' previously declared here}}
static func buildBlock<Identifier: ActionIdentifier>(_ components: [ActionLookup<Identifier>]...) -> ActionLookup<Identifier> { // expected-warning {{generic parameter 'Identifier' shadows generic parameter from outer scope with the same name; this is an error in the Swift 6 language mode}}
static func buildBlock<Identifier: ActionIdentifier>(_ components: [ActionLookup<Identifier>]...) -> ActionLookup<Identifier> { // expected-warning {{generic parameter 'Identifier' shadows generic parameter from outer scope with the same name; this is an error in the Swift 6 language mode}} expected-note {{found this candidate}}
fatalError()
}
static func buildBlock<Identifier: ActionIdentifier>(_ components: [ActionLookup<Identifier>]...) -> [ActionLookup<Identifier>] { // expected-warning {{generic parameter 'Identifier' shadows generic parameter from outer scope with the same name; this is an error in the Swift 6 language mode}}
static func buildBlock<Identifier: ActionIdentifier>(_ components: [ActionLookup<Identifier>]...) -> [ActionLookup<Identifier>] { // expected-warning {{generic parameter 'Identifier' shadows generic parameter from outer scope with the same name; this is an error in the Swift 6 language mode}} expected-note {{found this candidate}}
[]
}
@@ -43,7 +41,8 @@ enum ActionType: String, ActionIdentifier, CaseIterable {
ActionTypeLookup(
.download
)
if true { // If condition is needed to make sure that `buildOptional` affects `buildBlock` resolution.
if true { // If condition without else is needed to make sure that `buildOptional` affects `buildBlock` resolution.
// expected-error@-1 {{ambiguous use of 'buildBlock'}}
ActionTypeLookup(
.upload
)