Introduce additional overridability rule
It works only for Java methods and it's purpose is Java overridability rules emulation, namely distinction of primitive types and their wrappers. For example `void foo(Integer x)` should not be an override for `void foo(int x)` #KT-11440 Fixed #KT-11389 Fixed
This commit is contained in:
+58
-2
@@ -16,15 +16,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.java
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithDifferentJvmName.sameAsRenamedInJvmBuiltin
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmType
|
||||
import org.jetbrains.kotlin.load.kotlin.forceSingleValueParameterBoxing
|
||||
import org.jetbrains.kotlin.load.kotlin.mapToJvmType
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isDocumentedAnnotation
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
|
||||
/**
|
||||
* This class contains Java-related overridability conditions that may force incompatibility
|
||||
@@ -39,6 +43,10 @@ class JavaIncompatibilityRulesOverridabilityCondition : ExternalOverridabilityCo
|
||||
return Result.INCOMPATIBLE
|
||||
}
|
||||
|
||||
if (doesJavaOverrideHaveIncompatibleValueParameterKinds(superDescriptor, subDescriptor)) {
|
||||
return Result.INCOMPATIBLE
|
||||
}
|
||||
|
||||
return Result.UNKNOWN
|
||||
}
|
||||
|
||||
@@ -84,5 +92,53 @@ class JavaIncompatibilityRulesOverridabilityCondition : ExternalOverridabilityCo
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun getContract() = ExternalOverridabilityCondition.Contract.CONFLICTS_ONLY
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Checks if any pair of corresponding value parameters has different type kinds, e.g. one is primitive and another is not
|
||||
*
|
||||
* As it comes from it's name it only checks overrides in Java classes
|
||||
*/
|
||||
fun doesJavaOverrideHaveIncompatibleValueParameterKinds(
|
||||
superDescriptor: CallableDescriptor,
|
||||
subDescriptor: CallableDescriptor
|
||||
): Boolean {
|
||||
if (subDescriptor !is JavaMethodDescriptor || superDescriptor !is FunctionDescriptor) return false
|
||||
assert(subDescriptor.valueParameters.size == superDescriptor.valueParameters.size) {
|
||||
"External overridability condition with CONFLICTS_ONLY should not be run with different value parameters size"
|
||||
}
|
||||
|
||||
for ((subParameter, superParameter) in subDescriptor.original.valueParameters.zip(superDescriptor.original.valueParameters)) {
|
||||
val isSubPrimitive = mapValueParameterType(subDescriptor, subParameter) is JvmType.Primitive
|
||||
val isSuperPrimitive = mapValueParameterType(superDescriptor, superParameter) is JvmType.Primitive
|
||||
|
||||
if (isSubPrimitive != isSuperPrimitive) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun mapValueParameterType(f: FunctionDescriptor, valueParameterDescriptor: ValueParameterDescriptor) =
|
||||
if (forceSingleValueParameterBoxing(f) || isPrimitiveCompareTo(f))
|
||||
valueParameterDescriptor.type.makeNullable().mapToJvmType()
|
||||
else
|
||||
valueParameterDescriptor.type.mapToJvmType()
|
||||
|
||||
// It's useful here to suppose that 'Int.compareTo(Int)' requires boxing of it's value parameter
|
||||
// As it happens in java.lang.Integer analogue
|
||||
// It only affects additional built-ins loading (see 'testLoadBuiltIns' tests)
|
||||
private fun isPrimitiveCompareTo(f: FunctionDescriptor): Boolean {
|
||||
if (f.valueParameters.size != 1) return false
|
||||
val classDescriptor =
|
||||
f.containingDeclaration as? ClassDescriptor ?: return false
|
||||
val parameterClass =
|
||||
f.valueParameters.single().type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
?: return false
|
||||
return KotlinBuiltIns.isPrimitiveClass(classDescriptor) && classDescriptor.fqNameSafe == parameterClass.fqNameSafe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -49,6 +49,7 @@ import org.jetbrains.kotlin.load.java.typeEnhancement.enhanceSignatures
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
|
||||
@@ -164,9 +165,12 @@ class LazyJavaClassMemberScope(
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.doesOverride(superDescriptor: CallableDescriptor): Boolean {
|
||||
return OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions(
|
||||
superDescriptor, this, /* checkReturnType = */ true
|
||||
).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
|
||||
val commonOverridabilityResult =
|
||||
OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions(superDescriptor, this, true).result
|
||||
|
||||
return commonOverridabilityResult == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE &&
|
||||
!JavaIncompatibilityRulesOverridabilityCondition.doesJavaOverrideHaveIncompatibleValueParameterKinds(
|
||||
superDescriptor, this)
|
||||
}
|
||||
|
||||
private fun PropertyDescriptor.findGetterOverride(
|
||||
|
||||
+20
-4
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.load.kotlin
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
||||
import org.jetbrains.kotlin.load.java.isFromJavaOrBuiltins
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
@@ -44,6 +46,17 @@ fun FunctionDescriptor.computeJvmDescriptor()
|
||||
}
|
||||
}.toString()
|
||||
|
||||
// Boxing is only necessary for 'remove(E): Boolean' of a MutableList<Int> implementation
|
||||
// Otherwise this method will clash with 'remove(I): E' also defined in the JDK interface (mapped to kotlin 'removeAt')
|
||||
fun forceSingleValueParameterBoxing(f: FunctionDescriptor): Boolean {
|
||||
if (f.isFromJavaOrBuiltins()) return false
|
||||
|
||||
if (f.name.asString() != "remove" ||
|
||||
(f.original.valueParameters.single().type.mapToJvmType() as? JvmType.Primitive)?.jvmPrimitiveType != JvmPrimitiveType.INT) return false
|
||||
|
||||
return BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f) != null
|
||||
}
|
||||
|
||||
// This method only returns not-null for class methods
|
||||
internal fun CallableDescriptor.computeJvmSignature(): String? = signatures {
|
||||
if (DescriptorUtils.isLocal(this@computeJvmSignature)) return null
|
||||
@@ -72,16 +85,19 @@ internal val ClassId.internalName: String
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendErasedType(type: KotlinType) {
|
||||
append(
|
||||
JvmTypeFactoryImpl.toString(
|
||||
mapType(type, JvmTypeFactoryImpl, TypeMappingMode.DEFAULT, TypeMappingConfigurationImpl, descriptorTypeWriter = null)))
|
||||
append(type.mapToJvmType())
|
||||
}
|
||||
|
||||
private sealed class JvmType {
|
||||
internal fun KotlinType.mapToJvmType() =
|
||||
mapType(this, JvmTypeFactoryImpl, TypeMappingMode.DEFAULT, TypeMappingConfigurationImpl, descriptorTypeWriter = null)
|
||||
|
||||
sealed class JvmType {
|
||||
// null means 'void'
|
||||
class Primitive(val jvmPrimitiveType: JvmPrimitiveType?) : JvmType()
|
||||
class Object(val internalName: String) : JvmType()
|
||||
class Array(val elementType: JvmType) : JvmType()
|
||||
|
||||
override fun toString() = JvmTypeFactoryImpl.toString(this)
|
||||
}
|
||||
|
||||
private object JvmTypeFactoryImpl : JvmTypeFactory<JvmType> {
|
||||
|
||||
Reference in New Issue
Block a user