Introduce TypeParameterUpperBoundEraser to memorize results of type parameters erasion computation
^KT-47785 Fixed
This commit is contained in:
committed by
teamcityserver
parent
f9cb0d61a8
commit
6706ee87ad
+6
@@ -28078,6 +28078,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("rawTypeWithNestedClass.kt")
|
@TestMetadata("rawTypeWithNestedClass.kt")
|
||||||
public void testRawTypeWithNestedClass() throws Exception {
|
public void testRawTypeWithNestedClass() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||||
|
// FULL_JDK
|
||||||
|
|
||||||
|
// FILE: AbstractAssert.java
|
||||||
|
public abstract class AbstractAssert<E extends AbstractAssert<E>> { }
|
||||||
|
|
||||||
|
// FILE: AbstractAssertWithOriginWithColumnsAndRows.java
|
||||||
|
public abstract class AbstractAssertWithOriginWithColumnsAndRows<E extends AbstractAssertWithOriginWithColumnsAndRows<E, O, D, A, C, CV, R, RV>, O extends OriginWithColumnsAndRows<C, R>, D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>> {}
|
||||||
|
|
||||||
|
// FILE: AbstractColumnAssert.java
|
||||||
|
public abstract class AbstractColumnAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractSubAssert<D, A, C, CV, C, CV, R, RV> implements ColumnElement {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractColumnValueAssert.java
|
||||||
|
public abstract class AbstractColumnValueAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractValueAssert<D, A, C, CV, C, CV, R, RV> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractDbAssert.java
|
||||||
|
public abstract class AbstractDbAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractAssert<A> implements OriginWithColumnsAndRows<C, R> {
|
||||||
|
public A hasNumberOfRows(int expected) {
|
||||||
|
return (A) new RequestAssert();
|
||||||
|
}
|
||||||
|
public R row() {
|
||||||
|
return (R) new RequestRowAssert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractDbData.java
|
||||||
|
public abstract class AbstractDbData<D extends AbstractDbData<D>> extends AbstractDbElement<D> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractDbElement.java
|
||||||
|
public abstract class AbstractDbElement<D extends AbstractDbElement<D>> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractRowAssert.java
|
||||||
|
public abstract class AbstractRowAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractSubAssert<D, A, R, RV, C, CV, R, RV> implements RowElement {
|
||||||
|
public RV value(String columnName) {
|
||||||
|
return (RV) new RequestRowValueAssert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractRowValueAssert.java
|
||||||
|
public abstract class AbstractRowValueAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractValueAssert<D, A, R, RV, C, CV, R, RV> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractSubAssert.java
|
||||||
|
public abstract class AbstractSubAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, S extends AbstractSubAssert<D, A, S, V, C, CV, R, RV>, V extends AbstractValueAssert<D, A, S, V, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractAssertWithOriginWithColumnsAndRows<S, A, D, A, C, CV, R, RV> implements OriginWithColumnsAndRows<C, R> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: AbstractValueAssert.java
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public abstract class AbstractValueAssert<D extends AbstractDbData<D>, A extends AbstractDbAssert<D, A, C, CV, R, RV>, S extends AbstractSubAssert<D, A, S, V, C, CV, R, RV>, V extends AbstractValueAssert<D, A, S, V, C, CV, R, RV>, C extends AbstractColumnAssert<D, A, C, CV, R, RV>, CV extends AbstractColumnValueAssert<D, A, C, CV, R, RV>, R extends AbstractRowAssert<D, A, C, CV, R, RV>, RV extends AbstractRowValueAssert<D, A, C, CV, R, RV>>
|
||||||
|
extends AbstractAssertWithOriginWithColumnsAndRows<V, S, D, A, C, CV, R, RV> {
|
||||||
|
public V isEqualTo(LocalDateTime expected) {
|
||||||
|
return (V) new RequestRowValueAssert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: ColumnElement.java
|
||||||
|
public interface ColumnElement { }
|
||||||
|
|
||||||
|
// FILE: OriginWithColumnsAndRows.java
|
||||||
|
public interface OriginWithColumnsAndRows<C extends ColumnElement, R extends RowElement> {}
|
||||||
|
|
||||||
|
// FILE: Request.java
|
||||||
|
public class Request extends AbstractDbData<Request> {}
|
||||||
|
|
||||||
|
// FILE: RequestAssert.java
|
||||||
|
public class RequestAssert
|
||||||
|
extends AbstractDbAssert<Request, RequestAssert, RequestColumnAssert, RequestColumnValueAssert, RequestRowAssert, RequestRowValueAssert> {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: RequestColumnAssert.java
|
||||||
|
public class RequestColumnAssert
|
||||||
|
extends AbstractColumnAssert<Request, RequestAssert, RequestColumnAssert, RequestColumnValueAssert, RequestRowAssert, RequestRowValueAssert> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: RequestColumnValueAssert.java
|
||||||
|
public class RequestColumnValueAssert
|
||||||
|
extends AbstractColumnValueAssert<Request, RequestAssert, RequestColumnAssert, RequestColumnValueAssert, RequestRowAssert, RequestRowValueAssert> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: RequestRowAssert.java
|
||||||
|
public class RequestRowAssert
|
||||||
|
extends AbstractRowAssert<Request, RequestAssert, RequestColumnAssert, RequestColumnValueAssert, RequestRowAssert, RequestRowValueAssert> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: RequestRowValueAssert.java
|
||||||
|
public class RequestRowValueAssert
|
||||||
|
extends AbstractRowValueAssert<Request, RequestAssert, RequestColumnAssert, RequestColumnValueAssert, RequestRowAssert, RequestRowValueAssert> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: RowElement.java
|
||||||
|
public interface RowElement {}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
fun test(x: RequestAssert) {
|
||||||
|
val timestamp = LocalDateTime.now()
|
||||||
|
x.hasNumberOfRows(1)
|
||||||
|
.row()
|
||||||
|
.value("message_time").isEqualTo(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
test(RequestAssert())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+1
-1
@@ -17,5 +17,5 @@ public open class Foo</*0*/ T : Boo<K!>!, /*1*/ K : Boo<X!>!, /*2*/ X : Boo<K!>!
|
|||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
|
||||||
// Static members
|
// Static members
|
||||||
public/*package*/ open fun test1(): (Foo<(raw) Boo<Boo<Boo<*>!>!>!, (raw) Boo<Boo<*>!>!, (raw) Boo<Boo<*>!>!>..Foo<out Boo<out Boo<out Boo<*>!>!>!, out Boo<out Boo<*>!>!, out Boo<out Boo<*>!>!>?)
|
public/*package*/ open fun test1(): (Foo<(raw) Boo<Boo<Boo<*>!>!>!, (raw) Boo<Boo<*>!>!, (raw) Boo<*>!>..Foo<out Boo<out Boo<out Boo<*>!>!>!, out Boo<out Boo<*>!>!, out Boo<*>!>?)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -27976,6 +27976,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("rawTypeWithNestedClass.kt")
|
@TestMetadata("rawTypeWithNestedClass.kt")
|
||||||
public void testRawTypeWithNestedClass() throws Exception {
|
public void testRawTypeWithNestedClass() throws Exception {
|
||||||
|
|||||||
+6
@@ -28078,6 +28078,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("rawTypeWithNestedClass.kt")
|
@TestMetadata("rawTypeWithNestedClass.kt")
|
||||||
public void testRawTypeWithNestedClass() throws Exception {
|
public void testRawTypeWithNestedClass() throws Exception {
|
||||||
|
|||||||
+5
@@ -23755,6 +23755,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/kt14989.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rawTypeWithNestedClass.kt")
|
@TestMetadata("rawTypeWithNestedClass.kt")
|
||||||
public void testRawTypeWithNestedClass() throws Exception {
|
public void testRawTypeWithNestedClass() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/platformTypes/rawTypeWithNestedClass.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/rawTypeWithNestedClass.kt");
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.load.java.components.JavaPropertyInitializerEvaluato
|
|||||||
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
|
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
|
||||||
import org.jetbrains.kotlin.load.java.components.SignaturePropagator
|
import org.jetbrains.kotlin.load.java.components.SignaturePropagator
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
|
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.types.TypeParameterUpperBoundEraser
|
||||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory
|
import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory
|
||||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
|
import org.jetbrains.kotlin.load.java.typeEnhancement.SignatureEnhancement
|
||||||
|
|||||||
+9
-71
@@ -41,6 +41,7 @@ class JavaTypeResolver(
|
|||||||
private val c: LazyJavaResolverContext,
|
private val c: LazyJavaResolverContext,
|
||||||
private val typeParameterResolver: TypeParameterResolver
|
private val typeParameterResolver: TypeParameterResolver
|
||||||
) {
|
) {
|
||||||
|
val typeParameterUpperBoundEraser = TypeParameterUpperBoundEraser()
|
||||||
|
|
||||||
fun transformJavaType(javaType: JavaType?, attr: JavaTypeAttributes): KotlinType {
|
fun transformJavaType(javaType: JavaType?, attr: JavaTypeAttributes): KotlinType {
|
||||||
return when (javaType) {
|
return when (javaType) {
|
||||||
@@ -229,9 +230,11 @@ class JavaTypeResolver(
|
|||||||
// so we get A<*, *>.
|
// so we get A<*, *>.
|
||||||
// Summary result for upper bound of T is `A<A<*, *>, A<*, *>>..A<out A<*, *>, out A<*, *>>`
|
// Summary result for upper bound of T is `A<A<*, *>, A<*, *>>..A<out A<*, *>, out A<*, *>>`
|
||||||
val erasedUpperBound = LazyWrappedType(c.storageManager) {
|
val erasedUpperBound = LazyWrappedType(c.storageManager) {
|
||||||
parameter.getErasedUpperBound(isRaw, attr) {
|
typeParameterUpperBoundEraser.getErasedUpperBound(
|
||||||
constructor.declarationDescriptor!!.defaultType.replaceArgumentsWithStarProjections()
|
parameter,
|
||||||
}
|
isRaw,
|
||||||
|
attr.withDefaultType(constructor.declarationDescriptor?.defaultType)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
RawSubstitution.computeProjection(
|
RawSubstitution.computeProjection(
|
||||||
@@ -310,9 +313,11 @@ data class JavaTypeAttributes(
|
|||||||
val flexibility: JavaTypeFlexibility = INFLEXIBLE,
|
val flexibility: JavaTypeFlexibility = INFLEXIBLE,
|
||||||
val isForAnnotationParameter: Boolean = false,
|
val isForAnnotationParameter: Boolean = false,
|
||||||
// we use it to prevent happening a recursion while compute type parameter's upper bounds
|
// we use it to prevent happening a recursion while compute type parameter's upper bounds
|
||||||
val visitedTypeParameters: Set<TypeParameterDescriptor>? = null
|
val visitedTypeParameters: Set<TypeParameterDescriptor>? = null,
|
||||||
|
val defaultType: SimpleType? = null
|
||||||
) {
|
) {
|
||||||
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
|
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
|
||||||
|
fun withDefaultType(type: SimpleType?) = copy(defaultType = type)
|
||||||
fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) =
|
fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) =
|
||||||
copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter))
|
copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter))
|
||||||
}
|
}
|
||||||
@@ -331,70 +336,3 @@ fun TypeUsage.toAttributes(
|
|||||||
isForAnnotationParameter = isForAnnotationParameter,
|
isForAnnotationParameter = isForAnnotationParameter,
|
||||||
visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf)
|
visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Definition:
|
|
||||||
// ErasedUpperBound(T : G<t>) = G<*> // UpperBound(T) is a type G<t> with arguments
|
|
||||||
// ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments
|
|
||||||
// ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F
|
|
||||||
internal fun TypeParameterDescriptor.getErasedUpperBound(
|
|
||||||
// Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound`
|
|
||||||
// E.g. `class A<T extends A, F extends A>`
|
|
||||||
// To prevent recursive calls return defaultValue() instead
|
|
||||||
isRaw: Boolean,
|
|
||||||
typeAttr: JavaTypeAttributes,
|
|
||||||
defaultValue: (() -> KotlinType) = { ErrorUtils.createErrorType("Can't compute erased upper bound of type parameter `$this`") }
|
|
||||||
): KotlinType {
|
|
||||||
val visitedTypeParameters = typeAttr.visitedTypeParameters
|
|
||||||
|
|
||||||
if (visitedTypeParameters != null && original in visitedTypeParameters) return defaultValue()
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We should do erasure of containing type parameters with their erasure to avoid creating inconsistent types.
|
|
||||||
* E.g. for `class Foo<T: Foo<B>, B>`, we'd have erasure for lower bound: Foo<Foo<*>, Any>,
|
|
||||||
* but it's wrong type: projection(*) != projection(Any).
|
|
||||||
* So we should substitute erasure of the corresponding type parameter: `Foo<Foo<Any>, Any>` or `Foo<Foo<*>, *>`.
|
|
||||||
*/
|
|
||||||
val erasedUpperBounds = defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate {
|
|
||||||
val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) {
|
|
||||||
RawSubstitution.computeProjection(
|
|
||||||
it,
|
|
||||||
// if erasure happens due to invalid arguments number, use star projections instead
|
|
||||||
if (isRaw) typeAttr else typeAttr.withFlexibility(INFLEXIBLE),
|
|
||||||
it.getErasedUpperBound(isRaw, typeAttr.withNewVisitedTypeParameter(this))
|
|
||||||
)
|
|
||||||
} else makeStarProjection(it, typeAttr)
|
|
||||||
|
|
||||||
it.typeConstructor to boundProjection
|
|
||||||
}
|
|
||||||
val erasedUpperBoundsSubstitutor = TypeSubstitutor.create(TypeConstructorSubstitution.createByConstructorsMap(erasedUpperBounds))
|
|
||||||
|
|
||||||
val firstUpperBound = upperBounds.first()
|
|
||||||
|
|
||||||
if (firstUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
|
|
||||||
return firstUpperBound.replaceArgumentsWithStarProjectionOrMapped(
|
|
||||||
erasedUpperBoundsSubstitutor,
|
|
||||||
erasedUpperBounds,
|
|
||||||
OUT_VARIANCE,
|
|
||||||
typeAttr.visitedTypeParameters
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val stopAt = typeAttr.visitedTypeParameters ?: setOf(this)
|
|
||||||
var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
|
|
||||||
|
|
||||||
while (current !in stopAt) {
|
|
||||||
val nextUpperBound = current.upperBounds.first()
|
|
||||||
if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
|
|
||||||
return nextUpperBound.replaceArgumentsWithStarProjectionOrMapped(
|
|
||||||
erasedUpperBoundsSubstitutor,
|
|
||||||
erasedUpperBounds,
|
|
||||||
OUT_VARIANCE,
|
|
||||||
typeAttr.visitedTypeParameters
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
current = nextUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -104,12 +104,15 @@ class RawTypeImpl private constructor(lowerBound: SimpleType, upperBound: Simple
|
|||||||
internal object RawSubstitution : TypeSubstitution() {
|
internal object RawSubstitution : TypeSubstitution() {
|
||||||
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
|
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
|
||||||
|
|
||||||
|
private val typeParameterUpperBoundEraser = TypeParameterUpperBoundEraser()
|
||||||
|
|
||||||
private val lowerTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND)
|
private val lowerTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND)
|
||||||
private val upperTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND)
|
private val upperTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND)
|
||||||
|
|
||||||
private fun eraseType(type: KotlinType, attr: JavaTypeAttributes = JavaTypeAttributes(TypeUsage.COMMON)): KotlinType {
|
private fun eraseType(type: KotlinType, attr: JavaTypeAttributes = JavaTypeAttributes(TypeUsage.COMMON)): KotlinType {
|
||||||
return when (val declaration = type.constructor.declarationDescriptor) {
|
return when (val declaration = type.constructor.declarationDescriptor) {
|
||||||
is TypeParameterDescriptor -> eraseType(declaration.getErasedUpperBound(isRaw = true, attr), attr)
|
is TypeParameterDescriptor ->
|
||||||
|
eraseType(typeParameterUpperBoundEraser.getErasedUpperBound(declaration, isRaw = true, attr), attr)
|
||||||
is ClassDescriptor -> {
|
is ClassDescriptor -> {
|
||||||
val declarationForUpper =
|
val declarationForUpper =
|
||||||
type.upperIfFlexible().constructor.declarationDescriptor
|
type.upperIfFlexible().constructor.declarationDescriptor
|
||||||
@@ -170,7 +173,7 @@ internal object RawSubstitution : TypeSubstitution() {
|
|||||||
fun computeProjection(
|
fun computeProjection(
|
||||||
parameter: TypeParameterDescriptor,
|
parameter: TypeParameterDescriptor,
|
||||||
attr: JavaTypeAttributes,
|
attr: JavaTypeAttributes,
|
||||||
erasedUpperBound: KotlinType = parameter.getErasedUpperBound(isRaw = true, attr)
|
erasedUpperBound: KotlinType = typeParameterUpperBoundEraser.getErasedUpperBound(parameter, isRaw = true, attr)
|
||||||
) = when (attr.flexibility) {
|
) = when (attr.flexibility) {
|
||||||
// Raw(List<T>) => (List<Any?>..List<*>)
|
// Raw(List<T>) => (List<Any?>..List<*>)
|
||||||
// Raw(Enum<T>) => (Enum<Enum<*>>..Enum<out Enum<*>>)
|
// Raw(Enum<T>) => (Enum<Enum<*>>..Enum<out Enum<*>>)
|
||||||
|
|||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.load.java.lazy.types
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.extractTypeParametersFromUpperBounds
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjectionOrMapped
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||||
|
|
||||||
|
class TypeParameterUpperBoundEraser {
|
||||||
|
private val storage = LockBasedStorageManager("Type parameter upper bound erasion results")
|
||||||
|
private val erroneousErasedBound by lazy {
|
||||||
|
ErrorUtils.createErrorType("Can't compute erased upper bound of type parameter `$this`")
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class DataToEraseUpperBound(
|
||||||
|
val typeParameter: TypeParameterDescriptor,
|
||||||
|
val isRaw: Boolean,
|
||||||
|
val typeAttr: JavaTypeAttributes
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is DataToEraseUpperBound) return false
|
||||||
|
return other.typeParameter == this.typeParameter
|
||||||
|
&& other.isRaw == this.isRaw
|
||||||
|
&& other.typeAttr.flexibility == this.typeAttr.flexibility
|
||||||
|
&& other.typeAttr.howThisTypeIsUsed == this.typeAttr.howThisTypeIsUsed
|
||||||
|
&& other.typeAttr.isForAnnotationParameter == this.typeAttr.isForAnnotationParameter
|
||||||
|
&& other.typeAttr.defaultType == this.typeAttr.defaultType
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = typeParameter.hashCode()
|
||||||
|
result += 31 * result + if (isRaw) 1 else 0
|
||||||
|
result += 31 * result + typeAttr.flexibility.hashCode()
|
||||||
|
result += 31 * result + typeAttr.howThisTypeIsUsed.hashCode()
|
||||||
|
result += 31 * result + if (typeAttr.isForAnnotationParameter) 1 else 0
|
||||||
|
result += 31 * result + typeAttr.defaultType.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val getErasedUpperBound = storage.createMemoizedFunction<DataToEraseUpperBound, KotlinType> {
|
||||||
|
with(it) { getErasedUpperBoundInternal(typeParameter, isRaw, typeAttr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getErasedUpperBound(
|
||||||
|
typeParameter: TypeParameterDescriptor,
|
||||||
|
isRaw: Boolean,
|
||||||
|
typeAttr: JavaTypeAttributes
|
||||||
|
) = getErasedUpperBound(DataToEraseUpperBound(typeParameter, isRaw, typeAttr))
|
||||||
|
|
||||||
|
private fun getDefaultType(typeAttr: JavaTypeAttributes) =
|
||||||
|
typeAttr.defaultType?.replaceArgumentsWithStarProjections() ?: erroneousErasedBound
|
||||||
|
|
||||||
|
// Definition:
|
||||||
|
// ErasedUpperBound(T : G<t>) = G<*> // UpperBound(T) is a type G<t> with arguments
|
||||||
|
// ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments
|
||||||
|
// ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F
|
||||||
|
private fun getErasedUpperBoundInternal(
|
||||||
|
// Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound`
|
||||||
|
// E.g. `class A<T extends A, F extends A>`
|
||||||
|
// To prevent recursive calls return defaultValue() instead
|
||||||
|
typeParameter: TypeParameterDescriptor,
|
||||||
|
isRaw: Boolean,
|
||||||
|
typeAttr: JavaTypeAttributes
|
||||||
|
): KotlinType {
|
||||||
|
val visitedTypeParameters = typeAttr.visitedTypeParameters
|
||||||
|
|
||||||
|
if (visitedTypeParameters != null && typeParameter.original in visitedTypeParameters)
|
||||||
|
return getDefaultType(typeAttr)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We should do erasure of containing type parameters with their erasure to avoid creating inconsistent types.
|
||||||
|
* E.g. for `class Foo<T: Foo<B>, B>`, we'd have erasure for lower bound: Foo<Foo<*>, Any>,
|
||||||
|
* but it's wrong type: projection(*) != projection(Any).
|
||||||
|
* So we should substitute erasure of the corresponding type parameter: `Foo<Foo<Any>, Any>` or `Foo<Foo<*>, *>`.
|
||||||
|
*/
|
||||||
|
val erasedUpperBounds = typeParameter.defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate {
|
||||||
|
val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) {
|
||||||
|
RawSubstitution.computeProjection(
|
||||||
|
it,
|
||||||
|
// if erasure happens due to invalid arguments number, use star projections instead
|
||||||
|
if (isRaw) typeAttr else typeAttr.withFlexibility(JavaTypeFlexibility.INFLEXIBLE),
|
||||||
|
getErasedUpperBound(it, isRaw, typeAttr.withNewVisitedTypeParameter(typeParameter))
|
||||||
|
)
|
||||||
|
} else makeStarProjection(it, typeAttr)
|
||||||
|
|
||||||
|
it.typeConstructor to boundProjection
|
||||||
|
}
|
||||||
|
val erasedUpperBoundsSubstitutor = TypeSubstitutor.create(TypeConstructorSubstitution.createByConstructorsMap(erasedUpperBounds))
|
||||||
|
|
||||||
|
val firstUpperBound = typeParameter.upperBounds.first()
|
||||||
|
|
||||||
|
if (firstUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
|
||||||
|
return firstUpperBound.replaceArgumentsWithStarProjectionOrMapped(
|
||||||
|
erasedUpperBoundsSubstitutor,
|
||||||
|
erasedUpperBounds,
|
||||||
|
Variance.OUT_VARIANCE,
|
||||||
|
typeAttr.visitedTypeParameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val stopAt = typeAttr.visitedTypeParameters ?: setOf(this)
|
||||||
|
var current = firstUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
|
||||||
|
|
||||||
|
while (current !in stopAt) {
|
||||||
|
val nextUpperBound = current.upperBounds.first()
|
||||||
|
if (nextUpperBound.constructor.declarationDescriptor is ClassDescriptor) {
|
||||||
|
return nextUpperBound.replaceArgumentsWithStarProjectionOrMapped(
|
||||||
|
erasedUpperBoundsSubstitutor,
|
||||||
|
erasedUpperBounds,
|
||||||
|
Variance.OUT_VARIANCE,
|
||||||
|
typeAttr.visitedTypeParameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
current = nextUpperBound.constructor.declarationDescriptor as TypeParameterDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultType(typeAttr)
|
||||||
|
}
|
||||||
|
}
|
||||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+5
@@ -18664,6 +18664,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/inferenceFlexibleTToNullable.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/inferenceFlexibleTToNullable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Generated
+5
@@ -18070,6 +18070,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/inferenceFlexibleTToNullable.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/inferenceFlexibleTToNullable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Generated
+5
@@ -18135,6 +18135,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/platformTypes/inferenceFlexibleTToNullable.kt");
|
runTest("compiler/testData/codegen/box/platformTypes/inferenceFlexibleTToNullable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt47785.kt")
|
||||||
|
public void testKt47785() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/platformTypes/kt47785.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
@TestMetadata("compiler/testData/codegen/box/platformTypes/primitives")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user