From 0889770ccd5d7d8171c2e113ddb4873d57714b3d Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Wed, 28 Feb 2024 12:11:09 +0100 Subject: [PATCH] [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 --- .../fir/java/FirSyntheticPropertiesStorage.kt | 9 +++- .../java/enhancement/SignatureEnhancement.kt | 2 + .../scopes/JavaClassUseSiteMemberScope.kt | 43 ++++++++++++++++--- .../synthetic/FirSyntheticProperty.kt | 4 +- .../synthetic/FirSyntheticPropertyAccessor.kt | 4 +- .../kjkPropertyAndExtensionProperty.fir.kt | 4 +- 6 files changed, 53 insertions(+), 13 deletions(-) diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirSyntheticPropertiesStorage.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirSyntheticPropertiesStorage.kt index 535aaeabe8c..9c66a24505c 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirSyntheticPropertiesStorage.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirSyntheticPropertiesStorage.kt @@ -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>> +data class SyntheticPropertiesCacheKey( + val name: Name, + val receiverParameterType: ConeKotlinType?, + val contextReceiverTypes: List +) + +typealias SyntheticPropertiesCache = FirCache>> class FirSyntheticPropertiesStorage(session: FirSession) : FirSessionComponent { private val cachesFactory = session.firCachesFactory diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt index ebe6cb73ff1..80c7c626ee1 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/enhancement/SignatureEnhancement.kt @@ -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) { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt index 46945b91c7d..d4bef126bf6 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt @@ -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>) { - 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() + 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() ?: 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 diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticProperty.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticProperty.kt index 6e37bb7fe0c..9c103533769 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticProperty.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticProperty.kt @@ -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 - get() = emptyList() + get() = getter.contextReceivers override fun acceptChildren(visitor: FirVisitor, data: D) { returnTypeRef.accept(visitor, data) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticPropertyAccessor.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticPropertyAccessor.kt index fb77f6a0361..391b1b2dcbd 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticPropertyAccessor.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/synthetic/FirSyntheticPropertyAccessor.kt @@ -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 - get() = emptyList() + get() = delegate.contextReceivers override val controlFlowGraphReference: FirControlFlowGraphReference? = null diff --git a/compiler/testData/diagnostics/tests/j+k/kjkPropertyAndExtensionProperty.fir.kt b/compiler/testData/diagnostics/tests/j+k/kjkPropertyAndExtensionProperty.fir.kt index fdd4e5ae8c3..e75a8da61fd 100644 --- a/compiler/testData/diagnostics/tests/j+k/kjkPropertyAndExtensionProperty.fir.kt +++ b/compiler/testData/diagnostics/tests/j+k/kjkPropertyAndExtensionProperty.fir.kt @@ -51,7 +51,7 @@ class F : J() { class F2 : JOverridesRegular() { fun test() { a - "".a + "".a } } @@ -65,6 +65,6 @@ class F3 : JOverridesExtension() { class F4 : JOVerridesBoth() { fun test() { a - "".a + "".a } }