Skip to content

Commit 32dc04b

Browse files
Merge pull request swiftlang#32716 from LucianoPAlmeida/SR-13088-array-elements
[SR-13088] Fixing remaining spurious cast to unrelated type warning involving collection elements
2 parents cb9e134 + 2127296 commit 32dc04b

File tree

9 files changed

+127
-27
lines changed

9 files changed

+127
-27
lines changed

lib/Sema/CSFix.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ CoerceToCheckedCast *CoerceToCheckedCast::attempt(ConstraintSystem &cs,
151151
return nullptr;
152152

153153
const auto castKind = TypeChecker::typeCheckCheckedCast(
154-
fromType, toType, CheckedCastContextKind::None, cs.DC,
154+
fromType, toType, CheckedCastContextKind::Coercion, cs.DC,
155155
SourceLoc(), coerceExpr->getSubExpr(), SourceRange());
156156

157157
// Invalid cast.

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3037,7 +3037,9 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
30373037
Expr *fromExpr,
30383038
SourceRange diagToRange) {
30393039
// Determine whether we should suppress diagnostics.
3040-
const bool suppressDiagnostics = contextKind == CheckedCastContextKind::None;
3040+
const bool suppressDiagnostics =
3041+
contextKind == CheckedCastContextKind::None ||
3042+
contextKind == CheckedCastContextKind::Coercion;
30413043
assert((suppressDiagnostics || diagLoc.isValid()) &&
30423044
"diagnostics require a valid source location");
30433045

@@ -3133,6 +3135,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
31333135

31343136
switch (contextKind) {
31353137
case CheckedCastContextKind::None:
3138+
case CheckedCastContextKind::Coercion:
31363139
llvm_unreachable("suppressing diagnostics");
31373140

31383141
case CheckedCastContextKind::ForcedCast: {
@@ -3264,7 +3267,15 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
32643267
return castKind;
32653268

32663269
case CheckedCastKind::Unresolved:
3267-
return failed();
3270+
// Even though we know the elements cannot be downcast, we cannot return
3271+
// failed() here as it's possible for an empty Array, Set or Dictionary to
3272+
// be cast to any element type at runtime (SR-6192). The one exception to
3273+
// this is when we're checking whether we can treat a coercion as a checked
3274+
// cast because we don't want to tell the user to use as!, as it's probably
3275+
// the wrong suggestion.
3276+
if (contextKind == CheckedCastContextKind::Coercion)
3277+
return failed();
3278+
return castKind;
32683279
}
32693280
llvm_unreachable("invalid cast type");
32703281
};
@@ -3295,15 +3306,19 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
32953306
BridgingCoercion);
32963307
break;
32973308

3309+
case CheckedCastKind::Unresolved:
3310+
// Handled the same as in checkElementCast; see comment there for
3311+
// rationale.
3312+
if (contextKind == CheckedCastContextKind::Coercion)
3313+
return failed();
3314+
LLVM_FALLTHROUGH;
3315+
32983316
case CheckedCastKind::ArrayDowncast:
32993317
case CheckedCastKind::DictionaryDowncast:
33003318
case CheckedCastKind::SetDowncast:
33013319
case CheckedCastKind::ValueCast:
33023320
hasCast = true;
33033321
break;
3304-
3305-
case CheckedCastKind::Unresolved:
3306-
return failed();
33073322
}
33083323

33093324
switch (typeCheckCheckedCast(fromKeyValue->second, toKeyValue->second,
@@ -3318,15 +3333,19 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
33183333
BridgingCoercion);
33193334
break;
33203335

3336+
case CheckedCastKind::Unresolved:
3337+
// Handled the same as in checkElementCast; see comment there for
3338+
// rationale.
3339+
if (contextKind == CheckedCastContextKind::Coercion)
3340+
return failed();
3341+
LLVM_FALLTHROUGH;
3342+
33213343
case CheckedCastKind::ArrayDowncast:
33223344
case CheckedCastKind::DictionaryDowncast:
33233345
case CheckedCastKind::SetDowncast:
33243346
case CheckedCastKind::ValueCast:
33253347
hasCast = true;
33263348
break;
3327-
3328-
case CheckedCastKind::Unresolved:
3329-
return failed();
33303349
}
33313350

33323351
if (hasCast) return CheckedCastKind::DictionaryDowncast;
@@ -3357,7 +3376,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
33573376
const auto &fromElt = fromTuple->getElement(i);
33583377
const auto &toElt = toTuple->getElement(i);
33593378

