Load special override as HIDDEN in case of signature clash

#KT-10151 Fixed
This commit is contained in:
Denis Zharkov
2015-12-09 14:09:38 +03:00
parent 45c0bc3610
commit 871fe7680b
40 changed files with 445 additions and 51 deletions
@@ -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
@@ -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
}
}
@@ -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,
@@ -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>
@@ -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
@@ -39,9 +39,19 @@ public interface FunctionDescriptor extends CallableMemberDescriptor {
@NotNull
Collection<? extends FunctionDescriptor> getOverriddenDescriptors();
/**
* @return descriptor that represents initial signature, e.g in case of result SimpleFunctionDescriptor.createRenamedCopy it returns
* descriptor before rename
*/
@Nullable
FunctionDescriptor getInitialSignatureDescriptor();
/**
* @return true if descriptor signature clashed with some other signature and it's supposed to be legal
* See java.nio.CharBuffer
*/
boolean isHiddenToOvercomeSignatureClash();
@NotNull
@Override
FunctionDescriptor copy(DeclarationDescriptor newOwner, Modality modality, Visibility visibility, Kind kind, boolean copyOverrides);
@@ -38,6 +38,9 @@ public interface SimpleFunctionDescriptor extends FunctionDescriptor {
@NotNull
SimpleFunctionDescriptor createCopyWithNewTypeParameters(@NotNull List<TypeParameterDescriptor> typeParameters);
@NotNull
SimpleFunctionDescriptor createHiddenCopyToOvercomeSignatureClash();
@NotNull
@Override
SimpleFunctionDescriptor getOriginal();
@@ -241,7 +241,7 @@ public class EnumEntrySyntheticClassDescriptor extends ClassDescriptorBase {
@NotNull Name name,
@NotNull Collection<D> fromSupertypes
) {
final Set<D> result = new HashSet<D>();
final Set<D> result = new LinkedHashSet<D>();
OverridingUtil.generateOverridesInFunctionGroup(
name, fromSupertypes, Collections.<D>emptySet(), EnumEntrySyntheticClassDescriptor.this,
@@ -47,6 +47,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
private boolean isExternal = false;
private boolean isInline = false;
private boolean isTailrec = false;
private boolean isHidden = false;
private boolean hasStableParameterNames = true;
private boolean hasSynthesizedParameterNames = false;
private final Set<FunctionDescriptor> overriddenFunctions = SmartSet.create();
@@ -129,6 +130,10 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
this.isTailrec = isTailrec;
}
public void setHidden(boolean hidden) {
isHidden = hidden;
}
public void setReturnType(@NotNull KotlinType unsubstitutedReturnType) {
if (this.unsubstitutedReturnType != null) {
// TODO: uncomment and fix tests
@@ -212,6 +217,11 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
return isTailrec;
}
@Override
public boolean isHiddenToOvercomeSignatureClash() {
return isHidden;
}
@Override
public void addOverriddenDescriptor(@NotNull CallableMemberDescriptor overriddenFunction) {
overriddenFunctions.add((FunctionDescriptor) overriddenFunction);
@@ -283,6 +293,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
protected @Nullable Name name;
protected boolean copyOverrides;
protected boolean signatureChange;
private boolean isHiddenToOvercomeSignatureClash;
private List<TypeParameterDescriptor> newTypeParameters = null;
public CopyConfiguration(
@@ -307,6 +318,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
this.name = name;
this.copyOverrides = true;
this.signatureChange = false;
this.isHiddenToOvercomeSignatureClash = isHiddenToOvercomeSignatureClash();
}
@NotNull
@@ -379,6 +391,12 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
return this;
}
@NotNull
public CopyConfiguration setHidden() {
isHiddenToOvercomeSignatureClash = true;
return this;
}
public FunctionDescriptor build() {
return doSubstitute(this);
}
@@ -483,6 +501,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
substitutedDescriptor.setTailrec(isTailrec);
substitutedDescriptor.setHasStableParameterNames(hasStableParameterNames);
substitutedDescriptor.setHasSynthesizedParameterNames(hasSynthesizedParameterNames);
substitutedDescriptor.setHidden(configuration.isHiddenToOvercomeSignatureClash);
if (configuration.signatureChange || getInitialSignatureDescriptor() != null) {
FunctionDescriptor initialSignature = (getInitialSignatureDescriptor() != null ? getInitialSignatureDescriptor() : this);
@@ -201,4 +201,9 @@ public abstract class PropertyAccessorDescriptorImpl extends DeclarationDescript
public void setInitialSignatureDescriptor(@Nullable FunctionDescriptor initialSignatureDescriptor) {
this.initialSignatureDescriptor = initialSignatureDescriptor;
}
@Override
public boolean isHiddenToOvercomeSignatureClash() {
return false;
}
}
@@ -129,4 +129,11 @@ public class SimpleFunctionDescriptorImpl extends FunctionDescriptorImpl impleme
//noinspection ConstantConditions
return (SimpleFunctionDescriptor) newCopyBuilder().setTypeParameters(typeParameters).build();
}
@NotNull
@Override
public SimpleFunctionDescriptor createHiddenCopyToOvercomeSignatureClash() {
//noinspection ConstantConditions
return (SimpleFunctionDescriptor) newCopyBuilder().setHidden().build();
}
}
@@ -603,6 +603,10 @@ internal class DescriptorRendererImpl(
renderOverride(function, builder)
renderMemberKind(function, builder)
if (verbose && function.isHiddenToOvercomeSignatureClash) {
builder.append("/*isHiddenToOvercomeSignatureClash*/ ")
}
builder.append(renderKeyword("fun")).append(" ")
renderTypeParameters(function.getTypeParameters(), builder, true)
renderReceiver(function, builder)
@@ -82,12 +82,14 @@ public class OverridingUtil {
@Nullable ClassDescriptor subClassDescriptor,
boolean checkReturnType
) {
boolean wasSuccessfulExternalCondition = false;
for (ExternalOverridabilityCondition externalCondition : EXTERNAL_CONDITIONS) {
ExternalOverridabilityCondition.Result result =
externalCondition.isOverridable(superDescriptor, subDescriptor, subClassDescriptor);
switch (result) {
case OVERRIDABLE:
return OverrideCompatibilityInfo.success();
wasSuccessfulExternalCondition = true;
break;
case CONFLICT:
return OverrideCompatibilityInfo.conflict("External condition failed");
case INCOMPATIBLE:
@@ -98,6 +100,10 @@ public class OverridingUtil {
}
}
if (wasSuccessfulExternalCondition) {
return OverrideCompatibilityInfo.success();
}
return isOverridableByWithoutExternalConditions(superDescriptor, subDescriptor, checkReturnType);
}
@@ -73,6 +73,12 @@ public class ErrorSimpleFunctionDescriptorImpl extends SimpleFunctionDescriptorI
return this;
}
@NotNull
@Override
public SimpleFunctionDescriptor createHiddenCopyToOvercomeSignatureClash() {
return this;
}
@Override
public void addOverriddenDescriptor(@NotNull CallableMemberDescriptor overriddenFunction) {
// nop