Reflection: add KVariance, KTypeParameter.{variance,upperBounds,equals,hashCode}

This commit is contained in:
Alexander Udalov
2016-07-22 15:34:54 +03:00
parent 127e7ab5b7
commit 170ea4dead
9 changed files with 272 additions and 3 deletions
@@ -0,0 +1,39 @@
// WITH_REFLECT
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
class A<T>
class B<T>
class Fun {
fun <T> foo(): T = null!!
}
class Fourple<A, B, in C, out D>
fun box(): String {
assertEquals(A::class.typeParameters, A::class.typeParameters)
assertEquals(A::class.typeParameters.single().hashCode(), A::class.typeParameters.single().hashCode())
fun getFoo() = Fun::class.members.single { it.name == "foo" }
assertEquals(getFoo().typeParameters, getFoo().typeParameters)
assertEquals(getFoo().typeParameters.single().hashCode(), getFoo().typeParameters.single().hashCode())
assertNotEquals(A::class.typeParameters.single(), B::class.typeParameters.single())
val fi = Fourple::class.typeParameters
val fj = Fourple::class.typeParameters
for (i in 0..fi.size - 1) {
for (j in 0..fj.size - 1) {
if (i == j) {
assertEquals(fi[i], fj[j])
assertEquals(fi[i].hashCode(), fj[j].hashCode())
} else {
assertNotEquals(fi[i], fj[j])
}
}
}
return "OK"
}
@@ -0,0 +1,23 @@
// WITH_REFLECT
import kotlin.reflect.KVariance
import kotlin.test.assertEquals
class Triple<in A, B, out C> {
fun <T> foo(): T = null!!
}
fun box(): String {
assertEquals(
listOf(
KVariance.IN,
KVariance.INVARIANT,
KVariance.OUT
),
Triple::class.typeParameters.map { it.variance }
)
assertEquals(KVariance.INVARIANT, Triple::class.members.single { it.name == "foo" }.typeParameters.single().variance)
return "OK"
}
@@ -0,0 +1,59 @@
// WITH_REFLECT
import kotlin.reflect.KTypeProjection
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class DefaultBound<T>
class NullableAnyBound<T : Any?>
class NotNullAnyBound<T : Any>
class TwoBounds<T : Cloneable> where T : Comparable<T>
class OtherParameterBound<T : U, U : Number>
class RecursiveGeneric<T : Enum<T>>
class FunctionTypeParameter {
fun <A : Cloneable> foo(): Cloneable = null!!
}
// helper functions to obtain KType instances
fun nullableAny(): Any? = null
fun notNullAny(): Any = null!!
fun box(): String {
assertEquals(listOf(::nullableAny.returnType), DefaultBound::class.typeParameters.single().upperBounds)
assertEquals(listOf(::nullableAny.returnType), NullableAnyBound::class.typeParameters.single().upperBounds)
assertEquals(listOf(::notNullAny.returnType), NotNullAnyBound::class.typeParameters.single().upperBounds)
TwoBounds::class.typeParameters.single().let {
val (cl, cm) = it.upperBounds
assertEquals(Cloneable::class, cl.classifier)
assertEquals(listOf(), cl.arguments)
assertEquals(Comparable::class, cm.classifier)
val cmt = cm.arguments.single()
assertTrue(cmt is KTypeProjection.Invariant)
assertEquals(it, cmt.type!!.classifier)
}
OtherParameterBound::class.typeParameters.let {
val (t, u) = it
assertEquals(u, t.upperBounds.single().classifier)
assertEquals(Number::class, u.upperBounds.single().classifier)
}
FunctionTypeParameter::class.members.single { it.name == "foo" }.let { foo ->
assertEquals(foo.returnType, foo.typeParameters.single().upperBounds.single())
}
val recursiveGenericTypeParameter = RecursiveGeneric::class.typeParameters.single()
val recursiveGenericBound = recursiveGenericTypeParameter.upperBounds.single()
assertEquals(Enum::class, recursiveGenericBound.classifier)
recursiveGenericBound.arguments.single().let { projection ->
assertTrue(projection is KTypeProjection.Invariant)
assertEquals(recursiveGenericTypeParameter, projection.type!!.classifier)
}
return "OK"
}
@@ -0,0 +1,26 @@
// WITH_REFLECT
import kotlin.reflect.KVariance
import kotlin.test.assertEquals
class Fourple<A, B, C, D>
fun foo(): Fourple<String, in String, out String, *> = null!!
fun listOfStrings(): List<String> = null!!
fun box(): String {
assertEquals(
listOf(
KVariance.INVARIANT,
KVariance.IN,
KVariance.OUT,
null
),
::foo.returnType.arguments.map { it.variance }
)
// Declaration-site variance should have no effect on the variance of the type projection:
assertEquals(KVariance.INVARIANT, ::listOfStrings.returnType.arguments.first().variance)
return "OK"
}
@@ -12567,6 +12567,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("typeParametersEqualsHashCode.kt")
public void testTypeParametersEqualsHashCode() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/typeParametersEqualsHashCode.kt");
doTest(fileName);
}
@TestMetadata("typeToString.kt")
public void testTypeToString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/methodsFromAny/typeToString.kt");
@@ -12951,11 +12957,23 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeParameters"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("declarationSiteVariance.kt")
public void testDeclarationSiteVariance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/typeParameters/declarationSiteVariance.kt");
doTest(fileName);
}
@TestMetadata("typeParametersAndNames.kt")
public void testTypeParametersAndNames() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/typeParameters/typeParametersAndNames.kt");
doTest(fileName);
}
@TestMetadata("upperBounds.kt")
public void testUpperBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/typeParameters/upperBounds.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/reflection/types")
@@ -13007,6 +13025,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/typeArguments.kt");
doTest(fileName);
}
@TestMetadata("useSiteVariance.kt")
public void testUseSiteVariance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/types/useSiteVariance.kt");
doTest(fileName);
}
}
}
+20 -3
View File
@@ -70,23 +70,37 @@ public sealed class KTypeProjection {
*/
public abstract val type: KType?
/**
* The use-site variance specified in the projection, or `null` if this is a star projection.
*/
public abstract val variance: KVariance?
/**
* Invariant projection of a type. Invariant projection is just the type itself, without any use-site variance modifiers applied to it.
* For example, in the type `Set<String>`, `String` is an invariant projection of the type represented by the class `String`.
*/
public data class Invariant(override val type: KType) : KTypeProjection()
public data class Invariant(override val type: KType) : KTypeProjection() {
override val variance: KVariance?
get() = KVariance.INVARIANT
}
/**
* Contravariant projection of a type, denoted by the `in` modifier applied to a type.
* For example, in the type `MutableList<in Number>`, `in Number` is a contravariant projection of the type of class `Number`.
*/
public data class In(override val type: KType) : KTypeProjection()
public data class In(override val type: KType) : KTypeProjection() {
override val variance: KVariance?
get() = KVariance.IN
}
/**
* Covariant projection of a type, denoted by the `out` modifier applied to a type.
* For example, in the type `Array<out Number>`, `out Number` is a covariant projection of the type of class `Number`.
*/
public data class Out(override val type: KType) : KTypeProjection()
public data class Out(override val type: KType) : KTypeProjection() {
override val variance: KVariance?
get() = KVariance.OUT
}
/**
* Star projection, denoted by the `*` character.
@@ -97,5 +111,8 @@ public sealed class KTypeProjection {
public object Star : KTypeProjection() {
override val type: KType?
get() = null
override val variance: KVariance?
get() = null
}
}
@@ -26,4 +26,18 @@ public interface KTypeParameter : KClassifier {
* The name of this type parameter as it was declared in the source code.
*/
public val name: String
/**
* Upper bounds, or generic constraints imposed on this type parameter.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/generics.html#upper-bounds)
* for more information.
*/
public val upperBounds: List<KType>
/**
* Declaration-site variance of this type parameter.
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance)
* for more information.
*/
public val variance: KVariance
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.reflect
/**
* Represents variance applied to a type parameter on the declaration site (*declaration-site variance*),
* or to a type in a projection (*use-site variance*).
*
* See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/generics.html#variance)
* for more information.
*
* @see [KTypeParameter.variance]
* @see [KTypeProjection]
*/
enum class KVariance {
/**
* The affected type parameter or type is *invariant*, which means it has no variance applied to it.
*/
INVARIANT,
/**
* The affected type parameter or type is *contravariant*. Denoted by the `in` modifier in the source code.
*/
IN,
/**
* The affected type parameter or type is *covariant*. Denoted by the `out` modifier in the source code.
*/
OUT,
}
@@ -17,9 +17,32 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.Variance
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.KVariance
internal class KTypeParameterImpl(override val descriptor: TypeParameterDescriptor) : KTypeParameter, KClassifierImpl {
override val name: String
get() = descriptor.name.asString()
override val upperBounds: List<KType>
get() = descriptor.upperBounds.map { kotlinType ->
KTypeImpl(kotlinType) {
TODO("Java type is not yet supported for type parameters: $descriptor")
}
}
override val variance: KVariance
get() = when (descriptor.variance) {
Variance.INVARIANT -> KVariance.INVARIANT
Variance.IN_VARIANCE -> KVariance.IN
Variance.OUT_VARIANCE -> KVariance.OUT
}
override fun equals(other: Any?) =
other is KTypeParameterImpl && descriptor == other.descriptor
override fun hashCode() =
descriptor.hashCode()
}