FIR Java: implement correct type matching in SuperTypeScope

This commit is contained in:
Mikhail Glukhikh
2019-11-06 18:52:40 +03:00
parent 7fffd3d0f2
commit 30679ebfaf
14 changed files with 264 additions and 149 deletions
@@ -19,9 +19,9 @@ import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
import org.jetbrains.kotlin.fir.java.scopes.JavaClassEnhancementScope
import org.jetbrains.kotlin.fir.java.scopes.JavaClassUseSiteMemberScope
import org.jetbrains.kotlin.fir.java.scopes.JavaSuperTypeScope
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.impl.FirSuperTypeScope
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -109,7 +109,7 @@ class JavaSymbolProvider(
}
JavaClassUseSiteMemberScope(
regularClass, useSiteSession,
FirSuperTypeScope(useSiteSession, superTypeEnhancementScopes), declaredScope
JavaSuperTypeScope(regularClass, useSiteSession, superTypeEnhancementScopes), declaredScope
)
}
}
@@ -5,24 +5,18 @@
package org.jetbrains.kotlin.fir.java.scopes
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.*
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.STOP
import org.jetbrains.kotlin.fir.scopes.impl.AbstractFirUseSiteMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.FirSuperTypeScope
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.Name
class JavaClassUseSiteMemberScope(
@@ -30,84 +24,14 @@ class JavaClassUseSiteMemberScope(
session: FirSession,
superTypesScope: FirSuperTypeScope,
declaredMemberScope: FirScope
) : AbstractFirUseSiteMemberScope(session, superTypesScope, declaredMemberScope) {
) : AbstractFirUseSiteMemberScope(
session,
JavaOverrideChecker(session, if (klass is FirJavaClass) klass.javaTypeParameterStack else JavaTypeParameterStack.EMPTY),
superTypesScope,
declaredMemberScope
) {
internal val symbol = klass.symbol
private val javaTypeParameterStack: JavaTypeParameterStack =
if (klass is FirJavaClass) klass.javaTypeParameterStack else JavaTypeParameterStack.EMPTY
private fun isEqualTypes(a: ConeKotlinType, b: ConeKotlinType, substitutor: ConeSubstitutor): Boolean {
if (a is ConeFlexibleType) return isEqualTypes(a.lowerBound, b, substitutor)
if (b is ConeFlexibleType) return isEqualTypes(a, b.lowerBound, substitutor)
return if (a is ConeClassType && b is ConeClassType) {
a.lookupTag.classId.let { it.readOnlyToMutable() ?: it } == b.lookupTag.classId.let { it.readOnlyToMutable() ?: it }
} else {
with(context) {
isEqualTypeConstructors(
substitutor.substituteOrSelf(a).typeConstructor(),
substitutor.substituteOrSelf(b).typeConstructor()
)
}
}
}
private fun isEqualTypes(a: FirTypeRef, b: FirTypeRef, substitutor: ConeSubstitutor) =
isEqualTypes(
a.toNotNullConeKotlinType(session, javaTypeParameterStack),
b.toNotNullConeKotlinType(session, javaTypeParameterStack),
substitutor
)
override fun isOverriddenFunCheck(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
// NB: overrideCandidate is from Java and has no receiver
val receiverTypeRef = baseDeclaration.receiverTypeRef
val baseParameterTypes = listOfNotNull(receiverTypeRef) + baseDeclaration.valueParameters.map { it.returnTypeRef }
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
if (overrideCandidate.typeParameters.size != baseDeclaration.typeParameters.size) return false
val types = baseDeclaration.typeParameters.map {
ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false)
}
val substitution = substitutorByMap(overrideCandidate.typeParameters.map { it.symbol }.zip(types).toMap())
if (!overrideCandidate.typeParameters.zip(baseDeclaration.typeParameters).all { (a, b) ->
a.bounds.size == b.bounds.size && a.bounds.zip(b.bounds).all { (aBound, bBound) ->
isEqualTypes(aBound, bBound, substitution)
}
}
) return false
return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitution)
}
}
override fun isOverriddenPropertyCheck(overrideCandidate: FirCallableMemberDeclaration<*>, baseDeclaration: FirProperty): Boolean {
if (baseDeclaration.modality == Modality.FINAL) return false
val receiverTypeRef = baseDeclaration.receiverTypeRef
return when (overrideCandidate) {
is FirSimpleFunction -> {
if (receiverTypeRef == null) {
// TODO: setters
return overrideCandidate.valueParameters.isEmpty()
} else {
if (overrideCandidate.valueParameters.size != 1) return false
return isEqualTypes(receiverTypeRef, overrideCandidate.valueParameters.single().returnTypeRef, ConeSubstitutor.Empty)
}
}
is FirProperty -> {
val overrideReceiverTypeRef = overrideCandidate.receiverTypeRef
return when {
receiverTypeRef == null -> overrideReceiverTypeRef == null
overrideReceiverTypeRef == null -> false
else -> isEqualTypes(receiverTypeRef, overrideReceiverTypeRef, ConeSubstitutor.Empty)
}
}
else -> false
}
}
internal fun bindOverrides(name: Name) {
val overrideCandidates = mutableSetOf<FirFunctionSymbol<*>>()
declaredMemberScope.processFunctionsByName(name) {
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.java.scopes
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.modality
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable
import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractOverrideChecker
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
class JavaOverrideChecker internal constructor(
private val session: FirSession,
private val javaTypeParameterStack: JavaTypeParameterStack
) : 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)
return if (candidateType is ConeClassType && baseType is ConeClassType) {
candidateType.lookupTag.classId.let { it.readOnlyToMutable() ?: it } == baseType.lookupTag.classId.let { it.readOnlyToMutable() ?: it }
} else {
with(context) {
isEqualTypeConstructors(
substitutor.substituteOrSelf(candidateType).typeConstructor(),
substitutor.substituteOrSelf(baseType).typeConstructor()
)
}
}
}
override fun isEqualTypes(candidateTypeRef: FirTypeRef, baseTypeRef: FirTypeRef, substitutor: ConeSubstitutor) =
isEqualTypes(
candidateTypeRef.toNotNullConeKotlinType(session, javaTypeParameterStack),
baseTypeRef.toNotNullConeKotlinType(session, javaTypeParameterStack),
substitutor
)
override fun isOverriddenFunction(overrideCandidate: FirSimpleFunction, baseDeclaration: FirSimpleFunction): Boolean {
// NB: overrideCandidate is from Java and has no receiver
val receiverTypeRef = baseDeclaration.receiverTypeRef
val baseParameterTypes = listOfNotNull(receiverTypeRef) + baseDeclaration.valueParameters.map { it.returnTypeRef }
if (overrideCandidate.valueParameters.size != baseParameterTypes.size) return false
val substitutor = getSubstitutorIfTypeParametersAreCompatible(overrideCandidate, baseDeclaration) ?: return false
return overrideCandidate.valueParameters.zip(baseParameterTypes).all { (paramFromJava, baseType) ->
isEqualTypes(paramFromJava.returnTypeRef, baseType, substitutor)
}
}
override fun isOverriddenProperty(overrideCandidate: FirCallableMemberDeclaration<*>, baseDeclaration: FirProperty): Boolean {
if (baseDeclaration.modality == Modality.FINAL) return false
val receiverTypeRef = baseDeclaration.receiverTypeRef
return when (overrideCandidate) {
is FirSimpleFunction -> {
if (receiverTypeRef == null) {
// TODO: setters
return overrideCandidate.valueParameters.isEmpty()
} else {
if (overrideCandidate.valueParameters.size != 1) return false
return isEqualTypes(receiverTypeRef, overrideCandidate.valueParameters.single().returnTypeRef, ConeSubstitutor.Empty)
}
}
is FirProperty -> {
val overrideReceiverTypeRef = overrideCandidate.receiverTypeRef
return when {
receiverTypeRef == null -> overrideReceiverTypeRef == null
overrideReceiverTypeRef == null -> false
else -> isEqualTypes(receiverTypeRef, overrideReceiverTypeRef, ConeSubstitutor.Empty)
}
}
else -> false
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.java.scopes
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.impl.FirSuperTypeScope
class JavaSuperTypeScope(
klass: FirRegularClass,
session: FirSession,
scopes: List<FirScope>
) : FirSuperTypeScope(
session,
JavaOverrideChecker(session, if (klass is FirJavaClass) klass.javaTypeParameterStack else JavaTypeParameterStack.EMPTY),
scopes
)