From 7f3d0af4f75534effc552b87e79e9eabd92d8113 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 15 Oct 2020 11:17:41 +0300 Subject: [PATCH] [FIR Java] Soften rules for matching types for may-be-special-builtins This commit is workaround for cases like MyJavaMap : java.util.Map. After signature changing and substitution we have MyJavaMap.get(Object) declared and MyJavaMap.get(KK) from supertype, which can't be otherwise matched as overriding one another. --- .../resolveWithStdlib/problems/EnumMapGet.kt | 9 ++++ .../resolveWithStdlib/problems/EnumMapGet.txt | 5 ++ ...FirDiagnosticsWithStdlibTestGenerated.java | 5 ++ .../fir/java/scopes/JavaOverrideChecker.kt | 50 +++++++++++++++---- 4 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.txt diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.kt new file mode 100644 index 00000000000..37dab26554f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.kt @@ -0,0 +1,9 @@ +// FULL_JDK + +import java.util.EnumMap + +typealias SomeEnumMap = EnumMap + +fun test(map: SomeEnumMap, qualifier: String?) { + val result = map[qualifier] +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.txt new file mode 100644 index 00000000000..87c0213999f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.txt @@ -0,0 +1,5 @@ +FILE: EnumMapGet.kt + public final typealias SomeEnumMap = R|java/util/EnumMap| + public final fun test(map: R|SomeEnumMap|, qualifier: R|kotlin/String?|): R|kotlin/Unit| { + lval result: R|kotlin/String?| = R|/map|.R|FakeOverride|(R|/qualifier|) + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java index 29bfdc6f9fc..4c9b3b770bd 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java @@ -1199,6 +1199,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/DeepCopyIrTree.kt"); } + @TestMetadata("EnumMapGet.kt") + public void testEnumMapGet() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/EnumMapGet.kt"); + } + @TestMetadata("invokePriority.kt") public void testInvokePriority() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/problems/invokePriority.kt"); diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt index 735530875fe..863783571f1 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaOverrideChecker.kt @@ -14,8 +14,11 @@ import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker +import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor +import org.jetbrains.kotlin.fir.symbols.StandardClassIds import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.load.java.SpecialGenericSignatures class JavaOverrideChecker internal constructor( private val session: FirSession, @@ -23,11 +26,27 @@ class JavaOverrideChecker internal constructor( ) : FirAbstractOverrideChecker() { private val context: ConeTypeContext = session.typeContext - private fun isEqualTypes(candidateType: ConeKotlinType, baseType: ConeKotlinType, substitutor: ConeSubstitutor): Boolean { - if (candidateType is ConeFlexibleType) return isEqualTypes(candidateType.lowerBound, baseType, substitutor) - if (baseType is ConeFlexibleType) return isEqualTypes(candidateType, baseType.lowerBound, substitutor) + private fun isEqualTypes( + candidateType: ConeKotlinType, + baseType: ConeKotlinType, + substitutor: ConeSubstitutor, + mayBeSpecialBuiltIn: Boolean + ): Boolean { + if (candidateType is ConeFlexibleType) return isEqualTypes(candidateType.lowerBound, baseType, substitutor, mayBeSpecialBuiltIn) + if (baseType is ConeFlexibleType) return isEqualTypes(candidateType, baseType.lowerBound, substitutor, mayBeSpecialBuiltIn) if (candidateType is ConeClassLikeType && baseType is ConeClassLikeType) { - return candidateType.lookupTag.classId.let { it.readOnlyToMutable() ?: it } == baseType.lookupTag.classId.let { it.readOnlyToMutable() ?: it } + return candidateType.lookupTag.classId.let { it.readOnlyToMutable() ?: it } == + baseType.lookupTag.classId.let { it.readOnlyToMutable() ?: it } + } + // TODO: handle the situation in more proper way + // Typical case: class EnumMap implements Map + // We have containsKey(Object) in Map which is enhanced to containsKey(K) in supertype scope + // In EnumMap we have overridden containsKey(Object) which is not yet enhanced + // K may be substituted but after that we will get containsKey(Enum) from supertype, which still does not match... + if (mayBeSpecialBuiltIn && baseType is ConeTypeParameterType && + candidateType is ConeClassLikeType && candidateType.classId == StandardClassIds.Any + ) { + return true } return with(context) { areEqualTypeConstructors( @@ -37,12 +56,17 @@ class JavaOverrideChecker internal constructor( } } - private fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor) = - isEqualTypes( - candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack), - baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack), - substitutor - ) + private fun isEqualTypes( + candidateTypeRef: FirTypeRef, + baseTypeRef: FirTypeRef, + substitutor: ConeSubstitutor, + mayBeSpecialBuiltIn: Boolean = false + ) = isEqualTypes( + candidateTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack), + baseTypeRef.toConeKotlinTypeProbablyFlexible(session, javaTypeParameterStack), + substitutor, + mayBeSpecialBuiltIn + ) private fun Collection.buildErasure() = associate { val symbol = it.symbol @@ -97,8 +121,12 @@ class JavaOverrideChecker internal constructor( if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false val substitutor = buildTypeParametersSubstitutorIfCompatible(overrideCandidate, baseDeclaration) ?: return false + val jvmDescriptor by lazy { baseDeclaration.computeJvmDescriptor() } + val mayBeSpecialBuiltIn = + baseDeclaration.name in SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SHORT_NAMES && + SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SIGNATURES.any { it.endsWith(jvmDescriptor) } return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) -> - isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor) + isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor, mayBeSpecialBuiltIn) } }