You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/DynamicCasting.md
+41-31Lines changed: 41 additions & 31 deletions
Original file line number
Diff line number
Diff line change
@@ -308,6 +308,26 @@ Note that it is _not_ sufficient for argument and return types to be castable; t
308
308
Conceptually, an "existential type" is an opaque wrapper that carries a type and an instance of that type.
309
309
The various existential types differ in what kinds of types they can hold (for example, `AnyObject` can only hold reference types) and in the capabilities exposed by the container (`AnyHashable` exposes equality testing and hashing).
310
310
311
+
The key invariant for existential types `E` is the following:
312
+
* Strong existential invariant: If `t` is any instance, `U` is any type, and `t is E` then `(t as! E) as? U` produces the same result as `t as? U`
313
+
314
+
Intuitively, this simply says that if you can put an instance `t` into an existential `E`, then you can take it back out again via casting.
315
+
For Equatable types, this implies that the results of the two operations here are equal to each other when they succeed.
316
+
It also implies that if either of these `as?` casts fails, so will the other.
317
+
318
+
`AnyObject` and `AnyHashable` do not fully satisfy the strong invariant described above. Instead, they satisfy the following weaker version:
319
+
* Weak existential invariant: If `t` is any instance, `U` is any type, and both `t is U` and `t is E`, then `(t as! E) as? U` produces the same result as `t as? U`
320
+
321
+
### Objective-C Interactions
322
+
323
+
The difference between the strong and weak existential invariants comes about because casting to `AnyObject` or `AnyHashable` can trigger Objective-C bridging conversions.
324
+
The result of these conversions may support casts that the original type did not.
325
+
As a result, `(t as! E)` may be castable to `U` even if `t` alone is not directly castable.
326
+
327
+
One example of this is Foundation's `NSNumber` type which conditionally bridges to several Swift numeric types.
328
+
As a result, when Foundation is in scope, `Int(7) is Double == false` but `(Int(7) as! AnyObject) is Double == true`.
329
+
In general, the ability to add new bridging behaviors from a single type to several distinct types implies that Swift casting cannot be transitive.
330
+
311
331
### Any
312
332
313
333
Any Swift instance can be cast to the type `Any`.
@@ -318,14 +338,7 @@ Invariants
318
338
* If `t` is any instance, then `t is Any == true`
319
339
* If `t` is any instance, `t as! Any` always succeeds
320
340
* For every type `T` (including protocol types), `T.self is Any.Type`
321
-
* If `t` is any instance and `U` is any `Equatable` type, then `t as? U == (t as! Any) as? U`.
322
-
323
-
This last invariant deserves some explanation, as a similar pattern appears repeatedly throughout this document.
324
-
In essence, this invariant just says that putting something into an "Any box" (`t as! Any`) and taking it out again (`as? U`) does not change the result.
325
-
The requirement that `U` be `Equatable` is a technical necessity for using `==` in this statement.
326
-
327
-
Note that in many cases, we've shortened such invariants to the form `t is U == (t as! Any) is U`.
328
-
Using `is` here simply avoids the technical necessity that `U` be `Equatable` but except where explicitly called out, the intention in every case is that such casting does not change the value.
341
+
* Strong existential invariant: If `t` is any instance and `U` is any type, then `(t as! Any) as? U` produces the same result as `t as? U`.
329
342
330
343
### AnyObject
331
344
@@ -334,49 +347,44 @@ Any class, enum, struct, tuple, function, metatype, or existential metatype inst
334
347
XXX TODO The runtime logic has code to cast protocol types to `AnyObject` only if they are compatible with `__SwiftValue`. What is the practical effect of this logic? Does it mean that casting a protocol type to `AnyObject` will sometimes unwrap (if the protocol is incompatible) and sometimes not? What protocols are affected by this?
335
348
336
349
The contents of an `AnyObject` container can be accessed by casting to another type:
337
-
* If `t` is any instance, `U` is any type, `t is AnyObject` and `t is U`, then `(t as! AnyObject) is U`.
350
+
*Weak existential invariant: If `t` is any instance, `U` is any type, `t is AnyObject`, and `t is U`, then `(t as! AnyObject) as? U` will produce the same result as `t as? U`
338
351
339
352
Implementation Note: `AnyObject` is represented in memory as a pointer to a refcounted object. The dynamic type of the object can be recovered from the "isa" field of the object. The optional form `AnyObject?` is the same except that it allows null. Reference types (class, metatype, or existential metatype instances) can be directly assigned to an `AnyObject` without any conversion. For non-reference types -- including struct, enum, and tuple types -- the casting logic will first look for an `_ObjectiveCBridgeable` conformance that it can use to convert the source into a tailored reference type. If that fails, the value will be copied into an opaque `_SwiftValue` container.
340
353
341
354
(See "The _ObjectiveCBridgeable Protocol" below for more details.)
342
355
343
-
### Objective-C Interactions
344
-
345
-
Note the invariant above cannot be an equality because Objective-C bridging allows libraries to introduce new relationships that can alter the behavior of seemingly-unrelated casts.
346
-
One example of this is Foundation's `NSNumber` type which conditionally bridges to several Swift numeric types.
347
-
As a result, when Foundation is in scope, `Int(7) is Double == false` but `(Int(7) as! AnyObject) is Double == true`.
348
-
In general, the ability to add new bridging behaviors from a single type to several distinct types implies that Swift casting cannot be transitive.
349
-
350
356
### Error (SE-0112)
351
357
352
-
Although the Error protocol is specially handled by the Swift compiler and runtime (as detailed in [SE-0112](https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md)), it behaves like an ordinary protocol type for casting purposes.
358
+
The `Error` type behaves like an ordinary existential type for casting purposes.
353
359
354
-
(See "Note: 'Self-conforming' protocols" below for additional details relevant to the Error protocol.)
360
+
(See "Note: 'Self-conforming' protocols" below for additional details relevant to the `Error` protocol.)
355
361
356
362
### AnyHashable (SE-0131)
357
363
358
364
For casting purposes, `AnyHashable` behaves like an existential type.
365
+
It satisfies the weak existential invariant above.
359
366
360
367
However, note that `AnyHashable` does not act like an existential for other purposes.
361
368
For example, it's metatype is named `AnyHashable.Type` and it does not have an existential metatype.
362
369
363
370
### Protocol Witness types
364
371
365
-
Caveat:
366
-
Protocols that have `associatedtype` properties or which make use of the `Self` typealias cannot be used as independent types.
367
-
As such, the discussion below does not apply to them.
372
+
Any protocol definition (except those that include an `associatedtype` property or which makes use of the `Self` typealias) has an associated existential type named after the protocol.
368
373
369
-
Any Swift instance of a concrete type `T` can be cast to `P` iff `T` conforms to `P`.
370
-
The result is a "protocol witness" instance that provides access only to those methods and properties defined on `P`.
374
+
Specifically, assume you have a protocol definition
375
+
```
376
+
protocol P {}
377
+
```
378
+
379
+
As a result of this definition, there is an existential type (also called `P`).
380
+
This existential type is also known as a "protocol witness type" since it exposes exactly the capabilities that are defined by the protocol.
371
381
Other capabilities of the type `T` are not accessible from a `P` instance.
382
+
Any Swift instance of a concrete type `T` can be cast to the type `P` iff `T` conforms to the protocol `P`.
372
383
373
384
The contents of a protocol witness can be accessed by casting to some other appropriate type:
374
-
* For any protocol `P`, instance `t`, and type `U`, if `t is P`, then `t as? U == (t as! P) as? U`
375
-
376
-
XXX TODO: The invariant above does not apply to AnyObject, AnyHashable.
377
-
Does it suffice to explicitly exclude those two, or do other protocols share that behavior? The alternative would seem to be to change the equality here into an implication.
385
+
* Strong existential Invariant: For any protocol `P`, instance `t`, and type `U`, if `t is P`, then `t as? U` produces the same result as `(t as! P) as? U`
378
386
379
-
In addition to the protocol witness type, every Swift protocol `P` implicitly defines two other types:
387
+
In addition to the protocol witness type`P`, every Swift protocol `P` implicitly defines two other types:
380
388
`P.Protocol` is the "protocol metatype", the type of `P.self`.
381
389
`P.Type` is the "protocol existential metatype".
382
390
These are described in more detail below.
@@ -422,9 +430,11 @@ S.svar // Shorthand for S.self.svar
422
430
```
423
431
424
432
For most Swift types, the metatype of `T` is named `T.Type`.
425
-
However, in two cases the metatype has a different name:
433
+
However, in the following cases the metatype has a different name:
426
434
* For a nominal protocol type `P`, the metatype is named `P.Protocol`
427
-
* For a type bound to a generic variable `G`, the metatype is named `G.Type`_even if `G` is bound to a protocol type_. Specifically, if `G` is bound to the nominal protocol type `P`, then `G.Type` is another name for the metatype `P.Protocol`, and hence `G.Type.self == P.Protocol.self`.
435
+
* For non-protocol existential types `E`, the metatype is also named `E.Protocol`. For example, `Any.Protocol`, `AnyObject.Protocol`, and `Error.Protocol`.
436
+
* For a type bound to a generic variable `G`, the metatype is named `G.Type`_even if `G` is bound to a protocol or existential type_. Specifically, if `G` is bound to the nominal protocol type `P`, then `G.Type` is another name for the metatype `P.Protocol`, and hence `G.Type.self == P.Protocol.self`.
437
+
* As explained above, although `AnyHashable` behaves like an existential type in some respects, its metatype is called `AnyHashable.Type`.
428
438
429
439
Example:
430
440
```
@@ -456,7 +466,7 @@ Invariants
456
466
* For a nominal non-protocol type `T`, `T.self is T.Type`
457
467
* For a nominal protocol type `P`, `P.self is P.Protocol`
458
468
*`P.Protocol` is a singleton: `T.self is P.Protocol` iff `T` is exactly `P`
459
-
* A non-protocol type `T` conforms to a protocol `P` iff `T.self is P.Type`
469
+
* A non-protocol type `T` conforms to a protocol `P` iff `T.self is P.Type` (If `T` is a protocol type, see "Self conforming existential types" below for details.)
460
470
*`T` is a subtype of a non-protocol type `U` iff `T.self is U.Type`
461
471
* Subtypes define metatype subtypes: if `T` and `U` are non-protocol types, `T.self is U.Type == T.Type.self is U.Type.Type`
462
472
* Subtypes define metatype subtypes: if `T` is a non-protocol type and `P` is a protocol type, `T.self is P.Protocol == T.Type.self is P.Protocol.Type`
0 commit comments