[FE 1.0] Unbind type parameter erasing from java
This commit is contained in:
committed by
teamcity
parent
5bfe6cd20a
commit
f31cf90de2
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 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 org.jetbrains.kotlin.load.java.components;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We convert Java types differently, depending on where they occur in the Java code
|
|
||||||
* This enum encodes the kinds of occurrences
|
|
||||||
*/
|
|
||||||
public enum TypeUsage {
|
|
||||||
SUPERTYPE,
|
|
||||||
COMMON
|
|
||||||
}
|
|
||||||
+1
-1
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies
|
import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME
|
||||||
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
|
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.PossiblyExternalAnnotationDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.PossiblyExternalAnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.load.java.FakePureImplementationsProvider
|
|||||||
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
|
import org.jetbrains.kotlin.load.java.components.JavaResolverCache
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
|
import org.jetbrains.kotlin.load.java.lazy.childForClassOrPackage
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.
|
|||||||
import org.jetbrains.kotlin.load.java.ClassicBuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName
|
import org.jetbrains.kotlin.load.java.ClassicBuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName
|
||||||
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.sameAsRenamedInJvmBuiltin
|
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.Companion.sameAsRenamedInJvmBuiltin
|
||||||
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils.resolveOverridesForNonStaticMembers
|
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils.resolveOverridesForNonStaticMembers
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.*
|
import org.jetbrains.kotlin.load.java.descriptors.*
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
import org.jetbrains.kotlin.load.java.lazy.childForMethod
|
import org.jetbrains.kotlin.load.java.lazy.childForMethod
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
|||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.load.java.lazy.descriptors
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||||
import org.jetbrains.kotlin.descriptors.impl.AbstractLazyTypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.AbstractLazyTypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
||||||
|
|||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.types.ErasureTypeAttributes
|
||||||
|
import org.jetbrains.kotlin.types.SimpleType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
|
|
||||||
|
data class JavaTypeAttributes(
|
||||||
|
override val howThisTypeIsUsed: TypeUsage,
|
||||||
|
val flexibility: JavaTypeFlexibility = JavaTypeFlexibility.INFLEXIBLE,
|
||||||
|
val isRaw: Boolean = false,
|
||||||
|
val isForAnnotationParameter: Boolean = false,
|
||||||
|
// we use it to prevent happening a recursion while compute type parameter's upper bounds
|
||||||
|
override val visitedTypeParameters: Set<TypeParameterDescriptor>? = null,
|
||||||
|
override val defaultType: SimpleType? = null
|
||||||
|
) : ErasureTypeAttributes(howThisTypeIsUsed, visitedTypeParameters, defaultType) {
|
||||||
|
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
|
||||||
|
fun markIsRaw(isRaw: Boolean) = copy(isRaw = isRaw)
|
||||||
|
override fun withDefaultType(type: SimpleType?) = copy(defaultType = type)
|
||||||
|
override fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) =
|
||||||
|
copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter))
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is JavaTypeAttributes) return false
|
||||||
|
return other.defaultType == this.defaultType
|
||||||
|
&& other.howThisTypeIsUsed == this.howThisTypeIsUsed
|
||||||
|
&& other.flexibility == this.flexibility
|
||||||
|
&& other.isRaw == this.isRaw
|
||||||
|
&& other.isForAnnotationParameter == this.isForAnnotationParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = defaultType.hashCode()
|
||||||
|
result += 31 * result + howThisTypeIsUsed.hashCode()
|
||||||
|
result += 31 * result + flexibility.hashCode()
|
||||||
|
result += 31 * result + if (isRaw) 1 else 0
|
||||||
|
result += 31 * result + if (isForAnnotationParameter) 1 else 0
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TypeUsage.toAttributes(
|
||||||
|
isForAnnotationParameter: Boolean = false,
|
||||||
|
isRaw: Boolean = false,
|
||||||
|
upperBoundForTypeParameter: TypeParameterDescriptor? = null
|
||||||
|
) = JavaTypeAttributes(
|
||||||
|
this,
|
||||||
|
isRaw = isRaw,
|
||||||
|
isForAnnotationParameter = isForAnnotationParameter,
|
||||||
|
visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf)
|
||||||
|
)
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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
|
||||||
|
|
||||||
|
enum class JavaTypeFlexibility {
|
||||||
|
INFLEXIBLE,
|
||||||
|
FLEXIBLE_UPPER_BOUND,
|
||||||
|
FLEXIBLE_LOWER_BOUND
|
||||||
|
}
|
||||||
+10
-48
@@ -20,9 +20,9 @@ import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMapper
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage.COMMON
|
import org.jetbrains.kotlin.types.TypeUsage.COMMON
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage.SUPERTYPE
|
import org.jetbrains.kotlin.types.TypeUsage.SUPERTYPE
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaAnnotations
|
||||||
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
|
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
|
||||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.java.structure.*
|
|||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection
|
||||||
import org.jetbrains.kotlin.types.Variance.*
|
import org.jetbrains.kotlin.types.Variance.*
|
||||||
import org.jetbrains.kotlin.types.error.ErrorUtils
|
import org.jetbrains.kotlin.types.error.ErrorUtils
|
||||||
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
||||||
@@ -43,8 +44,8 @@ class JavaTypeResolver(
|
|||||||
private val c: LazyJavaResolverContext,
|
private val c: LazyJavaResolverContext,
|
||||||
private val typeParameterResolver: TypeParameterResolver
|
private val typeParameterResolver: TypeParameterResolver
|
||||||
) {
|
) {
|
||||||
private val typeParameterUpperBoundEraser = TypeParameterUpperBoundEraser()
|
private val projectionComputer = RawProjectionComputer()
|
||||||
private val rawSubstitution = RawSubstitution(typeParameterUpperBoundEraser)
|
private val typeParameterUpperBoundEraser = TypeParameterUpperBoundEraser(projectionComputer)
|
||||||
|
|
||||||
fun transformJavaType(javaType: JavaType?, attr: JavaTypeAttributes): KotlinType {
|
fun transformJavaType(javaType: JavaType?, attr: JavaTypeAttributes): KotlinType {
|
||||||
return when (javaType) {
|
return when (javaType) {
|
||||||
@@ -225,15 +226,15 @@ class JavaTypeResolver(
|
|||||||
val erasedUpperBound = LazyWrappedType(c.storageManager) {
|
val erasedUpperBound = LazyWrappedType(c.storageManager) {
|
||||||
typeParameterUpperBoundEraser.getErasedUpperBound(
|
typeParameterUpperBoundEraser.getErasedUpperBound(
|
||||||
parameter,
|
parameter,
|
||||||
javaType.isRaw,
|
attr.withDefaultType(constructor.declarationDescriptor?.defaultType).markIsRaw(javaType.isRaw)
|
||||||
attr.withDefaultType(constructor.declarationDescriptor?.defaultType)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawSubstitution.computeProjection(
|
projectionComputer.computeProjection(
|
||||||
parameter,
|
parameter,
|
||||||
// if erasure happens due to invalid arguments number, use star projections instead
|
// if erasure happens due to invalid arguments number, use star projections instead
|
||||||
if (javaType.isRaw) attr else attr.withFlexibility(INFLEXIBLE),
|
attr.markIsRaw(javaType.isRaw),
|
||||||
|
typeParameterUpperBoundEraser,
|
||||||
erasedUpperBound
|
erasedUpperBound
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -309,42 +310,3 @@ class JavaTypeResolver(
|
|||||||
return !isForAnnotationParameter && howThisTypeIsUsed != SUPERTYPE
|
return !isForAnnotationParameter && howThisTypeIsUsed != SUPERTYPE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun makeStarProjection(
|
|
||||||
typeParameter: TypeParameterDescriptor,
|
|
||||||
attr: JavaTypeAttributes
|
|
||||||
): TypeProjection {
|
|
||||||
return if (attr.howThisTypeIsUsed == SUPERTYPE)
|
|
||||||
TypeProjectionImpl(typeParameter.starProjectionType())
|
|
||||||
else
|
|
||||||
StarProjectionImpl(typeParameter)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class JavaTypeAttributes(
|
|
||||||
val howThisTypeIsUsed: TypeUsage,
|
|
||||||
val flexibility: JavaTypeFlexibility = INFLEXIBLE,
|
|
||||||
val isForAnnotationParameter: Boolean = false,
|
|
||||||
// we use it to prevent happening a recursion while compute type parameter's upper bounds
|
|
||||||
val visitedTypeParameters: Set<TypeParameterDescriptor>? = null,
|
|
||||||
val defaultType: SimpleType? = null
|
|
||||||
) {
|
|
||||||
fun withFlexibility(flexibility: JavaTypeFlexibility) = copy(flexibility = flexibility)
|
|
||||||
fun withDefaultType(type: SimpleType?) = copy(defaultType = type)
|
|
||||||
fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) =
|
|
||||||
copy(visitedTypeParameters = if (visitedTypeParameters != null) visitedTypeParameters + typeParameter else setOf(typeParameter))
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class JavaTypeFlexibility {
|
|
||||||
INFLEXIBLE,
|
|
||||||
FLEXIBLE_UPPER_BOUND,
|
|
||||||
FLEXIBLE_LOWER_BOUND
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TypeUsage.toAttributes(
|
|
||||||
isForAnnotationParameter: Boolean = false,
|
|
||||||
upperBoundForTypeParameter: TypeParameterDescriptor? = null
|
|
||||||
) = JavaTypeAttributes(
|
|
||||||
this,
|
|
||||||
isForAnnotationParameter = isForAnnotationParameter,
|
|
||||||
visitedTypeParameters = upperBoundForTypeParameter?.let(::setOf)
|
|
||||||
)
|
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.TypeParameterUpperBoundEraser
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection
|
||||||
|
|
||||||
|
class RawProjectionComputer : ErasureProjectionComputer() {
|
||||||
|
override fun computeProjection(
|
||||||
|
parameter: TypeParameterDescriptor,
|
||||||
|
typeAttr: ErasureTypeAttributes,
|
||||||
|
typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser,
|
||||||
|
erasedUpperBound: KotlinType
|
||||||
|
): TypeProjection {
|
||||||
|
if (typeAttr !is JavaTypeAttributes) {
|
||||||
|
return super.computeProjection(parameter, typeAttr, typeParameterUpperBoundEraser, erasedUpperBound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if erasure happens due to invalid arguments number, use star projections instead
|
||||||
|
val newTypeAttr = if (typeAttr.isRaw) typeAttr else typeAttr.withFlexibility(JavaTypeFlexibility.INFLEXIBLE)
|
||||||
|
|
||||||
|
return when (newTypeAttr.flexibility) {
|
||||||
|
// Raw(List<T>) => (List<Any?>..List<*>)
|
||||||
|
// Raw(Enum<T>) => (Enum<Enum<*>>..Enum<out Enum<*>>)
|
||||||
|
// In the last case upper bound is equal to star projection `Enum<*>`,
|
||||||
|
// but we want to keep matching tree structure of flexible bounds (at least they should have the same size)
|
||||||
|
JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND -> TypeProjectionImpl(
|
||||||
|
// T : String -> String
|
||||||
|
// in T : String -> String
|
||||||
|
// T : Enum<T> -> Enum<*>
|
||||||
|
Variance.INVARIANT, erasedUpperBound
|
||||||
|
)
|
||||||
|
JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND, JavaTypeFlexibility.INFLEXIBLE -> {
|
||||||
|
if (!parameter.variance.allowsOutPosition) {
|
||||||
|
// in T -> Comparable<Nothing>
|
||||||
|
TypeProjectionImpl(Variance.INVARIANT, parameter.builtIns.nothingType)
|
||||||
|
} else if (erasedUpperBound.constructor.parameters.isNotEmpty()) {
|
||||||
|
// T : Enum<E> -> out Enum<*>
|
||||||
|
TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound)
|
||||||
|
} else {
|
||||||
|
// T : String -> *
|
||||||
|
makeStarProjection(parameter, newTypeAttr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.types.TypeUsage
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||||
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.TypeParameterUpperBoundEraser
|
||||||
|
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
||||||
|
import org.jetbrains.kotlin.types.error.ErrorUtils
|
||||||
|
|
||||||
|
internal class RawSubstitution(typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser? = null) : TypeSubstitution() {
|
||||||
|
private val projectionComputer = RawProjectionComputer()
|
||||||
|
private val typeParameterUpperBoundEraser = typeParameterUpperBoundEraser ?: TypeParameterUpperBoundEraser(projectionComputer)
|
||||||
|
|
||||||
|
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
|
||||||
|
|
||||||
|
private fun eraseType(type: KotlinType, attr: JavaTypeAttributes = JavaTypeAttributes(TypeUsage.COMMON)): KotlinType {
|
||||||
|
return when (val declaration = type.constructor.declarationDescriptor) {
|
||||||
|
is TypeParameterDescriptor ->
|
||||||
|
eraseType(typeParameterUpperBoundEraser.getErasedUpperBound(declaration, attr.markIsRaw(true)), attr)
|
||||||
|
is ClassDescriptor -> {
|
||||||
|
val declarationForUpper =
|
||||||
|
type.upperIfFlexible().constructor.declarationDescriptor
|
||||||
|
|
||||||
|
check(declarationForUpper is ClassDescriptor) {
|
||||||
|
"For some reason declaration for upper bound is not a class " +
|
||||||
|
"but \"$declarationForUpper\" while for lower it's \"$declaration\""
|
||||||
|
}
|
||||||
|
|
||||||
|
val (lower, isRawL) = eraseInflexibleBasedOnClassDescriptor(type.lowerIfFlexible(), declaration, lowerTypeAttr)
|
||||||
|
val (upper, isRawU) = eraseInflexibleBasedOnClassDescriptor(type.upperIfFlexible(), declarationForUpper, upperTypeAttr)
|
||||||
|
|
||||||
|
if (isRawL || isRawU) {
|
||||||
|
RawTypeImpl(lower, upper)
|
||||||
|
} else {
|
||||||
|
KotlinTypeFactory.flexibleType(lower, upper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> error("Unexpected declaration kind: $declaration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// false means that type cannot be raw
|
||||||
|
private fun eraseInflexibleBasedOnClassDescriptor(
|
||||||
|
type: SimpleType, declaration: ClassDescriptor, attr: JavaTypeAttributes
|
||||||
|
): Pair<SimpleType, Boolean> {
|
||||||
|
if (type.constructor.parameters.isEmpty()) return type to false
|
||||||
|
|
||||||
|
if (KotlinBuiltIns.isArray(type)) {
|
||||||
|
val componentTypeProjection = type.arguments[0]
|
||||||
|
val arguments = listOf(
|
||||||
|
TypeProjectionImpl(componentTypeProjection.projectionKind, eraseType(componentTypeProjection.type, attr))
|
||||||
|
)
|
||||||
|
return KotlinTypeFactory.simpleType(
|
||||||
|
type.attributes, type.constructor, arguments, type.isMarkedNullable
|
||||||
|
) to false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.isError) {
|
||||||
|
return ErrorUtils.createErrorType(ErrorTypeKind.ERROR_RAW_TYPE, type.constructor.toString()) to false
|
||||||
|
}
|
||||||
|
|
||||||
|
val memberScope = declaration.getMemberScope(this)
|
||||||
|
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
|
||||||
|
type.attributes, declaration.typeConstructor,
|
||||||
|
declaration.typeConstructor.parameters.map { parameter ->
|
||||||
|
projectionComputer.computeProjection(parameter, attr, typeParameterUpperBoundEraser)
|
||||||
|
},
|
||||||
|
type.isMarkedNullable, memberScope
|
||||||
|
) factory@{ kotlinTypeRefiner ->
|
||||||
|
val classId = (declaration as? ClassDescriptor)?.classId ?: return@factory null
|
||||||
|
|
||||||
|
@OptIn(TypeRefinement::class)
|
||||||
|
val refinedClassDescriptor = kotlinTypeRefiner.findClassAcrossModuleDependencies(classId) ?: return@factory null
|
||||||
|
if (refinedClassDescriptor == declaration) return@factory null
|
||||||
|
|
||||||
|
eraseInflexibleBasedOnClassDescriptor(type, refinedClassDescriptor, attr).first
|
||||||
|
} to true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isEmpty() = false
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val lowerTypeAttr = TypeUsage.COMMON.toAttributes(isRaw = true).withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND)
|
||||||
|
private val upperTypeAttr = TypeUsage.COMMON.toAttributes(isRaw = true).withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,21 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.load.java.lazy.types
|
package org.jetbrains.kotlin.load.java.lazy.types
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
|
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner
|
import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner
|
||||||
import org.jetbrains.kotlin.types.TypeRefinement
|
import org.jetbrains.kotlin.types.TypeRefinement
|
||||||
import org.jetbrains.kotlin.types.error.ErrorUtils
|
|
||||||
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||||
|
|
||||||
class RawTypeImpl private constructor(lowerBound: SimpleType, upperBound: SimpleType, disableAssertion: Boolean) :
|
class RawTypeImpl private constructor(lowerBound: SimpleType, upperBound: SimpleType, disableAssertion: Boolean) :
|
||||||
@@ -102,107 +95,3 @@ class RawTypeImpl private constructor(lowerBound: SimpleType, upperBound: Simple
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class RawSubstitution(typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser? = null) : TypeSubstitution() {
|
|
||||||
private val typeParameterUpperBoundEraser = typeParameterUpperBoundEraser ?: TypeParameterUpperBoundEraser(this)
|
|
||||||
|
|
||||||
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
|
|
||||||
|
|
||||||
private fun eraseType(type: KotlinType, attr: JavaTypeAttributes = JavaTypeAttributes(TypeUsage.COMMON)): KotlinType {
|
|
||||||
return when (val declaration = type.constructor.declarationDescriptor) {
|
|
||||||
is TypeParameterDescriptor ->
|
|
||||||
eraseType(typeParameterUpperBoundEraser.getErasedUpperBound(declaration, isRaw = true, attr), attr)
|
|
||||||
is ClassDescriptor -> {
|
|
||||||
val declarationForUpper =
|
|
||||||
type.upperIfFlexible().constructor.declarationDescriptor
|
|
||||||
|
|
||||||
check(declarationForUpper is ClassDescriptor) {
|
|
||||||
"For some reason declaration for upper bound is not a class " +
|
|
||||||
"but \"$declarationForUpper\" while for lower it's \"$declaration\""
|
|
||||||
}
|
|
||||||
|
|
||||||
val (lower, isRawL) = eraseInflexibleBasedOnClassDescriptor(type.lowerIfFlexible(), declaration, lowerTypeAttr)
|
|
||||||
val (upper, isRawU) = eraseInflexibleBasedOnClassDescriptor(type.upperIfFlexible(), declarationForUpper, upperTypeAttr)
|
|
||||||
|
|
||||||
if (isRawL || isRawU) {
|
|
||||||
RawTypeImpl(lower, upper)
|
|
||||||
} else {
|
|
||||||
KotlinTypeFactory.flexibleType(lower, upper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("Unexpected declaration kind: $declaration")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// false means that type cannot be raw
|
|
||||||
private fun eraseInflexibleBasedOnClassDescriptor(
|
|
||||||
type: SimpleType, declaration: ClassDescriptor, attr: JavaTypeAttributes
|
|
||||||
): Pair<SimpleType, Boolean> {
|
|
||||||
if (type.constructor.parameters.isEmpty()) return type to false
|
|
||||||
|
|
||||||
if (KotlinBuiltIns.isArray(type)) {
|
|
||||||
val componentTypeProjection = type.arguments[0]
|
|
||||||
val arguments = listOf(
|
|
||||||
TypeProjectionImpl(componentTypeProjection.projectionKind, eraseType(componentTypeProjection.type, attr))
|
|
||||||
)
|
|
||||||
return KotlinTypeFactory.simpleType(
|
|
||||||
type.attributes, type.constructor, arguments, type.isMarkedNullable
|
|
||||||
) to false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.isError) {
|
|
||||||
return ErrorUtils.createErrorType(ErrorTypeKind.ERROR_RAW_TYPE, type.constructor.toString()) to false
|
|
||||||
}
|
|
||||||
|
|
||||||
val memberScope = declaration.getMemberScope(this)
|
|
||||||
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
|
|
||||||
type.attributes, declaration.typeConstructor,
|
|
||||||
declaration.typeConstructor.parameters.map { parameter ->
|
|
||||||
computeProjection(parameter, attr)
|
|
||||||
},
|
|
||||||
type.isMarkedNullable, memberScope
|
|
||||||
) factory@{ kotlinTypeRefiner ->
|
|
||||||
val classId = (declaration as? ClassDescriptor)?.classId ?: return@factory null
|
|
||||||
@OptIn(TypeRefinement::class)
|
|
||||||
val refinedClassDescriptor = kotlinTypeRefiner.findClassAcrossModuleDependencies(classId) ?: return@factory null
|
|
||||||
if (refinedClassDescriptor == declaration) return@factory null
|
|
||||||
|
|
||||||
eraseInflexibleBasedOnClassDescriptor(type, refinedClassDescriptor, attr).first
|
|
||||||
} to true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computeProjection(
|
|
||||||
parameter: TypeParameterDescriptor,
|
|
||||||
attr: JavaTypeAttributes,
|
|
||||||
erasedUpperBound: KotlinType = typeParameterUpperBoundEraser.getErasedUpperBound(parameter, isRaw = true, attr)
|
|
||||||
) = when (attr.flexibility) {
|
|
||||||
// Raw(List<T>) => (List<Any?>..List<*>)
|
|
||||||
// Raw(Enum<T>) => (Enum<Enum<*>>..Enum<out Enum<*>>)
|
|
||||||
// In the last case upper bound is equal to star projection `Enum<*>`,
|
|
||||||
// but we want to keep matching tree structure of flexible bounds (at least they should have the same size)
|
|
||||||
JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND -> TypeProjectionImpl(
|
|
||||||
// T : String -> String
|
|
||||||
// in T : String -> String
|
|
||||||
// T : Enum<T> -> Enum<*>
|
|
||||||
Variance.INVARIANT, erasedUpperBound
|
|
||||||
)
|
|
||||||
JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND, JavaTypeFlexibility.INFLEXIBLE -> {
|
|
||||||
if (!parameter.variance.allowsOutPosition)
|
|
||||||
// in T -> Comparable<Nothing>
|
|
||||||
TypeProjectionImpl(Variance.INVARIANT, parameter.builtIns.nothingType)
|
|
||||||
else if (erasedUpperBound.constructor.parameters.isNotEmpty())
|
|
||||||
// T : Enum<E> -> out Enum<*>
|
|
||||||
TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound)
|
|
||||||
else
|
|
||||||
// T : String -> *
|
|
||||||
makeStarProjection(parameter, attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isEmpty() = false
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val lowerTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND)
|
|
||||||
private val upperTypeAttr = TypeUsage.COMMON.toAttributes().withFlexibility(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
-131
@@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.error.ErrorUtils
|
|
||||||
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.extractTypeParametersFromUpperBounds
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjectionOrMapped
|
|
||||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
|
||||||
|
|
||||||
internal class TypeParameterUpperBoundEraser(rawSubstitution: RawSubstitution? = null) {
|
|
||||||
private val storage = LockBasedStorageManager("Type parameter upper bound erasion results")
|
|
||||||
private val erroneousErasedBound by lazy {
|
|
||||||
ErrorUtils.createErrorType(ErrorTypeKind.CANNOT_COMPUTE_ERASED_BOUND, this.toString())
|
|
||||||
}
|
|
||||||
private val rawSubstitution = rawSubstitution ?: RawSubstitution(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.types
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
|
||||||
|
open class ErasureProjectionComputer {
|
||||||
|
open fun computeProjection(
|
||||||
|
parameter: TypeParameterDescriptor,
|
||||||
|
typeAttr: ErasureTypeAttributes,
|
||||||
|
typeParameterUpperBoundEraser: TypeParameterUpperBoundEraser,
|
||||||
|
erasedUpperBound: KotlinType = typeParameterUpperBoundEraser.getErasedUpperBound(parameter, typeAttr)
|
||||||
|
): TypeProjection = TypeProjectionImpl(Variance.OUT_VARIANCE, erasedUpperBound)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.types
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
|
||||||
|
open class ErasureTypeAttributes(
|
||||||
|
// we use it to prevent happening a recursion while compute type parameter's upper bounds
|
||||||
|
open val howThisTypeIsUsed: TypeUsage,
|
||||||
|
open val visitedTypeParameters: Set<TypeParameterDescriptor>? = null,
|
||||||
|
open val defaultType: SimpleType? = null
|
||||||
|
) {
|
||||||
|
open fun withDefaultType(type: SimpleType?) = ErasureTypeAttributes(howThisTypeIsUsed, visitedTypeParameters, defaultType = type)
|
||||||
|
|
||||||
|
open fun withNewVisitedTypeParameter(typeParameter: TypeParameterDescriptor) =
|
||||||
|
ErasureTypeAttributes(
|
||||||
|
howThisTypeIsUsed,
|
||||||
|
visitedTypeParameters = visitedTypeParameters?.let { it + typeParameter } ?: setOf(typeParameter),
|
||||||
|
defaultType
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is ErasureTypeAttributes) return false
|
||||||
|
return other.defaultType == this.defaultType && other.howThisTypeIsUsed == this.howThisTypeIsUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = defaultType.hashCode()
|
||||||
|
result += 31 * result + howThisTypeIsUsed.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.types
|
||||||
|
|
||||||
|
class TypeParameterErasureOptions(
|
||||||
|
val leaveNonTypeParameterTypes: Boolean,
|
||||||
|
val intersectUpperBounds: Boolean,
|
||||||
|
)
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* 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.types
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils.makeStarProjection
|
||||||
|
import org.jetbrains.kotlin.types.checker.intersectTypes
|
||||||
|
import org.jetbrains.kotlin.types.error.ErrorUtils
|
||||||
|
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.*
|
||||||
|
|
||||||
|
class TypeParameterUpperBoundEraser(
|
||||||
|
val projectionComputer: ErasureProjectionComputer,
|
||||||
|
val options: TypeParameterErasureOptions = TypeParameterErasureOptions(leaveNonTypeParameterTypes = false, intersectUpperBounds = false)
|
||||||
|
) {
|
||||||
|
private val storage = LockBasedStorageManager("Type parameter upper bound erasure results")
|
||||||
|
|
||||||
|
private val erroneousErasedBound by lazy {
|
||||||
|
ErrorUtils.createErrorType(ErrorTypeKind.CANNOT_COMPUTE_ERASED_BOUND, this.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class DataToEraseUpperBound(
|
||||||
|
val typeParameter: TypeParameterDescriptor,
|
||||||
|
val typeAttr: ErasureTypeAttributes
|
||||||
|
) {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is DataToEraseUpperBound) return false
|
||||||
|
return other.typeParameter == this.typeParameter && other.typeAttr == this.typeAttr
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = typeParameter.hashCode()
|
||||||
|
result += 31 * result + typeAttr.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val getErasedUpperBound = storage.createMemoizedFunction<DataToEraseUpperBound, KotlinType> {
|
||||||
|
with(it) { getErasedUpperBoundInternal(typeParameter, typeAttr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getErasedUpperBound(
|
||||||
|
typeParameter: TypeParameterDescriptor,
|
||||||
|
typeAttr: ErasureTypeAttributes
|
||||||
|
): KotlinType = getErasedUpperBound(DataToEraseUpperBound(typeParameter, typeAttr))
|
||||||
|
|
||||||
|
private fun getDefaultType(typeAttr: ErasureTypeAttributes) =
|
||||||
|
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,
|
||||||
|
typeAttr: ErasureTypeAttributes
|
||||||
|
): 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 erasedTypeParameters = typeParameter.defaultType.extractTypeParametersFromUpperBounds(visitedTypeParameters).associate {
|
||||||
|
val boundProjection = if (visitedTypeParameters == null || it !in visitedTypeParameters) {
|
||||||
|
projectionComputer.computeProjection(
|
||||||
|
it,
|
||||||
|
typeAttr,
|
||||||
|
typeParameterUpperBoundEraser = this,
|
||||||
|
getErasedUpperBound(it, typeAttr.withNewVisitedTypeParameter(typeParameter))
|
||||||
|
)
|
||||||
|
} else makeStarProjection(it, typeAttr)
|
||||||
|
|
||||||
|
it.typeConstructor to boundProjection
|
||||||
|
}
|
||||||
|
val erasedTypeParametersSubstitutor =
|
||||||
|
TypeSubstitutor.create(TypeConstructorSubstitution.createByConstructorsMap(erasedTypeParameters))
|
||||||
|
val erasedUpperBounds =
|
||||||
|
erasedTypeParametersSubstitutor.substituteErasedUpperBounds(typeParameter.upperBounds, typeAttr)
|
||||||
|
|
||||||
|
if (erasedUpperBounds.isNotEmpty()) {
|
||||||
|
if (!options.intersectUpperBounds) {
|
||||||
|
require(erasedUpperBounds.size == 1) { "Should only be one computed upper bound if no need to intersect all bounds" }
|
||||||
|
return erasedUpperBounds.single()
|
||||||
|
} else {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return intersectTypes(erasedUpperBounds.toList().map { it.unwrap() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultType(typeAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TypeSubstitutor.substituteErasedUpperBounds(
|
||||||
|
upperBounds: List<KotlinType>,
|
||||||
|
typeAttr: ErasureTypeAttributes
|
||||||
|
): Set<KotlinType> = buildSet {
|
||||||
|
for (upperBound in upperBounds) {
|
||||||
|
when (val declaration = upperBound.constructor.declarationDescriptor) {
|
||||||
|
is ClassDescriptor -> add(
|
||||||
|
upperBound.replaceArgumentsOfUpperBound(
|
||||||
|
substitutor = this@substituteErasedUpperBounds,
|
||||||
|
typeAttr.visitedTypeParameters,
|
||||||
|
options.leaveNonTypeParameterTypes
|
||||||
|
)
|
||||||
|
)
|
||||||
|
is TypeParameterDescriptor -> {
|
||||||
|
if (typeAttr.visitedTypeParameters?.contains(declaration) == true) {
|
||||||
|
add(getDefaultType(typeAttr))
|
||||||
|
} else {
|
||||||
|
addAll(substituteErasedUpperBounds(declaration.upperBounds, typeAttr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no need to intersect, take only the first bound
|
||||||
|
if (!options.intersectUpperBounds) break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun KotlinType.replaceArgumentsOfUpperBound(
|
||||||
|
substitutor: TypeSubstitutor,
|
||||||
|
visitedTypeParameters: Set<TypeParameterDescriptor>?,
|
||||||
|
leaveNonTypeParameterTypes: Boolean = false
|
||||||
|
): KotlinType {
|
||||||
|
val replacedArguments = replaceArgumentsByParametersWith { typeParameterDescriptor ->
|
||||||
|
val argument = arguments.getOrNull(typeParameterDescriptor.index)
|
||||||
|
if (leaveNonTypeParameterTypes && argument?.type?.containsTypeParameter() == false)
|
||||||
|
return@replaceArgumentsByParametersWith argument
|
||||||
|
|
||||||
|
val isTypeParameterVisited = visitedTypeParameters != null && typeParameterDescriptor in visitedTypeParameters
|
||||||
|
|
||||||
|
if (argument == null || isTypeParameterVisited || substitutor.substitution[argument.type] == null) {
|
||||||
|
StarProjectionImpl(typeParameterDescriptor)
|
||||||
|
} else {
|
||||||
|
argument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return substitutor.safeSubstitute(replacedArguments, Variance.OUT_VARIANCE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.types
|
||||||
|
|
||||||
|
enum class TypeUsage {
|
||||||
|
SUPERTYPE, COMMON
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ import org.jetbrains.kotlin.utils.SmartSet;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jetbrains.kotlin.types.TypeUsage.SUPERTYPE;
|
||||||
|
|
||||||
public class TypeUtils {
|
public class TypeUtils {
|
||||||
public static final SimpleType DONT_CARE = ErrorUtils.createErrorType(ErrorTypeKind.DONT_CARE);
|
public static final SimpleType DONT_CARE = ErrorUtils.createErrorType(ErrorTypeKind.DONT_CARE);
|
||||||
public static final SimpleType CANNOT_INFER_FUNCTION_PARAM_TYPE =
|
public static final SimpleType CANNOT_INFER_FUNCTION_PARAM_TYPE =
|
||||||
@@ -480,6 +482,15 @@ public class TypeUtils {
|
|||||||
return new StarProjectionImpl(parameterDescriptor);
|
return new StarProjectionImpl(parameterDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static TypeProjection makeStarProjection(@NotNull TypeParameterDescriptor parameterDescriptor, ErasureTypeAttributes attr) {
|
||||||
|
if (attr.getHowThisTypeIsUsed() == SUPERTYPE) {
|
||||||
|
return new TypeProjectionImpl(StarProjectionImplKt.starProjectionType(parameterDescriptor));
|
||||||
|
} else {
|
||||||
|
return new StarProjectionImpl(parameterDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static KotlinType getDefaultPrimitiveNumberType(@NotNull IntegerValueTypeConstructor numberValueTypeConstructor) {
|
public static KotlinType getDefaultPrimitiveNumberType(@NotNull IntegerValueTypeConstructor numberValueTypeConstructor) {
|
||||||
KotlinType type = getDefaultPrimitiveNumberType(numberValueTypeConstructor.getSupertypes());
|
KotlinType type = getDefaultPrimitiveNumberType(numberValueTypeConstructor.getSupertypes());
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
|||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.checker.*
|
import org.jetbrains.kotlin.types.checker.*
|
||||||
import org.jetbrains.kotlin.types.error.ErrorType
|
import org.jetbrains.kotlin.types.error.ErrorType
|
||||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
|
||||||
import org.jetbrains.kotlin.types.model.TypeArgumentMarker
|
import org.jetbrains.kotlin.types.model.TypeArgumentMarker
|
||||||
import org.jetbrains.kotlin.types.model.TypeVariableTypeConstructorMarker
|
import org.jetbrains.kotlin.types.model.TypeVariableTypeConstructorMarker
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
@@ -250,21 +249,6 @@ private fun KotlinType.containsSelfTypeParameter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KotlinType.replaceArgumentsWithStarProjectionOrMapped(
|
|
||||||
substitutor: TypeSubstitutor,
|
|
||||||
substitutionMap: Map<TypeConstructor, TypeProjection>,
|
|
||||||
variance: Variance,
|
|
||||||
visitedTypeParameters: Set<TypeParameterDescriptor>?
|
|
||||||
) =
|
|
||||||
replaceArgumentsByParametersWith { typeParameterDescriptor ->
|
|
||||||
val argument = arguments.getOrNull(typeParameterDescriptor.index)
|
|
||||||
val isTypeParameterVisited = visitedTypeParameters != null && typeParameterDescriptor in visitedTypeParameters
|
|
||||||
if (!isTypeParameterVisited && argument != null && argument.type.constructor in substitutionMap) {
|
|
||||||
argument
|
|
||||||
} else StarProjectionImpl(typeParameterDescriptor)
|
|
||||||
}.let { substitutor.safeSubstitute(it, variance) }
|
|
||||||
|
|
||||||
|
|
||||||
inline fun KotlinType.replaceArgumentsByParametersWith(replacement: (TypeParameterDescriptor) -> TypeProjection): KotlinType {
|
inline fun KotlinType.replaceArgumentsByParametersWith(replacement: (TypeParameterDescriptor) -> TypeProjection): KotlinType {
|
||||||
val unwrapped = unwrap()
|
val unwrapped = unwrap()
|
||||||
return when (unwrapped) {
|
return when (unwrapped) {
|
||||||
|
|||||||
Reference in New Issue
Block a user