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

Adrian Zubarev adrian.zubarev at devandartist.com
Fri Jul 15 14:24:54 CDT 2016

To summarize a few things that this proposal should change:

Make public .self construct Type<T> via Type<T>.sharedInstance
Let SE–0090 do the rest for public .self drop
Rename type for metatypes T.Type to Metatype<T> (or at least to T.Metatype)

Rename internal .self to .metatype which will return an instance of Metatype<T>

Use Type<T> instead of current T.Type

Implement public func dynamicType<T>(_ instance: T) -> Type<T> using internal _typeStorage. SE–0096

public func dynamicType<T>(_ instance: T) -> Type<T> {
    let dynamicMetatype = /* extract dynamic metatype from the `instance` */
    let identifier = _uniqueIdentifierOf(dynamicMetatype)
    // Check if the type storage contains an instance of our dynamicType
    if let type = _typeStorage.first(where: { $0.hashValue == identifier }) {
        // We only need to switch `Any` to `T` but keep the reference
        // `Type<T>` is implicitly converted to `Type<Type<T>>()`
        return unsafeBitCast(type, to: Type<T>)
    // Create and store an instance of `Type<T>`
    var type = Type<T>.sharedInstance
    // Check if the identifier for our dynamic metatype is equivalent
    // to the created instance, which implies `dynamicMetatype == T`
    if type.hashValue == identifier {
        return type
    // If the identifiers are not equivalent then
    // T is probably downcasted from dynamicMetatype
    type = Type<Any>(metatype: dynamicMetatype)
    // `Type<T>` is implicitly converted to `Type<Type<T>>()`
    return unsafeBitCast(type, to: Type<T>)
Combine SE–0101

Adrian Zubarev
Sent with Airmail

Am 15. Juli 2016 um 21:10:01, Adrian Zubarev (adrian.zubarev at devandartist.com) schrieb:

Not sure what you have tried to solve here. sharedInstance works just fine in my test implementation with Xcode 8 beta 2:

Here is a portion of the implementation that does compile and work as expected, at least I couldn’t find any bugs.

WARNING: the implementation we should tackle is here: https://gist.github.com/DevAndArtist/a5744f21107812b2d4e6baee2c55e0bf

It differs how this is done in the following example:

internal func _uniqueIdentifierOf(_ metatype: Any.Type) -> Int {
    return ObjectIdentifier.init(metatype).hashValue

public typealias Metatype<T> = T.Type
public typealias AnyMetatype = Any.Type

public final class Type<T> : Hashable {
    internal let _metatype: AnyMetatype
    internal init(metatype: AnyMetatype) {

        // check explicitly if `T` is `Any`
        let tIsAny = _uniqueIdentifierOf(Any.self) == _uniqueIdentifierOf(T.self)
        guard tIsAny || metatype is T.Type else {
            fatalError("'metatype' is not an instace of 'Metatype<T>'")
        self._metatype = metatype
    internal convenience init() {
        self.init(metatype: T.self)
    public static func cast<U>(_ 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.self) == _uniqueIdentifierOf(T.self)
        guard isTAny || otherType._metatype is Metatype<T> else {
            return nil

        return unsafeBitCast(otherType, to: Type<T>.self)
    public var metatype: T.Type {
        return unsafeBitCast(self._metatype, to: T.Type.self)
    public static var metatype: T.Type { return T.self }
    public var hashValue: Int { return _uniqueIdentifierOf(self._metatype) }

public func ==<T, U>(lhs: Type<T>, rhs: Type<U>) -> Bool {
    return lhs.hashValue == rhs.hashValue

internal var _typeStorage = Set<Type<Any>>()

extension Type {
    internal static var sharedInstance: Type<T> {
        let identifier = _uniqueIdentifierOf(Type<T>.metatype)
        let typeFromStorage = _typeStorage.first(where: { $0.hashValue == identifier })
        if let type = Type<T>.cast(typeFromStorage) {
            return type
        let newType = Type<T>()
        // downcast `T` to `Any`
        if let type = Type<Any>.cast(newType) {
        return newType


print(_typeStorage) // [Type<Swift.Int>]


print(_typeStorage) // [Type<Swift.Int>, Type<Swift.String>]

class A {}
class B: A {}


print(_typeStorage) // [Type<Swift.Int>, Type<B>, Type<Swift.String>]


print(_typeStorage) // [Type<Swift.String>, Type<Swift.Int>, Type<A>, Type<B>]

Adrian Zubarev
Sent with Airmail

Am 15. Juli 2016 um 19:58:28, Anton Zhilin (antonyzhilin at gmail.com) schrieb:

Consider what I said above, I feel like we should tackle this:

init(_ copy: Type<T>) - to keep equality of  
Type<T> references

make all initializer internal and as exchange make  
sharedInstance public.
This would ensure that you will always get a reference from the type storage, and that there can only exist one for each (dynamic) metatype.

I have another idea:

// No uniqueness guarantee
internal class _Type<T> {
    var _metatype: Metatype<T>

    init(_: AnyMetatype)
    init?<U>(casting: Type<U>)

    // Instead of a global dictionary
    class var sharedInstance: _Type<T>

// Uniqueness guarantee
public struct Type<T> {
    internal var impl: _Type<T>

    public init()
    public init(_: AnyMetatype)
    public init?<U>(casting: Type<U>)
I can’t foresee the future, so I’m wondering if the type may mutate somehow when reflections are added?

I don’t think so. If added at all, types should only be able to be created using some kind of builder:

let builder = TypeBuilder(name: "Person", kind: .struct)
builder.addField(name: "", type: Int.self)
let typeRef = builder.create()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160715/8143f529/attachment.html>

More information about the swift-evolution mailing list