[FIR] Fix computing compatibility of expect and actual modality
This commit is contained in:
committed by
teamcity
parent
e1d21d7a37
commit
ccb74b6477
+12
@@ -20000,6 +20000,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectObjectWithAbstractMember.kt")
|
||||
public void testExpectObjectWithAbstractMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||
@@ -20311,6 +20317,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalsOverrideInActualInterface.kt")
|
||||
public void testEqualsOverrideInActualInterface() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/equalsOverrideInActualInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
||||
|
||||
+12
@@ -20000,6 +20000,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectObjectWithAbstractMember.kt")
|
||||
public void testExpectObjectWithAbstractMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||
@@ -20311,6 +20317,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalsOverrideInActualInterface.kt")
|
||||
public void testEqualsOverrideInActualInterface() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/equalsOverrideInActualInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
||||
|
||||
+12
@@ -20000,6 +20000,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectObjectWithAbstractMember.kt")
|
||||
public void testExpectObjectWithAbstractMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||
@@ -20311,6 +20317,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalsOverrideInActualInterface.kt")
|
||||
public void testEqualsOverrideInActualInterface() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/equalsOverrideInActualInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
||||
|
||||
+34
-8
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Modality.*
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectEnumEntries
|
||||
@@ -31,7 +32,10 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.enumMapOf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.enumSetOf
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.*
|
||||
|
||||
object FirExpectActualResolver {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@@ -330,11 +334,13 @@ object FirExpectActualResolver {
|
||||
!areCompatibleModalities(
|
||||
expectDeclaration.modality,
|
||||
actualDeclaration.modality,
|
||||
(expectDeclaration.dispatchReceiverType?.toSymbol(expectSession) as? FirRegularClassSymbol)?.modality,
|
||||
expectContainingClass?.modality,
|
||||
actualContainingClass?.modality
|
||||
)
|
||||
) {
|
||||
return ExpectActualCompatibility.Incompatible.Modality
|
||||
if (expectDeclaration.dispatchReceiverType?.isAny != true) {
|
||||
return ExpectActualCompatibility.Incompatible.Modality
|
||||
}
|
||||
}
|
||||
|
||||
if (!areDeclarationsWithCompatibleVisibilities(expectDeclaration.resolvedStatus, actualDeclaration.resolvedStatus)) {
|
||||
@@ -460,19 +466,39 @@ object FirExpectActualResolver {
|
||||
expectContainingClassModality: Modality? = null,
|
||||
actualContainingClassModality: Modality? = null
|
||||
): Boolean {
|
||||
val result = (expectModality == actualModality) ||
|
||||
(expectModality == Modality.FINAL && (actualModality == Modality.OPEN || actualModality == Modality.ABSTRACT))
|
||||
if (result) return true
|
||||
if (expectContainingClassModality == null || actualContainingClassModality == null) return result
|
||||
return expectModality == expectContainingClassModality && actualModality == actualContainingClassModality
|
||||
val expectEffectiveModality = effectiveModality(expectModality, expectContainingClassModality)
|
||||
val actualEffectiveModality = effectiveModality(actualModality, actualContainingClassModality)
|
||||
|
||||
val result = actualEffectiveModality in compatibleModalityMap.getValue(expectEffectiveModality)
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
* If containing class is final then all declarations in it effectively final
|
||||
*/
|
||||
private fun effectiveModality(declarationModality: Modality?, containingClassModality: Modality?): Modality? {
|
||||
return when (containingClassModality) {
|
||||
FINAL -> FINAL
|
||||
else -> declarationModality
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Key is expect modality, value is a set of compatible actual modalities
|
||||
*/
|
||||
private val compatibleModalityMap: EnumMap<Modality, EnumSet<Modality>> = enumMapOf(
|
||||
ABSTRACT to enumSetOf(ABSTRACT, OPEN),
|
||||
OPEN to enumSetOf(OPEN),
|
||||
FINAL to enumSetOf(ABSTRACT, OPEN, FINAL),
|
||||
SEALED to enumSetOf(SEALED),
|
||||
)
|
||||
|
||||
private fun areDeclarationsWithCompatibleVisibilities(
|
||||
expectStatus: FirDeclarationStatus,
|
||||
actualStatus: FirDeclarationStatus
|
||||
): Boolean {
|
||||
val compare = Visibilities.compare(expectStatus.visibility, actualStatus.visibility)
|
||||
return if (expectStatus.modality != Modality.FINAL) {
|
||||
return if (expectStatus.modality != FINAL) {
|
||||
// For overridable declarations visibility should match precisely, see KT-19664
|
||||
compare == 0
|
||||
} else {
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// FIR_IDENTICAL
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
interface Base {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
expect object Implementation : Base
|
||||
|
||||
// MODULE: m1-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
abstract class RealImplementation : Base {
|
||||
override fun foo() {}
|
||||
}
|
||||
|
||||
actual object Implementation : RealImplementation(), Base
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public interface Base {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public expect object Implementation : Base {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final override /*1*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
// -- Module: <m1-jvm> --
|
||||
package
|
||||
|
||||
public interface Base {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public actual object Implementation : RealImplementation, Base {
|
||||
private constructor Implementation()
|
||||
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*fake_override*/ fun foo(): kotlin.Unit
|
||||
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public abstract class RealImplementation : Base {
|
||||
public constructor RealImplementation()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
// FIR_IDENTICAL
|
||||
// MODULE: m1-common
|
||||
expect interface Base
|
||||
|
||||
// MODULE: m1-jvm()()(m1-common)
|
||||
actual interface Base {
|
||||
override fun equals(other: Any?): Boolean
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public expect interface Base {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
// -- Module: <m1-jvm> --
|
||||
package
|
||||
|
||||
public actual interface Base {
|
||||
public abstract override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
Generated
+12
@@ -20006,6 +20006,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectObjectWithAbstractMember.kt")
|
||||
public void testExpectObjectWithAbstractMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectObjectWithAbstractMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||
@@ -20317,6 +20323,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equalsOverrideInActualInterface.kt")
|
||||
public void testEqualsOverrideInActualInterface() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/equalsOverrideInActualInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
||||
|
||||
@@ -256,3 +256,6 @@ inline fun <T, U, K, V> List<T>.flatGroupBy(
|
||||
}
|
||||
|
||||
fun <E> MutableList<E>.popLast(): E = removeAt(lastIndex)
|
||||
|
||||
fun <K : Enum<K>, V> enumMapOf(vararg pairs: Pair<K, V>): EnumMap<K, V> = EnumMap(mapOf(*pairs))
|
||||
fun <T : Enum<T>> enumSetOf(element: T, vararg elements: T): EnumSet<T> = EnumSet.of(element, *elements)
|
||||
|
||||
Reference in New Issue
Block a user