From 67410f7a5785dacc8262fbc15e5b1af85c8ea849 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 30 Sep 2019 15:54:46 +0300 Subject: [PATCH] Parametrize behavior of DescriptorEquivalenceForOverrides::areCallableDescriptorsEquivalent The changes introduced 471134d31ee7212d3c5fccea867f3f4007afda76 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 --- .../kotlin/backend/common/bridges/impl.kt | 4 +++- .../kotlin/resolve/DelegationResolver.kt | 10 +++++++- .../calls/smartcasts/IdentifierInfo.kt | 5 +++- .../results/OverloadingConflictResolver.kt | 16 +++++++++++-- .../kotlin/resolve/ExpectedResolveData.java | 2 +- .../DescriptorEquivalenceForOverrides.kt | 24 ++++++++++++------- .../kotlin/resolve/DescriptorUtils.kt | 5 +++- .../kotlin/resolve/OverridingUtil.java | 20 +++++++++++----- .../psi/patternMatching/KotlinPsiUnifier.kt | 2 +- .../kotlin/idea/search/usagesSearch/utils.kt | 8 ++++++- .../resolve/diagnostics/JsNameClashChecker.kt | 6 ++--- 11 files changed, 76 insertions(+), 26 deletions(-) diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt index b501ca53a81..8aa9b75e5f6 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized +import org.jetbrains.kotlin.resolve.descriptorUtil.isTypeRefinementEnabled +import org.jetbrains.kotlin.resolve.descriptorUtil.module fun generateBridgesForFunctionDescriptor( descriptor: FunctionDescriptor, @@ -128,6 +130,6 @@ fun firstSuperMethodFromKotlin( ): CallableMemberDescriptor? { return descriptor.overriddenDescriptors.firstOrNull { overridden -> overridden.modality != Modality.ABSTRACT && - (overridden == implementation || OverridingUtil.overrides(overridden, implementation)) + (overridden == implementation || OverridingUtil.overrides(overridden, implementation, overridden.module.isTypeRefinementEnabled())) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegationResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegationResolver.kt index 1ff2483111f..59e14cc48a8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegationResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegationResolver.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.psi.KtTypeReference import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE +import org.jetbrains.kotlin.resolve.descriptorUtil.isTypeRefinementEnabled +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.DelegationFilter import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.isDynamic @@ -172,7 +174,13 @@ class DelegationResolver private constructor( // this is the actual member of delegateExpressionType that we are delegating to (scope.getContributedFunctions(name, NoLookupLocation.WHEN_CHECK_OVERRIDES) + scope.getContributedVariables(name, NoLookupLocation.WHEN_CHECK_OVERRIDES)) - .firstOrNull { it == overriddenDescriptor || OverridingUtil.overrides(it, overriddenDescriptor) } + .firstOrNull { + it == overriddenDescriptor || OverridingUtil.overrides( + it, + overriddenDescriptor, + it.module.isTypeRefinementEnabled() + ) + } } actualDelegates.firstOrNull() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt index b0b625c7bf1..747e7c0e54c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/IdentifierInfo.kt @@ -61,7 +61,10 @@ interface IdentifierInfo { get() = kind == STABLE_VALUE override fun equals(other: Any?) = - other is Variable && DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent(variable, other.variable) + other is Variable && + DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent( + variable, other.variable, allowCopiesFromTheSameDeclaration = true + ) override fun hashCode() = variable.name.hashCode() * 31 + variable.containingDeclaration.original.hashCode() diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt index 98923a99ab2..12d95bd8dd8 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/results/OverloadingConflictResolver.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.synthetic.SyntheticMemberDescriptor import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode +import org.jetbrains.kotlin.resolve.descriptorUtil.isTypeRefinementEnabled import org.jetbrains.kotlin.resolve.descriptorUtil.varargParameterPosition import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -44,6 +45,8 @@ open class OverloadingConflictResolver( private val hasSAMConversion: ((C) -> Boolean)? ) { + private val isTypeRefinementEnabled by lazy { module.isTypeRefinementEnabled() } + private val resolvedCallHashingStrategy = object : TObjectHashingStrategy { override fun equals(call1: C?, call2: C?): Boolean = if (call1 != null && call2 != null) @@ -72,7 +75,10 @@ open class OverloadingConflictResolver( } val noEquivalentCalls = filterOutEquivalentCalls(fixedCandidates) - val noOverrides = OverridingUtil.filterOverrides(noEquivalentCalls) { a, b -> + val noOverrides = OverridingUtil.filterOverrides( + noEquivalentCalls, + isTypeRefinementEnabled + ) { a, b -> val aDescriptor = a.resultingDescriptor val bDescriptor = b.resultingDescriptor // Here we'd like to handle situation when we have two synthetic descriptors as in syntheticSAMExtensions.kt @@ -121,7 +127,13 @@ open class OverloadingConflictResolver( val me = meD.resultingDescriptor val other = otherD.resultingDescriptor val ignoreReturnType = isFromSources(me) != isFromSources(other) - if (DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent(me, other, ignoreReturnType)) { + if (DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent( + me, + other, + isTypeRefinementEnabled, + ignoreReturnType + ) + ) { continue@outerLoop } } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/resolve/ExpectedResolveData.java b/compiler/tests-common/tests/org/jetbrains/kotlin/resolve/ExpectedResolveData.java index ace832312b1..360429ed747 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/resolve/ExpectedResolveData.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/resolve/ExpectedResolveData.java @@ -329,7 +329,7 @@ public abstract class ExpectedResolveData { } private static void assertDescriptorsEqual(String message, DeclarationDescriptor expected, DeclarationDescriptor actual) { - if (DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(expected, actual)) { + if (DescriptorEquivalenceForOverrides.INSTANCE.areEquivalent(expected, actual, true)) { return; } String formatted = ""; diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt index 4c6ecc86ebe..7e9080649e5 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorEquivalenceForOverrides.kt @@ -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) } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index a3b69c50530..224f42c49b3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -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 \ No newline at end of file +fun ModuleDescriptor.getKotlinTypeRefiner(): KotlinTypeRefiner = getCapability(REFINER_CAPABILITY)?.value ?: KotlinTypeRefiner.Default + +@UseExperimental(TypeRefinement::class) +fun ModuleDescriptor.isTypeRefinementEnabled(): Boolean = getCapability(REFINER_CAPABILITY)?.value != null diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java index d6ac2aebdd8..d0e87ec94bd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java @@ -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 Set filterOutOverridden(@NotNull Set candidateSet) { - return filterOverrides(candidateSet, new Function2>() { + boolean allowDescriptorCopies = !candidateSet.isEmpty() && + DescriptorUtilsKt + .isTypeRefinementEnabled(DescriptorUtilsKt.getModule(candidateSet.iterator().next())); + + return filterOverrides(candidateSet, allowDescriptorCopies, new Function2>() { @Override public Pair invoke(D a, D b) { return new Pair(a, b); @@ -103,6 +108,7 @@ public class OverridingUtil { @NotNull public static Set filterOverrides( @NotNull Set candidateSet, + boolean allowDescriptorCopies, @NotNull Function2> transformFirst ) { if (candidateSet.size() <= 1) return candidateSet; @@ -115,10 +121,10 @@ public class OverridingUtil { Pair 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 boolean overrides(@NotNull D f, @NotNull D g) { + public static 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; } diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/psi/patternMatching/KotlinPsiUnifier.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/psi/patternMatching/KotlinPsiUnifier.kt index 455d146dc63..21ce4a65473 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/psi/patternMatching/KotlinPsiUnifier.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/psi/patternMatching/KotlinPsiUnifier.kt @@ -124,7 +124,7 @@ class KotlinPsiUnifier( } private fun matchDescriptors(d1: DeclarationDescriptor?, d2: DeclarationDescriptor?): Boolean { - if (DescriptorEquivalenceForOverrides.areEquivalent(d1, d2)) return true + if (DescriptorEquivalenceForOverrides.areEquivalent(d1, d2, allowCopiesFromTheSameDeclaration = true)) return true if (d2 in declarationPatternsToTargets[d1] || d1 in declarationPatternsToTargets[d2]) return true if (d1 == null || d2 == null) return false diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt index 3321fe3a124..e6304be539f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/utils.kt @@ -46,6 +46,8 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.descriptorUtil.isTypeRefinementEnabled +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode val KtDeclaration.descriptor: DeclarationDescriptor? @@ -265,7 +267,11 @@ fun PsiReference.isCallableOverrideUsage(declaration: KtNamedDeclaration): Boole when (it) { is KtDeclaration -> { val usageDescriptor = toDescriptor(it) - usageDescriptor != null && OverridingUtil.overrides(usageDescriptor, targetDescriptor) + usageDescriptor != null && OverridingUtil.overrides( + usageDescriptor, + targetDescriptor, + usageDescriptor.module.isTypeRefinementEnabled() + ) } is PsiMethod -> { declaration.toLightMethods().any { superMethod -> MethodSignatureUtil.isSuperMethod(superMethod, it) } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt index 7c2a0897a8f..59bdf2c3fb7 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsNameClashChecker.kt @@ -33,8 +33,6 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner -import org.jetbrains.kotlin.types.refinement.TypeRefinement class JsNameClashChecker( private val nameSuggestion: NameSuggestion, @@ -125,7 +123,9 @@ class JsNameClashChecker( } else { // If refinement is enabled, we can get duplicate descriptors for one and the same members (as refinement re-creates // descriptors), so, in this case, we have to compare descriptors structurally - DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent(existing, overrideDescriptor) + DescriptorEquivalenceForOverrides.areCallableDescriptorsEquivalent( + existing, overrideDescriptor, allowCopiesFromTheSameDeclaration = true + ) } }