Fix overload resolution ambiguity for types intersection

There are two different forms of types intestion:
1. Type parameters with multiple bounds
2. Smart casts

The problem was that when member scope of type intersection contained
effective duplicates and that lead to overload resolution ambiguity in
strange cases like `x.hashCode()`

For first type we do effectively the same thing as when building member
scope for class extending several interfaces: group all descriptors by
both-way-overridability relation and then choose most-specific in each
group.

For smart casts we do basically the same thing but with special
treatments:
1. From all descriptors that _equal_ to most specific we choose
   the one that works without smartcast if possible (i.e. we choose first from candidates list)
2. If smart-cast value seems to be unstable we use only member scope
   of receiver type + all descriptors from smart cast possible types
   that has incompatible signature. If we'd include all of them and
   choose one as more specific, and it would lead to false
   SMART_CAST_IMPOSIBLE (see test unstableSmartCast.kt)

 #KT-3996 Fixed
 #KT-10315 Fixed
This commit is contained in:
Denis Zharkov
2015-12-15 12:55:40 +03:00
parent 8d0c3281cd
commit b4bb92d136
75 changed files with 1226 additions and 105 deletions
@@ -23,14 +23,13 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.scopes.ChainedScope;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.resolve.scopes.LazyScopeAdapter;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.resolve.scopes.TypeIntersectionScope;
import org.jetbrains.kotlin.storage.NotNullLazyValue;
import org.jetbrains.kotlin.storage.StorageManager;
import org.jetbrains.kotlin.types.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -79,14 +78,7 @@ public abstract class AbstractTypeParameterDescriptor extends DeclarationDescrip
new Function0<MemberScope>() {
@Override
public MemberScope invoke() {
List<MemberScope> scopes = new ArrayList<MemberScope>();
for (KotlinType bound : getUpperBounds()) {
scopes.add(bound.getMemberScope());
}
return new ChainedScope(
"Scope for type parameter " + name.asString(),
scopes.toArray(new MemberScope[scopes.size()])
);
return TypeIntersectionScope.create("Scope for type parameter " + name.asString(), getUpperBounds());
}
}
))
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve;
import kotlin.CollectionsKt;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.Mutable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
@@ -331,7 +332,7 @@ public class OverridingUtil {
}
}
public static boolean isMoreSpecific(@NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) {
public static boolean isMoreSpecific(@NotNull CallableDescriptor a, @NotNull CallableDescriptor b) {
KotlinType aReturnType = a.getReturnType();
KotlinType bReturnType = b.getReturnType();
@@ -341,7 +342,7 @@ public class OverridingUtil {
if (a instanceof SimpleFunctionDescriptor) {
assert b instanceof SimpleFunctionDescriptor : "b is " + b.getClass();
return KotlinTypeChecker.DEFAULT.isSubtypeOf(aReturnType, bReturnType);
return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType);
}
if (a instanceof PropertyDescriptor) {
assert b instanceof PropertyDescriptor : "b is " + b.getClass();
@@ -349,20 +350,20 @@ public class OverridingUtil {
PropertyDescriptor pa = (PropertyDescriptor) a;
PropertyDescriptor pb = (PropertyDescriptor) b;
if (pa.isVar() && pb.isVar()) {
return KotlinTypeChecker.DEFAULT.equalTypes(aReturnType, bReturnType);
return DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters()).equalTypes(aReturnType, bReturnType);
}
else {
// both vals or var vs val: val can't be more specific then var
return !(!pa.isVar() && pb.isVar()) && KotlinTypeChecker.DEFAULT.isSubtypeOf(aReturnType, bReturnType);
return !(!pa.isVar() && pb.isVar()) && isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType);
}
}
throw new IllegalArgumentException("Unexpected callable: " + a.getClass());
}
private static boolean isMoreSpecificThenAllOf(@NotNull CallableMemberDescriptor candidate, @NotNull Collection<CallableMemberDescriptor> descriptors) {
private static boolean isMoreSpecificThenAllOf(@NotNull CallableDescriptor candidate, @NotNull Collection<CallableDescriptor> descriptors) {
// NB subtyping relation in Kotlin is not transitive in presence of flexible types:
// String? <: String! <: String, but not String? <: String
for (CallableMemberDescriptor descriptor : descriptors) {
for (CallableDescriptor descriptor : descriptors) {
if (!isMoreSpecific(candidate, descriptor)) {
return false;
}
@@ -370,20 +371,40 @@ public class OverridingUtil {
return true;
}
private static CallableMemberDescriptor selectMostSpecificMemberFromSuper(@NotNull Collection<CallableMemberDescriptor> overridables) {
private static boolean isReturnTypeMoreSpecific(
@NotNull CallableDescriptor a,
@NotNull KotlinType aReturnType,
@NotNull CallableDescriptor b,
@NotNull KotlinType bReturnType
) {
KotlinTypeChecker typeChecker = DEFAULT.createTypeChecker(a.getTypeParameters(), b.getTypeParameters());
return typeChecker.isSubtypeOf(aReturnType, bReturnType);
}
@NotNull
public static <H> H selectMostSpecificMember(
@NotNull Collection<H> overridables,
@NotNull Function1<H, CallableDescriptor> descriptorByHandle
) {
assert !overridables.isEmpty() : "Should have at least one overridable descriptor";
if (overridables.size() == 1) {
return CollectionsKt.first(overridables);
}
Collection<CallableMemberDescriptor> candidates = new ArrayList<CallableMemberDescriptor>(2);
CallableMemberDescriptor transitivelyMostSpecific = null;
for (CallableMemberDescriptor overridable : overridables) {
if (isMoreSpecificThenAllOf(overridable, overridables)) {
Collection<H> candidates = new ArrayList<H>(2);
List<CallableDescriptor> callableMemberDescriptors = CollectionsKt.map(overridables, descriptorByHandle);
H transitivelyMostSpecific = CollectionsKt.first(overridables);
CallableDescriptor transitivelyMostSpecificDescriptor = descriptorByHandle.invoke(transitivelyMostSpecific);
for (H overridable : overridables) {
CallableDescriptor descriptor = descriptorByHandle.invoke(overridable);
if (isMoreSpecificThenAllOf(descriptor, callableMemberDescriptors)) {
candidates.add(overridable);
}
if (transitivelyMostSpecific == null || isMoreSpecific(overridable, transitivelyMostSpecific)) {
if (isMoreSpecific(descriptor, transitivelyMostSpecificDescriptor)
&& !isMoreSpecific(transitivelyMostSpecificDescriptor, descriptor)) {
transitivelyMostSpecific = overridable;
}
}
@@ -395,9 +416,9 @@ public class OverridingUtil {
return CollectionsKt.first(candidates);
}
CallableMemberDescriptor firstNonFlexible = null;
for (CallableMemberDescriptor candidate : candidates) {
if (firstNonFlexible == null && !FlexibleTypesKt.isFlexible(candidate.getReturnType())) {
H firstNonFlexible = null;
for (H candidate : candidates) {
if (!FlexibleTypesKt.isFlexible(descriptorByHandle.invoke(candidate).getReturnType())) {
firstNonFlexible = candidate;
break;
}
@@ -427,7 +448,14 @@ 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 =
selectMostSpecificMember(effectiveOverridden,
new Function1<CallableMemberDescriptor, CallableDescriptor>() {
@Override
public CallableMemberDescriptor invoke(CallableMemberDescriptor descriptor) {
return descriptor;
}
});
CallableMemberDescriptor fakeOverride =
mostSpecific.copy(current, modality, visibility, CallableMemberDescriptor.Kind.FAKE_OVERRIDE, false);
for (CallableMemberDescriptor descriptor : effectiveOverridden) {
@@ -462,35 +490,79 @@ public class OverridingUtil {
});
}
/**
* @param <H> is something that handles CallableDescriptor inside
* @return
*/
@NotNull
private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
@NotNull CallableMemberDescriptor overrider,
@NotNull Queue<CallableMemberDescriptor> extractFrom,
@NotNull DescriptorSink sink
public static <H> Collection<H> extractMembersOverridableInBothWays(
@NotNull H overrider,
@NotNull @Mutable Collection<H> extractFrom,
@NotNull Function1<H, CallableDescriptor> descriptorByHandle,
@NotNull Function1<H, Unit> onConflict
) {
Collection<CallableMemberDescriptor> overridable = new ArrayList<CallableMemberDescriptor>();
Collection<H> overridable = new ArrayList<H>();
overridable.add(overrider);
for (Iterator<CallableMemberDescriptor> iterator = extractFrom.iterator(); iterator.hasNext(); ) {
CallableMemberDescriptor candidate = iterator.next();
CallableDescriptor overriderDescriptor = descriptorByHandle.invoke(overrider);
for (Iterator<H> iterator = extractFrom.iterator(); iterator.hasNext(); ) {
H candidate = iterator.next();
CallableDescriptor candidateDescriptor = descriptorByHandle.invoke(candidate);
if (overrider == candidate) {
iterator.remove();
continue;
}
OverrideCompatibilityInfo.Result result1 = DEFAULT.isOverridableBy(candidate, overrider, null).getResult();
OverrideCompatibilityInfo.Result result2 = DEFAULT.isOverridableBy(overrider, candidate, null).getResult();
if (result1 == OVERRIDABLE && result2 == OVERRIDABLE) {
OverrideCompatibilityInfo.Result finalResult = getBothWaysOverridability(overriderDescriptor, candidateDescriptor);
if (finalResult == OVERRIDABLE) {
overridable.add(candidate);
iterator.remove();
}
else if (result1 == CONFLICT || result2 == CONFLICT) {
sink.conflict(overrider, candidate);
else if (finalResult == CONFLICT) {
onConflict.invoke(candidate);
iterator.remove();
}
}
return overridable;
}
@Nullable
public static OverrideCompatibilityInfo.Result getBothWaysOverridability(
CallableDescriptor overriderDescriptor,
CallableDescriptor candidateDescriptor
) {
OverrideCompatibilityInfo.Result result1 = DEFAULT.isOverridableBy(candidateDescriptor, overriderDescriptor, null).getResult();
OverrideCompatibilityInfo.Result result2 = DEFAULT.isOverridableBy(overriderDescriptor, candidateDescriptor, null).getResult();
return result1 == OVERRIDABLE && result2 == OVERRIDABLE
? OVERRIDABLE
: ((result1 == CONFLICT || result2 == CONFLICT) ? CONFLICT : INCOMPATIBLE);
}
@NotNull
private static Collection<CallableMemberDescriptor> extractMembersOverridableInBothWays(
@NotNull final CallableMemberDescriptor overrider,
@NotNull Queue<CallableMemberDescriptor> extractFrom,
@NotNull final DescriptorSink sink
) {
return extractMembersOverridableInBothWays(overrider, extractFrom,
// ID
new Function1<CallableMemberDescriptor, CallableDescriptor>() {
@Override
public CallableDescriptor invoke(CallableMemberDescriptor descriptor) {
return descriptor;
}
},
new Function1<CallableMemberDescriptor, Unit>() {
@Override
public Unit invoke(CallableMemberDescriptor descriptor) {
sink.conflict(overrider, descriptor);
return Unit.INSTANCE;
}
});
}
public static void resolveUnknownVisibilityForMember(
@NotNull CallableMemberDescriptor memberDescriptor,
@Nullable Function1<CallableMemberDescriptor, Unit> cannotInferVisibility
@@ -17,12 +17,8 @@
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 org.jetbrains.kotlin.utils.SmartSet
import java.util.*
fun <TDescriptor : CallableDescriptor> TDescriptor.findTopMostOverriddenDescriptors(): List<TDescriptor> {
@@ -46,3 +42,42 @@ fun <TDescriptor : CallableDescriptor> TDescriptor.findOriginalTopMostOverridden
(it.original as TDescriptor)
}
}
/**
* @param <H> is something that handles CallableDescriptor inside
*/
public fun <H : Any> Collection<H>.selectMostSpecificInEachOverridableGroup(
descriptorByHandle: H.() -> CallableDescriptor
): Collection<H> {
if (size <= 1) return this
val queue = LinkedList<H>(this)
val result = SmartSet.create<H>()
while (queue.isNotEmpty()) {
val nextHandle: H = queue.first()
val conflictedHandles = SmartSet.create<H>()
val overridableGroup =
OverridingUtil.extractMembersOverridableInBothWays(nextHandle, queue, descriptorByHandle) { conflictedHandles.add(it) }
if (overridableGroup.size == 1 && overridableGroup.isEmpty()) {
result.add(overridableGroup.single())
continue
}
val mostSpecific = OverridingUtil.selectMostSpecificMember(overridableGroup, descriptorByHandle)
val mostSpecificDescriptor = mostSpecific.descriptorByHandle()
overridableGroup.filterNotTo(conflictedHandles) {
OverridingUtil.isMoreSpecific(mostSpecificDescriptor, it.descriptorByHandle())
}
if (conflictedHandles.isNotEmpty()) {
result.addAll(conflictedHandles)
}
result.add(mostSpecific)
}
return result
}
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.util.collectionUtils.getFromAllScopes
import org.jetbrains.kotlin.utils.Printer
public class ChainedScope(
private val debugName: String,
internal val debugName: String,
vararg scopes: MemberScope
) : MemberScope {
private val scopeChain = scopes.clone()
@@ -0,0 +1,54 @@
/*
* 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.resolve.scopes
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.selectMostSpecificInEachOverridableGroup
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.Printer
class TypeIntersectionScope private constructor(override val workerScope: ChainedScope) : AbstractScopeAdapter() {
override fun getContributedFunctions(name: Name, location: LookupLocation) =
super.getContributedFunctions(name, location).selectMostSpecificInEachOverridableGroup { this }
override fun getContributedVariables(name: Name, location: LookupLocation) =
super.getContributedVariables(name, location).selectMostSpecificInEachOverridableGroup { this }
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
val (callables, other) = super.getContributedDescriptors(kindFilter, nameFilter).partition { it is CallableDescriptor }
@Suppress("UNCHECKED_CAST")
return (callables as Collection<CallableDescriptor>).selectMostSpecificInEachOverridableGroup { this } + other
}
override fun printScopeStructure(p: Printer) {
p.print("TypeIntersectionScope for: " + workerScope.debugName)
super.printScopeStructure(p)
}
companion object {
@JvmStatic
fun create(message: String, types: List<KotlinType>): MemberScope {
val chainedScope = ChainedScope(message, *types.map { it.memberScope }.toTypedArray())
if (types.size <= 1) return chainedScope
return TypeIntersectionScope(chainedScope)
}
}
}