Skip to content

Commit 0a9a5c6

Browse files
committed
Diagnose 'case nil' in Non-Optional Switches
This is an anti-pattern since the resulting value will never compare equal to `nil`, and the entire switch-case is dead. This appears to be a misfeature as the subject value is simply type-checked against an optional which produces an injection which matches the global ~= for Optionals. Effectively, `case nil:` becomes `case $match ~= nil`. rdar://89742267
1 parent f807ef9 commit 0a9a5c6

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

lib/Sema/TypeCheckPattern.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,10 +1269,10 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern,
12691269
case PatternKind::Expr: {
12701270
assert(cast<ExprPattern>(P)->isResolved()
12711271
&& "coercing unresolved expr pattern!");
1272+
auto *EP = cast<ExprPattern>(P);
12721273
if (type->isBool()) {
12731274
// The type is Bool.
12741275
// Check if the pattern is a Bool literal
1275-
auto EP = cast<ExprPattern>(P);
12761276
if (auto *BLE = dyn_cast<BooleanLiteralExpr>(
12771277
EP->getSubExpr()->getSemanticsProvidingExpr())) {
12781278
P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue());
@@ -1282,20 +1282,24 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern,
12821282
}
12831283

12841284
// case nil is equivalent to .none when switching on Optionals.
1285-
if (type->getOptionalObjectType()) {
1286-
auto EP = cast<ExprPattern>(P);
1287-
if (auto *NLE = dyn_cast<NilLiteralExpr>(EP->getSubExpr())) {
1285+
if (auto *NLE = dyn_cast<NilLiteralExpr>(EP->getSubExpr())) {
1286+
if (type->getOptionalObjectType()) {
12881287
auto *NoneEnumElement = Context.getOptionalNoneDecl();
12891288
auto *BaseTE = TypeExpr::createImplicit(type, Context);
12901289
P = new (Context) EnumElementPattern(
12911290
BaseTE, NLE->getLoc(), DeclNameLoc(NLE->getLoc()),
12921291
NoneEnumElement->createNameRef(), NoneEnumElement, nullptr);
12931292
return TypeChecker::coercePatternToType(
12941293
pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options);
1294+
} else {
1295+
// ...but for non-optional types it can never match! Diagnose it.
1296+
diags.diagnose(NLE->getLoc(),
1297+
diag::value_type_comparison_with_nil_illegal, type);
1298+
return nullptr;
12951299
}
12961300
}
12971301

1298-
if (TypeChecker::typeCheckExprPattern(cast<ExprPattern>(P), dc, type))
1302+
if (TypeChecker::typeCheckExprPattern(EP, dc, type))
12991303
return nullptr;
13001304

13011305
return P;

test/stmt/switch_nil.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
enum Hey {
4+
case listen
5+
}
6+
7+
func test() {
8+
switch Hey.listen {
9+
case nil: // expected-error{{type 'Hey' is not optional, value can never be nil}}
10+
break
11+
default:
12+
break
13+
}
14+
}
15+

0 commit comments

Comments
 (0)