[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:
committed by
Space Team
parent
89ecb92551
commit
0889770ccd
+8
-1
@@ -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
|
||||
|
||||
+2
@@ -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) {
|
||||
|
||||
+37
-6
@@ -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
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user