From 3a81174a4c7bd13783b8f6a30f7887c8ffec0d73 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 26 Oct 2022 17:14:09 +0200 Subject: [PATCH] FIR2IR: support static fake overrides (functions) #KT-53441 Fixed --- .../kotlin/fir/backend/ConversionUtils.kt | 18 ++++--- .../generators/CallAndReferenceGenerator.kt | 3 +- .../generators/FakeOverrideGenerator.kt | 47 ++++++++++++------- .../FirBlackBoxCodegenTestGenerated.java | 12 +++++ .../scopes/impl/FirFakeOverrideGenerator.kt | 7 +++ .../box/fir/assertEqualsFakeOverride.kt | 31 ++++++++++++ .../package/inheritedPackageStaticFunction.kt | 20 ++++++++ .../IrBlackBoxCodegenTestGenerated.java | 12 +++++ 8 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 compiler/testData/codegen/box/fir/assertEqualsFakeOverride.kt create mode 100644 compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticFunction.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index dd7d67b86de..c3dc200c65e 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -179,7 +179,7 @@ fun FirReference.toSymbolForCall( dispatchReceiver: FirExpression, conversionScope: Fir2IrConversionScope, preferGetter: Boolean = true, - explicitReceiver: FirExpression? = null, // Actual only for callable references + explicitReceiver: FirExpression? = null, isDelegate: Boolean = false, isReference: Boolean = false ): IrSymbol? { @@ -227,23 +227,27 @@ private fun FirCallableSymbol<*>.toSymbolForCall( isDelegate: Boolean = false, isReference: Boolean = false ): IrSymbol? { - val dispatchReceiverLookupTag = when { - dispatchReceiver is FirNoReceiverExpression -> { + val dispatchReceiverType = when (dispatchReceiver) { + is FirNoReceiverExpression -> { val containingClass = containingClassLookupTag() if (containingClass != null && containingClass.classId != StandardClassIds.Any) { // Make sure that symbol is not extension and is not from inline class - val coneType = ((explicitReceiver as? FirResolvedQualifier)?.symbol as? FirClassSymbol)?.defaultType() - coneType?.findClassRepresentation(coneType, declarationStorage.session) + ((explicitReceiver as? FirResolvedQualifier)?.symbol as? FirClassSymbol)?.defaultType() } else { null } } + is FirResolvedQualifier -> { + if (isStatic) (dispatchReceiver.symbol as? FirClassSymbol)?.defaultType() else dispatchReceiver.typeRef.coneType + } + else -> { - val coneType = dispatchReceiver.typeRef.coneType - dispatchReceiver.typeRef.coneType.findClassRepresentation(coneType, declarationStorage.session) + dispatchReceiver.typeRef.coneType } } + val dispatchReceiverLookupTag = dispatchReceiverType?.findClassRepresentation(dispatchReceiverType, declarationStorage.session) + return when (this) { is FirSimpleSyntheticPropertySymbol -> { if (isDelegate) { diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 9571f6a037d..3992922afd9 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -399,7 +399,8 @@ class CallAndReferenceGenerator( val symbol = calleeReference.toSymbolForCall( dispatchReceiver, - conversionScope + conversionScope, + explicitReceiver = qualifiedAccess.explicitReceiver ) // We might have had a dynamic receiver, but resolved diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt index 2f4c6d31b27..f5c4aedc2c7 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/FakeOverrideGenerator.kt @@ -11,22 +11,17 @@ import org.jetbrains.kotlin.fir.backend.Fir2IrConversionScope import org.jetbrains.kotlin.fir.backend.Fir2IrDeclarationStorage import org.jetbrains.kotlin.fir.backend.unwrapSubstitutionAndIntersectionOverrides import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.declarations.utils.allowsToHaveFakeOverride -import org.jetbrains.kotlin.fir.declarations.utils.isExpect -import org.jetbrains.kotlin.fir.declarations.utils.isLocal -import org.jetbrains.kotlin.fir.declarations.utils.visibility +import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.toSymbol -import org.jetbrains.kotlin.fir.scopes.FirTypeScope -import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions -import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenProperties +import org.jetbrains.kotlin.fir.scopes.* import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator -import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.isStatic import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol @@ -98,18 +93,27 @@ class FakeOverrideGenerator( // This parameter is only needed for data-class methods that is irrelevant for lazy library classes realDeclarationSymbols = emptySet() ) + if (firClass.isEnumClass) return@buildList // F/O for values/valueOf/entries aren't needed, for other members aren't possible + val staticScope = firClass.scopeProvider.getStaticMemberScopeForCallables(firClass, session, scopeSession) + if (staticScope != null) { + generateFakeOverridesForName( + irClass, staticScope, name, firClass, this, + // This parameter is only needed for data-class methods that is irrelevant for lazy library classes + realDeclarationSymbols = emptySet() + ) + } } internal fun generateFakeOverridesForName( irClass: IrClass, - useSiteMemberScope: FirTypeScope, + useSiteOrStaticScope: FirScope, name: Name, firClass: FirClass, result: MutableList, realDeclarationSymbols: Set> ) { val isLocal = firClass !is FirRegularClass || firClass.isLocal - useSiteMemberScope.processFunctionsByName(name) { functionSymbol -> + useSiteOrStaticScope.processFunctionsByName(name) { functionSymbol -> createFakeOverriddenIfNeeded( firClass, irClass, isLocal, functionSymbol, declarationStorage::getCachedIrFunction, @@ -129,11 +133,11 @@ class FakeOverrideGenerator( }, realDeclarationSymbols, FirTypeScope::getDirectOverriddenFunctions, - useSiteMemberScope, + useSiteOrStaticScope, ) } - useSiteMemberScope.processPropertiesByName(name) { propertySymbol -> + useSiteOrStaticScope.processPropertiesByName(name) { propertySymbol -> createFakeOverriddenIfNeeded( firClass, irClass, isLocal, propertySymbol, declarationStorage::getCachedIrProperty, @@ -154,7 +158,7 @@ class FakeOverrideGenerator( }, realDeclarationSymbols, FirTypeScope::getDirectOverriddenProperties, - useSiteMemberScope, + useSiteOrStaticScope, ) } } @@ -191,13 +195,13 @@ class FakeOverrideGenerator( containsErrorTypes: (I) -> Boolean, realDeclarationSymbols: Set>, computeDirectOverridden: FirTypeScope.(S) -> List, - scope: FirTypeScope, + scope: FirScope, ) { if (originalSymbol !is S) return val classLookupTag = klass.symbol.toLookupTag() val originalDeclaration = originalSymbol.fir - if (originalSymbol.dispatchReceiverClassLookupTagOrNull() == classLookupTag && !originalDeclaration.origin.fromSupertypes) return + if (originalSymbol.containingClassLookupTag() == classLookupTag && !originalDeclaration.origin.fromSupertypes) return // Data classes' methods from Any (toString/equals/hashCode) are not handled by the line above because they have Any-typed dispatch receiver // (there are no special FIR method for them, it's just fake overrides) // But they are treated differently in IR (real declarations have already been declared before) and such methods are present among realDeclarationSymbols @@ -321,13 +325,17 @@ class FakeOverrideGenerator( private inline fun > computeBaseSymbols( symbol: S, directOverridden: FirTypeScope.(S) -> List, - scope: FirTypeScope, + scope: FirScope, containingClass: ConeClassLikeLookupTag, ): List { if (symbol.fir.origin == FirDeclarationOrigin.SubstitutionOverride) { return listOf(symbol.originalForSubstitutionOverride!!) } + if (scope !is FirTypeScope) { + return emptyList() + } + return scope.directOverridden(symbol).map { // Unwrapping should happen only for fake overrides members from the same class, not from supertypes if (it.fir.isSubstitutionOverride && it.dispatchReceiverClassLookupTagOrNull() == containingClass) @@ -390,8 +398,11 @@ class FakeOverrideGenerator( ?: return emptyList() return superClasses.mapNotNull { superClass -> - if (superClass == overriddenContainingIrClass) { - // `overridden` was a FIR declaration in some of the supertypes + if (superClass == overriddenContainingIrClass || + // Note: Kotlin static scopes cannot find base symbol in this case, so we have to fallback to the very base declaration + overridden.isStatic && superClass.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB + ) { + // `overridden` was a FIR declaration in some supertypes irProducer(overridden) } else { // There were no FIR declaration in supertypes, but we know that we have fake overrides in some of them diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 73b01be669a..d7622dff9dd 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -18031,6 +18031,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/fir"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("assertEqualsFakeOverride.kt") + public void testAssertEqualsFakeOverride() throws Exception { + runTest("compiler/testData/codegen/box/fir/assertEqualsFakeOverride.kt"); + } + @Test @TestMetadata("callableReferenceToJavaField.kt") public void testCallableReferenceToJavaField() throws Exception { @@ -28740,6 +28746,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaVisibility/package"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("inheritedPackageStaticFunction.kt") + public void testInheritedPackageStaticFunction() throws Exception { + runTest("compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticFunction.kt"); + } + @Test @TestMetadata("kt2781.kt") public void testKt2781() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt index 25987d8a14e..fbcc9f043dd 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirFakeOverrideGenerator.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.declarations.builder.* import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.declarations.utils.isExpect +import org.jetbrains.kotlin.fir.declarations.utils.isStatic import org.jetbrains.kotlin.fir.resolve.substitution.ChainedSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap @@ -89,6 +90,9 @@ object FirFakeOverrideGenerator { fakeOverrideSubstitution = fakeOverrideSubstitution ).apply { originalForSubstitutionOverrideAttr = baseFunction + if (isStatic) { + containingClassForStaticMemberAttr = (newDispatchReceiverType as? ConeClassLikeType)?.lookupTag + } } } @@ -298,6 +302,9 @@ object FirFakeOverrideGenerator { fakeOverrideSubstitution = fakeOverrideSubstitution ).apply { originalForSubstitutionOverrideAttr = baseProperty + if (isStatic) { + containingClassForStaticMemberAttr = (newDispatchReceiverType as? ConeClassLikeType)?.lookupTag + } } return symbolForSubstitutionOverride } diff --git a/compiler/testData/codegen/box/fir/assertEqualsFakeOverride.kt b/compiler/testData/codegen/box/fir/assertEqualsFakeOverride.kt new file mode 100644 index 00000000000..ebb6c4bcb5f --- /dev/null +++ b/compiler/testData/codegen/box/fir/assertEqualsFakeOverride.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM_IR +// FILE: AbstractBlackBoxCodegenTest.java + +public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {} + +// FILE: CodegenTestCase.java + +public abstract class CodegenTestCase extends KotlinBaseTest {} + +// FILE: KotlinBaseTest.kt + +abstract class KotlinBaseTest : KtUsefulTestCase() {} + +// FILE: KtUsefulTestCase.java + +public abstract class KtUsefulTestCase extends TestCase {} + +// FILE: TestCase.java + +public abstract class TestCase { + public static void assertEquals(int expected, int actual) { + + } +} + +// FILE: test.kt + +fun box(): String { + AbstractBlackBoxCodegenTest.assertEquals(42, 42) + return "OK" +} diff --git a/compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticFunction.kt b/compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticFunction.kt new file mode 100644 index 00000000000..fbfe119b806 --- /dev/null +++ b/compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticFunction.kt @@ -0,0 +1,20 @@ +// TARGET_BACKEND: JVM_IR +// ISSUE: KT-53441 +// MODULE: lib +// FILE: test/J.java +package test; + +class I { + public static String foo() { return "O"; } + + public static String bar() { return "K"; } +} + +public class J extends I {} + +// MODULE: main(lib) +// FILE: k.kt +import test.J +import test.J.bar + +fun box() = J.foo() + bar() diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index fbca70ed175..a8830a63124 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -18031,6 +18031,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/fir"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("assertEqualsFakeOverride.kt") + public void testAssertEqualsFakeOverride() throws Exception { + runTest("compiler/testData/codegen/box/fir/assertEqualsFakeOverride.kt"); + } + @Test @TestMetadata("callableReferenceToJavaField.kt") public void testCallableReferenceToJavaField() throws Exception { @@ -28740,6 +28746,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaVisibility/package"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("inheritedPackageStaticFunction.kt") + public void testInheritedPackageStaticFunction() throws Exception { + runTest("compiler/testData/codegen/box/javaVisibility/package/inheritedPackageStaticFunction.kt"); + } + @Test @TestMetadata("kt2781.kt") public void testKt2781() throws Exception {