From 9b89759755bc3b9b880d1be4b1433dd97fc9f946 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Wed, 8 Mar 2023 14:08:01 +0100 Subject: [PATCH] [FIR] Explicitly resolve synthetic property return type if necessary During implicit body resolve phase, we can encounter a reference to a not yet resolved Kotlin class that inherits a synthetic property from a Java class. In that case, resolve the return type in FirSyntheticPropertiesScope. #KT-57166 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 18 ++++++++++ ...eeOldFrontendDiagnosticsTestGenerated.java | 18 ++++++++++ ...siOldFrontendDiagnosticsTestGenerated.java | 18 ++++++++++ .../kotlin/fir/resolve/calls/Synthetics.kt | 21 +++++++++--- .../tower/FirInvokeResolveTowerExtension.kt | 3 +- .../calls/tower/FirTowerResolveTask.kt | 13 +++++--- .../fir/resolve/calls/tower/TowerLevels.kt | 7 ++-- ...ntheticAssignmentInLambdaExpressionBody.kt | 22 +++++++++++++ .../tests/j+k/syntheticPropertyOverridden.kt | 26 +++++++++++++++ .../j+k/syntheticPropertyOverridden2.fir.kt | 33 +++++++++++++++++++ .../tests/j+k/syntheticPropertyOverridden2.kt | 33 +++++++++++++++++++ .../test/runners/DiagnosticTestGenerated.java | 18 ++++++++++ 12 files changed, 218 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.fir.kt create mode 100644 compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index fdbb30ca60d..3e76f09bdcf 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -19570,6 +19570,24 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/j+k/supertypeUsesNested.kt"); } + @Test + @TestMetadata("syntheticAssignmentInLambdaExpressionBody.kt") + public void testSyntheticAssignmentInLambdaExpressionBody() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden.kt") + public void testSyntheticPropertyOverridden() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden2.kt") + public void testSyntheticPropertyOverridden2() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt"); + } + @Test @TestMetadata("traitDefaultCall.kt") public void testTraitDefaultCall() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 6fbb5147ece..c8d5aa00206 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -19570,6 +19570,24 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/j+k/supertypeUsesNested.kt"); } + @Test + @TestMetadata("syntheticAssignmentInLambdaExpressionBody.kt") + public void testSyntheticAssignmentInLambdaExpressionBody() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden.kt") + public void testSyntheticPropertyOverridden() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden2.kt") + public void testSyntheticPropertyOverridden2() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt"); + } + @Test @TestMetadata("traitDefaultCall.kt") public void testTraitDefaultCall() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index 302830932bf..f5afaf68c33 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -19576,6 +19576,24 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/j+k/supertypeUsesNested.kt"); } + @Test + @TestMetadata("syntheticAssignmentInLambdaExpressionBody.kt") + public void testSyntheticAssignmentInLambdaExpressionBody() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden.kt") + public void testSyntheticPropertyOverridden() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden2.kt") + public void testSyntheticPropertyOverridden2() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt"); + } + @Test @TestMetadata("traitDefaultCall.kt") public void testTraitDefaultCall() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt index d2740ef2705..362db411e7b 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/calls/Synthetics.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.declarations.utils.isStatic +import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol import org.jetbrains.kotlin.fir.symbols.impl.* @@ -40,20 +41,23 @@ class FirSyntheticPropertiesScope private constructor( val session: FirSession, private val baseScope: FirTypeScope, private val dispatchReceiverType: ConeKotlinType, - private val syntheticNamesProvider: FirSyntheticNamesProvider + private val syntheticNamesProvider: FirSyntheticNamesProvider, + private val returnTypeCalculator: ReturnTypeCalculator?, ) : FirContainingNamesAwareScope() { companion object { fun createIfSyntheticNamesProviderIsDefined( session: FirSession, dispatchReceiverType: ConeKotlinType, - baseScope: FirTypeScope + baseScope: FirTypeScope, + returnTypeCalculator: ReturnTypeCalculator? = null, ): FirSyntheticPropertiesScope? { val syntheticNamesProvider = session.syntheticNamesProvider ?: return null return FirSyntheticPropertiesScope( session, baseScope, dispatchReceiverType, - syntheticNamesProvider + syntheticNamesProvider, + returnTypeCalculator, ) } } @@ -104,7 +108,14 @@ class FirSyntheticPropertiesScope private constructor( if (getter.typeParameters.isNotEmpty()) return if (getter.valueParameters.isNotEmpty()) return if (getter.isStatic) return - val getterReturnType = (getter.returnTypeRef as? FirResolvedTypeRef)?.type + + var getterReturnType = (getter.returnTypeRef as? FirResolvedTypeRef)?.type + if (getterReturnType == null && needCheckForSetter) { + // During implicit body resolve phase, we can encounter a reference to a not yet resolved Kotlin class that inherits a + // synthetic property from a Java class. In that case, resolve the return type here, ignoring error types (e.g. cycles). + getterReturnType = returnTypeCalculator?.tryCalculateReturnTypeOrNull(getter)?.type?.takeUnless { it is ConeErrorType } + } + if ((getterReturnType as? ConeClassLikeType)?.lookupTag?.classId == StandardClassIds.Unit) return if (!getterSymbol.hasJavaOverridden()) return @@ -195,7 +206,7 @@ class FirSyntheticPropertiesScope private constructor( baseScope.processDirectOverriddenFunctionsWithBaseScope(symbolToStart) l@{ symbol, scope -> if (hasMatchingSetter) return@l ProcessorAction.STOP val baseDispatchReceiverType = symbol.dispatchReceiverType ?: return@l ProcessorAction.NEXT - val syntheticScope = FirSyntheticPropertiesScope(session, scope, baseDispatchReceiverType, syntheticNamesProvider) + val syntheticScope = FirSyntheticPropertiesScope(session, scope, baseDispatchReceiverType, syntheticNamesProvider, returnTypeCalculator) val baseProperties = syntheticScope.getProperties(propertyName) val propertyFound = baseProperties.any { val baseProperty = it.fir diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt index 56be0b556b7..dacac5e204b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirInvokeResolveTowerExtension.kt @@ -105,7 +105,8 @@ internal class FirInvokeResolveTowerExtension( towerDataElementsForName = towerDataElementsForName, invokeBuiltinExtensionMode = true ) { - it.runResolverForNoReceiver(invokeReceiverVariableWithNoReceiverInfo) + // Synthetic properties can never have an extension function type + it.runResolverForNoReceiver(invokeReceiverVariableWithNoReceiverInfo, skipSynthetics = true) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt index 0ce837c10ea..d31b0a1f71a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/FirTowerResolveTask.kt @@ -121,9 +121,11 @@ internal abstract class FirBaseTowerResolveTask( protected fun ReceiverValue.toMemberScopeTowerLevel( extensionReceiver: ReceiverValue? = null, contextReceiverGroup: ContextReceiverGroup? = null, + skipSynthetics: Boolean = false, ) = MemberScopeTowerLevel( components, this, givenExtensionReceiverOptions = contextReceiverGroup ?: listOfNotNull(extensionReceiver), + skipSynthetics = skipSynthetics, ) protected fun ContextReceiverGroup.toMemberScopeTowerLevel( @@ -315,7 +317,8 @@ internal open class FirTowerResolveTask( } suspend fun runResolverForNoReceiver( - info: CallInfo + info: CallInfo, + skipSynthetics: Boolean = false, ) { val emptyScopes = mutableSetOf() val implicitReceiverValuesWithEmptyScopes = mutableSetOf>() @@ -342,7 +345,8 @@ internal open class FirTowerResolveTask( info, group, implicitReceiverValuesWithEmptyScopes, - emptyScopes + emptyScopes, + skipSynthetics, ) }, onContextReceiverGroup = { contextReceiverGroup, towerGroup -> @@ -401,7 +405,8 @@ internal open class FirTowerResolveTask( info: CallInfo, parentGroup: TowerGroup, implicitReceiverValuesWithEmptyScopes: MutableSet>, - emptyScopes: MutableSet + emptyScopes: MutableSet, + skipSynthetics: Boolean, ) { processExtensionsThatHideMembers( info, receiver, TowerGroup.EmptyRoot, @@ -409,7 +414,7 @@ internal open class FirTowerResolveTask( ) processLevel( - receiver.toMemberScopeTowerLevel(), info, parentGroup.Member, + receiver.toMemberScopeTowerLevel(skipSynthetics = skipSynthetics), info, parentGroup.Member, onEmptyLevel = { implicitReceiverValuesWithEmptyScopes += receiver } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt index fadf1f4b05a..07552ce1f59 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/tower/TowerLevels.kt @@ -74,6 +74,7 @@ class MemberScopeTowerLevel( private val bodyResolveComponents: BodyResolveComponents, val dispatchReceiverValue: ReceiverValue, private val givenExtensionReceiverOptions: List, + private val skipSynthetics: Boolean, ) : TowerScopeLevel() { private val scopeSession: ScopeSession get() = bodyResolveComponents.scopeSession private val session: FirSession get() = bodyResolveComponents.session @@ -117,7 +118,7 @@ class MemberScopeTowerLevel( ) } - if (givenExtensionReceiverOptions.isEmpty()) { + if (givenExtensionReceiverOptions.isEmpty() && !skipSynthetics) { val dispatchReceiverType = dispatchReceiverValue.type val useSiteForSyntheticScope: FirTypeScope @@ -148,7 +149,9 @@ class MemberScopeTowerLevel( session, typeForSyntheticScope, useSiteForSyntheticScope, + bodyResolveComponents.returnTypeCalculator, ) + withSynthetic?.processScopeMembers { symbol -> empty = false output.consumeCandidate( @@ -291,7 +294,7 @@ class ContextReceiverGroupMemberScopeTowerLevel( givenExtensionReceiverOptions: List = emptyList(), ) : TowerScopeLevel() { private val memberScopeLevels = contextReceiverGroup.map { - MemberScopeTowerLevel(bodyResolveComponents, it, givenExtensionReceiverOptions) + MemberScopeTowerLevel(bodyResolveComponents, it, givenExtensionReceiverOptions, false) } override fun processFunctionsByName(info: CallInfo, processor: TowerScopeLevelProcessor>): ProcessResult { diff --git a/compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt b/compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt new file mode 100644 index 00000000000..0f25eed3a9d --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// ISSUE: KT-57166 +// File order is important. + +// FILE: InstanceObject.java +public interface InstanceObject { + long getDeallocTime(); + void setDeallocTime(long deallocTime); +} + +// FILE: LiveAllocationCaptureObject.kt +class LiveAllocationCaptureObject { + private fun queryJavaInstanceDelta() = run { + LiveAllocationInstanceObject().deallocTime = 42L + } +} + +// FILE: LiveAllocationInstanceObject.kt +class LiveAllocationInstanceObject: InstanceObject { + override fun getDeallocTime() = 42L + override fun setDeallocTime(deallocTime: Long) {} +} diff --git a/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt b/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt new file mode 100644 index 00000000000..f22626a5c8a --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt @@ -0,0 +1,26 @@ +// FIR_IDENTICAL +// ISSUE: KT-57166 + +// FILE: PsiType.java + +public abstract class PsiType { +} + +// FILE: JavaCodeFragment.java + +public interface JavaCodeFragment { + PsiType getThisType(); + void setThisType(PsiType psiType); +} + +// FILE: KtCodeFragment.kt + +abstract class KtCodeFragment : JavaCodeFragment { + private var thisType: PsiType? = null + + override fun getThisType() = thisType + + override fun setThisType(psiType: PsiType?) { + thisType = psiType + } +} diff --git a/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.fir.kt b/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.fir.kt new file mode 100644 index 00000000000..bcaf683633d --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.fir.kt @@ -0,0 +1,33 @@ +// ISSUE: KT-57166 + +// FILE: Modality.kt +enum class Modality { + FINAL +} + +// FILE: ClassDescriptor.java + +public interface ClassDescriptor { + @NotNull + Modality getModality(); +} + +// FILE: DeserializedClassDescriptor.kt + +object ProtoEnumFlags { + fun modality(): Modality = Modality.FINAL +} + +class DeserializedClassDescriptor : ClassDescriptor { + private val modality = ProtoEnumFlags.modality() + + override fun getModality() = modality +} + +fun modality(): Modality = Modality.FINAL + +class DeserializedClassDescriptor2 : ClassDescriptor { + private val modality = modality() + + override fun getModality() = modality +} diff --git a/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt b/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt new file mode 100644 index 00000000000..1655a63b08a --- /dev/null +++ b/compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt @@ -0,0 +1,33 @@ +// ISSUE: KT-57166 + +// FILE: Modality.kt +enum class Modality { + FINAL +} + +// FILE: ClassDescriptor.java + +public interface ClassDescriptor { + @NotNull + Modality getModality(); +} + +// FILE: DeserializedClassDescriptor.kt + +object ProtoEnumFlags { + fun modality(): Modality = Modality.FINAL +} + +class DeserializedClassDescriptor : ClassDescriptor { + private val modality = ProtoEnumFlags.modality() + + override fun getModality() = modality +} + +fun modality(): Modality = Modality.FINAL + +class DeserializedClassDescriptor2 : ClassDescriptor { + private val modality = modality() + + override fun getModality() = modality +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 3384555e26b..5071743003d 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -19576,6 +19576,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/j+k/supertypeUsesNested.kt"); } + @Test + @TestMetadata("syntheticAssignmentInLambdaExpressionBody.kt") + public void testSyntheticAssignmentInLambdaExpressionBody() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticAssignmentInLambdaExpressionBody.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden.kt") + public void testSyntheticPropertyOverridden() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden.kt"); + } + + @Test + @TestMetadata("syntheticPropertyOverridden2.kt") + public void testSyntheticPropertyOverridden2() throws Exception { + runTest("compiler/testData/diagnostics/tests/j+k/syntheticPropertyOverridden2.kt"); + } + @Test @TestMetadata("traitDefaultCall.kt") public void testTraitDefaultCall() throws Exception {