Reflection: add KVariance, KTypeParameter.{variance,upperBounds,equals,hashCode}
This commit is contained in:
+39
@@ -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"
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user