[FIR Java] Soften rules for matching types for may-be-special-builtins

This commit is workaround for cases like
MyJavaMap<KK : Bound, V> : java.util.Map<K, V>.
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.
This commit is contained in:
Mikhail Glukhikh
2020-10-15 11:17:41 +03:00
parent 81529a835b
commit 7f3d0af4f7
4 changed files with 58 additions and 11 deletions
@@ -0,0 +1,9 @@
// FULL_JDK
import java.util.EnumMap
typealias SomeEnumMap = EnumMap<String, String?>
fun test(map: SomeEnumMap, qualifier: String?) {
val result = map[qualifier]
}
@@ -0,0 +1,5 @@
FILE: EnumMapGet.kt
public final typealias SomeEnumMap = R|java/util/EnumMap<kotlin/String, kotlin/String?>|
public final fun test(map: R|SomeEnumMap|, qualifier: R|kotlin/String?|): R|kotlin/Unit| {
lval result: R|kotlin/String?| = R|<local>/map|.R|FakeOverride<java/util/EnumMap.get: R|kotlin/String?|>|(R|<local>/qualifier|)
}
@@ -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");
@@ -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<K extends Enum, V> implements Map<K, V>
// 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<FirTypeParameterRef>.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)
}
}