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 d93d1449fdb..f1d322238a8 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 @@ -97,6 +97,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt"); } + @Test + @TestMetadata("callConflictsOnExpectAndActualWeaklyCompatible.kt") + public void testCallConflictsOnExpectAndActualWeaklyCompatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.kt"); + } + @Test @TestMetadata("checkNoActualForExpectInLastModule.kt") public void testCheckNoActualForExpectInLastModule() 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 68a6d3b41d4..281c9a780af 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 @@ -97,6 +97,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt"); } + @Test + @TestMetadata("callConflictsOnExpectAndActualWeaklyCompatible.kt") + public void testCallConflictsOnExpectAndActualWeaklyCompatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.kt"); + } + @Test @TestMetadata("checkNoActualForExpectInLastModule.kt") public void testCheckNoActualForExpectInLastModule() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt index a09264eea6a..a6eb792b003 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConeOverloadConflictResolver.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration import org.jetbrains.kotlin.fir.declarations.getSingleExpectForActualOrNull +import org.jetbrains.kotlin.fir.declarations.getSingleCompatibleOrWeaklyIncompatibleExpectForActualOrNull import org.jetbrains.kotlin.fir.declarations.utils.isActual import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.modality @@ -289,7 +290,10 @@ class ConeOverloadConflictResolver( val expectForActualSymbols = candidates .mapNotNullTo(mutableSetOf()) { val callableSymbol = it.symbol as? FirCallableSymbol<*> ?: return@mapNotNullTo null - runIf(callableSymbol.isActual) { callableSymbol.getSingleExpectForActualOrNull() } + runIf(callableSymbol.isActual) { + callableSymbol.getSingleExpectForActualOrNull() + ?: callableSymbol.getSingleCompatibleOrWeaklyIncompatibleExpectForActualOrNull() + } } return if (expectForActualSymbols.isEmpty()) { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/ExpectActualAttributes.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/ExpectActualAttributes.kt index cceaeaf3925..708226f4791 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/ExpectActualAttributes.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/ExpectActualAttributes.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility +import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible private object ExpectForActualAttributeKey : FirDeclarationDataKey() @@ -26,6 +27,13 @@ fun FirBasedSymbol<*>.getSingleExpectForActualOrNull(): FirBasedSymbol<*>? { return expectForActual?.values?.singleOrNull()?.singleOrNull() } +fun FirBasedSymbol<*>.getSingleCompatibleOrWeaklyIncompatibleExpectForActualOrNull(): FirBasedSymbol<*>? { + val expectForActual = expectForActual ?: return null + val compatibleOrWeakCompatible: List> = + expectForActual.entries.singleOrNull { it.key.isCompatibleOrWeaklyIncompatible }?.value ?: return null + return compatibleOrWeakCompatible.singleOrNull() +} + val FirBasedSymbol<*>.expectForActual: ExpectForActualData? get() { lazyResolveToPhase(FirResolvePhase.EXPECT_ACTUAL_MATCHING) diff --git a/compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.fir.kt new file mode 100644 index 00000000000..076ed3f5271 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.fir.kt @@ -0,0 +1,33 @@ +// ISSUE: KT-61732 +// based on of kotlin.text.startsWith from kotlin-stdlib + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +expect fun String.foo(prefix: String, ignoreCase: Boolean = false): Boolean + +expect fun String.foo(prefix: String, startIndex: Int, ignoreCase: Boolean = false): Boolean + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: jvm.kt + + +@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") +actual fun String.foo(prefix: String, ignoreCase: Boolean = false): Boolean { + return true +} + +@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") +actual fun String.foo(prefix: String, startIndex: Int, ignoreCase: Boolean = false): Boolean { + return true +} + +// MODULE: client(jvm)()() +// TARGET_PLATFORM: JVM +// FILE: client.kt + +fun main() { + "".foo("") +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.kt b/compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.kt new file mode 100644 index 00000000000..d0393096e8e --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.kt @@ -0,0 +1,33 @@ +// ISSUE: KT-61732 +// based on of kotlin.text.startsWith from kotlin-stdlib + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +expect fun String.foo(prefix: String, ignoreCase: Boolean = false): Boolean + +expect fun String.foo(prefix: String, startIndex: Int, ignoreCase: Boolean = false): Boolean + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: jvm.kt + + +@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") +actual fun String.foo(prefix: String, ignoreCase: Boolean = false): Boolean { + return true +} + +@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") +actual fun String.foo(prefix: String, startIndex: Int, ignoreCase: Boolean = false): Boolean { + return true +} + +// MODULE: client(jvm)()() +// TARGET_PLATFORM: JVM +// FILE: client.kt + +fun main() { + "".foo("") +} \ No newline at end of file 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 23056025a48..4e672ea3fc2 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 @@ -23020,6 +23020,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt"); } + @Test + @TestMetadata("callConflictsOnExpectAndActualWeaklyCompatible.kt") + public void testCallConflictsOnExpectAndActualWeaklyCompatible() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/callConflictsOnExpectAndActualWeaklyCompatible.kt"); + } + @Test @TestMetadata("checkNoActualForExpectInLastModule.kt") public void testCheckNoActualForExpectInLastModule() 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 f1bd34bd773..b79e3163c62 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 @@ -99,6 +99,10 @@ sealed class ExpectActualCompatibility { object Compatible : ExpectActualCompatibility() } +val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean + get() = this is ExpectActualCompatibility.Compatible + || this is ExpectActualCompatibility.Incompatible.WeakIncompatible + val ExpectActualCompatibility<*>.compatible: Boolean get() = this == ExpectActualCompatibility.Compatible