[IR Actualizer] Fix fake-override generation in actual A -> common B -> actual C hierarchy

...  where C defines a member x and A overrides the member x

#KT-61166 Fixed
This commit is contained in:
Kirill Rakhman
2023-08-29 16:32:32 +02:00
committed by Space Team
parent 293474ee50
commit ac102dedac
10 changed files with 155 additions and 72 deletions
@@ -33701,6 +33701,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
}
@Test
@TestMetadata("kt61166.kt")
public void testKt61166() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
}
@Test
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
@@ -33701,6 +33701,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
}
@Test
@TestMetadata("kt61166.kt")
public void testKt61166() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
}
@Test
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.overrides.isOverridableByWithoutExternalConditions
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
/**
* It adds fake overrides to non-expect classes inside common or multi-platform module,
@@ -79,6 +81,32 @@ internal class ActualFakeOverridesAdder(
) {
for (symbolFromSupertype in membersFromSupertype) {
val memberFromSupertype = symbolFromSupertype.owner as IrDeclaration
if (memberFromSupertype is IrOverridableMember) {
// We can land here because of a hierarchy like
// actual A -> common B -> actual C
// where C defines a member x and A overrides the member x.
// We will first add a fake-override x to B and then land here.
// In this case we don't want to add a fake-override on top of the real override to A.
// Instead, we add the fake-override x to the overridden symbols of A.x.
@Suppress("UNCHECKED_CAST")
val override = klass.declarations.firstOrNull {
it is IrOverridableMember &&
typeSystemContext.isOverridableByWithoutExternalConditions(
superMember = memberFromSupertype,
subMember = it,
checkIsInlineFlag = false,
checkReturnType = false
).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
} as? IrOverridableDeclaration<IrSymbol>
if (override != null) {
override.overriddenSymbols += symbolFromSupertype
continue
}
}
val newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass)
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
newMember.symbol,
@@ -627,63 +627,64 @@ class IrOverridingUtil(
checkIsInlineFlag: Boolean,
checkReturnType: Boolean
): OverrideCompatibilityInfo {
return isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag, checkReturnType)
return typeSystem.isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag, checkReturnType)
// The frontend goes into external overridability condition details here, but don't deal with them in IR (yet?).
}
}
private fun isOverridableByWithoutExternalConditions(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
checkIsInlineFlag: Boolean,
checkReturnType: Boolean
): OverrideCompatibilityInfo {
val superTypeParameters: List<IrTypeParameter>
val subTypeParameters: List<IrTypeParameter>
fun IrTypeSystemContext.isOverridableByWithoutExternalConditions(
superMember: IrOverridableMember,
subMember: IrOverridableMember,
checkIsInlineFlag: Boolean,
checkReturnType: Boolean
): OverrideCompatibilityInfo {
val superTypeParameters: List<IrTypeParameter>
val subTypeParameters: List<IrTypeParameter>
val superValueParameters: List<IrValueParameter>
val subValueParameters: List<IrValueParameter>
val superValueParameters: List<IrValueParameter>
val subValueParameters: List<IrValueParameter>
when (superMember) {
is IrSimpleFunction -> when {
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
when (superMember) {
is IrSimpleFunction -> when {
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
else -> {
superTypeParameters = superMember.typeParameters
subTypeParameters = subMember.typeParameters
superValueParameters = superMember.compiledValueParameters
subValueParameters = subMember.compiledValueParameters
}
else -> {
superTypeParameters = superMember.typeParameters
subTypeParameters = subMember.typeParameters
superValueParameters = superMember.compiledValueParameters
subValueParameters = subMember.compiledValueParameters
}
is IrProperty -> when {
subMember !is IrProperty -> return incompatible("Member kind mismatch")
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
}
is IrProperty -> when {
subMember !is IrProperty -> return incompatible("Member kind mismatch")
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
else -> {
superTypeParameters = superMember.typeParameters
subTypeParameters = subMember.typeParameters
superValueParameters = superMember.compiledValueParameters
subValueParameters = subMember.compiledValueParameters
}
else -> {
superTypeParameters = superMember.typeParameters
subTypeParameters = subMember.typeParameters
superValueParameters = superMember.compiledValueParameters
subValueParameters = subMember.compiledValueParameters
}
else -> error("Unexpected type of declaration: ${superMember::class.java}, $superMember")
}
else -> error("Unexpected type of declaration: ${superMember::class.java}, $superMember")
}
when {
superMember.name != subMember.name -> {
// Check name after member kind checks. This way FO builder will first check types of overridable members and crash
// if member types are not supported (ex: IrConstructor).
return incompatible("Name mismatch")
}
when {
superMember.name != subMember.name -> {
// Check name after member kind checks. This way FO builder will first check types of overridable members and crash
// if member types are not supported (ex: IrConstructor).
return incompatible("Name mismatch")
}
superTypeParameters.size != subTypeParameters.size -> return incompatible("Type parameter number mismatch")
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
}
superTypeParameters.size != subTypeParameters.size -> return incompatible("Type parameter number mismatch")
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
}
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
// superTypeParameters.forEachIndexed { index, parameter ->
// if (!AbstractTypeChecker.areTypeParametersEquivalent(
// typeCheckerContext as AbstractTypeCheckerContext,
@@ -693,34 +694,33 @@ class IrOverridingUtil(
// ) return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch")
// }
val typeCheckerState = createIrTypeCheckerState(
IrTypeSystemContextWithAdditionalAxioms(
typeSystem,
superTypeParameters,
subTypeParameters
)
val typeCheckerState = createIrTypeCheckerState(
IrTypeSystemContextWithAdditionalAxioms(
this,
superTypeParameters,
subTypeParameters
)
)
superValueParameters.forEachIndexed { index, parameter ->
if (!AbstractTypeChecker.equalTypes(
typeCheckerState,
subValueParameters[index].type,
parameter.type
)
) return incompatible("Value parameter type mismatch")
}
if (checkReturnType) {
if (!AbstractTypeChecker.isSubtypeOf(
typeCheckerState,
subMember.returnType,
superMember.returnType
)
) return conflict("Return type mismatch")
}
return success()
superValueParameters.forEachIndexed { index, parameter ->
if (!AbstractTypeChecker.equalTypes(
typeCheckerState,
subValueParameters[index].type,
parameter.type
)
) return incompatible("Value parameter type mismatch")
}
if (checkReturnType) {
if (!AbstractTypeChecker.isSubtypeOf(
typeCheckerState,
subMember.returnType,
superMember.returnType
)
) return conflict("Return type mismatch")
}
return success()
}
private val IrSimpleFunction?.hasExtensionReceiver: Boolean
+26
View File
@@ -0,0 +1,26 @@
// LANGUAGE: +MultiPlatformProjects
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: ANY
// ISSUE: KT-60854
// WITH_STDLIB
// FULL_JDK
// MODULE: common
// FILE: common.kt
expect object A
abstract class B : C()
expect abstract class C()
// MODULE: intermediate()()(common)
// FILE: intermediate.kt
actual object A : B() {
override val x: String get() = "OK"
}
actual abstract class C {
abstract val x: String
}
// MODULE: platform()()(intermediate)
// FILE: platform.kt
fun box(): String = A.x
@@ -9,7 +9,7 @@ expect open class C1()
expect interface I1
open class A : C1(), I1
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED{JVM}!>open class B : I1, C1()<!>
open class B : I1, C1()
expect abstract class C2()
expect interface I2
@@ -8,7 +8,7 @@
expect interface S1
expect interface S2
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED{JVM}!>open class A : S1, S2<!>
open class A : S1, S2
class B : A()
@@ -33701,6 +33701,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
}
@Test
@TestMetadata("kt61166.kt")
public void testKt61166() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
}
@Test
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
@@ -33701,6 +33701,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
}
@Test
@TestMetadata("kt61166.kt")
public void testKt61166() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
}
@Test
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
@@ -28702,6 +28702,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
}
@TestMetadata("kt61166.kt")
public void testKt61166() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
}
@TestMetadata("noArgActualConstructor.kt")
public void testNoArgActualConstructor() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");