Skip to content

Commit 9f7d3fc

Browse files
authored
Merge pull request swiftlang#40708 from xedin/disable-multi-stmt-inference-inside-result-builder-bodies
[ConstraintSystem] SE-0326: Temporarily prevent multi-statement closure inference in result builder contexts
2 parents 1c9bcc9 + fe9224d commit 9f7d3fc

File tree

5 files changed

+144
-36
lines changed

5 files changed

+144
-36
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5292,6 +5292,10 @@ class ConstraintSystem {
52925292
/// part of the constraint system.
52935293
void forEachExpr(Expr *expr, llvm::function_ref<Expr *(Expr *)> callback);
52945294

5295+
/// Determine whether one of the parent closures the given one is nested
5296+
/// in (if any) has a result builder applied to its body.
5297+
bool isInResultBuilderContext(ClosureExpr *closure) const;
5298+
52955299
SWIFT_DEBUG_DUMP;
52965300
SWIFT_DEBUG_DUMPER(dump(Expr *));
52975301

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8023,6 +8023,8 @@ namespace {
80238023
/// \returns true if any part of the processing fails.
80248024
bool processDelayed() {
80258025
bool hadError = false;
8026+
auto &solution = Rewriter.solution;
8027+
auto &cs = solution.getConstraintSystem();
80268028

80278029
while (!ClosuresToTypeCheck.empty()) {
80288030
auto *closure = ClosuresToTypeCheck.pop_back_val();
@@ -8034,10 +8036,7 @@ namespace {
80348036
// as a stack because multi-statement closures could
80358037
// have other multi-statement closures in the body.
80368038
auto &ctx = closure->getASTContext();
8037-
if (ctx.TypeCheckerOpts.EnableMultiStatementClosureInference) {
8038-
auto &solution = Rewriter.solution;
8039-
auto &cs = solution.getConstraintSystem();
8040-
8039+
if (cs.participatesInInference(closure)) {
80418040
hadError |= cs.applySolutionToBody(
80428041
solution, closure, Rewriter.dc,
80438042
[&](SolutionApplicationTarget target) {

lib/Sema/CSClosure.cpp

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -512,28 +512,28 @@ class ClosureConstraintGenerator
512512
}
513513

514514
void visitBreakStmt(BreakStmt *breakStmt) {
515-
if (!isSupportedMultiStatementClosure())
516-
llvm_unreachable("Unsupported statement: Break");
515+
assert(isSupportedMultiStatementClosure() &&
516+
"Unsupported statement: Break");
517517
}
518518

519519
void visitContinueStmt(ContinueStmt *continueStmt) {
520-
if (!isSupportedMultiStatementClosure())
521-
llvm_unreachable("Unsupported statement: Continue");
520+
assert(isSupportedMultiStatementClosure() &&
521+
"Unsupported statement: Continue");
522522
}
523523

524524
void visitDeferStmt(DeferStmt *deferStmt) {
525-
if (!isSupportedMultiStatementClosure())
526-
llvm_unreachable("Unsupported statement: Defer");
525+
assert(isSupportedMultiStatementClosure() &&
526+
"Unsupported statement: Defer");
527527
}
528528

529529
void visitFallthroughStmt(FallthroughStmt *fallthroughStmt) {
530-
if (!isSupportedMultiStatementClosure())
531-
llvm_unreachable("Unsupported statement: Fallthrough");
530+
assert(isSupportedMultiStatementClosure() &&
531+
"Unsupported statement: Fallthrough");
532532
}
533533

534534
void visitIfStmt(IfStmt *ifStmt) {
535-
if (!isSupportedMultiStatementClosure())
536-
llvm_unreachable("Unsupported statement: If");
535+
assert(isSupportedMultiStatementClosure() &&
536+
"Unsupported statement: If");
537537

538538
SmallVector<ElementInfo, 4> elements;
539539

@@ -562,8 +562,8 @@ class ClosureConstraintGenerator
562562
}
563563

564564
void visitGuardStmt(GuardStmt *guardStmt) {
565-
if (!isSupportedMultiStatementClosure())
566-
llvm_unreachable("Unsupported statement: Guard");
565+
assert(isSupportedMultiStatementClosure() &&
566+
"Unsupported statement: Guard");
567567

568568
createConjunction(cs,
569569
{makeElement(guardStmt->getCondPointer(),
@@ -574,8 +574,8 @@ class ClosureConstraintGenerator
574574
}
575575

576576
void visitWhileStmt(WhileStmt *whileStmt) {
577-
if (!isSupportedMultiStatementClosure())
578-
llvm_unreachable("Unsupported statement: Guard");
577+
assert(isSupportedMultiStatementClosure() &&
578+
"Unsupported statement: While");
579579

580580
createConjunction(cs,
581581
{makeElement(whileStmt->getCondPointer(),
@@ -586,15 +586,15 @@ class ClosureConstraintGenerator
586586
}
587587

588588
void visitDoStmt(DoStmt *doStmt) {
589-
if (!isSupportedMultiStatementClosure())
590-
llvm_unreachable("Unsupported statement: Do");
589+
assert(isSupportedMultiStatementClosure() &&
590+
"Unsupported statement: Do");
591591

592592
visitBraceStmt(doStmt->getBody());
593593
}
594594

595595
void visitRepeatWhileStmt(RepeatWhileStmt *repeatWhileStmt) {
596-
if (!isSupportedMultiStatementClosure())
597-
llvm_unreachable("Unsupported statement: RepeatWhile");
596+
assert(isSupportedMultiStatementClosure() &&
597+
"Unsupported statement: RepeatWhile");
598598

599599
createConjunction(cs,
600600
{makeElement(repeatWhileStmt->getCond(),
@@ -606,8 +606,8 @@ class ClosureConstraintGenerator
606606
}
607607

608608
void visitPoundAssertStmt(PoundAssertStmt *poundAssertStmt) {
609-
if (!isSupportedMultiStatementClosure())
610-
llvm_unreachable("Unsupported statement: PoundAssert");
609+
assert(isSupportedMultiStatementClosure() &&
610+
"Unsupported statement: PoundAssert");
611611

612612
createConjunction(cs,
613613
{makeElement(poundAssertStmt->getCondition(),
@@ -618,8 +618,8 @@ class ClosureConstraintGenerator
618618
}
619619

620620
void visitThrowStmt(ThrowStmt *throwStmt) {
621-
if (!isSupportedMultiStatementClosure())
622-
llvm_unreachable("Unsupported statement: Throw");
621+
assert(isSupportedMultiStatementClosure() &&
622+
"Unsupported statement: Throw");
623623

624624
Type errType =
625625
cs.getASTContext().getErrorDecl()->getDeclaredInterfaceType();
@@ -641,8 +641,8 @@ class ClosureConstraintGenerator
641641
}
642642

643643
void visitForEachStmt(ForEachStmt *forEachStmt) {
644-
if (!isSupportedMultiStatementClosure())
645-
llvm_unreachable("Unsupported statement: ForEach");
644+
assert(isSupportedMultiStatementClosure() &&
645+
"Unsupported statement: ForEach");
646646

647647
auto *stmtLoc = cs.getConstraintLocator(locator);
648648

@@ -679,8 +679,8 @@ class ClosureConstraintGenerator
679679
}
680680

681681
void visitSwitchStmt(SwitchStmt *switchStmt) {
682-
if (!isSupportedMultiStatementClosure())
683-
llvm_unreachable("Unsupported statement: Switch");
682+
assert(isSupportedMultiStatementClosure() &&
683+
"Unsupported statement: Switch");
684684

685685
auto *switchLoc = cs.getConstraintLocator(
686686
locator, LocatorPathElt::ClosureBodyElement(switchStmt));
@@ -705,8 +705,8 @@ class ClosureConstraintGenerator
705705
}
706706

707707
void visitDoCatchStmt(DoCatchStmt *doStmt) {
708-
if (!isSupportedMultiStatementClosure())
709-
llvm_unreachable("Unsupported statement: DoCatch");
708+
assert(isSupportedMultiStatementClosure() &&
709+
"Unsupported statement: DoCatch");
710710

711711
auto *doLoc = cs.getConstraintLocator(
712712
locator, LocatorPathElt::ClosureBodyElement(doStmt));
@@ -725,8 +725,8 @@ class ClosureConstraintGenerator
725725
}
726726

727727
void visitCaseStmt(CaseStmt *caseStmt) {
728-
if (!isSupportedMultiStatementClosure())
729-
llvm_unreachable("Unsupported statement: Case");
728+
assert(isSupportedMultiStatementClosure() &&
729+
"Unsupported statement: Case");
730730

731731
Type contextualTy;
732732

@@ -919,6 +919,19 @@ bool ConstraintSystem::generateConstraints(ClosureExpr *closure) {
919919
return false;
920920
}
921921

922+
bool ConstraintSystem::isInResultBuilderContext(ClosureExpr *closure) const {
923+
if (!closure->hasSingleExpressionBody()) {
924+
auto *DC = closure->getParent();
925+
do {
926+
if (auto *parentClosure = dyn_cast<ClosureExpr>(DC)) {
927+
if (resultBuilderTransformed.count(parentClosure))
928+
return true;
929+
}
930+
} while ((DC = DC->getParent()));
931+
}
932+
return false;
933+
}
934+
922935
bool isConditionOfStmt(ConstraintLocatorBuilder locator) {
923936
auto last = locator.last();
924937
if (!(last && last->is<LocatorPathElt::Condition>()))

lib/Sema/ConstraintSystem.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5930,8 +5930,13 @@ bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
59305930
return false;
59315931

59325932
auto &ctx = closure->getASTContext();
5933-
return !closure->hasEmptyBody() &&
5934-
ctx.TypeCheckerOpts.EnableMultiStatementClosureInference;
5933+
if (closure->hasEmptyBody() ||
5934+
!ctx.TypeCheckerOpts.EnableMultiStatementClosureInference)
5935+
return false;
5936+
5937+
// If body is nested in a parent that has a function builder applied,
5938+
// let's prevent inference until result builders.
5939+
return !isInResultBuilderContext(closure);
59355940
}
59365941

59375942
TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -swift-version 5 -experimental-multi-statement-closures
2+
// REQUIRES: objc_interop
3+
// REQUIRES: OS=macosx
4+
5+
import SwiftUI
6+
7+
enum Status {
8+
case complete
9+
case waiting
10+
}
11+
12+
struct Item : Hashable {
13+
var question: String
14+
var answer: Int
15+
}
16+
17+
func transform(_ v: Int) -> String? { return String(v) }
18+
func transform(_ v: Double) -> String? { return String(v) }
19+
20+
struct MyView : View {
21+
var status: Status
22+
23+
var items: [Item] = []
24+
25+
var currItem: Item {
26+
get { fatalError() }
27+
nonmutating set { }
28+
}
29+
30+
var body: some View {
31+
ZStack {
32+
ItemsView {
33+
EmptyView()
34+
} results: {
35+
switch (status) {
36+
case .complete:
37+
ForEach(items, id: \.self) { item in
38+
if let question = item.question,
39+
let answer = item.answer {
40+
ItemView {
41+
currItem.question = question
42+
currItem.answer = answer
43+
} content: {
44+
AnswerView(title: "",
45+
color: .red,
46+
answer: transform(answer) ?? "",
47+
selected: false)
48+
}
49+
}
50+
}
51+
52+
default:
53+
EmptyView()
54+
}
55+
}
56+
}
57+
}
58+
}
59+
60+
struct AnswerView : View {
61+
var title: String
62+
var color: Color
63+
var answer: String
64+
var selected: Bool
65+
66+
var body: some View {
67+
EmptyView()
68+
}
69+
}
70+
71+
struct ItemsView<Content: View, Results: View>: View {
72+
@ViewBuilder var content: Content
73+
@ViewBuilder var results: Results
74+
75+
var body: some View {
76+
EmptyView()
77+
}
78+
}
79+
80+
struct ItemView<Content: View> : View {
81+
var fn: () -> Void
82+
@ViewBuilder var content: Content
83+
84+
var body: some View {
85+
EmptyView()
86+
}
87+
}

0 commit comments

Comments
 (0)