Load special override as HIDDEN in case of signature clash
#KT-10151 Fixed
This commit is contained in:
+2
-1
@@ -1,2 +1,3 @@
|
||||
org.jetbrains.kotlin.load.java.FieldOverridabilityCondition
|
||||
org.jetbrains.kotlin.load.java.ErasedOverridabilityCondition
|
||||
org.jetbrains.kotlin.load.java.ErasedOverridabilityCondition
|
||||
org.jetbrains.kotlin.load.java.BuiltinOverridabilityCondition
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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.resolve.ExternalOverridabilityCondition
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition.Result
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isDocumentedAnnotation
|
||||
|
||||
class BuiltinOverridabilityCondition : ExternalOverridabilityCondition {
|
||||
override fun isOverridable(superDescriptor: CallableDescriptor, subDescriptor: CallableDescriptor, subClassDescriptor: ClassDescriptor?): Result {
|
||||
if (superDescriptor !is CallableMemberDescriptor || subDescriptor !is FunctionDescriptor || subDescriptor.isFromBuiltins()) {
|
||||
return Result.UNKNOWN
|
||||
}
|
||||
|
||||
if (!subDescriptor.name.sameAsBuiltinMethodWithErasedValueParameters && !subDescriptor.name.sameAsRenamedInJvmBuiltin) {
|
||||
return Result.UNKNOWN
|
||||
}
|
||||
|
||||
// This overridability condition checks two things:
|
||||
// 1. Method accidentally having the same signature as special builtin has does not supposed to be override for it in Java class
|
||||
// 2. In such Java class (with special signature clash) special builtin is loaded as hidden function with special signature, and
|
||||
// it should not override non-special method in further inheritance
|
||||
// See java.nio.Buffer
|
||||
|
||||
val overriddenBuiltin = superDescriptor.getOverriddenSpecialBuiltin()
|
||||
|
||||
// Checking second condition: special hidden override is not supposed to be an override to non-special irrelevant Java declaration
|
||||
val isOneOfDescriptorsHidden =
|
||||
subDescriptor.isHiddenToOvercomeSignatureClash != (superDescriptor as? FunctionDescriptor)?.isHiddenToOvercomeSignatureClash
|
||||
if ((overriddenBuiltin == null || !subDescriptor.isHiddenToOvercomeSignatureClash) && isOneOfDescriptorsHidden) {
|
||||
return Result.INCOMPATIBLE
|
||||
}
|
||||
|
||||
// If new containing class is not Java class or subDescriptor signature was artificially changed, use basic overridability rules
|
||||
if (subClassDescriptor !is JavaClassDescriptor || subDescriptor.initialSignatureDescriptor != null) {
|
||||
return Result.UNKNOWN
|
||||
}
|
||||
|
||||
// If current Java class has Kotlin super class with override of overriddenBuiltin, then common overridability rules can be applied
|
||||
// because of final special bridge generated in Kotlin super class
|
||||
if (subClassDescriptor.hasRealKotlinSuperClassWithOverrideOf(overriddenBuiltin ?: return Result.UNKNOWN)) return Result.UNKNOWN
|
||||
|
||||
// Here we know that something in Java with common signature is going to override some special builtin that is supposed to be
|
||||
// incompatible override
|
||||
return Result.INCOMPATIBLE
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -30,10 +30,7 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public final class DescriptorResolverUtils {
|
||||
private DescriptorResolverUtils() {
|
||||
@@ -47,7 +44,7 @@ public final class DescriptorResolverUtils {
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
@NotNull final ErrorReporter errorReporter
|
||||
) {
|
||||
final Set<D> result = new HashSet<D>();
|
||||
final Set<D> result = new LinkedHashSet<D>();
|
||||
|
||||
OverridingUtil.generateOverridesInFunctionGroup(
|
||||
name, membersFromSupertypes, membersFromCurrent, classDescriptor,
|
||||
|
||||
+46
-13
@@ -152,8 +152,8 @@ public class LazyJavaClassMemberScope(
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.doesOverride(superDescriptor: CallableDescriptor): Boolean {
|
||||
return OverridingUtil.DEFAULT.isOverridableByIncludingReturnType(
|
||||
superDescriptor, this
|
||||
return OverridingUtil.DEFAULT.isOverridableByWithoutExternalConditions(
|
||||
superDescriptor, this, /* checkReturnType = */ true
|
||||
).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
|
||||
}
|
||||
|
||||
@@ -214,7 +214,11 @@ public class LazyJavaClassMemberScope(
|
||||
val functionsFromSupertypes = getFunctionsFromSupertypes(name)
|
||||
|
||||
if (!name.sameAsRenamedInJvmBuiltin && !name.sameAsBuiltinMethodWithErasedValueParameters) {
|
||||
addFunctionFromSupertypes(result, name, functionsFromSupertypes.filter { isVisibleAsFunctionInCurrentClass(it) })
|
||||
// Simple fast path in case of name is not suspicious (i.e. name is not one of builtins that have different signature in Java)
|
||||
addFunctionFromSupertypes(
|
||||
result, name,
|
||||
functionsFromSupertypes.filter { isVisibleAsFunctionInCurrentClass(it) },
|
||||
isSpecialBuiltinName = false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -237,16 +241,33 @@ public class LazyJavaClassMemberScope(
|
||||
val visibleFunctionsFromSupertypes =
|
||||
functionsFromSupertypes.filter { isVisibleAsFunctionInCurrentClass(it) } + specialBuiltinsFromSuperTypes
|
||||
|
||||
addFunctionFromSupertypes(result, name, visibleFunctionsFromSupertypes)
|
||||
addFunctionFromSupertypes(result, name, visibleFunctionsFromSupertypes, isSpecialBuiltinName = true)
|
||||
}
|
||||
|
||||
private fun addFunctionFromSupertypes(
|
||||
result: MutableCollection<SimpleFunctionDescriptor>,
|
||||
name: Name,
|
||||
functionsFromSupertypes: Collection<SimpleFunctionDescriptor>
|
||||
functionsFromSupertypes: Collection<SimpleFunctionDescriptor>,
|
||||
isSpecialBuiltinName: Boolean
|
||||
) {
|
||||
result.addAll(DescriptorResolverUtils.resolveOverrides(
|
||||
name, functionsFromSupertypes, result, ownerDescriptor, c.components.errorReporter))
|
||||
|
||||
val additionalOverrides =
|
||||
DescriptorResolverUtils.resolveOverrides(name, functionsFromSupertypes, result, ownerDescriptor, c.components.errorReporter)
|
||||
|
||||
if (!isSpecialBuiltinName) {
|
||||
result.addAll(additionalOverrides)
|
||||
}
|
||||
else {
|
||||
val allDescriptors = result + additionalOverrides
|
||||
result.addAll(
|
||||
additionalOverrides.map {
|
||||
resolvedOverride ->
|
||||
val overriddenBuiltin = resolvedOverride.getOverriddenSpecialBuiltin()
|
||||
?: return@map resolvedOverride
|
||||
|
||||
resolvedOverride.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(overriddenBuiltin, allDescriptors)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOverriddenBuiltinMethods(
|
||||
@@ -259,14 +280,14 @@ public class LazyJavaClassMemberScope(
|
||||
for (descriptor in candidatesForOverride) {
|
||||
val overriddenBuiltin = descriptor.getOverriddenBuiltinWithDifferentJvmName() ?: continue
|
||||
|
||||
if (alreadyDeclaredFunctions.any { it.doesOverride(overriddenBuiltin) }) continue
|
||||
|
||||
val nameInJava = getJvmMethodNameIfSpecial(overriddenBuiltin)!!
|
||||
for (method in functions(Name.identifier(nameInJava))) {
|
||||
val renamedCopy = method.createRenamedCopy(name)
|
||||
|
||||
if (isOverridableRenamedDescriptor(overriddenBuiltin, renamedCopy)) {
|
||||
result.add(renamedCopy)
|
||||
result.add(
|
||||
renamedCopy.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(overriddenBuiltin, alreadyDeclaredFunctions))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,17 +297,29 @@ public class LazyJavaClassMemberScope(
|
||||
BuiltinMethodsWithSpecialGenericSignature.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(descriptor)
|
||||
?: continue
|
||||
|
||||
if (alreadyDeclaredFunctions.any { it.doesOverride(overriddenBuiltin) }) continue
|
||||
|
||||
createOverrideForBuiltinFunctionWithErasedParameterIfNeeded(overriddenBuiltin, functions)?.let {
|
||||
override ->
|
||||
if (isVisibleAsFunctionInCurrentClass(override)) {
|
||||
result.add(override)
|
||||
result.add(override.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(overriddenBuiltin, alreadyDeclaredFunctions))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In case when Java has declaration with signature reflecting one of special builtin we load override of builtin as hidden function
|
||||
// Unless we do it then signature clash happens.
|
||||
// For example see java.nio.CharBuffer implementing CharSequence and defining irrelevant 'get' method having the same signature as in kotlin.CharSequence
|
||||
// We load java.nio.CharBuffer as having both 'get' functions, but one that is override of kotlin.CharSequence is hidden,
|
||||
// so when someone calls CharBuffer.get it results in invoking java method CharBuffer.get
|
||||
// But we still have the way to call 'charAt' java method by upcasting CharBuffer to kotlin.CharSequence
|
||||
private fun SimpleFunctionDescriptor.createHiddenCopyIfBuiltinAlreadyAccidentallyOverridden(
|
||||
specialBuiltin: CallableDescriptor,
|
||||
alreadyDeclaredFunctions: Collection<SimpleFunctionDescriptor>
|
||||
) = if (alreadyDeclaredFunctions.none { this != it && it.doesOverride(specialBuiltin) })
|
||||
this
|
||||
else
|
||||
createHiddenCopyToOvercomeSignatureClash()
|
||||
|
||||
private fun createOverrideForBuiltinFunctionWithErasedParameterIfNeeded(
|
||||
overridden: FunctionDescriptor,
|
||||
functions: (Name) -> Collection<SimpleFunctionDescriptor>
|
||||
|
||||
+2
-2
@@ -286,10 +286,10 @@ public fun ClassDescriptor.hasRealKotlinSuperClassWithOverrideOf(
|
||||
}
|
||||
|
||||
// Util methods
|
||||
private val CallableMemberDescriptor.isFromJava: Boolean
|
||||
public val CallableMemberDescriptor.isFromJava: Boolean
|
||||
get() = propertyIfAccessor is JavaCallableMemberDescriptor && propertyIfAccessor.containingDeclaration is JavaClassDescriptor
|
||||
|
||||
private fun CallableMemberDescriptor.isFromBuiltins(): Boolean {
|
||||
public fun CallableMemberDescriptor.isFromBuiltins(): Boolean {
|
||||
val fqName = propertyIfAccessor.fqNameOrNull() ?: return false
|
||||
return fqName.toUnsafe().startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME) &&
|
||||
this.module == this.builtIns.builtInsModule
|
||||
|
||||
Reference in New Issue
Block a user