Skip to content

Commit e23a36a

Browse files
[TypeConstraints] Warning cast to unrelated of protocol composition to no existential type
1 parent 70d583e commit e23a36a

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3554,6 +3554,20 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
35543554
if (!couldDynamicallyConformToProtocol(toType, protocolDecl, dc)) {
35553555
return failed();
35563556
}
3557+
} else if (auto protocolComposition =
3558+
fromType->getAs<ProtocolCompositionType>()) {
3559+
if (!protocolComposition->getMembers().empty() &&
3560+
llvm::any_of(protocolComposition->getMembers(),
3561+
[&](Type protocolType) {
3562+
if (auto protocolDecl = dyn_cast_or_null<ProtocolDecl>(
3563+
protocolType->getAnyNominal())) {
3564+
return !couldDynamicallyConformToProtocol(
3565+
toType, protocolDecl, dc);
3566+
}
3567+
return false;
3568+
})) {
3569+
return failed();
3570+
}
35573571
}
35583572

35593573
// If neither type is class-constrained, anything goes.

test/Constraints/casts.swift

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ func protocol_concrete_casts(_ p1: P1, p2: P2, p12: P1 & P2) {
113113

114114
_ = p2 as! S1 // expected-warning {{cast from 'P2' to unrelated type 'S1' always fails}}
115115

116-
_ = p12 as! S1
117-
_ = p12 as! S2
116+
_ = p12 as! S1 // expected-warning {{cast from 'P1 & P2' to unrelated type 'S1' always fails}}
117+
_ = p12 as! S2 // expected-warning {{cast from 'P1 & P2' to unrelated type 'S2' always fails}}
118118
_ = p12 as! S12
119-
_ = p12 as! S3
119+
_ = p12 as! S3 // expected-warning {{cast from 'P1 & P2' to unrelated type 'S3' always fails}}
120120

121121
// Type queries.
122122
var _:Bool = p1 is S1
@@ -128,10 +128,10 @@ func protocol_concrete_casts(_ p1: P1, p2: P2, p12: P1 & P2) {
128128

129129
var _:Bool = p2 is S1 // expected-warning {{cast from 'P2' to unrelated type 'S1' always fails}}
130130

131-
var _:Bool = p12 is S1
132-
var _:Bool = p12 is S2
131+
var _:Bool = p12 is S1 // expected-warning {{cast from 'P1 & P2' to unrelated type 'S1' always fails}}
132+
var _:Bool = p12 is S2 // expected-warning {{cast from 'P1 & P2' to unrelated type 'S2' always fails}}
133133
var _:Bool = p12 is S12
134-
var _:Bool = p12 is S3
134+
var _:Bool = p12 is S3 // expected-warning {{cast from 'P1 & P2' to unrelated type 'S3' always fails}}
135135
}
136136

137137
func conditional_cast(_ b: B) -> D? {
@@ -372,6 +372,26 @@ enum ConcreteA: EventA {
372372
}
373373
}
374374

375+
protocol ProtocolP1 {}
376+
protocol ProtocolQ1 {}
377+
typealias Composition = ProtocolP1 & ProtocolQ1
378+
379+
protocol ProtocolP {}
380+
protocol ProtocolQ {}
381+
382+
class ConcreteP: ProtocolP {}
383+
class ConcreteQ: ProtocolQ {}
384+
class ConcretePQ: ProtocolP, ProtocolQ {}
385+
class ConcreteCPQ: ConcreteP, ProtocolQ {}
386+
387+
class ConcreteP1: ProtocolP1 {}
388+
class ConcretePQ1: ProtocolP1, ProtocolQ1 {}
389+
390+
class ConcretePPQ1: ProtocolP, ProtocolP1, ProtocolQ1 {}
391+
class NotConforms {}
392+
struct StructNotComforms {}
393+
final class NotConformsFinal {}
394+
375395
func tests_SR13088_false_positive_always_fail_casts() {
376396
// SR-13081
377397
let x: JSON = [4] // [4]
@@ -403,3 +423,20 @@ func tests_SR13088_false_positive_always_fail_casts() {
403423
}
404424
}
405425
}
426+
427+
// Protocol composition
428+
func protocol_composition(_ c: ProtocolP & ProtocolQ, _ c1: ProtocolP & Composition) {
429+
_ = c as? ConcretePQ // Ok
430+
_ = c as? ConcreteCPQ // Ok
431+
_ = c as? ConcreteP // Ok
432+
_ = c as? NotConforms // Ok
433+
_ = c as? StructNotComforms // expected-warning {{cast from 'ProtocolP & ProtocolQ' to unrelated type 'StructNotComforms' always fails}}
434+
_ = c as? NotConformsFinal // expected-warning {{cast from 'ProtocolP & ProtocolQ' to unrelated type 'NotConformsFinal' always fails}}
435+
_ = c1 as? ConcreteP // Ok
436+
_ = c1 as? ConcreteP1 // OK
437+
_ = c1 as? ConcretePQ1 // OK
438+
_ = c1 as? ConcretePPQ1 // Ok
439+
_ = c1 as? NotConforms // Ok
440+
_ = c1 as? StructNotComforms // expected-warning {{cast from 'ProtocolP & Composition' (aka 'ProtocolP & ProtocolP1 & ProtocolQ1') to unrelated type 'StructNotComforms' always fails}}
441+
_ = c1 as? NotConformsFinal // expected-warning {{cast from 'ProtocolP & Composition' (aka 'ProtocolP & ProtocolP1 & ProtocolQ1') to unrelated type 'NotConformsFinal' always fails}}
442+
}

0 commit comments

Comments
 (0)