Parametrize behavior of DescriptorEquivalenceForOverrides::areCallableDescriptorsEquivalent

The changes introduced 471134d31e are only needed
for the case of HMPP project while for other cases it might break the behavior
a bit like in KT-34027

See org.jetbrains.kotlin.resolve.calls.results.OverloadingConflictResolver#filterOutEquivalentCalls

Before 471134d we were comparing
"fun foo(x: String)" with "[substituted] fun foo(x: String)"
and areCallableDescriptorsEquivalent returned false for such case.
Thus, both overrides were left in the resulting set.

After 471134d, those two descriptors
becamed considered as equal thus having a possibility to remove any of them.

The problem is that "areCallableDescriptorsEquivalent" has kind of
unclear contract. Effectively it checks whether two descriptors match
to the same declaration

But straightforward fixing of this exact call-site (using original descriptors)
doesn't help: behavior might change in a very subtle way (see org.jetbrains.kotlin.spec.checkers.DiagnosticsTestSpecGenerated.NotLinked.Dfa.Pos#test72)

So, the main idea is changing the contract for areCallableDescriptorsEquivalent
only when project is HMPP one.

^KT-34027 In Progress
This commit is contained in:
Denis Zharkov
2019-09-30 15:54:46 +03:00
parent 08794d17a0
commit 67410f7a57
11 changed files with 76 additions and 26 deletions
@@ -21,16 +21,20 @@ import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo
object DescriptorEquivalenceForOverrides {
fun areEquivalent(a: DeclarationDescriptor?, b: DeclarationDescriptor?): Boolean {
fun areEquivalent(
a: DeclarationDescriptor?,
b: DeclarationDescriptor?,
allowCopiesFromTheSameDeclaration: Boolean
): Boolean {
return when {
a is ClassDescriptor &&
b is ClassDescriptor -> areClassesEquivalent(a, b)
a is TypeParameterDescriptor &&
b is TypeParameterDescriptor -> areTypeParametersEquivalent(a, b)
b is TypeParameterDescriptor -> areTypeParametersEquivalent(a, b, allowCopiesFromTheSameDeclaration)
a is CallableDescriptor &&
b is CallableDescriptor -> areCallableDescriptorsEquivalent(a, b)
b is CallableDescriptor -> areCallableDescriptorsEquivalent(a, b, allowCopiesFromTheSameDeclaration)
a is PackageFragmentDescriptor &&
b is PackageFragmentDescriptor -> (a).fqName == (b).fqName
@@ -47,12 +51,13 @@ object DescriptorEquivalenceForOverrides {
private fun areTypeParametersEquivalent(
a: TypeParameterDescriptor,
b: TypeParameterDescriptor,
allowCopiesFromTheSameDeclaration: Boolean,
equivalentCallables: (DeclarationDescriptor?, DeclarationDescriptor?) -> Boolean = { _, _ -> false }
): Boolean {
if (a == b) return true
if (a.containingDeclaration == b.containingDeclaration) return false
if (!ownersEquivalent(a, b, equivalentCallables)) return false
if (!ownersEquivalent(a, b, equivalentCallables, allowCopiesFromTheSameDeclaration)) return false
return a.index == b.index // We ignore type parameter names
}
@@ -66,11 +71,13 @@ object DescriptorEquivalenceForOverrides {
fun areCallableDescriptorsEquivalent(
a: CallableDescriptor,
b: CallableDescriptor,
allowCopiesFromTheSameDeclaration: Boolean,
ignoreReturnType: Boolean = false
): Boolean {
if (a == b) return true
if (a.name != b.name) return false
if (a.containingDeclaration == b.containingDeclaration) {
if (!allowCopiesFromTheSameDeclaration) return false
if (a.singleSource() != b.singleSource()) return false
if (a is MemberDescriptor && b is MemberDescriptor && a.isExpect != b.isExpect) return false
}
@@ -78,7 +85,7 @@ object DescriptorEquivalenceForOverrides {
// Distinct locals are not equivalent
if (DescriptorUtils.isLocal(a) || DescriptorUtils.isLocal(b)) return false
if (!ownersEquivalent(a, b, { _, _ -> false })) return false
if (!ownersEquivalent(a, b, { _, _ -> false }, allowCopiesFromTheSameDeclaration)) return false
val overridingUtil = OverridingUtil.createWithEqualityAxioms eq@{ c1, c2 ->
if (c1 == c2) return@eq true
@@ -88,7 +95,7 @@ object DescriptorEquivalenceForOverrides {
if (d1 !is TypeParameterDescriptor || d2 !is TypeParameterDescriptor) return@eq false
areTypeParametersEquivalent(d1, d2, { x, y -> x == a && y == b })
areTypeParametersEquivalent(d1, d2, allowCopiesFromTheSameDeclaration) { x, y -> x == a && y == b }
}
return overridingUtil.isOverridableBy(a, b, null, !ignoreReturnType).result == OverrideCompatibilityInfo.Result.OVERRIDABLE
@@ -99,7 +106,8 @@ object DescriptorEquivalenceForOverrides {
private fun ownersEquivalent(
a: DeclarationDescriptor,
b: DeclarationDescriptor,
equivalentCallables: (DeclarationDescriptor?, DeclarationDescriptor?) -> Boolean
equivalentCallables: (DeclarationDescriptor?, DeclarationDescriptor?) -> Boolean,
allowCopiesFromTheSameDeclaration: Boolean
): Boolean {
val aOwner = a.containingDeclaration
val bOwner = b.containingDeclaration
@@ -109,7 +117,7 @@ object DescriptorEquivalenceForOverrides {
return if (aOwner is CallableMemberDescriptor || bOwner is CallableMemberDescriptor) {
equivalentCallables(aOwner, bOwner)
} else {
areEquivalent(aOwner, bOwner)
areEquivalent(aOwner, bOwner, allowCopiesFromTheSameDeclaration)
}
}
@@ -446,4 +446,7 @@ fun DeclarationDescriptor.isPrimaryConstructorOfInlineClass(): Boolean =
this is ConstructorDescriptor && this.isPrimary && this.constructedClass.isInline
@TypeRefinement
fun ModuleDescriptor.getKotlinTypeRefiner(): KotlinTypeRefiner = getCapability(REFINER_CAPABILITY)?.value ?: KotlinTypeRefiner.Default
fun ModuleDescriptor.getKotlinTypeRefiner(): KotlinTypeRefiner = getCapability(REFINER_CAPABILITY)?.value ?: KotlinTypeRefiner.Default
@UseExperimental(TypeRefinement::class)
fun ModuleDescriptor.isTypeRefinementEnabled(): Boolean = getCapability(REFINER_CAPABILITY)?.value != null
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.types.FlexibleTypesKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.KotlinTypeKt;
@@ -92,7 +93,11 @@ public class OverridingUtil {
*/
@NotNull
public static <D extends CallableDescriptor> Set<D> filterOutOverridden(@NotNull Set<D> candidateSet) {
return filterOverrides(candidateSet, new Function2<D, D, Pair<CallableDescriptor, CallableDescriptor>>() {
boolean allowDescriptorCopies = !candidateSet.isEmpty() &&
DescriptorUtilsKt
.isTypeRefinementEnabled(DescriptorUtilsKt.getModule(candidateSet.iterator().next()));
return filterOverrides(candidateSet, allowDescriptorCopies, new Function2<D, D, Pair<CallableDescriptor, CallableDescriptor>>() {
@Override
public Pair<CallableDescriptor, CallableDescriptor> invoke(D a, D b) {
return new Pair<CallableDescriptor, CallableDescriptor>(a, b);
@@ -103,6 +108,7 @@ public class OverridingUtil {
@NotNull
public static <D> Set<D> filterOverrides(
@NotNull Set<D> candidateSet,
boolean allowDescriptorCopies,
@NotNull Function2<? super D, ? super D, Pair<CallableDescriptor, CallableDescriptor>> transformFirst
) {
if (candidateSet.size() <= 1) return candidateSet;
@@ -115,10 +121,10 @@ public class OverridingUtil {
Pair<CallableDescriptor, CallableDescriptor> meAndOther = transformFirst.invoke(meD, otherD);
CallableDescriptor me = meAndOther.component1();
CallableDescriptor other = meAndOther.component2();
if (overrides(me, other)) {
if (overrides(me, other, allowDescriptorCopies)) {
iterator.remove();
}
else if (overrides(other, me)) {
else if (overrides(other, me, allowDescriptorCopies)) {
continue outerLoop;
}
}
@@ -133,7 +139,9 @@ public class OverridingUtil {
/**
* @return whether f overrides g
*/
public static <D extends CallableDescriptor> boolean overrides(@NotNull D f, @NotNull D g) {
public static <D extends CallableDescriptor> boolean overrides(
@NotNull D f, @NotNull D g, boolean allowDeclarationCopies
) {
// In a multi-module project different "copies" of the same class may be present in different libraries,
// that's why we use structural equivalence for members (DescriptorEquivalenceForOverrides).
@@ -142,11 +150,11 @@ public class OverridingUtil {
// we'll be getting sets of members that do not override each other, but are structurally equivalent.
// As other code relies on no equal descriptors passed here, we guard against f == g, but this may not be necessary
// Note that this is needed for the usage of this function in the IDE code
if (!f.equals(g) && DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(f.getOriginal(), g.getOriginal())) return true;
if (!f.equals(g) && DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(f.getOriginal(), g.getOriginal(), allowDeclarationCopies)) return true;
CallableDescriptor originalG = g.getOriginal();
for (D overriddenFunction : DescriptorUtils.getAllOverriddenDescriptors(f)) {
if (DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(originalG, overriddenFunction)) return true;
if (DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(originalG, overriddenFunction, allowDeclarationCopies)) return true;
}
return false;
}