diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/signatureEnhancement.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/signatureEnhancement.kt index 84ea8d8fdc8..1b01e6993c3 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/signatureEnhancement.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/signatureEnhancement.kt @@ -16,14 +16,12 @@ package org.jetbrains.kotlin.load.java.components -import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor -import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.types.JetType fun enhanceSignatures(platformSignatures: Collection): Collection { @@ -79,7 +77,7 @@ fun D.enhance(): D { } fun > SignatureParts.enhance(): T { - val qualifiers = fromOverride.type.computeQualifiersForOverride(this.fromOverridden.map { it.type }, fromOverride.isCovariant) + val qualifiers = fromOverride.type.computeIndexedQualifiersForOverride(this.fromOverridden.map { it.type }, fromOverride.isCovariant) return fromOverride.replaceType(fromOverride.type.enhance(qualifiers)) } @@ -98,13 +96,13 @@ interface SignaturePart { } fun ReceiverParameterDescriptor.toPart() = object : SignaturePart { - override val type = getType() + override val type = this@toPart.getType() // workaround for KT-7557 override fun replaceType(newType: JetType) = newType } fun ValueParameterDescriptor.toPart() = object : SignaturePart { - override val type = getType() + override val type = this@toPart.getType() // workaround for KT-7557 override fun replaceType(newType: JetType) = ValueParameterDescriptorImpl( getContainingDeclaration(), @@ -114,7 +112,7 @@ fun ValueParameterDescriptor.toPart() = object : SignaturePart>`, indices go as follows: `0 - A<...>, 1 - B, 2 - C, 3 - D, 4 - E`, +// which corresponds to the left-to-right breadth-first walk of the tree representation of the type. +// For flexible types, both bounds are indexed in the same way: `(A..C)` gives `0 - (A..C), 1 - B and D`. +fun JetType.enhance(qualifiers: (Int) -> JavaTypeQualifiers) = this.enhancePossiblyFlexible(qualifiers, 0).type -fun JetType.enhance(qualifiers: JavaTypeQualifiers): JetType { - val mutabilityEnhanced = - if (this.isFlexible()) - this.flexibility().enhanceMutability(qualifiers.mutability) - else this - return mutabilityEnhanced.enhanceNullability(qualifiers.nullability) + +private enum class TypeComponentPosition { + FLEXIBLE_LOWER, + FLEXIBLE_UPPER, + INFLEXIBLE } -private fun Flexibility.enhanceMutability(qualifier: MutabilityQualifier?): JetType { +data class Result(val type: JetType, val subtreeSize: Int) + +private fun JetType.enhancePossiblyFlexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int): Result { + if (this.isError()) return Result(this, 1) + return if (this.isFlexible()) { + with(this.flexibility()) { + val lowerResult = lowerBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_LOWER) + val upperResult = upperBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_UPPER) + assert(lowerResult.subtreeSize == upperResult.subtreeSize) { + "Different tree sizes of bounds: " + + "lower = ($lowerBound, ${lowerResult.subtreeSize}), " + + "upper = ($upperBound, ${upperResult.subtreeSize})" + } + Result( + DelegatingFlexibleType.create(lowerResult.type, upperResult.type, extraCapabilities), lowerResult.subtreeSize + ) + } + } + else this.enhanceInflexible(qualifiers, index, TypeComponentPosition.INFLEXIBLE) +} + +private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int, position: TypeComponentPosition): Result { + val shouldEnhance = position.shouldEnhance() + if (!shouldEnhance && getArguments().isEmpty()) return Result(this, 1) + + val originalClass = getConstructor().getDeclarationDescriptor() as? ClassDescriptor + ?: return Result(this, 1) + + val effectiveQualifiers = qualifiers(index) + val enhancedClass = originalClass.enhanceMutability(effectiveQualifiers, position) + + var globalArgIndex = index + 1 + val enhancedArguments = getArguments().mapIndexed { + localArgIndex, arg -> + if (arg.isStarProjection()) { + globalArgIndex++ + TypeUtils.makeStarProjection(enhancedClass.getTypeConstructor().getParameters()[localArgIndex]) + } + else { + val (enhancedType, subtreeSize) = arg.getType().enhancePossiblyFlexible(qualifiers, globalArgIndex) + globalArgIndex += subtreeSize + TypeProjectionImpl( + arg.getProjectionKind(), + enhancedType + ) + } + } + + val enhancedType = JetTypeImpl( + getAnnotations(), + enhancedClass.getTypeConstructor(), + this.getEnhancedNullability(effectiveQualifiers, position), + enhancedArguments, + enhancedClass.getMemberScope(enhancedArguments) + ) + return Result(enhancedType, globalArgIndex - index) +} + +private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE + +private fun ClassDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): ClassDescriptor { + if (!position.shouldEnhance()) return this + val mapping = JavaToKotlinClassMap.INSTANCE - val (newLower, newUpper) = run { - when (qualifier) { - READ_ONLY -> { - val lowerClass = TypeUtils.getClassDescriptor(lowerBound) - if (lowerClass != null && mapping.isMutable(lowerClass)) { - return@run Pair(lowerBound.replaceClass(mapping.convertMutableToReadOnly(lowerClass)), upperBound) - } - } - MUTABLE -> { - val upperClass = TypeUtils.getClassDescriptor(upperBound) - if (upperClass != null && mapping.isReadOnly(upperClass) ) { - return@run Pair(lowerBound, upperBound.replaceClass(mapping.convertReadOnlyToMutable(upperClass))) - } + when (qualifiers.mutability) { + READ_ONLY -> { + if (position == TypeComponentPosition.FLEXIBLE_LOWER && mapping.isMutable(this)) { + return mapping.convertMutableToReadOnly(this) + } + } + MUTABLE -> { + if (position == TypeComponentPosition.FLEXIBLE_UPPER && mapping.isReadOnly(this) ) { + return mapping.convertReadOnlyToMutable(this) } } - return@run Pair(lowerBound, upperBound) } - return DelegatingFlexibleType.create(newLower, newUpper, extraCapabilities) + return this } -private fun JetType.enhanceNullability(qualifier: NullabilityQualifier?): JetType { - return when (qualifier) { - NULLABLE -> TypeUtils.makeNullable(this) - NOT_NULL -> TypeUtils.makeNotNullable(this) - else -> this +private fun JetType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): Boolean { + if (!position.shouldEnhance()) return this.isMarkedNullable() + + return when (qualifiers.nullability) { + NULLABLE -> true + NOT_NULL -> false + else -> this.isMarkedNullable() } } - -private fun JetType.replaceClass(newClass: ClassDescriptor): JetType { - assert(newClass.getTypeConstructor().getParameters().size() == getArguments().size(), - {"Can't replace type constructor ${getConstructor()} by ${newClass}: type parameter count does not match"}) - return JetTypeImpl( - getAnnotations(), - newClass.getTypeConstructor(), - isMarkedNullable(), - getArguments(), - newClass.getMemberScope(getArguments()) - ) -} \ No newline at end of file diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/typeQualifiers.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/typeQualifiers.kt index a95f22544c9..ff5bf35211c 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/typeQualifiers.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/components/typeQualifiers.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.load.java.components +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.load.java.components.MutabilityQualifier.MUTABLE @@ -27,6 +28,8 @@ import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.flexibility import org.jetbrains.kotlin.types.isFlexible +import org.jetbrains.kotlin.utils.getOrDefault +import java.util.ArrayList enum class NullabilityQualifier { NULLABLE, @@ -57,7 +60,11 @@ val MUTABLE_ANNOTATIONS = listOf( class JavaTypeQualifiers( val nullability: NullabilityQualifier?, val mutability: MutabilityQualifier? -) +) { + companion object { + val NONE = JavaTypeQualifiers(null, null) + } +} private fun JetType.extractQualifiers(): JavaTypeQualifiers { val (lower, upper) = @@ -82,7 +89,37 @@ private fun Annotations.extractQualifiers(): JavaTypeQualifiers { ) } -fun JetType.computeQualifiersForOverride(fromSupertypes: Collection, isCovariant: Boolean): JavaTypeQualifiers { +fun JetType.computeIndexedQualifiersForOverride(fromSupertypes: Collection, isCovariant: Boolean): (Int) -> JavaTypeQualifiers { + fun JetType.toIndexed(): List { + val list = ArrayList(1) + + fun add(type: JetType) { + list.add(type) + for (arg in type.getArguments()) { + if (arg.isStarProjection()) { + list.add(arg.getType()) + } + else { + add(arg.getType()) + } + } + } + + add(this) + return list + } + + val indexedFromSupertypes = fromSupertypes.map { it.toIndexed() } + val indexedThisType = this.toIndexed() + + return _r@ fun(index: Int): JavaTypeQualifiers { + val qualifiers = indexedThisType.getOrDefault(index, { return JavaTypeQualifiers.NONE }) + val verticalSlice = indexedFromSupertypes.map { it.getOrDefault(index, { null }) }.filterNotNull() + return qualifiers.computeQualifiersForOverride(verticalSlice, isCovariant) + } +} + +private fun JetType.computeQualifiersForOverride(fromSupertypes: Collection, isCovariant: Boolean): JavaTypeQualifiers { val nullabilityFromSupertypes = fromSupertypes.map { it.extractQualifiers().nullability }.filterNotNull().toSet() val mutabilityFromSupertypes = fromSupertypes.map { it.extractQualifiers().mutability }.filterNotNull().toSet() val own = getAnnotations().extractQualifiers() diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt index 4e97127de1d..64bd09855ce 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt @@ -105,3 +105,5 @@ public fun Collection.toReadOnlyList(): List = if (isEmpty()) Collections.emptyList() else ArrayList(this) public fun T?.singletonOrEmptyList(): List = if (this != null) Collections.singletonList(this) else Collections.emptyList() + +public inline fun List.getOrDefault(index: Int, default: (Int) -> T): T = if (index in 0..size() - 1) this[index] else default(index) \ No newline at end of file