mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The type checker implements logic for handling checked casts in two
places: the constraint solver (for type-checking expressions
containing "as!" or "as?") and as a top-level entrypoint for
type-checking as?/as! for diagnostics and is/as patterns. Needless to
say, the two implementations were inconsistent, and in fact both were
wrong, leading to various problems---rejecting perfectly-valid "as!"
and "as?" casts outright, bogus warnings that particular as!/as? casts
always-succeed or always-fail when they wouldn't, and so on.
Start detangling the mess in two ways. First, drastically simplify the
handling of checked casts in the constraint solver, eliminating the
unprincipled "subtype" constraint checks that (among other things)
broke the handling of checked casts that involved bridging or optional
unwrapping. The simpler code is more permissive and more correct; it
essentially accepts that the user knows what she is doing with the
cast.
Second, make the type checker's checking of casts far more thorough,
which includes:
* When we're performing a collection cast, actually check that the
element types (and key types, for a dictionary) are castable, rather
than assuming all collection casts are legitimate. This means we'll
get more useful "always fails" and "always succeeds" diagnostics for
array/set/dictionary.
* Handle casts from a bridged value type to a subclass of the
corresponding bridged class type. Previously, these would be
incorrectly classified as "always fails".
While I'm here, eliminate a spurious diagnostic that occurs when using
a conditional cast ("as?") that could have been a coercion/bridging
conversion ("as"). The optional injection we synthesize to get the
resulting type correct was getting diagnosed as an implicit coercion,
but shouldn't have been.
112 lines
3.1 KiB
Swift
112 lines
3.1 KiB
Swift
// RUN: %target-typecheck-verify-swift
|
|
|
|
// REQUIRES: objc_interop
|
|
|
|
// FIXME: Should go into the standard library.
|
|
public extension _ObjectiveCBridgeable {
|
|
static func _unconditionallyBridgeFromObjectiveC(_ source: _ObjectiveCType?)
|
|
-> Self {
|
|
var result: Self?
|
|
_forceBridgeFromObjectiveC(source!, result: &result)
|
|
return result!
|
|
}
|
|
}
|
|
|
|
class Root : Hashable {
|
|
var hashValue: Int {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func ==(x: Root, y: Root) -> Bool { return true }
|
|
|
|
class ObjC : Root {
|
|
var x = 0
|
|
}
|
|
|
|
class DerivesObjC : ObjC { }
|
|
|
|
class Unrelated : Root { }
|
|
|
|
struct BridgedToObjC : Hashable, _ObjectiveCBridgeable {
|
|
func _bridgeToObjectiveC() -> ObjC {
|
|
return ObjC()
|
|
}
|
|
static func _forceBridgeFromObjectiveC(
|
|
_ x: ObjC,
|
|
result: inout BridgedToObjC?
|
|
) {
|
|
}
|
|
static func _conditionallyBridgeFromObjectiveC(
|
|
_ x: ObjC,
|
|
result: inout BridgedToObjC?
|
|
) -> Bool {
|
|
return true
|
|
}
|
|
|
|
var hashValue: Int {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func ==(x: BridgedToObjC, y: BridgedToObjC) -> Bool { return true }
|
|
|
|
func testUpcastBridge() {
|
|
var setR = Set<Root>()
|
|
var setO = Set<ObjC>()
|
|
var setD = Set<DerivesObjC>()
|
|
var setB = Set<BridgedToObjC>()
|
|
|
|
// Upcast to object types.
|
|
setR = setB as Set<Root>; _ = setR
|
|
setO = setB as Set<ObjC>; _ = setO
|
|
|
|
// Upcast object to bridged type
|
|
setB = setO // expected-error{{cannot assign value of type 'Set<ObjC>' to type 'Set<BridgedToObjC>'}}
|
|
|
|
// Failed upcast
|
|
setD = setB // expected-error{{cannot assign value of type 'Set<BridgedToObjC>' to type 'Set<DerivesObjC>'}}
|
|
_ = setD
|
|
}
|
|
|
|
func testForcedDowncastBridge() {
|
|
let setR = Set<Root>()
|
|
let setO = Set<ObjC>()
|
|
let setD = Set<DerivesObjC>()
|
|
let setB = Set<BridgedToObjC>()
|
|
|
|
_ = setR as! Set<BridgedToObjC>
|
|
_ = setO as Set<BridgedToObjC>
|
|
_ = setD as! Set<BridgedToObjC> // expected-warning{{forced cast from 'Set<DerivesObjC>' to 'Set<BridgedToObjC>' always succeeds; did you mean to use 'as'?}}
|
|
|
|
_ = setB as! Set<Root> // expected-warning{{forced cast from 'Set<BridgedToObjC>' to 'Set<Root>' always succeeds; did you mean to use 'as'?}}
|
|
_ = setB as! Set<ObjC> // expected-warning{{forced cast from 'Set<BridgedToObjC>' to 'Set<ObjC>' always succeeds; did you mean to use 'as'?}}
|
|
_ = setB as! Set<DerivesObjC>
|
|
}
|
|
|
|
func testConditionalDowncastBridge() {
|
|
let setR = Set<Root>()
|
|
let setO = Set<ObjC>()
|
|
let setD = Set<DerivesObjC>()
|
|
let setB = Set<BridgedToObjC>()
|
|
|
|
if let s = setR as? Set<BridgedToObjC> { _ = s }
|
|
let s1 = setO as Set<BridgedToObjC>
|
|
if let s = setD as? Set<BridgedToObjC> { _ = s } // expected-warning {{conditional cast from 'Set<DerivesObjC>' to 'Set<BridgedToObjC>' always succeeds}}
|
|
|
|
if let s = setB as? Set<Root> { _ = s } // expected-warning{{conditional cast from 'Set<BridgedToObjC>' to 'Set<Root>' always succeeds}}
|
|
if let s = setB as? Set<ObjC> { _ = s } // expected-warning{{conditional cast from 'Set<BridgedToObjC>' to 'Set<ObjC>' always succeeds}}
|
|
if let s = setB as? Set<DerivesObjC> { _ = s }
|
|
if let s = setB as? Set<Unrelated> { _ = s } // expected-warning {{cast from 'Set<BridgedToObjC>' to unrelated type 'Set<Unrelated>' always fails}}
|
|
|
|
_ = setR
|
|
_ = setO
|
|
_ = setD
|
|
_ = setB
|
|
_ = s1
|
|
}
|
|
|
|
|
|
|
|
|