From 94bea54db3c8d0c74b60f07c9d18ee3ba7f90bbf Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 7 Dec 2015 17:16:14 +0300 Subject: [PATCH] Change "most specific return type" definition for fake overrides. Given overridden descriptors D = d[i]. 1. Find D*, subset of D: returnType(d* from D*) <: returnType(d) for each d from D. Always prefer var to val. 2. Prefer non-flexible return type to flexible. Check for var/val overrides properly (NB: this will report PROPERTY_TYPE_MISMATCH_ON_OVERRIDE for all properties, not just overrides involving vars as it was before). --- .../kotlin/resolve/OverrideResolver.java | 39 +++--- .../tests/CovariantOverrideType.kt | 6 +- ...tedPropertyOverridedInTraitTypeMismatch.kt | 2 +- .../Delegation_ClashingFunctions.kt | 8 +- .../flexibleReturnType.txt | 2 +- .../flexibleReturnTypeList.kt | 13 +- .../flexibleReturnTypeList.txt | 6 +- .../genericWithUpperBound.kt | 56 ++++++++ .../genericWithUpperBound.txt | 132 ++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 6 + .../kotlin/resolve/OverridingUtil.java | 24 ++-- .../kotlin/resolve/overridingUtils.kt | 66 +++++++++ 12 files changed, 312 insertions(+), 48 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.kt create mode 100644 compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java index df2242e110c..4369aac58c6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OverrideResolver.java @@ -499,7 +499,7 @@ public class OverrideResolver { Set relevantDirectlyOverridden = getRelevantDirectlyOverridden(overriddenDeclarationsByDirectParent, allFilteredOverriddenDeclarations); - checkInheritedDescriptorsGroup(relevantDirectlyOverridden, reportingStrategy); + checkInheritedDescriptorsGroup(relevantDirectlyOverridden, descriptor, reportingStrategy); if (kind == DELEGATION && overrideReportStrategyForDelegates != null) { checkOverridesForMember(descriptor, relevantDirectlyOverridden, overrideReportStrategyForDelegates); @@ -816,28 +816,25 @@ public class OverrideResolver { private static void checkInheritedDescriptorsGroup( @NotNull Collection inheritedDescriptors, + @NotNull CallableMemberDescriptor mostSpecific, @NotNull CheckInheritedSignaturesReportStrategy reportingStrategy ) { - // FIXME This algorithm depends on transitiveness of sub-typing relation, which is broken in presence of flexible types. if (inheritedDescriptors.size() > 1) { - Iterator inheritedIterator = inheritedDescriptors.iterator(); - CallableMemberDescriptor mostSpecificInherited = inheritedIterator.next(); - while (inheritedIterator.hasNext()) { - CallableMemberDescriptor overriddenDescriptor = inheritedIterator.next(); - if (OverridingUtil.isMoreSpecific(overriddenDescriptor, mostSpecificInherited)) { - mostSpecificInherited = overriddenDescriptor; - } - } + PropertyDescriptor mostSpecificProperty = mostSpecific instanceof PropertyDescriptor ? (PropertyDescriptor) mostSpecific : null; for (CallableMemberDescriptor inheritedDescriptor : inheritedDescriptors) { - if (!OverridingUtil.isMoreSpecific(mostSpecificInherited, inheritedDescriptor)) { - if (inheritedDescriptor instanceof PropertyDescriptor) { - reportingStrategy.propertyTypeMismatchOnInheritance(mostSpecificInherited, inheritedDescriptor); - } - else { - reportingStrategy.returnTypeMismatchOnInheritance(mostSpecificInherited, inheritedDescriptor); + if (mostSpecificProperty != null) { + assert inheritedDescriptor instanceof PropertyDescriptor + : inheritedDescriptor + " inherited from " + mostSpecificProperty + " is not a property"; + PropertyDescriptor inheritedPropertyDescriptor = (PropertyDescriptor) inheritedDescriptor; + + if (!isPropertyTypeOkForOverride(inheritedPropertyDescriptor, mostSpecificProperty)) { + reportingStrategy.propertyTypeMismatchOnInheritance(mostSpecific, inheritedDescriptor); } } + else if (!isReturnTypeOkForOverride(inheritedDescriptor, mostSpecific)) { + reportingStrategy.returnTypeMismatchOnInheritance(mostSpecific, inheritedDescriptor); + } } } } @@ -935,11 +932,15 @@ public class OverrideResolver { TypeSubstitutor typeSubstitutor = prepareTypeSubstitutor(superDescriptor, subDescriptor); if (typeSubstitutor == null) return false; - if (!superDescriptor.isVar()) return true; - KotlinType substitutedSuperReturnType = typeSubstitutor.substitute(superDescriptor.getType(), Variance.OUT_VARIANCE); assert substitutedSuperReturnType != null; - return KotlinTypeChecker.DEFAULT.equalTypes(subDescriptor.getType(), substitutedSuperReturnType); + + if (superDescriptor.isVar()) { + return KotlinTypeChecker.DEFAULT.equalTypes(subDescriptor.getType(), substitutedSuperReturnType); + } + else { + return KotlinTypeChecker.DEFAULT.isSubtypeOf(subDescriptor.getType(), substitutedSuperReturnType); + } } private void checkOverrideForComponentFunction(@NotNull final CallableMemberDescriptor componentFunction) { diff --git a/compiler/testData/diagnostics/tests/CovariantOverrideType.kt b/compiler/testData/diagnostics/tests/CovariantOverrideType.kt index ed3e349603c..edfdda26a12 100644 --- a/compiler/testData/diagnostics/tests/CovariantOverrideType.kt +++ b/compiler/testData/diagnostics/tests/CovariantOverrideType.kt @@ -16,11 +16,11 @@ abstract class B() : A { override fun foo2() : Unit { } - override val a : Double = 1.toDouble() - override val a1 = 1.toDouble() + override val a : Double = 1.toDouble() + override val a1 = 1.toDouble() abstract override fun g() : Int abstract override fun g1() : List - abstract override val g : Iterator + abstract override val g : Iterator } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.kt b/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.kt index 3c0d685e506..81552ad084d 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.kt @@ -7,7 +7,7 @@ interface A { } class AImpl: A { - override val prop by Delegate() + override val prop by Delegate() } fun foo() { diff --git a/compiler/testData/diagnostics/tests/delegation/Delegation_ClashingFunctions.kt b/compiler/testData/diagnostics/tests/delegation/Delegation_ClashingFunctions.kt index 906b40675ab..50e1c698178 100644 --- a/compiler/testData/diagnostics/tests/delegation/Delegation_ClashingFunctions.kt +++ b/compiler/testData/diagnostics/tests/delegation/Delegation_ClashingFunctions.kt @@ -10,9 +10,9 @@ interface Three { public fun foo(): String } -class Test123(val v1: One, val v2: Two, val v3: Three) : One by v1, Two by v2, Three by v3 { } -class Test132(val v1: One, val v2: Two, val v3: Three) : One by v1, Three by v3, Two by v2 { } +class Test123(val v1: One, val v2: Two, val v3: Three) : One by v1, Two by v2, Three by v3 { } +class Test132(val v1: One, val v2: Two, val v3: Three) : One by v1, Three by v3, Two by v2 { } class Test312(val v1: One, val v2: Two, val v3: Three) : Three by v3, One by v1, Two by v2 { } class Test321(val v1: One, val v2: Two, val v3: Three) : Three by v3, Two by v2, One by v1 { } -class Test231(val v1: One, val v2: Two, val v3: Three) : Two by v2, Three by v3, One by v1 { } -class Test213(val v1: One, val v2: Two, val v3: Three) : Two by v2, One by v1, Three by v3 { } \ No newline at end of file +class Test231(val v1: One, val v2: Two, val v3: Three) : Two by v2, Three by v3, One by v1 { } +class Test213(val v1: One, val v2: Two, val v3: Three) : Two by v2, One by v1, Three by v3 { } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnType.txt b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnType.txt index f960030f435..8d699928b6e 100644 --- a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnType.txt +++ b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnType.txt @@ -60,7 +60,7 @@ public interface KDerived1b : J, K1 { public interface KDerived2a : K2, J { public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public abstract override /*2*/ /*fake_override*/ fun foo(): kotlin.String! + public abstract override /*2*/ /*fake_override*/ fun foo(): kotlin.String? public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String } diff --git a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.kt b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.kt index fc4ef8d6d44..eaa3f7471d0 100644 --- a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.kt +++ b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.kt @@ -30,9 +30,14 @@ interface Test5 : ILNS, IMLS, J interface Test6 : ILNS, J, IMLS interface Test7 : J, ILNS, IMLS -// ILS and IMLNS are incompatible +// Return types of ILS::foo and IMLNS::foo are incompatible themselves. +// However, return type of J::foo is (Mutable)List!, +// which is subtype of both List and MutalbeList. +// Thus, inheriting from J, IMLNS, and ILS is Ok, +// but inheriting from IMLNS and ILS is not. + +interface Test8 : J, IMLNS, ILS +interface Test9 : IMLNS, J, ILS +interface Test10 : IMLNS, ILS, J -interface Test8 : J, IMLNS, ILS -interface Test9 : IMLNS, J, ILS -interface Test10 : IMLNS, ILS, J interface Test11 : IMLNS, ILS \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.txt b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.txt index 23305841a99..3a5af10116f 100644 --- a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.txt +++ b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/flexibleReturnTypeList.txt @@ -46,7 +46,7 @@ public interface Test1 : ILNS, J { public interface Test10 : IMLNS, ILS, J { public open override /*3*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public abstract override /*3*/ /*fake_override*/ fun foo(): kotlin.MutableList + public abstract override /*3*/ /*fake_override*/ fun foo(): kotlin.(Mutable)List! public open override /*3*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*3*/ /*fake_override*/ fun toString(): kotlin.String } @@ -102,14 +102,14 @@ public interface Test7 : J, ILNS, IMLS { public interface Test8 : J, IMLNS, ILS { public open override /*3*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public abstract override /*3*/ /*fake_override*/ fun foo(): kotlin.MutableList + public abstract override /*3*/ /*fake_override*/ fun foo(): kotlin.(Mutable)List! public open override /*3*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*3*/ /*fake_override*/ fun toString(): kotlin.String } public interface Test9 : IMLNS, J, ILS { public open override /*3*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public abstract override /*3*/ /*fake_override*/ fun foo(): kotlin.MutableList + public abstract override /*3*/ /*fake_override*/ fun foo(): kotlin.(Mutable)List! public open override /*3*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*3*/ /*fake_override*/ fun toString(): kotlin.String } diff --git a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.kt b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.kt new file mode 100644 index 00000000000..453cbb87571 --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.kt @@ -0,0 +1,56 @@ +// FILE: JFooWithUpperBound.java +public interface JFooWithUpperBound { + T foo(); +} + +// FILE: JFooWithUpperBoundDerived.java +public interface JFooWithUpperBoundDerived extends JFooWithUpperBound { +} + +// FILE: JCFooWithUpperBound.java +public class JCFooWithUpperBound { + public T foo() { + return null; + } +} + +// FILE: JCFooWithUpperBoundDerived.java +public class JCFooWithUpperBoundDerived extends JCFooWithUpperBound { +} + +// FILE: K.kt +interface IBase + +interface IDerived : IBase + +interface IFooWithUpperBound { + fun foo(): T +} + +interface IFooT { + fun foo(): T +} + +interface IFoo { + fun foo(): IBase +} + +interface IFooDerived : IFoo { + override fun foo(): IDerived +} + +interface IFooWithUpperBoundDerived : IFooWithUpperBound + +interface Test1 : IFooWithUpperBound, IFoo + +interface Test2 : IFooT, IFoo + +interface Test3 : IFooWithUpperBoundDerived, IFooDerived + +interface Test4 : JFooWithUpperBound, IFoo + +interface Test5 : JFooWithUpperBoundDerived, IFooDerived + +class Test6 : JCFooWithUpperBound(), IFoo + +class Test7 : JCFooWithUpperBoundDerived(), IFooDerived \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.txt b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.txt new file mode 100644 index 00000000000..ccde6e233ac --- /dev/null +++ b/compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.txt @@ -0,0 +1,132 @@ +package + +public /*synthesized*/ fun JFooWithUpperBound(/*0*/ function: () -> T!): JFooWithUpperBound +public /*synthesized*/ fun JFooWithUpperBoundDerived(/*0*/ function: () -> T!): JFooWithUpperBoundDerived + +public interface IBase { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IDerived : IBase { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IFoo { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): IBase + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IFooDerived : IFoo { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ fun foo(): IDerived + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IFooT { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IFooWithUpperBound { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface IFooWithUpperBoundDerived : IFooWithUpperBound { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun foo(): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class JCFooWithUpperBound { + public constructor JCFooWithUpperBound() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open fun foo(): T! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class JCFooWithUpperBoundDerived : JCFooWithUpperBound { + public constructor JCFooWithUpperBoundDerived() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun foo(): T! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface JFooWithUpperBound { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract fun foo(): T! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface JFooWithUpperBoundDerived : JFooWithUpperBound { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun foo(): T! + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Test1 : IFooWithUpperBound, IFoo { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*2*/ /*fake_override*/ fun foo(): T + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Test2 : IFooT, IFoo { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*2*/ /*fake_override*/ fun foo(): T + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Test3 : IFooWithUpperBoundDerived, IFooDerived { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*2*/ /*fake_override*/ fun foo(): T + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Test4 : JFooWithUpperBound, IFoo { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*2*/ /*fake_override*/ fun foo(): T! + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Test5 : JFooWithUpperBoundDerived, IFooDerived { + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*2*/ /*fake_override*/ fun foo(): T! + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Test6 : JCFooWithUpperBound, IFoo { + public constructor Test6() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun foo(): T! + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Test7 : JCFooWithUpperBoundDerived, IFooDerived { + public constructor Test7() + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun foo(): T! + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index db44933a8e9..d4a611f6091 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -11360,6 +11360,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("genericWithUpperBound.kt") + public void testGenericWithUpperBound() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance/genericWithUpperBound.kt"); + doTest(fileName); + } + @TestMetadata("returnTypeMismatch.kt") public void testReturnTypeMismatch() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/override/clashesOnInheritance/returnTypeMismatch.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java index e613712bb99..769db1671e5 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java @@ -28,14 +28,12 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.types.KotlinType; -import org.jetbrains.kotlin.types.TypeCapabilitiesKt; import org.jetbrains.kotlin.types.TypeConstructor; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; import java.util.*; import static org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.*; -import static org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.INCOMPATIBLE; public class OverridingUtil { @@ -322,11 +320,10 @@ public class OverridingUtil { public static boolean isMoreSpecific(@NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) { KotlinType aReturnType = a.getReturnType(); - assert aReturnType != null : "Return type is null for " + a; - // Use upper bound for flexible type. - aReturnType = TypeCapabilitiesKt.getSupertypeRepresentative(aReturnType); KotlinType bReturnType = b.getReturnType(); - assert bReturnType != null : "Return type is null for " + b; + + assert aReturnType != null : "Return type of " + a + " is null"; + assert bReturnType != null : "Return type of " + b + " is null"; if (a instanceof SimpleFunctionDescriptor) { assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass(); @@ -341,13 +338,9 @@ public class OverridingUtil { if (pa.isVar() && pb.isVar()) { return KotlinTypeChecker.DEFAULT.equalTypes(aReturnType, bReturnType); } - else if (!pa.isVar() && pb.isVar()) { - // val can't be more specific than var, regardless of return type. - return false; - } else { - // both vals or var TDescriptor.findOriginalTopMostOverridden (it.original as TDescriptor) } } + + +private fun CallableDescriptor.isVar(): Boolean = + this is PropertyDescriptor && isVar + +private fun CallableDescriptor.isVal(): Boolean = + this is PropertyDescriptor && !isVar + +private fun isMostSpecificByReturnTypeAndKind( + type: KotlinType, + returnTypeOwner: CallableDescriptor, + descriptors: Collection, + typeChecker: KotlinTypeChecker +): Boolean = + descriptors.all { + otherDescriptor -> + otherDescriptor == returnTypeOwner || + (!(returnTypeOwner.isVal() && otherDescriptor.isVar()) && + otherDescriptor.returnType?.let { typeChecker.isSubtypeOf(type, it) } ?: true) + } + + +fun getOverriddenWithMostSpecificReturnTypeOrNull( + typeChecker: KotlinTypeChecker, + descriptors: Collection +): TDescriptor? { + val candidates = arrayListOf() + + // Need this to avoid recursion on lazy types. + if (descriptors.isEmpty()) { + return null + } else if (descriptors.size == 1) { + return descriptors.first() + } + + for (descriptor in descriptors) { + val returnType = descriptor.returnType ?: continue + + if (isMostSpecificByReturnTypeAndKind(returnType, descriptor, descriptors, typeChecker)) { + candidates.add(descriptor) + } + else if (!TypeUtils.canHaveSubtypes(typeChecker, returnType)) { + return null + } + } + + if (candidates.isEmpty()) { + return null + } + + var withNonErrorReturnType: TDescriptor? = null + var withNonFlexibleReturnType: TDescriptor? = null + for (candidate in candidates) { + val returnType = candidate.returnType ?: continue + withNonErrorReturnType = candidate + if (!returnType.isFlexible()) { + withNonFlexibleReturnType = candidate + } + } + return withNonFlexibleReturnType ?: withNonErrorReturnType +} \ No newline at end of file