Type (of:) function doesn't take ~Copyable arguments

For example:

struct Foo: ~Copyable {
    var bar = 0
}
let u = Foo ()
print (type (of: u))
// error: Metatype 'Foo.Type' cannot be cast to 'Any' because 'Foo' is noncopyable

print("\(type (of: u))")
// error: Noncopyable type 'Foo' cannot be erased to copyable existential type 'Any'

I was expecting it to be able to take ~Copyable things, but it doesn't.

Should it?

1 Like

According to proposal that has introduced noncopyable types, that is currently not allowed by the language:

At this time, as noted above, generic types are still always required to beCopyable, so noncopyable types themselves are not allowed to be used as a generic type argument. This means a noncopyable type cannot:

  • conform to any protocols, except for Sendable.
  • serve as a type witness for an associatedtype requirement.
  • be used as a type argument when instantiating generic types or calling generic functions.
  • be cast to (or from) Any or any other existential.
  • be accessed through reflection.
  • appear in a tuple.

I suppose that would be possible with SE-0427 that introduces noncopyable generics.

3 Likes

The nice thing about type(of:) is that it can be written naturally without any internal black magic. This makes it easy to write your own overload that takes non-copyable types.

func type<T: ~Copyable>(of value: borrowing T) -> T.Type {
  T.self
}

You do need non-copyable generics enabled for this to work though.

3 Likes

type(of:) has two special behaviors that cannot be simulated with your function:

  • applied to an existential, it takes the dynamic type of the value and outputs an existential metatype (and not the metatype of the existential)
  • applied to a class it returns the dynamic type of the instance (and not the static type)

type(of:) not working for non-Copyable types is an oversight that should be fixed.

14 Likes