[FIR2IR] Properly calculate overridden functions for lazy fake overrides

For detailed explanation see the comment to `computeBaseSymbolsWithContainingClass`
  function in `FakeOverrideGenerator.kt`

^KT-65592
This commit is contained in:
Dmitriy Novozhilov
2024-02-20 14:01:23 +02:00
committed by Space Team
parent 1761c8eb25
commit e431a96897
13 changed files with 219 additions and 26 deletions
@@ -19334,6 +19334,12 @@ public class LLFirBlackBoxCodegenBasedTestGenerated extends AbstractLLFirBlackBo
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -19334,6 +19334,12 @@ public class LLFirReversedBlackBoxCodegenBasedTestGenerated extends AbstractLLFi
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -5,13 +5,13 @@
package org.jetbrains.kotlin.fir.util
interface Multimap<K, out V, out C : Collection<V>> {
interface Multimap<K, out V, out C : Collection<V>> : Iterable<Map.Entry<K, C>> {
operator fun get(key: K): C
operator fun contains(key: K): Boolean
val keys: Set<K>
val values: Collection<V>
operator fun iterator(): Iterator<Map.Entry<K, C>>
override operator fun iterator(): Iterator<Map.Entry<K, C>>
}
interface MutableMultimap<K, V, C : Collection<V>> : Multimap<K, V, C> {
@@ -9,10 +9,7 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.isRealOwnerOf
import org.jetbrains.kotlin.fir.resolve.toFirRegularClass
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
@@ -23,6 +20,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.fir.util.setMultimapOf
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
@@ -30,6 +28,7 @@ import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
@RequiresOptIn(
level = RequiresOptIn.Level.ERROR,
@@ -225,40 +224,130 @@ class FakeOverrideGenerator(
klass: FirClass,
originalFunction: FirNamedFunctionSymbol,
): List<Pair<FirNamedFunctionSymbol, ConeClassLikeLookupTag>> {
return computeBaseSymbolsWithContainingClass(klass, originalFunction, FirTypeScope::getDirectOverriddenFunctions)
return computeBaseSymbolsWithContainingClass(
klass,
originalFunction,
FirTypeScope::getDirectOverriddenFunctions,
FirTypeScope::processOverriddenFunctions
)
}
internal fun computeBaseSymbolsWithContainingClass(
klass: FirClass,
originalFunction: FirPropertySymbol,
): List<Pair<FirPropertySymbol, ConeClassLikeLookupTag>> {
return computeBaseSymbolsWithContainingClass(klass, originalFunction, FirTypeScope::getDirectOverriddenProperties)
return computeBaseSymbolsWithContainingClass(
klass,
originalFunction,
FirTypeScope::getDirectOverriddenProperties,
FirTypeScope::processOverriddenProperties
)
}
/**
* This functions takes a list of overridden symbols and associate each one with one of the supertypes, from which this symbol came
* from. This mapping will be used later to generate proper fake-overrides in IR
*
* ```
* open class A {
* fun foo() {}
* }
*
* interface B
*
* open class C : A()
*
* class D : C(), B {
* override fun foo() {}
* }
* ```
*
* In this example FIR returns that `C.foo` overrides `A.foo`
* But in IR there are fake-overrides on each level of the hierarchy, so we need to associate `A.foo` with class `C`,
* so later it will be converted to IR f/o `B.foo`
* To understand that we should choose `C` here instead of `B`, we check if the dispatch receiver of the overridden is supertype
* of each of supertypes (here `A` from `A.foo` is not supertype of `B`, so supertype `B` is discarded)
*
* There might be some cases when the same overridden function is accessible from multiple supertypes.
* Note that this example is simplified, and actually requires java classes on the way (see KT-65592)
*
* ```
* interface A {
* fun foo()
* }
*
* open class AImpl : A {
* override fun foo() {}
* }
*
* interface B : A
* open class BImpl : AImpl(), B
*
* interface C : B
* class CImpl : BImpl(), C
* ```
*
* Here we have f/o `CImpl.foo` function, which overrides `A.foo` and `AImpl.foo` from FIR point of view
* `AImpl.foo` matches only with supertype `BImpl`
* But receiver of `A.foo` is a supertype both of `BImpl` and `C`, which leads to the situation, when there are two different base
* overridden functions matches `BImpl` supertype
* To resolve this ambiguity (which one choose, `A.foo` or `AImpl.foo`), we need to understand, which of those functions override
* all other candidates (see [chooseMostSpecificOverridden] function) and leave only `AImpl.foo`
*/
private inline fun <reified S : FirCallableSymbol<*>> computeBaseSymbolsWithContainingClass(
klass: FirClass,
originalSymbol: S,
directOverridden: FirTypeScope.(S) -> List<S>,
processOverridden: FirTypeScope.(S, (S) -> ProcessorAction) -> ProcessorAction
): List<Pair<S, ConeClassLikeLookupTag>> {
val scope = klass.unsubstitutedScope()
val classLookupTag = klass.symbol.toLookupTag()
val overriddenFirSymbols = computeBaseSymbols(originalSymbol, directOverridden, scope, classLookupTag)
val superTypes = klass.superConeTypes
val superTypes = klass.superConeTypes.toMutableList()
val typeContext = session.typeContext
return overriddenFirSymbols.flatMap { symbol ->
with(typeContext) {
val symbolDispatchReceiver = symbol.containingClassLookupTag() ?: return@flatMap emptyList()
superTypes.mapNotNull { superType ->
val overriddenPerSupertype = setMultimapOf<ConeClassLikeLookupTag, S>()
with(typeContext) {
for (symbol in overriddenFirSymbols) {
val symbolDispatchReceiver = symbol.containingClassLookupTag() ?: continue
for (superType in superTypes) {
val compatibleType = superType.anySuperTypeConstructor {
it.typeConstructor() == symbolDispatchReceiver
}
if (!compatibleType) {
return@mapNotNull null
continue
}
symbol to superType.lookupTag
overriddenPerSupertype.put(superType.lookupTag, symbol)
}
}
}
val result = overriddenPerSupertype.map { (superType, overridden) ->
val chosenOverridden = when (overridden.size) {
0 -> shouldNotBeCalled()
1 -> overridden.first()
else -> chooseMostSpecificOverridden(superType, overridden, processOverridden)
}
chosenOverridden to superType
}
return result
}
private inline fun <reified S : FirCallableSymbol<*>> chooseMostSpecificOverridden(
containingClassLookupTag: ConeClassLikeLookupTag,
overridden: Collection<S>,
processOverridden: FirTypeScope.(S, (S) -> ProcessorAction) -> ProcessorAction
): S {
val scope = containingClassLookupTag.toFirRegularClassSymbol(session)?.unsubstitutedScope() ?: return overridden.first()
val result = overridden.firstOrNull { s1 ->
overridden.all { s2 ->
if (s1 == s2) return@all true
scope.anyOverriddenOf(s1, processOverridden) { it == s2 }
}
}
return result ?: overridden.first()
}
internal fun calcBaseSymbolsForFakeOverrideProperty(
@@ -19263,6 +19263,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -19263,6 +19263,12 @@ public class FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -19263,6 +19263,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -80,21 +80,29 @@ fun FirTypeScope.processOverriddenFunctions(
mutableSetOf()
)
inline fun <reified S : FirCallableSymbol<*>> FirTypeScope.anyOverriddenOf(
symbol: S,
processOverridden: FirTypeScope.(S, (S) -> ProcessorAction) -> ProcessorAction,
noinline predicate: (S) -> Boolean,
): Boolean {
var result = false
processOverridden(symbol) {
if (predicate(it)) {
result = true
return@processOverridden ProcessorAction.STOP
}
return@processOverridden ProcessorAction.NEXT
}
return result
}
fun FirTypeScope.anyOverriddenOf(
functionSymbol: FirNamedFunctionSymbol,
predicate: (FirNamedFunctionSymbol) -> Boolean
): Boolean {
var result = false
processOverriddenFunctions(functionSymbol) {
if (predicate(it)) {
result = true
return@processOverriddenFunctions ProcessorAction.STOP
}
return@processOverriddenFunctions ProcessorAction.NEXT
}
return result
return anyOverriddenOf(functionSymbol, FirTypeScope::processOverriddenFunctions, predicate)
}
private fun FirTypeScope.processOverriddenFunctionsWithVisited(
@@ -0,0 +1,43 @@
// TARGET_BACKEND: JVM_IR
// ISSUE: KT-65592
// FILE: A.java
public interface A {
String f();
}
// FILE: AImpl.java
import org.jetbrains.annotations.NotNull;
public class AImpl implements A {
@Override
@NotNull
public String f() {
return "OK";
}
}
// FILE: B.kt
interface B : A
// FILE: BImpl.kt
open class BImpl : AImpl(), B
// FILE: C.java
public interface C extends B { }
// FILE: CImpl.java
public class CImpl extends BImpl implements C { }
// FILE: D.java
public interface D extends C { }
// FILE: DImpl.java
public class DImpl extends CImpl implements D { }
// FILE: box.kt
class Z : DImpl(), D
fun box(): String {
return Z().f()
}
@@ -19263,6 +19263,12 @@ public class JvmAbiConsistencyTestBoxGenerated extends AbstractJvmAbiConsistency
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -19263,6 +19263,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -19263,6 +19263,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@Test
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@Test
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
@@ -16021,6 +16021,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/fakeOverride/diamondFunction.kt");
}
@TestMetadata("diamondWithNullabilityAnnotations.kt")
public void testDiamondWithNullabilityAnnotations() {
runTest("compiler/testData/codegen/box/fakeOverride/diamondWithNullabilityAnnotations.kt");
}
@TestMetadata("fakeOverrideInAnonymousObject.kt")
public void testFakeOverrideInAnonymousObject() {
runTest("compiler/testData/codegen/box/fakeOverride/fakeOverrideInAnonymousObject.kt");