3360-
// We should only perform name validation if both element have a label,
3379+
// We should only perform name validation if both elements have a label,
33613380
// because unlabeled tuple elements can be converted to labeled ones
33623381
// e.g.
33633382
//
@@ -3423,6 +3442,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
34233442
case CheckedCastContextKind::EnumElementPattern:
34243443
case CheckedCastContextKind::IsExpr:
34253444
case CheckedCastContextKind::None:
3445+
case CheckedCastContextKind::Coercion:
34263446
break;
34273447
}
34283448
}
@@ -3554,6 +3574,19 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
35543574
if (!couldDynamicallyConformToProtocol(toType, protocolDecl, dc)) {
35553575
return failed();
35563576
}
3577+
} else if (auto protocolComposition =
3578+
fromType->getAs<ProtocolCompositionType>()) {
3579+
if (llvm::any_of(protocolComposition->getMembers(),
3580+
[&](Type protocolType) {
3581+
if (auto protocolDecl = dyn_cast_or_null<ProtocolDecl>(
3582+
protocolType->getAnyNominal())) {
3583+
return !couldDynamicallyConformToProtocol(
3584+
toType, protocolDecl, dc);
3585+
}
3586+
return false;
3587+
})) {
3588+
return failed();
3589+
}
35573590
}
35583591

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

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4548,7 +4548,7 @@ TypeChecker::couldDynamicallyConformToProtocol(Type type, ProtocolDecl *Proto,
45484548

45494549
ModuleDecl *M = DC->getParentModule();
45504550
// For standard library collection types such as Array, Set or Dictionary
4551-
// which have custom casting machinery implemented in situations like:
4551+
// which have custom casting machinery implemented for situations like:
45524552
//
45534553
// func encodable(_ value: Encodable) {
45544554
// _ = value as! [String : Encodable]

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ enum class CheckedCastContextKind {
341341
IsPattern,
342342
/// An enum-element pattern.
343343
EnumElementPattern,
344+
/// Coerce to checked cast. Used when we verify if it is possible to
345+
/// suggest to convert a coercion to a checked cast.
346+
Coercion,
344347
};
345348

346349
namespace TypeChecker {

test/Constraints/casts.swift

Lines changed: 71 additions & 7 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? {
@@ -232,7 +232,7 @@ func test_tuple_casts_no_warn() {
232232
_ = arr as! [(Foo, Foo)] // Ok
233233
_ = tup as! (Foo, Foo) // Ok
234234

235-
_ = arr as! [(Foo, Foo, Foo)] // expected-warning {{cast from '[(Any, Any)]' to unrelated type '[(Foo, Foo, Foo)]' always fails}}
235+
_ = arr as! [(Foo, Foo, Foo)] // Ok
236236
_ = tup as! (Foo, Foo, Foo) // expected-warning {{cast from '(Any, Any)' to unrelated type '(Foo, Foo, Foo)' always fails}}
237237

238238
_ = arr as! [(a: Foo, Foo)] // Ok
@@ -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]
@@ -402,4 +422,48 @@ func tests_SR13088_false_positive_always_fail_casts() {
402422
break
403423
}
404424
}
425+
426+
// SR-7187
427+
let a: [Any] = [String?.some("hello") as Any, String?.none as Any]
428+
let b: [AnyObject] = [String?.some("hello") as AnyObject, String?.none as AnyObject]
429+
430+
_ = a is [String?] // Ok
431+
_ = a as? [String?] as Any // OK
432+
_ = b is [String?] // Ok
433+
_ = b as? [String?] as AnyObject // OK
434+
435+
// SR-6192
436+
let items = [String]()
437+
let dict = [String: Any]()
438+
let set = Set<String>()
439+
440+
_ = items is [Int] // Ok
441+
_ = items as? [Int] as Any // Ok
442+
_ = items as! [Int] // Ok
443+
444+
_ = dict is [Int: Any] // Ok
445+
_ = dict as? [Int: Any] as Any // Ok
446+
_ = dict as! [Int: Any] as Any // Ok
447+
448+
_ = set is Set<Int> // Ok
449+
_ = set as? Set<Int> as Any // Ok
450+
_ = set as! Set<Int> // Ok
451+
452+
}
453+
454+
// Protocol composition
455+
func protocol_composition(_ c: ProtocolP & ProtocolQ, _ c1: ProtocolP & Composition) {
456+
_ = c as? ConcretePQ // Ok
457+
_ = c as? ConcreteCPQ // Ok
458+
_ = c as? ConcreteP // Ok
459+
_ = c as? NotConforms // Ok
460+
_ = c as? StructNotComforms // expected-warning {{cast from 'ProtocolP & ProtocolQ' to unrelated type 'StructNotComforms' always fails}}
461+
_ = c as? NotConformsFinal // expected-warning {{cast from 'ProtocolP & ProtocolQ' to unrelated type 'NotConformsFinal' always fails}}
462+
_ = c1 as? ConcreteP // Ok
463+
_ = c1 as? ConcreteP1 // OK
464+
_ = c1 as? ConcretePQ1 // OK
465+
_ = c1 as? ConcretePPQ1 // Ok
466+
_ = c1 as? NotConforms // Ok
467+
_ = c1 as? StructNotComforms // expected-warning {{cast from 'ProtocolP & Composition' (aka 'ProtocolP & ProtocolP1 & ProtocolQ1') to unrelated type 'StructNotComforms' always fails}}
468+
_ = c1 as? NotConformsFinal // expected-warning {{cast from 'ProtocolP & Composition' (aka 'ProtocolP & ProtocolP1 & ProtocolQ1') to unrelated type 'NotConformsFinal' always fails}}
405469
}

test/expr/cast/as_coerce.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ Double(1) as Double as String // expected-error{{cannot convert value of type 'D
9292
(1, 1.0, "a", [1, 23]) as (Int, Double, String, [String])
9393
// expected-error@-1 2 {{cannot convert value of type 'Int' to expected element type 'String'}}
9494

95-
_ = [1] as! [String] // expected-warning{{cast from '[Int]' to unrelated type '[String]' always fails}}
96-
_ = [(1, (1, 1))] as! [(Int, (String, Int))] // expected-warning{{cast from '[(Int, (Int, Int))]' to unrelated type '[(Int, (String, Int))]' always fails}}
95+
_ = [1] as! [String] // OK
96+
_ = [(1, (1, 1))] as! [(Int, (String, Int))] // OK
9797

9898
// <rdar://problem/19495253> Incorrect diagnostic for explicitly casting to the same type
9999
_ = "hello" as! String // expected-warning{{forced cast of 'String' to same type has no effect}} {{13-24=}}

test/expr/cast/dictionary_downcast.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dictCC as Dictionary<U, U> // expected-error{{cannot convert value of type '[C :
4343
// expected-note@-2 {{arguments to generic parameter 'Value' ('C' and 'U') are expected to be equal}}
4444

4545
// Test dictionary conditional downcasts to unrelated types
46-
if let _ = dictCC as? Dictionary<D, U> { } // expected-warning{{cast from '[C : C]' to unrelated type 'Dictionary<D, U>' always fails}}
47-
if let _ = dictCC as? Dictionary<U, D> { } // expected-warning{{cast from '[C : C]' to unrelated type 'Dictionary<U, D>' always fails}}
48-
if let _ = dictCC as? Dictionary<U, U> { } // expected-warning{{cast from '[C : C]' to unrelated type 'Dictionary<U, U>' always fails}}
46+
if let _ = dictCC as? Dictionary<D, U> { } // Ok
47+
if let _ = dictCC as? Dictionary<U, D> { } // Ok
48+
if let _ = dictCC as? Dictionary<U, U> { } // Ok
4949

test/expr/cast/set_bridge.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func testConditionalDowncastBridge() {
9494
if let s = setB as? Set<Root> { _ = s } // expected-warning{{conditional cast from 'Set<BridgedToObjC>' to 'Set<Root>' always succeeds}}
9595
if let s = setB as? Set<ObjC> { _ = s } // expected-warning{{conditional cast from 'Set<BridgedToObjC>' to 'Set<ObjC>' always succeeds}}
9696
if let s = setB as? Set<DerivesObjC> { _ = s }
97-
if let s = setB as? Set<Unrelated> { _ = s } // expected-warning {{cast from 'Set<BridgedToObjC>' to unrelated type 'Set<Unrelated>' always fails}}
97+
if let s = setB as? Set<Unrelated> { _ = s } // OK
9898

9999
_ = setR
100100
_ = setO

test/expr/cast/set_downcast.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ setD = setC as! Set<D>
3030
if let _ = setC as? Set<D> { }
3131

3232
// Test set downcasts to unrelated types.
33-
_ = setC as! Set<U> // expected-warning{{cast from 'Set<C>' to unrelated type 'Set<U>' always fails}}
33+
_ = setC as! Set<U> // Ok
3434

35-
// Test set conditional downcasts to unrelated types
36-
if let _ = setC as? Set<U> { } // expected-warning{{cast from 'Set<C>' to unrelated type 'Set<U>' always fails}}
35+
// Test set conditional downcasts to unrelated types.
36+
if let _ = setC as? Set<U> { } // Ok

0 commit comments

Comments
 (0)