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).
This commit is contained in:
@@ -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 <? val
|
||||
return KotlinTypeChecker.DEFAULT.isSubtypeOf(aReturnType, bReturnType);
|
||||
// both vals or var vs val: val can't be more specific then var
|
||||
return !(!pa.isVar() && pb.isVar()) && KotlinTypeChecker.DEFAULT.isSubtypeOf(aReturnType, bReturnType);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
|
||||
@@ -381,7 +374,12 @@ public class OverridingUtil {
|
||||
// Should be 'foo(s: String): String'.
|
||||
Modality modality = getMinimalModality(effectiveOverridden);
|
||||
Visibility visibility = allInvisible ? Visibilities.INVISIBLE_FAKE : Visibilities.INHERITED;
|
||||
CallableMemberDescriptor mostSpecific = selectMostSpecificMemberFromSuper(effectiveOverridden);
|
||||
CallableMemberDescriptor mostSpecific =
|
||||
OverridingUtilsKt.getOverriddenWithMostSpecificReturnTypeOrNull(KotlinTypeChecker.DEFAULT, effectiveOverridden);
|
||||
if (mostSpecific == null) {
|
||||
// Use some (possibly erroneous) inherited signature. Will be reported as an error later.
|
||||
mostSpecific = selectMostSpecificMemberFromSuper(effectiveOverridden);
|
||||
}
|
||||
CallableMemberDescriptor fakeOverride =
|
||||
mostSpecific.copy(current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false);
|
||||
for (CallableMemberDescriptor descriptor : effectiveOverridden) {
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import java.util.*
|
||||
|
||||
@@ -41,3 +46,64 @@ fun <TDescriptor : CallableDescriptor> 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<CallableDescriptor>,
|
||||
typeChecker: KotlinTypeChecker
|
||||
): Boolean =
|
||||
descriptors.all {
|
||||
otherDescriptor ->
|
||||
otherDescriptor == returnTypeOwner ||
|
||||
(!(returnTypeOwner.isVal() && otherDescriptor.isVar()) &&
|
||||
otherDescriptor.returnType?.let { typeChecker.isSubtypeOf(type, it) } ?: true)
|
||||
}
|
||||
|
||||
|
||||
fun <TDescriptor : CallableDescriptor> getOverriddenWithMostSpecificReturnTypeOrNull(
|
||||
typeChecker: KotlinTypeChecker,
|
||||
descriptors: Collection<TDescriptor>
|
||||
): TDescriptor? {
|
||||
val candidates = arrayListOf<TDescriptor>()
|
||||
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user