Reified Generics on JVM and/or object code level?

Hi all,

wouldn’t it be possible to lift the restriction that reified type parameters are allowed only for inline functions, also on the JVM implementation, by extending the .class files with additional annotations added (and understood) by the compiler? Re java interop, this would look like additional annotations only, for kotlin, this information could be used to work around type erasure and carry type argument information over to runtime.

1 Like

What would such annotation contain?

Keep in mind that a class with a generic type is compiled to a .class once without any knowledge of what types it will be used with.

What you describte is pretty much Reflection. All you need to do is to pass in the KClass of the reified type as an additional parameter. This would give you the same functionallity as a reified type.
The problem is that this is not available on all kotlin target platforms and also has a negative performance impact not to mention that your binary size increases by the big reflection libraries needed.
Not sure how this would work on JS and Native though. I think there are plans to allow for reflection on JS in the future and since the kotlin team designs the native runtime this should be possible on native (probably without the downsides it will have on the JVM).

Don’t get me wrong, I don’t want to dissmis the idea outright. I think there are some merrits to this. Converting the normal reified type syntax into a reflection based sytem would be nice in some cases.
But what are the actual usecases. Reified is great, but in my experience it’s often used as a nice syntax extension around a reflection based api or just done for some quick type checks/casts. Either can easily be done by inline functions.

The annotations I think of may be applied to the site of realization of the generic type, i.e. when a generic type is used, the actual values of the source-level type arguments should be remembered and this way, a completely new type is formed. The correct typing of further uses of this type can then be checked at compile time, but also at runtime, and the stored type arguments can be used as type literals, because the compiler and runtime know their actual value. For platforms other than JVM, the back-end representation (e.g. binary oder js) can be freely designed to accomodate for this information, but on the JVM, additional metadata must be added to the .class files, where I think annotations are the least intrusive way, so that code compiled from kotlin will be interoperable with other JVM languages.
The restriction to inline functions inhibits many good use cases of reified generics, most notably, classes and interfaces cannot be declared with reified generic type parameters…

I’m not sure I follow.

What do you mean by “a completely new type is formed”? Are you suggesting recompiling all parametrized types at each use (like C++ templates)?
That is not possible on the JVM the compiler has no visibility of sources of the classes you’re using. Each class is compiled individually and independently.

If you add any annotation on the on the calling side of a generic class/function that will only be visible to the caller and not to the callee.

Yes, to a certain extent it is like recompiling the code, but you do not need the source code if you use synthetic annotations as proposed.
For example, consider a generically defined class like this:

class Klass<A> where A() { // "()" should denote a constraint that concrete A definitions must have a no-argument constructor
   fun createA() = A()
}

and the realization site:

val klass = Klass<OtherClass>()
val otherClassesInstance = klass.createA()

At compile time of the first part (the generic type definition) the use of the “A” type parameter in the code of the function could be marked with a synthetic type annotation, and of course, allow reifiable use of A in the source.

Then, at the realization site, the runtime could create a (partial) copy of the byte code of “Klass” with the usage of A in the code (which as mentioned was annotated before) replaced by the actual type argument given at generic type realization. This way, “Klass” and “Klass” become different class definitions on the byte code level. The byte code manipulation could be triggered by code inserted by the compiler at the realization site, something, when “expanded” might look like:

val klass = createReifiedGenericInstance(Class::class, OtherClass::class)
1 Like

At compile time of the first part (the generic type definition) the use of the “A” type parameter in the code of the function could be marked with a synthetic type annotation, and of course, allow reifiable use of A in the source.

I do not quite understand how you propose to do that. Can you show an example of what you expect the produced byte code to look like?

1 Like

I think an annotation on a function to require to pass a hidden Class<T> would suffice to get runtime type information. Annotating it over class would require all methods in it to pass it implicitly, from the Java side it needs to be manually done.
Still, this only work for new methods and classes, not for existing.

This variant would automatize the example presented in: Link.

Yes, it would be possible. You would need to store N objects of class java.lang.reflect.Type for each type parameter as a hidden fields, pass them as a hidden parameters to constructors, etc. But obviously it would work only for Kotlin classes. Java interop would not be very convenient. And considering the fact, that Kotlin mostly reuses Java collections library, it would be very inconvinient.

Honestly while this feature would be nice to have in some situations, in practice you can work around it.