[FIR] Fix scope of Java classes that inherit multiple properties with the same name

Use the receiver and context receiver types in addition to the property
name as cache keys for the synthetic property generation.
Also, fix logic that searches for accessor overrides by comparing
receiver and context receiver types.
Finally, let synthetic properties delegate their receiverParameter and
contextReceivers to their accessors.

#KT-65464 Fixed
#KT-66195
This commit is contained in:
Kirill Rakhman
2024-02-28 12:11:09 +01:00
committed by Space Team
parent 89ecb92551
commit 0889770ccd
6 changed files with 53 additions and 13 deletions
@@ -15,9 +15,16 @@ import org.jetbrains.kotlin.fir.java.scopes.JavaClassUseSiteMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScopeContext.ResultOfIntersection import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScopeContext.ResultOfIntersection
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
typealias SyntheticPropertiesCache = FirCache<Name, FirSyntheticPropertySymbol?, Pair<JavaClassUseSiteMemberScope, ResultOfIntersection<FirPropertySymbol>>> data class SyntheticPropertiesCacheKey(
val name: Name,
val receiverParameterType: ConeKotlinType?,
val contextReceiverTypes: List<ConeKotlinType>
)
typealias SyntheticPropertiesCache = FirCache<SyntheticPropertiesCacheKey, FirSyntheticPropertySymbol?, Pair<JavaClassUseSiteMemberScope, ResultOfIntersection<FirPropertySymbol>>>
class FirSyntheticPropertiesStorage(session: FirSession) : FirSessionComponent { class FirSyntheticPropertiesStorage(session: FirSession) : FirSessionComponent {
private val cachesFactory = session.firCachesFactory private val cachesFactory = session.firCachesFactory
@@ -268,6 +268,8 @@ class FirSignatureEnhancement(
val defaultQualifiers = firMethod.computeDefaultQualifiers() val defaultQualifiers = firMethod.computeDefaultQualifiers()
val overriddenMembers = precomputedOverridden ?: (firMethod as? FirSimpleFunction)?.overridden().orEmpty() val overriddenMembers = precomputedOverridden ?: (firMethod as? FirSimpleFunction)?.overridden().orEmpty()
// TODO(KT-66195) handle context receivers
val hasReceiver = overriddenMembers.any { it.receiverParameter != null } val hasReceiver = overriddenMembers.any { it.receiverParameter != null }
val newReceiverTypeRef = if (firMethod is FirSimpleFunction && hasReceiver) { val newReceiverTypeRef = if (firMethod is FirSimpleFunction && hasReceiver) {
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertySetter
import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.java.SyntheticPropertiesCacheKey
import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass import org.jetbrains.kotlin.fir.java.declarations.FirJavaClass
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethodCopy import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethodCopy
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.MembersByScope
import org.jetbrains.kotlin.fir.scopes.impl.isIntersectionOverride import org.jetbrains.kotlin.fir.scopes.impl.isIntersectionOverride
import org.jetbrains.kotlin.fir.scopes.impl.similarFunctionsOrBothProperties import org.jetbrains.kotlin.fir.scopes.impl.similarFunctionsOrBothProperties
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptorRepresentation
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
@@ -47,6 +49,7 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
/** /**
@@ -163,7 +166,12 @@ class JavaClassUseSiteMemberScope(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
for (overriddenProperty in propertiesFromSupertypes as List<ResultOfIntersection<FirPropertySymbol>>) { for (overriddenProperty in propertiesFromSupertypes as List<ResultOfIntersection<FirPropertySymbol>>) {
val overrideInClass = syntheticPropertyCache.getValue(name, this to overriddenProperty) val key = SyntheticPropertiesCacheKey(
name,
overriddenProperty.chosenSymbol.receiverParameter?.typeRef?.coneType,
overriddenProperty.chosenSymbol.resolvedContextReceivers.ifNotEmpty { map { it.typeRef.coneType } } ?: emptyList()
)
val overrideInClass = syntheticPropertyCache.getValue(key, this to overriddenProperty)
val chosenSymbol = overrideInClass ?: overriddenProperty.chosenSymbol val chosenSymbol = overrideInClass ?: overriddenProperty.chosenSymbol
directOverriddenProperties[chosenSymbol] = listOf(overriddenProperty) directOverriddenProperties[chosenSymbol] = listOf(overriddenProperty)
@@ -208,18 +216,20 @@ class JavaClassUseSiteMemberScope(
): FirNamedFunctionSymbol? { ): FirNamedFunctionSymbol? {
val specialGetterName = if (canUseSpecialGetters) getBuiltinSpecialPropertyGetterName() else null val specialGetterName = if (canUseSpecialGetters) getBuiltinSpecialPropertyGetterName() else null
val name = specialGetterName?.asString() ?: JvmAbi.getterName(fir.name.asString()) val name = specialGetterName?.asString() ?: JvmAbi.getterName(fir.name.asString())
return findGetterByName(name, scope) return findGetterOverride(name, scope)
} }
private fun FirPropertySymbol.findGetterByName( private fun FirPropertySymbol.findGetterOverride(
getterName: String, getterName: String,
scope: FirScope, scope: FirScope,
): FirNamedFunctionSymbol? { ): FirNamedFunctionSymbol? {
val propertyFromSupertype = fir val propertyFromSupertype = fir
val expectedReturnType = propertyFromSupertype.returnTypeRef.coneTypeSafe<ConeKotlinType>() val expectedReturnType = propertyFromSupertype.returnTypeRef.coneTypeSafe<ConeKotlinType>()
val receiverCount = (if (receiverParameter != null) 1 else 0) + resolvedContextReceivers.size
return scope.getFunctions(Name.identifier(getterName)).firstNotNullOfOrNull factory@{ candidateSymbol -> return scope.getFunctions(Name.identifier(getterName)).firstNotNullOfOrNull factory@{ candidateSymbol ->
val candidate = candidateSymbol.fir val candidate = candidateSymbol.fir
if (candidate.valueParameters.isNotEmpty()) return@factory null if (candidate.valueParameters.size != receiverCount) return@factory null
if (!checkValueParameters(candidate)) return@factory null
val candidateReturnType = candidate.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack) val candidateReturnType = candidate.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
@@ -239,14 +249,18 @@ class JavaClassUseSiteMemberScope(
scope: FirScope, scope: FirScope,
): FirNamedFunctionSymbol? { ): FirNamedFunctionSymbol? {
val propertyType = fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return null val propertyType = fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return null
val receiverCount = (if (receiverParameter != null) 1 else 0) + resolvedContextReceivers.size
return scope.getFunctions(Name.identifier(JvmAbi.setterName(fir.name.asString()))).firstNotNullOfOrNull factory@{ candidateSymbol -> return scope.getFunctions(Name.identifier(JvmAbi.setterName(fir.name.asString()))).firstNotNullOfOrNull factory@{ candidateSymbol ->
val candidate = candidateSymbol.fir val candidate = candidateSymbol.fir
if (candidate.valueParameters.size != 1) return@factory null
if (candidate.valueParameters.size != receiverCount + 1) return@factory null
if (!checkValueParameters(candidate)) return@factory null
if (!candidate.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack).isUnit) return@factory null if (!candidate.returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack).isUnit) return@factory null
val parameterType = val parameterType =
candidate.valueParameters.single().returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack) candidate.valueParameters.last().returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
candidateSymbol.takeIf { candidateSymbol.takeIf {
candidate.isAcceptableAsAccessorOverride() && AbstractTypeChecker.equalTypes( candidate.isAcceptableAsAccessorOverride() && AbstractTypeChecker.equalTypes(
@@ -256,6 +270,23 @@ class JavaClassUseSiteMemberScope(
} }
} }
private fun FirPropertySymbol.checkValueParameters(candidate: FirSimpleFunction): Boolean {
var parameterIndex = 0
for (contextReceiver in this.resolvedContextReceivers) {
if (contextReceiver.typeRef.coneType.computeJvmDescriptorRepresentation() !=
candidate.valueParameters[parameterIndex++].returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
.computeJvmDescriptorRepresentation()
) {
return false
}
}
return receiverParameter == null ||
receiverParameter!!.typeRef.coneType.computeJvmDescriptorRepresentation() ==
candidate.valueParameters[parameterIndex].returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
.computeJvmDescriptorRepresentation()
}
private fun FirSimpleFunction.isAcceptableAsAccessorOverride(): Boolean { private fun FirSimpleFunction.isAcceptableAsAccessorOverride(): Boolean {
// We don't accept here accessors with type parameters from Kotlin to avoid strange cases like KT-59038 // We don't accept here accessors with type parameters from Kotlin to avoid strange cases like KT-59038
// However, we (temporarily, see below) accept accessors from Kotlin in general to keep K1 compatibility in cases like KT-59550 // However, we (temporarily, see below) accept accessors from Kotlin in general to keep K1 compatibility in cases like KT-59550
@@ -62,7 +62,7 @@ class FirSyntheticProperty @FirImplementationDetail internal constructor(
get() = false get() = false
override val receiverParameter: FirReceiverParameter? override val receiverParameter: FirReceiverParameter?
get() = null get() = getter.receiverParameter
override val isVal: Boolean override val isVal: Boolean
get() = !isVar get() = !isVar
@@ -84,7 +84,7 @@ class FirSyntheticProperty @FirImplementationDetail internal constructor(
get() = FirPropertyBodyResolveState.ALL_BODIES_RESOLVED get() = FirPropertyBodyResolveState.ALL_BODIES_RESOLVED
override val contextReceivers: List<FirContextReceiver> override val contextReceivers: List<FirContextReceiver>
get() = emptyList() get() = getter.contextReceivers
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) { override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
returnTypeRef.accept(visitor, data) returnTypeRef.accept(visitor, data)
@@ -47,7 +47,7 @@ class FirSyntheticPropertyAccessor @FirImplementationDetail internal constructor
get() = delegate.dispatchReceiverType get() = delegate.dispatchReceiverType
override val receiverParameter: FirReceiverParameter? override val receiverParameter: FirReceiverParameter?
get() = null get() = delegate.receiverParameter
override val deprecationsProvider: DeprecationsProvider override val deprecationsProvider: DeprecationsProvider
get() = delegate.deprecationsProvider get() = delegate.deprecationsProvider
@@ -75,7 +75,7 @@ class FirSyntheticPropertyAccessor @FirImplementationDetail internal constructor
} }
override val contextReceivers: List<FirContextReceiver> override val contextReceivers: List<FirContextReceiver>
get() = emptyList() get() = delegate.contextReceivers
override val controlFlowGraphReference: FirControlFlowGraphReference? = null override val controlFlowGraphReference: FirControlFlowGraphReference? = null
@@ -51,7 +51,7 @@ class F : J() {
class F2 : JOverridesRegular() { class F2 : JOverridesRegular() {
fun test() { fun test() {
a a
"".<!UNRESOLVED_REFERENCE!>a<!> "".a
} }
} }
@@ -65,6 +65,6 @@ class F3 : JOverridesExtension() {
class F4 : JOVerridesBoth() { class F4 : JOVerridesBoth() {
fun test() { fun test() {
a a
"".<!UNRESOLVED_REFERENCE!>a<!> "".a
} }
} }