Skip to content

Commit 7f87104

Browse files
committed
[TypeChecker] Diagnose empty switch statements in function builder bodies
If there are no 'case' statements in the body let's try to diagnose this situation via limited exhaustiveness check before failing a builder transform, otherwise type-checker might end up without any diagnostics which leads to crashes in SILGen. Resolves: rdar://problem/65983237 (cherry picked from commit 0cac079)
1 parent 91d3180 commit 7f87104

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

lib/Sema/BuilderTransform.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,18 @@ class BuilderClosureVisitor
707707
if (!cs)
708708
return nullptr;
709709

710+
// If there are no 'case' statements in the body let's try
711+
// to diagnose this situation via limited exhaustiveness check
712+
// before failing a builder transform, otherwise type-checker
713+
// might end up without any diagnostics which leads to crashes
714+
// in SILGen.
715+
if (capturedCaseVars.empty()) {
716+
TypeChecker::checkSwitchExhaustiveness(switchStmt, dc,
717+
/*limitChecking=*/true);
718+
hadError = true;
719+
return nullptr;
720+
}
721+
710722
// Form the expressions that inject the result of each case into the
711723
// appropriate
712724
llvm::TinyPtrVector<Expr *> injectedCaseExprs;
@@ -755,6 +767,18 @@ class BuilderClosureVisitor
755767
}
756768

757769
VarDecl *visitCaseStmt(CaseStmt *caseStmt, Expr *subjectExpr) {
770+
auto *body = caseStmt->getBody();
771+
772+
// Explicitly disallow `case` statements with empty bodies
773+
// since that helps to diagnose other issues with switch
774+
// statements by excluding invalid cases.
775+
if (auto *BS = dyn_cast<BraceStmt>(body)) {
776+
if (BS->getNumElements() == 0) {
777+
hadError = true;
778+
return nullptr;
779+
}
780+
}
781+
758782
// If needed, generate constraints for everything in the case statement.
759783
if (cs) {
760784
auto locator = cs->getConstraintLocator(

test/Constraints/function_builder_diags.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,4 +584,22 @@ struct MyView {
584584
// expected-note@-1 {{opaque return type declared here}}
585585
DoesNotConform()
586586
}
587+
588+
@TupleBuilder var emptySwitch: some P {
589+
switch Optional.some(1) { // expected-error {{'switch' statement body must have at least one 'case' or 'default' block; do you want to add a default case?}}
590+
}
591+
}
592+
593+
@TupleBuilder var invalidSwitchOne: some P {
594+
switch Optional.some(1) {
595+
case . // expected-error {{expected ':' after 'case'}}
596+
} // expected-error {{expected identifier after '.' expression}}
597+
}
598+
599+
@TupleBuilder var invalidSwitchMultiple: some P {
600+
switch Optional.some(1) {
601+
case .none: // expected-error {{'case' label in a 'switch' should have at least one executable statement}}
602+
case . // expected-error {{expected ':' after 'case'}}
603+
} // expected-error {{expected identifier after '.' expression}}
604+
}
587605
}

0 commit comments

Comments
 (0)