[BuilderTransform] Case: Save result of buildBlock into a variable before using in buildEither

Fixes implementation issue where build block is type-checked as part
of `buildEither(...)`. This is incorrect according to the original
proposal (SE-0289), `buildBlock` should be type-checked independently
and `buildEither` should have no effect on what overload of `buildBlock`
gets selected.
This commit is contained in:
Pavel Yaskevich
2025-01-18 18:49:29 -08:00
parent 7c258f2057
commit 3add37514e
2 changed files with 42 additions and 2 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);
}
}
@@ -634,7 +643,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

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