[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");
|
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
|
@Test
|
||||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||||
@@ -20311,6 +20317,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
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
|
@Test
|
||||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
||||||
|
|||||||
+12
@@ -20000,6 +20000,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.kt");
|
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
|
@Test
|
||||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||||
@@ -20311,6 +20317,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
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
|
@Test
|
||||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
||||||
|
|||||||
+12
@@ -20000,6 +20000,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/expectInterfaceApplicability.kt");
|
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
|
@Test
|
||||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||||
@@ -20311,6 +20317,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
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
|
@Test
|
||||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
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.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality.*
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectEnumEntries
|
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.types.model.TypeConstructorMarker
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
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 org.jetbrains.kotlin.utils.keysToMap
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
object FirExpectActualResolver {
|
object FirExpectActualResolver {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
@@ -330,11 +334,13 @@ object FirExpectActualResolver {
|
|||||||
!areCompatibleModalities(
|
!areCompatibleModalities(
|
||||||
expectDeclaration.modality,
|
expectDeclaration.modality,
|
||||||
actualDeclaration.modality,
|
actualDeclaration.modality,
|
||||||
(expectDeclaration.dispatchReceiverType?.toSymbol(expectSession) as? FirRegularClassSymbol)?.modality,
|
expectContainingClass?.modality,
|
||||||
actualContainingClass?.modality
|
actualContainingClass?.modality
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return ExpectActualCompatibility.Incompatible.Modality
|
if (expectDeclaration.dispatchReceiverType?.isAny != true) {
|
||||||
|
return ExpectActualCompatibility.Incompatible.Modality
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!areDeclarationsWithCompatibleVisibilities(expectDeclaration.resolvedStatus, actualDeclaration.resolvedStatus)) {
|
if (!areDeclarationsWithCompatibleVisibilities(expectDeclaration.resolvedStatus, actualDeclaration.resolvedStatus)) {
|
||||||
@@ -460,19 +466,39 @@ object FirExpectActualResolver {
|
|||||||
expectContainingClassModality: Modality? = null,
|
expectContainingClassModality: Modality? = null,
|
||||||
actualContainingClassModality: Modality? = null
|
actualContainingClassModality: Modality? = null
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val result = (expectModality == actualModality) ||
|
val expectEffectiveModality = effectiveModality(expectModality, expectContainingClassModality)
|
||||||
(expectModality == Modality.FINAL && (actualModality == Modality.OPEN || actualModality == Modality.ABSTRACT))
|
val actualEffectiveModality = effectiveModality(actualModality, actualContainingClassModality)
|
||||||
if (result) return true
|
|
||||||
if (expectContainingClassModality == null || actualContainingClassModality == null) return result
|
val result = actualEffectiveModality in compatibleModalityMap.getValue(expectEffectiveModality)
|
||||||
return expectModality == expectContainingClassModality && actualModality == actualContainingClassModality
|
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(
|
private fun areDeclarationsWithCompatibleVisibilities(
|
||||||
expectStatus: FirDeclarationStatus,
|
expectStatus: FirDeclarationStatus,
|
||||||
actualStatus: FirDeclarationStatus
|
actualStatus: FirDeclarationStatus
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val compare = Visibilities.compare(expectStatus.visibility, actualStatus.visibility)
|
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
|
// For overridable declarations visibility should match precisely, see KT-19664
|
||||||
compare == 0
|
compare == 0
|
||||||
} else {
|
} 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");
|
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
|
@Test
|
||||||
@TestMetadata("headerFunInNonHeaderClass.kt")
|
@TestMetadata("headerFunInNonHeaderClass.kt")
|
||||||
public void testHeaderFunInNonHeaderClass() throws Exception {
|
public void testHeaderFunInNonHeaderClass() throws Exception {
|
||||||
@@ -20317,6 +20323,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/dontOverrideMethodsFromInterfaceInCommonCode.kt");
|
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
|
@Test
|
||||||
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
@TestMetadata("expectClassWithExplicitAbstractMember.kt")
|
||||||
public void testExpectClassWithExplicitAbstractMember() throws Exception {
|
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 <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