Skip to content

Commit e5015e7

Browse files
authored
Merge pull request #9469 from CodaFi/proof-by-handwaving
[4.0] Treat irrefutable casts as irrefutable patterns.
2 parents 3247fbc + 3068ca4 commit e5015e7

File tree

4 files changed

+81
-2
lines changed

4 files changed

+81
-2
lines changed

lib/Sema/TypeCheckSwitchStmt.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,25 @@ namespace {
251251

252252
// Special Case: A constructor pattern may include the head but not
253253
// the payload patterns. In that case the space is covered.
254+
// This also acts to short-circuit comparisons with payload-less
255+
// constructors.
254256
if (other.getSpaces().empty()) {
255257
return true;
256258
}
257259

260+
// If 'this' constructor pattern has no payload and the other space
261+
// does, then 'this' covers more of the space only if the other
262+
// constructor isn't the explicit form.
263+
//
264+
// .case <= .case(_, _, _, ...)
265+
if (this->getSpaces().empty()) {
266+
return std::accumulate(other.getSpaces().begin(),
267+
other.getSpaces().end(),
268+
true, [](bool acc, const Space sp){
269+
return acc && sp.getKind() == SpaceKind::Type;
270+
});
271+
}
272+
258273
// H(a1, ..., an) <= H(b1, ..., bn) iff a1 <= b1 && ... && an <= bn
259274
auto i = this->getSpaces().begin();
260275
auto j = other.getSpaces().begin();
@@ -1071,8 +1086,25 @@ namespace {
10711086
auto *BP = cast<BoolPattern>(item);
10721087
return Space(BP->getValue());
10731088
}
1089+
case PatternKind::Is: {
1090+
auto *IP = cast<IsPattern>(item);
1091+
switch (IP->getCastKind()) {
1092+
case CheckedCastKind::Coercion:
1093+
case CheckedCastKind::BridgingCoercion:
1094+
// These coercions are irrefutable. Project with the original type
1095+
// instead of the cast's target type to maintain consistency with the
1096+
// scrutinee's type.
1097+
return Space(IP->getType());
1098+
case CheckedCastKind::Unresolved:
1099+
case CheckedCastKind::ValueCast:
1100+
case CheckedCastKind::ArrayDowncast:
1101+
case CheckedCastKind::DictionaryDowncast:
1102+
case CheckedCastKind::SetDowncast:
1103+
case CheckedCastKind::Swift3BridgingDowncast:
1104+
return Space();
1105+
}
1106+
}
10741107
case PatternKind::Typed:
1075-
case PatternKind::Is:
10761108
case PatternKind::Expr:
10771109
return Space();
10781110
case PatternKind::Var: {

test/Sema/exhaustive_switch.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-typecheck-verify-swift
2+
23
func foo(a: Int?, b: Int?) -> Int {
34
switch (a, b) {
45
case (.none, _): return 1
@@ -286,3 +287,25 @@ func switcheroo(a: XX, b: XX) -> Int {
286287
return 13
287288
}
288289
}
290+
291+
enum PatternCasts {
292+
case one(Any)
293+
case two
294+
}
295+
296+
func checkPatternCasts() {
297+
// Pattern casts with this structure shouldn't warn about duplicate cases.
298+
let x: PatternCasts = .one("One")
299+
switch x {
300+
case .one(let s as String): print(s)
301+
case .one: break
302+
case .two: break
303+
}
304+
305+
// But should warn here.
306+
switch x {
307+
case .one(_): print(s)
308+
case .one: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
309+
case .two: break
310+
}
311+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s
2+
// REQUIRES: objc_interop
3+
4+
import Foundation
5+
6+
// Treat irrefutable casts as irrefutable patterns.
7+
enum Castbah {
8+
case shareef(NSInteger)
9+
case dont(NSString)
10+
case like(Int)
11+
case it(Error)
12+
}
13+
14+
func rock(the c : Castbah) {
15+
switch (c, c, c) {
16+
case (.shareef(let rock as NSObject), .dont(let the as String), .like(let castbah as Any)):
17+
print(rock, the, castbah)
18+
case (.it(let e as NSError), _, _):
19+
print(e)
20+
case let obj as Any:
21+
print(obj)
22+
}
23+
}

test/stmt/statements.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,8 @@ func test_is_as_patterns() {
384384
switch 4 {
385385
case is Int: break // expected-warning {{'is' test is always true}}
386386
case _ as Int: break // expected-warning {{'as' test is always true}}
387-
case _: break
387+
// expected-warning@-1 {{case is already handled by previous patterns; consider removing it}}
388+
case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
388389
}
389390
}
390391

0 commit comments

Comments
 (0)