From 456d3e0f42a285a41d82d57555b8f6ddde3eb354 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 4 Apr 2023 16:02:39 +0200 Subject: [PATCH] [FE] Prohibit expect fun interface to have non-fun actual counterpart In K1 .isFun is always false for Java classes, so extra check is added for that. This is not needed for K2, because .isFun is true for all Java classes. Here it is not necessary to check that interface has only one method, because such check will be done in the place where interface implementation is created. ^KT-39362 Fixed --- ...DiagnosticsWithLightTreeTestGenerated.java | 6 +++ ...endMPPDiagnosticsWithPsiTestGenerated.java | 6 +++ .../mpp/FirExpectActualResolver.kt | 3 ++ .../ClassicPositioningStrategies.kt | 3 +- .../multiplatform/ExpectedActualResolver.kt | 7 +++ .../headerClass/expectFunInterface.fir.kt | 54 +++++++++++++++++++ .../headerClass/expectFunInterface.kt | 54 +++++++++++++++++++ .../incompatibleClasses/common.kt | 8 +++ .../multiplatform/incompatibleClasses/jvm.kt | 10 ++++ .../incompatibleClasses/output.txt | 12 +++++ .../test/runners/DiagnosticTestGenerated.java | 6 +++ .../ExpectActualCompatibility.kt | 2 + 12 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java index 1ffcb6cdd8e..623debfbcca 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java @@ -414,6 +414,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFinalActualOpen.kt"); } + @Test + @TestMetadata("expectFunInterface.kt") + public void testExpectFunInterface() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt"); + } + @Test @TestMetadata("explicitConstructorDelegation.kt") public void testExplicitConstructorDelegation() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java index b09ae473ae0..765e69ef2b7 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java @@ -414,6 +414,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFinalActualOpen.kt"); } + @Test + @TestMetadata("expectFunInterface.kt") + public void testExpectFunInterface() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt"); + } + @Test @TestMetadata("explicitConstructorDelegation.kt") public void testExplicitConstructorDelegation() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt index 46d8b231a3c..f10c052cda7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualResolver.kt @@ -123,6 +123,9 @@ object FirExpectActualResolver { if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline /*|| it.isValue*/) }) { return ExpectActualCompatibility.Incompatible.ClassModifiers } + if (expectClassSymbol.isFun && !actualClass.isFun) { + return ExpectActualCompatibility.Incompatible.FunInterfaceModifier + } val expectTypeParameterSymbols = expectClassSymbol.typeParameterSymbols val actualTypeParameterSymbols = actualClass.typeParameterSymbols diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt index ff46343862d..cdf9abac561 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/ClassicPositioningStrategies.kt @@ -90,7 +90,8 @@ object ClassicPositioningStrategies { ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset, ExpectActualCompatibility.Incompatible.PropertyLateinitModifier, ExpectActualCompatibility.Incompatible.PropertyConstModifier, - ExpectActualCompatibility.Incompatible.ClassModifiers -> { + ExpectActualCompatibility.Incompatible.ClassModifiers, + ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> { element.modifierList } ExpectActualCompatibility.Incompatible.PropertyKind -> { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt index 43a527efd3d..b1df5dcd167 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectedActualResolver.kt @@ -387,6 +387,9 @@ object ExpectedActualResolver { if (a.kind != b.kind) return Incompatible.ClassKind if (!equalBy(a, b) { listOf(it.isCompanionObject, it.isInner, it.isInline || it.isValue) }) return Incompatible.ClassModifiers + if (a.isFun && !b.isFun && b.isNotJavaSamInterface()) { + return Incompatible.FunInterfaceModifier + } val aTypeParams = a.declaredTypeParameters val bTypeParams = b.declaredTypeParameters @@ -563,3 +566,7 @@ fun DeclarationDescriptor.findActuals(inModule: ModuleDescriptor): Listactual interface F2 { + actual fun run() +} + +actual typealias F3 = java.lang.Runnable + +actual fun interface F4 { + actual fun run() +} + +fun interface F5Typealias { + fun run() +} + +actual typealias F5 = F5Typealias + +interface F6Typealias { + fun run() +} + +actual typealias F6 = F6Typealias diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt new file mode 100644 index 00000000000..8dc49612f35 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt @@ -0,0 +1,54 @@ +// MODULE: m1-common +// FILE: common.kt + +expect fun interface F1 { + fun run() +} + +expect fun interface F2 { + fun run() +} + +expect fun interface F3 { + fun run() +} + +expect interface F4 { + fun run() +} + +expect fun interface F5 { + fun run() +} + +expect fun interface F6 { + fun run() +} + +// MODULE: m2-jvm()()(m1-common) + +actual fun interface F1 { + actual fun run() +} + +actual interface F2 { + actual fun run() +} + +actual typealias F3 = java.lang.Runnable + +actual fun interface F4 { + actual fun run() +} + +fun interface F5Typealias { + fun run() +} + +actual typealias F5 = F5Typealias + +interface F6Typealias { + fun run() +} + +actual typealias F6 = F6Typealias \ No newline at end of file diff --git a/compiler/testData/multiplatform/incompatibleClasses/common.kt b/compiler/testData/multiplatform/incompatibleClasses/common.kt index c4516abc934..5b7f7041d73 100644 --- a/compiler/testData/multiplatform/incompatibleClasses/common.kt +++ b/compiler/testData/multiplatform/incompatibleClasses/common.kt @@ -19,3 +19,11 @@ expect class C4 expect abstract class ExtendsNumber : Number + +expect fun interface FunInterface { + fun run() +} + +expect fun interface FunInterface2 { + fun run() +} diff --git a/compiler/testData/multiplatform/incompatibleClasses/jvm.kt b/compiler/testData/multiplatform/incompatibleClasses/jvm.kt index b8f9716dd1b..5d12737acd0 100644 --- a/compiler/testData/multiplatform/incompatibleClasses/jvm.kt +++ b/compiler/testData/multiplatform/incompatibleClasses/jvm.kt @@ -19,3 +19,13 @@ actual typealias C4 = C4Impl class C4Impl actual abstract class ExtendsNumber : Any() + +actual interface FunInterface { + actual fun run() +} + +interface FunInterface2Typealias { + fun run() +} + +actual typealias FunInterface2 = FunInterface2Typealias \ No newline at end of file diff --git a/compiler/testData/multiplatform/incompatibleClasses/output.txt b/compiler/testData/multiplatform/incompatibleClasses/output.txt index 0ee676c7567..9703ce14f65 100644 --- a/compiler/testData/multiplatform/incompatibleClasses/output.txt +++ b/compiler/testData/multiplatform/incompatibleClasses/output.txt @@ -113,3 +113,15 @@ The following declaration is incompatible because some supertypes are missing in actual abstract class ExtendsNumber : Any() ^ +compiler/testData/multiplatform/incompatibleClasses/jvm.kt:23:1: error: actual interface 'FunInterface' has no corresponding expected declaration +The following declaration is incompatible because actual declaration for fun expect interface is not a functional interface: + public expect fun interface FunInterface + +actual interface FunInterface { +^ +compiler/testData/multiplatform/incompatibleClasses/jvm.kt:31:1: error: actual typealias 'FunInterface2' has no corresponding expected declaration +The following declaration is incompatible because actual declaration for fun expect interface is not a functional interface: + public expect fun interface FunInterface2 + +actual typealias FunInterface2 = FunInterface2Typealias +^ diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 724942b2387..283d79e1c74 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -22193,6 +22193,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFinalActualOpen.kt"); } + @Test + @TestMetadata("expectFunInterface.kt") + public void testExpectFunInterface() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectFunInterface.kt"); + } + @Test @TestMetadata("explicitConstructorDelegation.kt") public void testExplicitConstructorDelegation() throws Exception { diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt index 8a8287216a6..948d2b00cfc 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt @@ -65,6 +65,8 @@ sealed class ExpectActualCompatibility { object ClassModifiers : Incompatible("modifiers are different (companion, inner, inline)") + object FunInterfaceModifier : Incompatible("actual declaration for fun expect interface is not a functional interface") + object Supertypes : Incompatible("some supertypes are missing in the actual declaration") class ClassScopes(