Parametrize behavior of DescriptorEquivalenceForOverrides::areCallableDescriptorsEquivalent
The changes introduced471134d31eare 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 Before471134dwe 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. After471134d, 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:
@@ -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 <Signature> 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()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T : CallableMemberDescriptor> 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()
|
||||
|
||||
+4
-1
@@ -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()
|
||||
|
||||
|
||||
+14
-2
@@ -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<C : Any>(
|
||||
private val hasSAMConversion: ((C) -> Boolean)?
|
||||
) {
|
||||
|
||||
private val isTypeRefinementEnabled by lazy { module.isTypeRefinementEnabled() }
|
||||
|
||||
private val resolvedCallHashingStrategy = object : TObjectHashingStrategy<C> {
|
||||
override fun equals(call1: C?, call2: C?): Boolean =
|
||||
if (call1 != null && call2 != null)
|
||||
@@ -72,7 +75,10 @@ open class OverloadingConflictResolver<C : Any>(
|
||||
}
|
||||
|
||||
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<C : Any>(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
+16
-8
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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) }
|
||||
|
||||
+3
-3
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user