0
votes

I think this code snippet should compile:

package bug

import java.lang.reflect.*

data class Descriptor(val clazz: Class<*>, val modifiers: Int, val info: List<String>) {
    constructor(clazz: Class<*>, modifiers: Int, vararg info: List<String>) :
            this(clazz, modifiers, mutableListOf<String>().apply { info.forEach { [email protected](it) } })
}

private val AnnotatedElement.info: List<String>
    get() = getAnnotation(Info::class.java)?.values?.toList() ?: listOf<String>()

annotation class Info(val values: Array<String>)

/**
 * A simple abstraction of com.google.common.reflect.Invokable of Guava 20.0.
 * https://github.com/google/guava/blob/v20.0/guava/src/com/google/common/reflect/Invokable.java
 */
abstract class Invokable<T, R> : AccessibleObject(), Member, GenericDeclaration

val <T : AccessibleObject> T.descriptor: Descriptor
    get() = when (this) {
        is Invokable<*, *> ->
            Descriptor(declaringClass,
                       modifiers,
                       info,
                       declaringClass.info)
        else -> throw AssertionError()
    }

Here is a convenient reference to Google Guava's Invokable. Brief Invokable definition is given.

The above code should compile all fine but compiler yields 3 weird message, here is the log output (after proper normalization):

e: /path/to/source.kt: (34, 44): Type inference failed: val Invokable.declaringClass: Class! cannot be applied to receiver: T#2 (type parameter of descriptor) arguments: ()

e: /path/to/source.kt: (35, 44): Type inference failed: val Invokable.modifiers: Int cannot be applied to receiver: T#2 (type parameter of descriptor) arguments: ()

e: /path/to/source.kt: (37, 44): Type inference failed: val Invokable.declaringClass: Class! cannot be applied to receiver: T#2 (type parameter of descriptor) arguments: ()

The solution is simple: assign this to a variable and use that local variable. No manual type cast or other bothering stuff and it will compile. But I'm interested if this is a bug of kotlin compiler or I'm missing some info about kotlin generics

EDIT: Working code:

package solution

import java.lang.reflect.*

data class Descriptor(val clazz: Class<*>, val modifiers: Int, val info: List<String>) {
    constructor(clazz: Class<*>, modifiers: Int, vararg info: List<String>) :
            this(clazz, modifiers, mutableListOf<String>().apply { info.forEach { [email protected](it) } })
}

private val AnnotatedElement.info: List<String>
    get() = getAnnotation(Info::class.java)?.values?.toList() ?: listOf<String>()

annotation class Info(val values: Array<String>)

/**
 * A simple abstraction of com.google.common.reflect.Invokable of Guava 20.0.
 * https://github.com/google/guava/blob/v20.0/guava/src/com/google/common/reflect/Invokable.java
 */
abstract class Invokable<T, R> : AccessibleObject(), Member, GenericDeclaration

val <T : AccessibleObject> T.descriptor: Descriptor
    get() = when (this) {
        is Invokable<*, *> -> {
            val o = this // <---------------------------------CHANGES BEGIN FROM THIS LINE
            Descriptor(o.declaringClass,
                       o.modifiers,
                       info,
                       o.declaringClass.info)
        }
        else -> throw AssertionError()
    }

This has been submitted to JetBrains but they didn't respond yet, so I will keep this open until they verified or someone come up scolding my stupidness.

1
What is foo? Please show its declarationvoddan
@voddan Well, it's Descriptor, actually. Sorry for the mistake.glee8e
I don't see val Invokable.declaringClass anywhere. Could you provide that code too?voddan
@voddan It's a google guava class and an online reference has already been provided above.glee8e
Oh, I thought this was yours extension property on Invokable. Thanks!voddan

1 Answers

0
votes

It seems that smart cast is not smart enough. But you can give it a hint using:

val <T : AccessibleObject> T.descriptor: Descriptor
get() = when (this) {
    is Invokable<*, *> -> {
        Descriptor(this.declaringClass,
                this.modifiers,
                info,
                this.declaringClass.info)
    }
    else -> throw AssertionError()
}

No need of using the val stuff. You only need to give it "something" to work on. It can be messy when there is nothing to "highlight" on the IDE :)