diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java index 47d9bc0257b..77969089feb 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java @@ -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 { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index 5a82fbe81c4..94683f0720d 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java @@ -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 { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt index e48f15cd2fe..59793db7c85 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ActualFakeOverridesAdder.kt @@ -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 + + if (override != null) { + override.overriddenSymbols += symbolFromSupertype + continue + } + } + val newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass) val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect( newMember.symbol, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt index 089584fabb3..283cafbe382 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt @@ -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 - val subTypeParameters: List +fun IrTypeSystemContext.isOverridableByWithoutExternalConditions( + superMember: IrOverridableMember, + subMember: IrOverridableMember, + checkIsInlineFlag: Boolean, + checkReturnType: Boolean +): OverrideCompatibilityInfo { + val superTypeParameters: List + val subTypeParameters: List - val superValueParameters: List - val subValueParameters: List + val superValueParameters: List + val subValueParameters: List - 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 diff --git a/compiler/testData/codegen/box/multiplatform/kt61166.kt b/compiler/testData/codegen/box/multiplatform/kt61166.kt new file mode 100644 index 00000000000..b75c48736c0 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/kt61166.kt @@ -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 diff --git a/compiler/testData/diagnostics/tests/multiplatform/manyImplMemberNotImplemented.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/manyImplMemberNotImplemented.fir.kt index ec06bcde3a5..eb17c078219 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/manyImplMemberNotImplemented.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/manyImplMemberNotImplemented.fir.kt @@ -9,7 +9,7 @@ expect open class C1() expect interface I1 open class A : C1(), I1 -open class B : I1, C1() +open class B : I1, C1() expect abstract class C2() expect interface I2 diff --git a/compiler/testData/diagnostics/tests/multiplatform/manyInterfacesMemberNotImplemented.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/manyInterfacesMemberNotImplemented.fir.kt index 0221ffb1650..273deca4159 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/manyInterfacesMemberNotImplemented.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/manyInterfacesMemberNotImplemented.fir.kt @@ -8,7 +8,7 @@ expect interface S1 expect interface S2 -open class A : S1, S2 +open class A : S1, S2 class B : A() 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 d8da6da9821..4912e3d3f1f 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 @@ -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 { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java index 7026b86cedb..4a864c33bc7 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java @@ -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 { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 1e6c587e243..3bb411a66f2 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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");