[swift-evolution] [Discussion] Seal `T.Type` into `Type<T>`

Adrian Zubarev adrian.zubarev at devandartist.com
Fri Jul 15 10:55:52 CDT 2016

You’ve got two things wrong there:

Derived: Base otherwise you cannot cast at all.
w === x //=> FALSE
The reason why this does not work is because it’s impossible to cast a type to an different type with the help of an initializer. As I cleaned up the current implementation I though about replacing init?(casting: Type<T>?) with something like this:

public static func cast<U>(from optionalType: Type<U>?) -> Type<T>? {
        guard let otherType = optionalType else { return nil }
        // Check if we can up- or downcast the metatype from `otherType` to `Metatype<T>`
        // Bug: SR-2085
        // Workaround: Check explicitly if `T` is `Any`
        // let isTAny = _uniqueIdentifierOf(Any.metatype) == _uniqueIdentifierOf(T.metatype)
        // guard isTAny || otherType._metatype is Metatype<T> else {
        //      return nil
        // }
        guard otherType._metatype is Metatype<T> else {
            return nil
        // `T` implicitly converted to `Type<T>()`
        return unsafeBitCast(otherType, to: T)
Your example would become this:

let x = Type<Derived>()

// downcast
let y = Type<Base>.cast(from: x)!

y === x //=> true

// this is safe - upcast
let z = unsafeBitCast(y, to: Type<Derived>())

z === y //=> true
z === x //=> true

let w = Type<Derived>.cast(from: y)!

w === x //=> true
w === y //=> true
w === z //=> true
Global dictionary is not involved here at all. We can change this too and put the reference for all different Type<T> into the dictionary from the designated initializer.

Remember that the global dictionary does only contain unique (dynamic) types, which means we could have this.

[1: Type<Base>(), 2: Type<Derived>()]

Type<Derived>.sharedInstance will give you (2) which you can downcast to Base or Any

The reference will remain pointing to (2) in the global dictionary, which is the correct behavior.
Type<Base>.sharedInstance will give you (1) which you only can downcast to Any but not upcast to Derived

The reason for this is the (dynamic) metatype inside each Type<T> instance.

Here is an example how it works in Swift 2.2:

class Base {}
class Derived: Base {}

let baseMetatype = Base.self
(baseMetatype is Base.Type) == true
(baseMetatype is Any.Type) == true
(baseMetatype is Derived.Type) == false

let derivedMetatype = Derived.self
(derivedMetatype is Base.Type) == true
(derivedMetatype is Any.Type) == true
(derivedMetatype is Derived.Type) == true
Problem solved? Lets tackle next one.

PS: I removed _identifier because now _metatype does the most work.

Adrian Zubarev
Sent with Airmail

Am 15. Juli 2016 um 17:12:29, Anton Zhilin (antonyzhilin at gmail.com) schrieb:

Here you are, full example:

let x = Type<Derived>()

// Type<Derived> is not a subtype of Type<Base>, we must construct a new instance of Type<Base>
let y = Type<Base>(casting: x)!

y === x //=> false

// Type<T> are reference types. z will point to the same instance as y
let z = unsafeBitCast(y, to: Type<Derived>())

z === y //=> true
z === x //=> false

// Static and dynamic types are equal here, so we decide to draw from global dictionary
let w = Type<Derived>(casting: y)!

w === x //=> true
w === y //=> false
w === z //=> false
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160715/456679d7/attachment.html>

More information about the swift-evolution mailing list