Generic type weirdities

I was playing around with generic types, trying to answer another question for someone and stumbled across, what I would call, some weird behaviour:

fileprivate var _instanceCount: Int = 0

struct Generic<typeT>
{
  static var instanceCount: Int
  {
    return _instanceCount
  }
  
  init()
  {
    _instanceCount += 1
  }
}

Test code:

  {
    let genericInt = Generic<Int>()
    
    let genericString = Generic<String>()
    
    let ic = Generic<Int>.instanceCount // returns 2
    
    let ic = Generic<String>.instanceCount // returns 2
    
    let nc = Generic<Never>.instanceCount // returns 2
  }

So, it would seem that we can address a static member of a generic type without having to bind it to a "proper" type; instead, we can use Never as the parameter.

This feels like it could help to avoid having to use type erasure boilerplate in some circumstances.

Is this a known "feature"? Or am I being somewhat abusive of the type system?

BTW, typing po Generic<Never>.instanceCount or po Generic<Int>.instanceCount into the Xcode console gives me the unexpected:

(lldb) po Generic<Never>.instanceCount
error: <EXPR>:3:1: error: cannot specialize a non-generic definition
Generic<Never>.instanceCount
^

<EXPR>:3:8: note: while parsing this '<' as a type parameter bracket
Generic<Never>.instanceCount
       ^

And, of course, more expected:

(lldb) po Generic.instanceCount
error: <EXPR>:3:1: error: ambiguous reference to member 'Generic'
Generic.instanceCount
^~~~~~~

Never is a "proper" type; it doesn't have any values, but you can substitute it freely into generic parameters. The lldb behavior looks like a bug.

1 Like

That's just what I had assumed; I've just not yet found any documentation that says so much.

Do I need to report it?

Please do report the bug!

Will do.

Can I just add that it is starting to look like doing static stuff on generic types still seems to be a bit of a minefield.

The question I was looking at was the one about static stored properties not being allowed on generic types. As you can see there is the global var workaround but that can only record instance of all specialisations and is not much good if you ever wanted to get the instance count of just Generic or Generic

Is there such thing as an improper type? Seems like a type system is a system of rules, and anything participating in that system is proper as long as it's not a bug. (Or something along those lines.) I suppose you could always decide to formally name something "improper", like we have "strange" quarks.

lldb bug reported as 44644900

1 Like

I meant Never is not normally used as a type except in very specialised cases, for which it is a very proper type :wink:

I can't think of any in Swift, but in C++ void is not a proper type (but there was a proposal to make it one).

void is certainly a proper type in C++, in the sense of being able to instantiate templates on it. It's an incomplete type, which in C++ese means it can't be the type of a value, but so are forward-declared structs.

1 Like