Remake Raw type representation.

(cherry picked from commit b21cede)
This commit is contained in:
Stanislav Erokhin
2016-05-31 16:22:33 +03:00
parent 04e97f5058
commit 203c4cd94d
42 changed files with 277 additions and 354 deletions
@@ -18,15 +18,14 @@ package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.lazy.types.RawSubstitution
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.types.RawTypeCapability
import org.jetbrains.kotlin.types.getCapability
import org.jetbrains.kotlin.types.unwrap
import org.jetbrains.kotlin.utils.singletonOrEmptyList
class ErasedOverridabilityCondition : ExternalOverridabilityCondition {
@@ -40,7 +39,7 @@ class ErasedOverridabilityCondition : ExternalOverridabilityCondition {
subDescriptor.returnType!! +
subDescriptor.extensionReceiverParameter?.type.singletonOrEmptyList()
if (signatureTypes.any { it.arguments.isNotEmpty() && it.getCapability<RawTypeCapability>() == null }) return Result.UNKNOWN
if (signatureTypes.any { it.arguments.isNotEmpty() && it.unwrap() !is RawTypeImpl }) return Result.UNKNOWN
var erasedSuper = superDescriptor.substitute(RawSubstitution.buildSubstitutor()) ?: return Result.UNKNOWN
@@ -138,9 +138,8 @@ class LazyJavaAnnotationDescriptor(
val arguments = listOf(TypeProjectionImpl(type))
val javaClassObjectType = object : AbstractLazyType(c.storageManager) {
override fun computeTypeConstructor() = jlClass.getTypeConstructor()
override fun computeTypeConstructor() = jlClass.typeConstructor
override fun computeArguments() = arguments
override fun computeMemberScope() = jlClass.getMemberScope(arguments)
}
return factory.createKClassValue(javaClassObjectType)
@@ -54,13 +54,7 @@ class LazyJavaTypeResolver(
if (primitiveType != null) c.module.builtIns.getPrimitiveKotlinType(primitiveType)
else c.module.builtIns.getUnitType()
}
is JavaClassifierType ->
if (attr.allowFlexible && attr.howThisTypeIsUsed != SUPERTYPE)
KotlinTypeFactory.flexibleType(
LazyJavaClassifierType(javaType, attr.toFlexible(FLEXIBLE_LOWER_BOUND)),
LazyJavaClassifierType(javaType, attr.toFlexible(FLEXIBLE_UPPER_BOUND))
)
else LazyJavaClassifierType(javaType, attr)
is JavaClassifierType -> transformJavaClassifierType(javaType, attr)
is JavaArrayType -> transformArrayType(javaType, attr)
// Top level type can be a wildcard only in case of broken Java code, but we should not fail with exceptions in such cases
is JavaWildcardType -> javaType.bound?.let { transformJavaType(it, attr) } ?: c.module.builtIns.defaultBound
@@ -68,6 +62,27 @@ class LazyJavaTypeResolver(
}
}
private fun transformJavaClassifierType(javaType: JavaClassifierType, attr: JavaTypeAttributes): KotlinType {
val allowFlexible = attr.allowFlexible && attr.howThisTypeIsUsed != SUPERTYPE
val lowerAttr = if (allowFlexible) attr.toFlexible(FLEXIBLE_LOWER_BOUND) else attr
val upperAttr = if (allowFlexible) attr.toFlexible(FLEXIBLE_UPPER_BOUND) else attr
return if (javaType.isRaw) {
RawTypeImpl(LazyJavaClassifierType(javaType, lowerAttr.toRawBound(RawBound.LOWER)),
LazyJavaClassifierType(javaType, upperAttr.toRawBound(RawBound.UPPER)))
}
else if (allowFlexible) {
KotlinTypeFactory.flexibleType(
LazyJavaClassifierType(javaType, lowerAttr),
LazyJavaClassifierType(javaType, upperAttr)
)
}
else {
LazyJavaClassifierType(javaType, attr)
}
}
fun transformArrayType(arrayType: JavaArrayType, attr: JavaTypeAttributes, isVararg: Boolean = false): KotlinType {
return run {
val javaComponentType = arrayType.componentType
@@ -176,19 +191,18 @@ class LazyJavaTypeResolver(
private fun JavaType?.isSuperWildcard(): Boolean = (this as? JavaWildcardType)?.let { it.bound != null && !it.isExtends } ?: false
private fun isRaw(): Boolean {
if (javaType.isRaw) return true
private fun eraseTypeParameters(): Boolean {
if (attr.rawBound != RawBound.NOT_RAW) return true
// This option is needed because sometimes we get weird versions of JDK classes in the class path,
// such as collections with no generics, so the Java types are not raw, formally, but they don't match with
// their Kotlin analogs, so we treat them as raw to avoid exceptions
// No type arguments, but some are expected => raw
return javaType.typeArguments.isEmpty() && !constructor.parameters.isEmpty()
}
override fun computeArguments(): List<TypeProjection> {
val typeParameters = constructor.parameters
if (isRaw()) {
if (eraseTypeParameters()) {
return typeParameters.map {
parameter ->
// Some activity for preventing recursion in cases like `class A<T extends A, F extends T>`
@@ -258,8 +272,6 @@ class LazyJavaTypeResolver(
return this != typeParameter.variance
}
override val capabilities: TypeCapabilities get() = if (isRaw()) RawTypeCapabilities else TypeCapabilities.NONE
private val nullable = c.storageManager.createLazyValue l@ {
if (attr.flexibility == FLEXIBLE_LOWER_BOUND) return@l false
if (attr.flexibility == FLEXIBLE_UPPER_BOUND) return@l true
@@ -306,6 +318,14 @@ interface JavaTypeAttributes {
// Current type is upper bound of this type parameter
val upperBoundOfTypeParameter: TypeParameterDescriptor?
get() = null
val rawBound: RawBound
get() = RawBound.NOT_RAW
}
enum class RawBound {
LOWER,
UPPER,
NOT_RAW
}
enum class JavaTypeFlexibility {
@@ -358,6 +378,11 @@ fun JavaTypeAttributes.toFlexible(flexibility: JavaTypeFlexibility) =
override val flexibility = flexibility
}
fun JavaTypeAttributes.toRawBound(rawBound: RawBound) =
object : JavaTypeAttributes by this {
override val rawBound = rawBound
}
// Definition:
// ErasedUpperBound(T : G<t>) = G<*> // UpperBound(T) is a type G<t> with arguments
@@ -19,128 +19,139 @@ 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.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererOptions
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
object RawTypeCapabilities : TypeCapabilities {
class RawTypeImpl(lowerBound: SimpleType, upperBound: SimpleType) : DelegatingFlexibleType(lowerBound, upperBound), RawType {
init {
assert (KotlinTypeChecker.DEFAULT.isSubtypeOf(lowerBound, upperBound)) {
"Lower bound $lowerBound of a flexible type must be a subtype of the upper bound $upperBound"
}
}
override fun getDelegate(): KotlinType = delegateType
private object Impl : RawTypeCapability {
override val substitution: TypeSubstitution?
get() = RawSubstitution
override val substitutionToComposeWith: TypeSubstitution?
get() = RawSubstitution
override val delegateType: KotlinType get() = lowerBound
private fun DescriptorRenderer.renderArguments(jetType: KotlinType) = jetType.arguments.map { renderTypeProjection(it) }
override val memberScope: MemberScope
get() {
val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor
?: error("Incorrect classifier: ${constructor.declarationDescriptor}")
return classDescriptor.getMemberScope(RawSubstitution)
}
private fun String.replaceArgs(newArgs: String): String {
override fun replaceAnnotations(newAnnotations: Annotations): KotlinType
= RawTypeImpl(lowerBound.replaceAnnotations(newAnnotations).asSimpleType(), upperBound)
override fun makeNullableAsSpecified(nullable: Boolean): KotlinType
= RawTypeImpl(TypeUtils.makeNullableAsSpecified(lowerBound, nullable), TypeUtils.makeNullableAsSpecified(upperBound, nullable))
override fun render(renderer: DescriptorRenderer, options: DescriptorRendererOptions): String {
fun onlyOutDiffers(first: String, second: String) = first == second.removePrefix("out ") || second == "*"
fun renderArguments(type: KotlinType) = type.arguments.map { renderer.renderTypeProjection(it) }
fun String.replaceArgs(newArgs: String): String {
if (!contains('<')) return this
return "${substringBefore('<')}<$newArgs>${substringAfterLast('>')}"
}
override fun renderInflexible(type: KotlinType, renderer: DescriptorRenderer): String? {
if (type.arguments.isNotEmpty()) return null
val lowerRendered = renderer.renderType(lowerBound)
val upperRendered = renderer.renderType(upperBound)
return buildString {
append(renderer.renderTypeConstructor(type.constructor))
append("(raw)")
if (type.isMarkedNullable) append('?')
}
if (options.debugMode) {
return "raw ($lowerRendered..$upperRendered)"
}
if (upperBound.arguments.isEmpty()) return renderer.renderFlexibleType(lowerRendered, upperRendered)
override fun renderBounds(flexibleType: FlexibleType, renderer: DescriptorRenderer): Pair<String, String>? {
val lowerArgs = renderer.renderArguments(flexibleType.lowerBound)
val upperArgs = renderer.renderArguments(flexibleType.upperBound)
val lowerRendered = renderer.renderType(flexibleType.lowerBound)
val upperRendered = renderer.renderType(flexibleType.upperBound)
if (!upperArgs.isNotEmpty()) return null
val newArgs = lowerArgs.map { "(raw) $it" }.joinToString(", ")
val newUpper =
if (lowerArgs.zip(upperArgs).all { onlyOutDiffers(it.first, it.second) })
upperRendered.replaceArgs(newArgs)
else upperRendered
return Pair(lowerRendered.replaceArgs(newArgs), newUpper)
}
private fun onlyOutDiffers(first: String, second: String) = first == second.removePrefix("out ") || second == "*"
}
override fun <T : TypeCapability> getCapability(capabilityClass: Class<T>): T? {
@Suppress("UNCHECKED_CAST")
return when(capabilityClass) {
RawTypeCapability::class.java -> Impl as T
else -> null
}
val lowerArgs = renderArguments(lowerBound)
val upperArgs = renderArguments(upperBound)
val newArgs = lowerArgs.map { "(raw) $it" }.joinToString(", ")
val newUpper =
if (lowerArgs.zip(upperArgs).all { onlyOutDiffers(it.first, it.second) })
upperRendered.replaceArgs(newArgs)
else upperRendered
val newLower = lowerRendered.replaceArgs(newArgs)
if (newLower == newUpper) return newLower
return renderer.renderFlexibleType(newLower, newUpper)
}
}
internal object RawSubstitution : TypeSubstitution() {
override fun get(key: KotlinType) = TypeProjectionImpl(eraseType(key))
private val lowerTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes().toFlexible(JavaTypeFlexibility.FLEXIBLE_LOWER_BOUND)
private val upperTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes().toFlexible(JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND)
private val lowerTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes().toRawBound(RawBound.LOWER)
private val upperTypeAttr = TypeUsage.MEMBER_SIGNATURE_INVARIANT.toAttributes().toRawBound(RawBound.UPPER)
fun eraseType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor
return when (declaration) {
is TypeParameterDescriptor -> eraseType(declaration.getErasedUpperBound())
is ClassDescriptor -> {
val lower = type.lowerIfFlexible()
val upper = type.upperIfFlexible()
KotlinTypeFactory.flexibleType(
eraseInflexibleBasedOnClassDescriptor(lower, declaration, lowerTypeAttr),
eraseInflexibleBasedOnClassDescriptor(upper, declaration, upperTypeAttr)
)
val (lower, isRawL) = eraseInflexibleBasedOnClassDescriptor(type.lowerIfFlexible(), declaration, lowerTypeAttr)
val (upper, isRawU) = eraseInflexibleBasedOnClassDescriptor(type.upperIfFlexible(), declaration, upperTypeAttr)
if (isRawL || isRawU) {
RawTypeImpl(lower, upper)
}
else {
KotlinTypeFactory.flexibleType(lower, upper)
}
}
else -> error("Unexpected declaration kind: $declaration")
}
}
private fun eraseInflexibleBasedOnClassDescriptor(type: KotlinType, declaration: ClassDescriptor, attr: JavaTypeAttributes): SimpleType {
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))
)
return KotlinTypeFactory.simpleType(type.annotations, type.constructor, arguments,
type.isMarkedNullable, (type.constructor.declarationDescriptor as ClassDescriptor).getMemberScope(arguments)
)
return KotlinTypeFactory.simpleType(
type.annotations, type.constructor, arguments, type.isMarkedNullable,
declaration.getMemberScope(arguments)
) to false
}
if (type.isError) return ErrorUtils.createErrorType("Raw error type: ${type.constructor}")
if (type.isError) return ErrorUtils.createErrorType("Raw error type: ${type.constructor}") to false
val constructor = type.constructor
return KotlinTypeImpl.create(
type.annotations, constructor, type.isMarkedNullable,
return KotlinTypeFactory.simpleType(
type.annotations, type.constructor,
type.constructor.parameters.map {
parameter ->
computeProjection(parameter, attr)
},
declaration.getMemberScope(RawSubstitution),
RawTypeCapabilities
)
type.isMarkedNullable, declaration.getMemberScope(RawSubstitution)
) to true
}
fun computeProjection(
parameter: TypeParameterDescriptor,
attr: JavaTypeAttributes,
erasedUpperBound: KotlinType = parameter.getErasedUpperBound()
) = when (attr.flexibility) {
) = when (attr.rawBound) {
// 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(
RawBound.LOWER -> TypeProjectionImpl(
// T : String -> String
// in T : String -> String
// T : Enum<T> -> Enum<*>
Variance.INVARIANT, erasedUpperBound
)
JavaTypeFlexibility.FLEXIBLE_UPPER_BOUND, JavaTypeFlexibility.INFLEXIBLE -> {
RawBound.UPPER, RawBound.NOT_RAW -> {
if (!parameter.variance.allowsOutPosition)
// in T -> Comparable<Nothing>
TypeProjectionImpl(Variance.INVARIANT, parameter.builtIns.nothingType)
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.typeEnhancement.MutabilityQualifier.READ_ONLY
import org.jetbrains.kotlin.load.java.typeEnhancement.NullabilityQualifier.NOT_NULL
@@ -56,7 +57,7 @@ private data class Result(val type: KotlinType, val subtreeSize: Int, val wereCh
}
private fun KotlinType.enhancePossiblyFlexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int): Result {
if (this.isError) return Result(this, 1, false)
if (this.isError || unwrap() is RawTypeImpl) return Result(this, 1, false) // todo: Raw
return if (this.isFlexible()) {
with(this.asFlexibleType()) {
val lowerResult = lowerBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_LOWER)
@@ -121,9 +122,7 @@ private fun KotlinType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers
enhancedNullabilityAnnotations
).filterNotNull().compositeAnnotationsOrSingle()
val newSubstitution = computeNewSubstitution(
typeConstructor, enhancedArguments
)
val newSubstitution = TypeConstructorSubstitution.create(typeConstructor, enhancedArguments)
val newCapabilities =
if (effectiveQualifiers.isNotNullTypeParameter)
@@ -42,7 +42,7 @@ class DeserializationComponentsForJava(
components = DeserializationComponents(
storageManager, moduleDescriptor, classDataFinder, annotationAndConstantLoader, packageFragmentProvider, localClassResolver,
errorReporter, lookupTracker, JavaFlexibleTypeDeserializer, ClassDescriptorFactory.EMPTY,
notFoundClasses, JavaTypeCapabilitiesLoader,
notFoundClasses,
additionalClassPartsProvider = settings,
platformDependentDeclarationFilter = settings
)
@@ -16,7 +16,10 @@
package org.jetbrains.kotlin.load.kotlin
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
@@ -25,8 +28,11 @@ import org.jetbrains.kotlin.types.SimpleType
object JavaFlexibleTypeDeserializer : FlexibleTypeDeserializer {
val id = "kotlin.jvm.PlatformType"
override fun create(flexibleId: String, lowerBound: SimpleType, upperBound: SimpleType): KotlinType {
override fun create(proto: ProtoBuf.Type, flexibleId: String, lowerBound: SimpleType, upperBound: SimpleType): KotlinType {
if (flexibleId != id) return ErrorUtils.createErrorType("Error java flexible type with id: $flexibleId. ($lowerBound..$upperBound)")
if (proto.hasExtension(JvmProtoBuf.isRaw)) {
return RawTypeImpl(lowerBound, upperBound)
}
return KotlinTypeFactory.flexibleType(lowerBound, upperBound)
}
}
@@ -1,28 +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.kotlin
import org.jetbrains.kotlin.load.java.lazy.types.RawTypeCapabilities
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.TypeCapabilitiesLoader
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.types.TypeCapabilities
object JavaTypeCapabilitiesLoader : TypeCapabilitiesLoader() {
override fun loadCapabilities(type: ProtoBuf.Type): TypeCapabilities =
if (type.hasExtension(JvmProtoBuf.isRaw)) RawTypeCapabilities else TypeCapabilities.NONE
}