[FIR] fix resolution ambiguities between weakly compatible expect and actual
There is a corresponding example inside the stdlib, see `kotlin.text.startsWith`. JVM and common counterpart are weakly-compatible as the actual declaration has default arguments, which results in `ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters` This commit allows such cases. ^KT-61732 fixed
This commit is contained in:
committed by
Space Team
parent
209d59440b
commit
030250d387
+6
@@ -97,6 +97,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt");
|
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
|
@Test
|
||||||
@TestMetadata("checkNoActualForExpectInLastModule.kt")
|
@TestMetadata("checkNoActualForExpectInLastModule.kt")
|
||||||
public void testCheckNoActualForExpectInLastModule() throws Exception {
|
public void testCheckNoActualForExpectInLastModule() throws Exception {
|
||||||
|
|||||||
+6
@@ -97,6 +97,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt");
|
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
|
@Test
|
||||||
@TestMetadata("checkNoActualForExpectInLastModule.kt")
|
@TestMetadata("checkNoActualForExpectInLastModule.kt")
|
||||||
public void testCheckNoActualForExpectInLastModule() throws Exception {
|
public void testCheckNoActualForExpectInLastModule() throws Exception {
|
||||||
|
|||||||
+5
-1
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.Modality
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.getSingleExpectForActualOrNull
|
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.isActual
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
||||||
@@ -289,7 +290,10 @@ class ConeOverloadConflictResolver(
|
|||||||
val expectForActualSymbols = candidates
|
val expectForActualSymbols = candidates
|
||||||
.mapNotNullTo(mutableSetOf()) {
|
.mapNotNullTo(mutableSetOf()) {
|
||||||
val callableSymbol = it.symbol as? FirCallableSymbol<*> ?: return@mapNotNullTo null
|
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()) {
|
return if (expectForActualSymbols.isEmpty()) {
|
||||||
|
|||||||
@@ -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.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||||
|
import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible
|
||||||
|
|
||||||
private object ExpectForActualAttributeKey : FirDeclarationDataKey()
|
private object ExpectForActualAttributeKey : FirDeclarationDataKey()
|
||||||
|
|
||||||
@@ -26,6 +27,13 @@ fun FirBasedSymbol<*>.getSingleExpectForActualOrNull(): FirBasedSymbol<*>? {
|
|||||||
return expectForActual?.values?.singleOrNull()?.singleOrNull()
|
return expectForActual?.values?.singleOrNull()?.singleOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FirBasedSymbol<*>.getSingleCompatibleOrWeaklyIncompatibleExpectForActualOrNull(): FirBasedSymbol<*>? {
|
||||||
|
val expectForActual = expectForActual ?: return null
|
||||||
|
val compatibleOrWeakCompatible: List<FirBasedSymbol<*>> =
|
||||||
|
expectForActual.entries.singleOrNull { it.key.isCompatibleOrWeaklyIncompatible }?.value ?: return null
|
||||||
|
return compatibleOrWeakCompatible.singleOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
val FirBasedSymbol<*>.expectForActual: ExpectForActualData?
|
val FirBasedSymbol<*>.expectForActual: ExpectForActualData?
|
||||||
get() {
|
get() {
|
||||||
lazyResolveToPhase(FirResolvePhase.EXPECT_ACTUAL_MATCHING)
|
lazyResolveToPhase(FirResolvePhase.EXPECT_ACTUAL_MATCHING)
|
||||||
|
|||||||
+33
@@ -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(<!ERROR_SUPPRESSION!>"ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS"<!>)
|
||||||
|
actual fun String.foo(prefix: String, ignoreCase: Boolean = false): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress(<!ERROR_SUPPRESSION!>"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("")
|
||||||
|
}
|
||||||
Vendored
+33
@@ -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("")
|
||||||
|
}
|
||||||
Generated
+6
@@ -23020,6 +23020,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/arraySortFixed.kt");
|
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
|
@Test
|
||||||
@TestMetadata("checkNoActualForExpectInLastModule.kt")
|
@TestMetadata("checkNoActualForExpectInLastModule.kt")
|
||||||
public void testCheckNoActualForExpectInLastModule() throws Exception {
|
public void testCheckNoActualForExpectInLastModule() throws Exception {
|
||||||
|
|||||||
+4
@@ -99,6 +99,10 @@ sealed class ExpectActualCompatibility<out D> {
|
|||||||
object Compatible : ExpectActualCompatibility<Nothing>()
|
object Compatible : ExpectActualCompatibility<Nothing>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean
|
||||||
|
get() = this is ExpectActualCompatibility.Compatible
|
||||||
|
|| this is ExpectActualCompatibility.Incompatible.WeakIncompatible
|
||||||
|
|
||||||
val ExpectActualCompatibility<*>.compatible: Boolean
|
val ExpectActualCompatibility<*>.compatible: Boolean
|
||||||
get() = this == ExpectActualCompatibility.Compatible
|
get() = this == ExpectActualCompatibility.Compatible
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user