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:
Dmitry Petrov
2015-12-07 17:16:14 +03:00
parent 594039ac42
commit 94bea54db3
12 changed files with 312 additions and 48 deletions
@@ -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
}