[Result builder transform] Don't abort when we encounter a case with no statements

Remove code that aborts the result builder transform when we encounter
a case that has no statements in it. This can occur when the only
statements were behind a `#if` that evaluataed empty, so it should not
cause an abort.

Previously, the presence of an IfConfigDecl within the case statement
would have prevented us from aborting the traversal here. However, the
removal of IfConfigDecl from the AST turned this previously-accepted
code into a compiler crash.

Fixes rdar://139312426.
This commit is contained in:
Doug Gregor
2024-11-07 22:58:18 -08:00
parent 3f8ffaa46c
commit f89218941b
2 changed files with 41 additions and 13 deletions

View File

@@ -630,19 +630,6 @@ protected:
transformCase(CaseStmt *caseStmt) {
auto *body = caseStmt->getBody();
// Explicitly disallow `case` statements with empty bodies
// since that helps to diagnose other issues with switch
// statements by excluding invalid cases.
if (auto *BS = dyn_cast<BraceStmt>(body)) {
if (BS->getNumElements() == 0) {
// HACK: still allow empty bodies if typechecking for code
// completion. Code completion ignores diagnostics
// and won't get any types if we fail.
if (!ctx.SourceMgr.hasIDEInspectionTargetBuffer())
return std::nullopt;
}
}
NullablePtr<Expr> caseVarRef;
std::optional<UnsupportedElt> unsupported;
SmallVector<ASTNode, 4> newBody;

View File

@@ -0,0 +1,41 @@
// RUN: %target-swift-emit-silgen %s -verify | %FileCheck %s
// Tests for a crash that occurred when the result builder transform encountered
// an empty case statement.
protocol V { }
struct EV: V { }
@resultBuilder
struct VB {
static func buildBlock(_ components: any V...) -> any V { EV() }
static func buildEither(first: any V) -> any V { first }
static func buildEither(second: any V) -> any V { second }
}
extension String: V { }
enum E {
case a(Int)
case b(String)
}
struct S {
var flag: E
// CHECK-LABEL: sil hidden [ossa] @$s25result_builder_empty_case1SV4testAA1V_pyF
// CHECK: switch_enum
@VB
func test() -> any V {
switch flag {
case .a:
// When NOT_DEFINED is... not defined... this ends up being an empty case.
// We permit this
#if NOT_DEFINED
EV()
#endif
case .b:
EV()
}
}
}