[FIR] Fix computing compatibility of expect and actual modality

This commit is contained in:
Dmitriy Novozhilov
2022-02-15 12:45:44 +03:00
committed by teamcity
parent e1d21d7a37
commit ccb74b6477
10 changed files with 172 additions and 8 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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)