FIR2IR: support static fake overrides (functions) #KT-53441 Fixed

This commit is contained in:
Mikhail Glukhikh
2022-10-26 17:14:09 +02:00
committed by Space Team
parent ec77e1896c
commit 3a81174a4c
8 changed files with 124 additions and 26 deletions
@@ -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) {
@@ -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
@@ -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<IrDeclaration>,
realDeclarationSymbols: Set<FirBasedSymbol<*>>
) {
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<FirBasedSymbol<*>>,
computeDirectOverridden: FirTypeScope.(S) -> List<S>,
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 <reified S : FirCallableSymbol<*>> computeBaseSymbols(
symbol: S,
directOverridden: FirTypeScope.(S) -> List<S>,
scope: FirTypeScope,
scope: FirScope,
containingClass: ConeClassLikeLookupTag,
): List<S> {
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
@@ -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 {
@@ -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
}
@@ -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<CharSequence> {}
// FILE: KotlinBaseTest.kt
abstract class KotlinBaseTest<F : CharSequence> : 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"
}
@@ -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()
@@ -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 {