WIP! Fri 4 Aug 20:17:06 CEST 2023
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 9087a52..75c4433 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
@@ -1085,6 +1085,12 @@ } @Test + @TestMetadata("injectGenericUpperBoundOverload.kt") + public void testInjectGenericUpperBoundOverload() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectGenericUpperBoundOverload.kt"); + } + + @Test @TestMetadata("injectInnerClass.kt") public void testInjectInnerClass() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectInnerClass.kt"); @@ -1157,6 +1163,12 @@ } @Test + @TestMetadata("propertyKindOverride.kt") + public void testPropertyKindOverride() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.kt"); + } + + @Test @TestMetadata("severalMismatches.kt") public void testSeveralMismatches() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/severalMismatches.kt");
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 e7bd5b9..78e5035 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
@@ -1085,6 +1085,12 @@ } @Test + @TestMetadata("injectGenericUpperBoundOverload.kt") + public void testInjectGenericUpperBoundOverload() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectGenericUpperBoundOverload.kt"); + } + + @Test @TestMetadata("injectInnerClass.kt") public void testInjectInnerClass() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectInnerClass.kt"); @@ -1157,6 +1163,12 @@ } @Test + @TestMetadata("propertyKindOverride.kt") + public void testPropertyKindOverride() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.kt"); + } + + @Test @TestMetadata("severalMismatches.kt") public void testSeveralMismatches() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/severalMismatches.kt");
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt index 8ee3a38..142a5bf 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt
@@ -150,6 +150,7 @@ expect.unsubstitutedScope(context.session, context.scopeSession, withForcedTypeCalculator = false, memberRequiredPhase = null) val actualScope = actual.unsubstitutedScope(context.session, context.scopeSession, withForcedTypeCalculator = false, memberRequiredPhase = null) + val classTypeSubstitutor = createExpectActualTypeParameterSubstitutor(expect.typeParameterSymbols, actual.typeParameterSymbols, context.session) val expectClassCallables = expectScope.extractNonPrivateCallables(classTypeSubstitutor, ExpectActual.EXPECT) @@ -292,4 +293,5 @@ ExpectActualMemberDiff.Kind.ModalityChangedInOverride -> FirErrors.MODALITY_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION ExpectActualMemberDiff.Kind.VisibilityChangedInOverride -> FirErrors.VISIBILITY_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride -> FirErrors.PARAMETER_NAME_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION + ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride -> TODO() }
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt index 281f344..f291fe7 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ExpectActualUtils.kt
@@ -31,6 +31,7 @@ return substitutor.chain(parentSubstitutor) } +// note: copy-pasted to: org.jetbrains.kotlin.fir.resolve.transformers.mpp.FirExpectActualMatchingContextImpl.areCompatibleExpectActualTypes fun areCompatibleExpectActualTypes( expectedType: ConeKotlinType?, actualType: ConeKotlinType?,
diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt index 818d007..8a08938 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt
@@ -286,6 +286,7 @@ override val TypeParameterSymbolMarker.isReified: Boolean get() = asSymbol().isReified + // note: copy-pasted to: org.jetbrains.kotlin.fir.types.ExpectActualUtilsKt.areCompatibleExpectActualTypes override fun areCompatibleExpectActualTypes( expectType: KotlinTypeMarker?, actualType: KotlinTypeMarker?,
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt index 7217458..2e88090 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker.kt
@@ -19,9 +19,11 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMemberDiff import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver +import org.jetbrains.kotlin.resolve.multiplatform.createExpectActualTypeParameterSubstitutor import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeSubstitutor import java.util.* import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -132,82 +134,118 @@ ): Set<ExpectActualMemberDiff<CallableMemberDescriptor, ClassDescriptor>> { val expectScope = expect.unsubstitutedMemberScope val actualScope = actual.unsubstitutedMemberScope - val expectClassCallables = expectScope.extractNonPrivateCallables() + + val classTypeSubstitutor = createExpectActualTypeParameterSubstitutor( + expect.declaredTypeParameters, + actual.declaredTypeParameters, + parentSubstitutor = null + ) + val expectClassCallables = expectScope.extractNonPrivateCallables(classTypeSubstitutor, ExpectActual.EXPECT) val nameAndKindToExpectCallable = expectClassCallables.groupBy { it.name to it.kind } - return (actualScope.extractNonPrivateCallables() - expectClassCallables).asSequence() + return (actualScope.extractNonPrivateCallables(classTypeSubstitutor, ExpectActual.ACTUAL) - expectClassCallables).asSequence() .flatMap { unmatchedActualCallable -> when (val expectCallablesWithTheSameName = nameAndKindToExpectCallable[unmatchedActualCallable.name to unmatchedActualCallable.kind]) { null -> listOf(ExpectActualMemberDiff.Kind.NonPrivateCallableAdded) else -> expectCallablesWithTheSameName.map { - calculateDiffKind(expect = it, actual = unmatchedActualCallable) ?: error("Not equal callables can't have zero diff") + calculateExpectActualMemberDiffKind(expect = it, actual = unmatchedActualCallable) + ?: error("Not equal callables can't have zero diff") } }.map { kind -> ExpectActualMemberDiff(kind, unmatchedActualCallable.descriptor, expect) } } .toSet() } -private fun MemberScope.extractNonPrivateCallables(): Set<Callable> { +private fun MemberScope.extractNonPrivateCallables( + classTypeSubstitutor: TypeSubstitutor, + expectActual: ExpectActual +): Set<Callable> { val functions = getFunctionNames().asSequence().flatMap { getContributedFunctions(it, NoLookupLocation.FROM_FRONTEND_CHECKER) } val properties = getVariableNames().asSequence().flatMap { getContributedVariables(it, NoLookupLocation.FROM_FRONTEND_CHECKER) } return (functions + properties).filter { !Visibilities.isPrivate(it.visibility.delegate) } - .map { descriptor -> - val returnType = descriptor.returnType - Callable( - descriptor.name, - when (descriptor) { - is PropertyDescriptor -> Kind.PROPERTY - is FunctionDescriptor -> Kind.FUNCTION - else -> error("Unknown kind $descriptor") - }, - descriptor.extensionReceiverParameter?.type, - descriptor.contextReceiverParameters.map(ValueDescriptor::getType), - descriptor.valueParameters.map { Parameter(it.name, it.type) }, - returnType ?: error("Can't get return type"), - descriptor.modality, - descriptor.visibility.delegate, - descriptor, - ) - } + .map { descriptor -> Callable(descriptor, expectActual, classTypeSubstitutor) } .toSet() } private data class Parameter(val name: Name, val type: KotlinType) private enum class Kind { FUNCTION, PROPERTY } +private enum class ExpectActual { EXPECT, ACTUAL } private class Callable( - val name: Name, - val kind: Kind, - val extensionReceiverType: KotlinType?, - val contextReceiverTypes: List<KotlinType>, - val parameters: List<Parameter>, - val returnType: KotlinType, - val modality: Modality, - val visibility: Visibility, val descriptor: CallableMemberDescriptor, + val expectActual: ExpectActual, + val classTypeSubstitutor: TypeSubstitutor, ) { - override fun equals(other: Any?): Boolean = other is Callable && calculateDiffKind(this, other) == null - override fun hashCode(): Int = - Objects.hash(name, kind, extensionReceiverType, contextReceiverTypes, parameters, returnType, modality, visibility) + val name: Name = descriptor.name + val kind: Kind = when (descriptor) { + is PropertyDescriptor -> Kind.PROPERTY + is FunctionDescriptor -> Kind.FUNCTION + else -> error("Unknown kind $descriptor") + } + val extensionReceiverType: KotlinType? = descriptor.extensionReceiverParameter?.type + val contextReceiverTypes: List<KotlinType> = descriptor.contextReceiverParameters.map(ValueDescriptor::getType) + val parameters: List<Parameter> = descriptor.valueParameters.map { Parameter(it.name, it.type) } + val returnType: KotlinType = descriptor.returnType ?: error("Can't get return type") + val modality: Modality = descriptor.modality + val visibility: Visibility = descriptor.visibility.delegate + + override fun equals(other: Any?): Boolean { + if (other !is Callable) return false + check(classTypeSubstitutor === other.classTypeSubstitutor) + return if (expectActual == other.expectActual) { + name == other.name && + kind == other.kind && + extensionReceiverType == other.extensionReceiverType && + contextReceiverTypes == other.contextReceiverTypes && + parameters == other.parameters && + returnType == other.returnType && + modality == other.modality && + visibility == other.visibility + } else { + val (expect, actual) = if (expectActual == ExpectActual.EXPECT) this to other else other to this + calculateExpectActualMemberDiffKind(expect, actual) == null + } + } + + override fun hashCode(): Int = // Don't hash types because type comparison is complicated + Objects.hash( + name, + kind, + extensionReceiverType != null, + contextReceiverTypes.size, + parameters.map(Parameter::name), + modality, + visibility + ) } private val CallableMemberDescriptor.psiIfReal: KtCallableDeclaration? get() = takeIf { it.kind.isReal }?.source?.let { it as? KotlinSourceElement }?.psi as? KtCallableDeclaration -private fun calculateDiffKind(expect: Callable, actual: Callable): ExpectActualMemberDiff.Kind? = when { - expect.name != actual.name || - expect.kind != actual.kind || - expect.extensionReceiverType != actual.extensionReceiverType || - expect.contextReceiverTypes != actual.contextReceiverTypes || - expect.parameters.map(Parameter::type) != actual.parameters.map(Parameter::type) -> - ExpectActualMemberDiff.Kind.NonPrivateCallableAdded - expect.parameters.map(Parameter::name) != actual.parameters.map(Parameter::name) -> - ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride - expect.returnType != actual.returnType -> ExpectActualMemberDiff.Kind.ReturnTypeCovariantOverride - expect.modality != actual.modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride - expect.visibility != actual.visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride - else -> null +private fun calculateExpectActualMemberDiffKind(expect: Callable, actual: Callable): ExpectActualMemberDiff.Kind? { + check(expect.expectActual == ExpectActual.EXPECT) + check(actual.expectActual == ExpectActual.ACTUAL) + check(expect.classTypeSubstitutor === actual.classTypeSubstitutor) + val substitutor = createExpectActualTypeParameterSubstitutor( + expect.descriptor.typeParameters, + actual.descriptor.typeParameters, + actual.classTypeSubstitutor + ) + return when { + expect.name != actual.name || + expect.kind != actual.kind || + expect.extensionReceiverType != actual.extensionReceiverType || + expect.contextReceiverTypes != actual.contextReceiverTypes || + expect.parameters.map(Parameter::type) != actual.parameters.map(Parameter::type) -> + ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + expect.parameters.map(Parameter::name) != actual.parameters.map(Parameter::name) -> + ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride + expect.returnType != actual.returnType -> ExpectActualMemberDiff.Kind.ReturnTypeCovariantOverride + expect.modality != actual.modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride + expect.visibility != actual.visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride + else -> null + } } private val ExpectActualMemberDiff.Kind.factory: DiagnosticFactory1<KtCallableDeclaration, ExpectActualMemberDiff<CallableMemberDescriptor, ClassDescriptor>> @@ -217,4 +255,5 @@ ExpectActualMemberDiff.Kind.ModalityChangedInOverride -> Errors.MODALITY_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION ExpectActualMemberDiff.Kind.VisibilityChangedInOverride -> Errors.VISIBILITY_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride -> Errors.PARAMETER_NAME_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION + ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride -> TODO() }
diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt index 1412dc8..8961b1c 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered @@ -144,20 +143,11 @@ expectTypeParameters: List<TypeParameterSymbolMarker>, actualTypeParameters: List<TypeParameterSymbolMarker>, parentSubstitutor: TypeSubstitutorMarker?, - ): TypeSubstitutorMarker { - val expectParameters = expectTypeParameters.castAll<TypeParameterDescriptor>() - val actualParameters = actualTypeParameters.castAll<TypeParameterDescriptor>() - val substitutor = TypeSubstitutor.create( - TypeConstructorSubstitution.createByParametersMap(expectParameters.keysToMap { - actualParameters[it.index].defaultType.asTypeProjection() - }) - ) - return when (parentSubstitutor) { - null -> substitutor - is TypeSubstitutor -> TypeSubstitutor.createChainedSubstitutor(parentSubstitutor.substitution, substitutor.substitution) - else -> error("Unsupported substitutor type: $parentSubstitutor") - } - } + ): TypeSubstitutorMarker = org.jetbrains.kotlin.resolve.multiplatform.createExpectActualTypeParameterSubstitutor( + expectTypeParameters.castAll<TypeParameterDescriptor>(), + actualTypeParameters.castAll<TypeParameterDescriptor>(), + parentSubstitutor as? TypeSubstitutor ?: error("Unsupported substitutor type: $parentSubstitutor") + ) override fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List<DeclarationSymbolMarker> { return asDescriptor().getMembers(name = null) @@ -221,15 +211,8 @@ get() = asDescriptor().isReified override fun areCompatibleExpectActualTypes(expectType: KotlinTypeMarker?, actualType: KotlinTypeMarker?): Boolean { - if (expectType == null) return actualType == null - if (actualType == null) return false - require(expectType is KotlinType && actualType is KotlinType) - return if (platformModule.isTypeRefinementEnabled()) { - areCompatibleTypesViaTypeRefinement(expectType, actualType) - } else { - areCompatibleTypesViaTypeContext(expectType, actualType) - } + return areCompatibleExpectActualTypes(expectType, actualType) } override val RegularClassSymbolMarker.defaultType: KotlinTypeMarker @@ -387,3 +370,30 @@ this is CallableMemberDescriptor && kind == CallableMemberDescriptor.Kind.SYNTHESIZED } } + +fun createExpectActualTypeParameterSubstitutor( + expectParameters: List<TypeParameterDescriptor>, + actualParameters: List<TypeParameterDescriptor>, + parentSubstitutor: TypeSubstitutor?, +): TypeSubstitutor { + val substitutor = TypeSubstitutor.create( + TypeConstructorSubstitution.createByParametersMap(expectParameters.keysToMap { + actualParameters[it.index].defaultType.asTypeProjection() + }) + ) + return when (parentSubstitutor) { + null -> substitutor + else -> TypeSubstitutor.createChainedSubstitutor(parentSubstitutor.substitution, substitutor.substitution) + } +} + +fun areCompatibleExpectActualTypes(expectType: KotlinType?, actualType: KotlinType?, platformModule: ModuleDescriptor): Boolean { + if (expectType == null) return actualType == null + if (actualType == null) return false + + return if (platformModule.isTypeRefinementEnabled()) { + areCompatibleTypesViaTypeRefinement(expectType, actualType) + } else { + areCompatibleTypesViaTypeContext(expectType, actualType) + } +}
diff --git a/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectGenericUpperBoundOverload.kt b/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectGenericUpperBoundOverload.kt new file mode 100644 index 0000000..e2a41e1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectGenericUpperBoundOverload.kt
@@ -0,0 +1,16 @@ +// FIR_IDENTICAL +// MODULE: m1-common +// FILE: common.kt + +open class Base { + open fun <T> foo(t: T) {} +} + +expect class Foo : Base + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +actual class Foo : Base() { + fun <T : Comparable<T>> <!NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION!>foo<!>(t: T) {} +}
diff --git a/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.fir.kt new file mode 100644 index 0000000..a2d3afc --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.fir.kt
@@ -0,0 +1,15 @@ +// MODULE: m1-common +// FILE: common.kt + +open class Base { + open val foo: Int = 1 +} + +expect open class Foo : Base + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +actual open class Foo : Base() { + override var foo: Int = 1 +} \ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.kt b/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.kt new file mode 100644 index 0000000..aa1214a --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.kt
@@ -0,0 +1,15 @@ +// MODULE: m1-common +// FILE: common.kt + +open class Base { + open val foo: Int = 1 +} + +expect open class Foo : Base + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt + +actual open class Foo : Base() { + override var <!NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION!>foo<!>: Int = 1 +}
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 336c73f..3286c68 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
@@ -23740,6 +23740,12 @@ } @Test + @TestMetadata("injectGenericUpperBoundOverload.kt") + public void testInjectGenericUpperBoundOverload() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectGenericUpperBoundOverload.kt"); + } + + @Test @TestMetadata("injectInnerClass.kt") public void testInjectInnerClass() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/injectInnerClass.kt"); @@ -23812,6 +23818,12 @@ } @Test + @TestMetadata("propertyKindOverride.kt") + public void testPropertyKindOverride() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/propertyKindOverride.kt"); + } + + @Test @TestMetadata("severalMismatches.kt") public void testSeveralMismatches() throws Exception { runTest("compiler/testData/diagnostics/tests/multiplatform/injectIntoExpectOpenClassDuringActualization/severalMismatches.kt");
diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt index 30bc451..a0d9374 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt
@@ -8,6 +8,8 @@ data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember: M, val expectClass: C) { /** * Diff kinds that are legal for fake-overrides in final `expect class`, but illegal for non-final `expect class` + * + * Also see: [toDiffKind] */ enum class Kind(val rawMessage: String) { NonPrivateCallableAdded( @@ -35,6 +37,50 @@ "This error happens because the expect class ''{1}'' is non-final." ), - // todo type parameters? + PropertyKindChangedInOverride( + "{0}: The property kind (val vs var) of this member must be the same in the expect class and the actual class. " + + "This error happens because the expect class ''{1}'' is non-final." + ), + } +} + +/** + * This function serves a purpose of type-safe documentation. A mapping can be established between [ExpectActualCompatibility.Incompatible] + * and [ExpectActualMemberDiff.Kind]. This exhaustive when fixates this mapping, ensuring that we won't forget to add new + * [ExpectActualMemberDiff.Kind] when [ExpectActualCompatibility.Incompatible] is updated and vice versa. + */ +@Suppress("unused") +private fun ExpectActualCompatibility.Incompatible<*>.toDiffKind(): ExpectActualMemberDiff.Kind? { + return when (this) { + ExpectActualCompatibility.Incompatible.CallableKind -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + ExpectActualCompatibility.Incompatible.ParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + ExpectActualCompatibility.Incompatible.ParameterShape -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + ExpectActualCompatibility.Incompatible.ParameterTypes -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeCovariantOverride + ExpectActualCompatibility.Incompatible.TypeParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + ExpectActualCompatibility.Incompatible.TypeParameterUpperBounds -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded + ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override + ExpectActualCompatibility.Incompatible.ClassKind -> null // Not applicable because ExpectActualMemberDiff is about members + ExpectActualCompatibility.Incompatible.ClassModifiers -> null // Not applicable because ExpectActualMemberDiff is about members + is ExpectActualCompatibility.Incompatible.ClassScopes -> null // Not applicable because ExpectActualMemberDiff is about members + ExpectActualCompatibility.Incompatible.EnumEntries -> null // Not applicable because ExpectActualMemberDiff is about members + ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> null // Not applicable because ExpectActualMemberDiff is about members + ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier + ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier + ExpectActualCompatibility.Incompatible.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride + ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride + ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const can't be overridden + ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride + ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> TODO() + ExpectActualCompatibility.Incompatible.PropertySetterVisibility -> TODO() + ExpectActualCompatibility.Incompatible.Supertypes -> null // Not applicable because ExpectActualMemberDiff is about members. But related: ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_SUPERTYPES_AS_NON_FINAL_EXPECT_CLASSIFIER + ExpectActualCompatibility.Incompatible.TypeParameterNames -> TODO() + ExpectActualCompatibility.Incompatible.TypeParameterReified -> TODO() + ExpectActualCompatibility.Incompatible.TypeParameterVariance -> TODO() + ExpectActualCompatibility.Incompatible.Unknown -> TODO() + ExpectActualCompatibility.Incompatible.ValueParameterCrossinline -> TODO() + ExpectActualCompatibility.Incompatible.ValueParameterNoinline -> TODO() + ExpectActualCompatibility.Incompatible.ValueParameterVararg -> TODO() + ExpectActualCompatibility.Incompatible.Visibility -> TODO() } }