[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.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
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 {
private val cachesFactory = session.firCachesFactory
@@ -268,6 +268,8 @@ class FirSignatureEnhancement(
val defaultQualifiers = firMethod.computeDefaultQualifiers()
val overriddenMembers = precomputedOverridden ?: (firMethod as? FirSimpleFunction)?.overridden().orEmpty()
// TODO(KT-66195) handle context receivers
val hasReceiver = overriddenMembers.any { it.receiverParameter != null }
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.buildSyntheticProperty
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.FirJavaMethod
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.similarFunctionsOrBothProperties
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.types.*
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.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import org.jetbrains.kotlin.utils.addToStdlib.runIf
/**
@@ -163,7 +166,12 @@ class JavaClassUseSiteMemberScope(
@Suppress("UNCHECKED_CAST")
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
directOverriddenProperties[chosenSymbol] = listOf(overriddenProperty)
@@ -208,18 +216,20 @@ class JavaClassUseSiteMemberScope(
): FirNamedFunctionSymbol? {
val specialGetterName = if (canUseSpecialGetters) getBuiltinSpecialPropertyGetterName() else null
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,
scope: FirScope,
): FirNamedFunctionSymbol? {
val propertyFromSupertype = fir
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 ->
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)
@@ -239,14 +249,18 @@ class JavaClassUseSiteMemberScope(
scope: FirScope,
): FirNamedFunctionSymbol? {
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 ->
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
val parameterType =
candidate.valueParameters.single().returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
candidate.valueParameters.last().returnTypeRef.toConeKotlinTypeProbablyFlexible(session, typeParameterStack)
candidateSymbol.takeIf {
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 {
// 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
@@ -62,7 +62,7 @@ class FirSyntheticProperty @FirImplementationDetail internal constructor(
get() = false
override val receiverParameter: FirReceiverParameter?
get() = null
get() = getter.receiverParameter
override val isVal: Boolean
get() = !isVar
@@ -84,7 +84,7 @@ class FirSyntheticProperty @FirImplementationDetail internal constructor(
get() = FirPropertyBodyResolveState.ALL_BODIES_RESOLVED
override val contextReceivers: List<FirContextReceiver>
get() = emptyList()
get() = getter.contextReceivers
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
returnTypeRef.accept(visitor, data)
@@ -47,7 +47,7 @@ class FirSyntheticPropertyAccessor @FirImplementationDetail internal constructor
get() = delegate.dispatchReceiverType
override val receiverParameter: FirReceiverParameter?
get() = null
get() = delegate.receiverParameter
override val deprecationsProvider: DeprecationsProvider
get() = delegate.deprecationsProvider
@@ -75,7 +75,7 @@ class FirSyntheticPropertyAccessor @FirImplementationDetail internal constructor
}
override val contextReceivers: List<FirContextReceiver>
get() = emptyList()
get() = delegate.contextReceivers
override val controlFlowGraphReference: FirControlFlowGraphReference? = null
@@ -51,7 +51,7 @@ class F : J() {
class F2 : JOverridesRegular() {
fun test() {
a
"".<!UNRESOLVED_REFERENCE!>a<!>
"".a
}
}
@@ -65,6 +65,6 @@ class F3 : JOverridesExtension() {
class F4 : JOVerridesBoth() {
fun test() {
a
"".<!UNRESOLVED_REFERENCE!>a<!>
"".a
}
}