[IR] Fix missing AMBIGUOUS_ACTUALS

^KT-59938 Fixed
Review: https://jetbrains.team/p/kt/reviews/13760

After this patch:

    > The /compiler/testData/diagnostics/tests/multiplatform/topLevelFun/conflictingImplDeclarations.kt test:

    Fixed

    > The /compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithWeakIncompatibilities.kt test:

    Declarations are in the same module. `PACKAGE_OR_CLASSIFIER_REDECLARATION` is reported anyway

    > The /compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.kt test:

    Expect-actual matcher doesn't match fields in K2 https://youtrack.jetbrains.com/issue/KT-63667 => green code

    > The /compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPrivateFieldAndPublicMethod.kt test:

    Expect-actual matcher doesn't match fields in K2 https://youtrack.jetbrains.com/issue/KT-63667 => green code

    > The /compiler/testData/diagnostics/tests/multiplatform/java/implicitJavaActualization_multipleActuals.kt test:

    K2 doesn't have implicit Java actualization. And PACKAGE_OR_CLASSIFIER_REDECLARATION is reported anyway

    > The /compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.kt test:

    Expect-actual matcher doesn't match fields in K2 https://youtrack.jetbrains.com/issue/KT-63667 => green code

    > The /compiler/testData/diagnostics/tests/multiplatform/actualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker/injectContextReceiverOverload.kt test:

    Context receivers are not supported in expect-actual matcher. https://youtrack.jetbrains.com/issue/KT-61447
    And K2 reports another error right now anyway
This commit is contained in:
Nikita Bobko
2023-12-05 15:10:32 +01:00
committed by Space Team
parent e6860054ea
commit 013b5e3780
25 changed files with 129 additions and 24 deletions
@@ -26103,6 +26103,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp"), Pattern.compile("^(.+)\\.(kt|kts)$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true);
}
@Test
@TestMetadata("ambiguousActuals.kt")
public void testAmbiguousActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/ambiguousActuals.kt");
}
@Test
@TestMetadata("intermediateActualHasAdditionalSupertypes.kt")
public void testIntermediateActualHasAdditionalSupertypes() throws Exception {
@@ -26103,6 +26103,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp"), Pattern.compile("^(.+)\\.(kt|kts)$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true);
}
@Test
@TestMetadata("ambiguousActuals.kt")
public void testAmbiguousActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/ambiguousActuals.kt");
}
@Test
@TestMetadata("intermediateActualHasAdditionalSupertypes.kt")
public void testIntermediateActualHasAdditionalSupertypes() throws Exception {
@@ -1800,6 +1800,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("ambiguousActuals.kt")
public void testAmbiguousActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/ambiguousActuals.kt");
}
@Test
@TestMetadata("intermediateActualHasAdditionalSupertypes.kt")
public void testIntermediateActualHasAdditionalSupertypes() throws Exception {
@@ -1800,6 +1800,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("ambiguousActuals.kt")
public void testAmbiguousActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/ambiguousActuals.kt");
}
@Test
@TestMetadata("intermediateActualHasAdditionalSupertypes.kt")
public void testIntermediateActualHasAdditionalSupertypes() throws Exception {
@@ -309,12 +309,12 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
destination[expectClassSymbol] = actualClassSymbol
expectActualTracker?.reportWithCurrentFile(actualClassSymbol)
recordActualForExpectDeclaration(expectClassSymbol, actualClassSymbol, destination)
recordActualForExpectDeclaration(expectClassSymbol, actualClassSymbol, destination, diagnosticsReporter)
}
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
expectActualTracker?.reportWithCurrentFile(actualSymbol)
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination)
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination, diagnosticsReporter)
}
override fun onIncompatibleMembersFromClassScope(
@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.backend.common.actualizer
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
import org.jetbrains.kotlin.ir.declarations.IrOverridableDeclaration
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.isExpect
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
* This Actualizer processes expect overridable declarations in non-expect classes and replaces them with the associated actual overridable
* declarations overriding the actual base class members.The newly created actual fake overrides are stored in expectActualMap.
*/
internal class FakeOverridesActualizer(private val expectActualMap: MutableMap<IrSymbol, IrSymbol>) : IrElementVisitorVoid {
internal class FakeOverridesActualizer(
private val expectActualMap: MutableMap<IrSymbol, IrSymbol>,
private val diagnosticReporter: IrDiagnosticReporter
) : IrElementVisitorVoid {
override fun visitClass(declaration: IrClass) {
if (!declaration.isExpect) {
actualizeFakeOverrides(declaration)
@@ -47,7 +50,7 @@ internal class FakeOverridesActualizer(private val expectActualMap: MutableMap<I
val actualizedOverrides = overriddenSymbols.map { (it.owner as IrDeclaration).actualize() }
val actualFakeOverride = createFakeOverrideMember(actualizedOverrides, parent as IrClass)
recordActualForExpectDeclaration(this.symbol, actualFakeOverride.symbol, expectActualMap)
recordActualForExpectDeclaration(this.symbol, actualFakeOverride.symbol, expectActualMap, diagnosticReporter)
return actualFakeOverride
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibil
internal object IrActualizationErrors {
val NO_ACTUAL_FOR_EXPECT by error2<PsiElement, String, ModuleDescriptor>()
val AMBIGUOUS_ACTUALS by error2<PsiElement, String, ModuleDescriptor>()
val EXPECT_ACTUAL_MISMATCH by error3<PsiElement, String, String, ExpectActualMatchingCompatibility.Mismatch>()
val EXPECT_ACTUAL_INCOMPATIBILITY by error3<PsiElement, String, String, ExpectActualCheckingCompatibility.Incompatible<*>>()
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3<PsiElement, IrSymbol, IrSymbol, ExpectActualAnnotationsIncompatibilityType<IrConstructorCall>>()
@@ -34,6 +35,12 @@ internal object IrActualizationErrors {
internal object KtDefaultIrActualizationErrorMessages : BaseDiagnosticRendererFactory() {
override val MAP = KtDiagnosticFactoryToRendererMap("KT").also { map ->
map.put(
IrActualizationErrors.AMBIGUOUS_ACTUALS,
"{0} has several compatible actual declarations in modules {1}",
CommonRenderers.STRING,
IrActualizationDiagnosticRenderers.MODULE_WITH_PLATFORM,
)
map.put(
IrActualizationErrors.NO_ACTUAL_FOR_EXPECT,
"Expected {0} has no actual declaration in module {1}",
@@ -83,7 +83,7 @@ class IrActualizer(
if (!useIrFakeOverrideBuilder) {
// 2. Actualize expect fake overrides in non-expect classes inside common or multi-platform module.
// It's probably important to run FakeOverridesActualizer before ActualFakeOverridesAdder
FakeOverridesActualizer(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
FakeOverridesActualizer(expectActualMap, ktDiagnosticReporter).apply { dependentFragments.forEach { visitModuleFragment(it) } }
// 3. Add fake overrides to non-expect classes inside common or multi-platform module,
// taken from these non-expect classes actualized super classes.
@@ -55,18 +55,13 @@ internal fun recordActualForExpectDeclaration(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
destination: MutableMap<IrSymbol, IrSymbol>,
diagnosticsReporter: IrDiagnosticReporter,
) {
val expectDeclaration = expectSymbol.owner as IrDeclarationBase
val actualDeclaration = actualSymbol.owner as IrDeclaration
val registeredActual = destination.put(expectSymbol, actualSymbol)
require(registeredActual == null || registeredActual == actualSymbol) {
"""
Expect symbol already has registered mapping
Expect declaration: ${expectDeclaration.render()}
Actual declaration: ${actualDeclaration.render()}
Already registered: ${registeredActual!!.owner.render()}
""".trimIndent()
if (registeredActual != null && registeredActual != actualSymbol) {
diagnosticsReporter.reportAmbiguousActuals(expectDeclaration)
}
if (expectDeclaration is IrTypeParametersContainer) {
recordTypeParametersMapping(destination, expectDeclaration, actualDeclaration as IrTypeParametersContainer)
@@ -107,6 +102,15 @@ internal fun IrDiagnosticReporter.reportMissingActual(irDeclaration: IrDeclarati
)
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
internal fun IrDiagnosticReporter.reportAmbiguousActuals(expectSymbol: IrDeclaration) {
at(expectSymbol).report(
IrActualizationErrors.AMBIGUOUS_ACTUALS,
(expectSymbol as? IrDeclarationWithName)?.name?.asString().orEmpty(),
expectSymbol.module
)
}
internal fun IrDiagnosticReporter.reportExpectActualIncompatibility(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibil
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.partitionIsInstance
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
@@ -107,20 +108,22 @@ object AbstractExpectActualMatcher {
}
}
val incompatibilityMap = mutableMapOf<ExpectActualMatchingCompatibility.Mismatch, MutableList<DeclarationSymbolMarker>>()
val matched = ArrayList<DeclarationSymbolMarker>()
val mismatched = HashMap<ExpectActualMatchingCompatibility.Mismatch, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return actualMember
matched.add(actualMember)
}
is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
is ExpectActualMatchingCompatibility.Mismatch -> mismatched.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
mismatchedMembers?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
matched.singleOrNull()?.let { return it }
mismatchedMembers?.add(expectMember to mismatched)
onMismatchedMembersFromClassScope(expectMember, mismatched, expectClassSymbol, actualClassSymbol)
return null
}
@@ -9,7 +9,7 @@ expect open class Foo {
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
actual open class Foo {
actual open class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo<!> {
actual fun foo() {}
// Expected: NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION_WARNING.
@@ -0,0 +1,10 @@
// MODULE: common
// TARGET_PLATFORM: Common
<!AMBIGUOUS_ACTUALS{JVM}, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!>
// MODULE: intermediate()()(common)
// TARGET_PLATFORM: Common
actual fun foo() {}
// MODULE: main()()(common, intermediate)
actual fun foo() {}
@@ -0,0 +1,10 @@
// MODULE: common
// TARGET_PLATFORM: Common
expect fun <!AMBIGUOUS_ACTUALS{JVM}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{COMMON}!>foo<!>()
// MODULE: intermediate()()(common)
// TARGET_PLATFORM: Common
<!CONFLICTING_OVERLOADS{JVM}!>actual fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>foo<!>()<!> {}
// MODULE: main()()(common, intermediate)
<!CONFLICTING_OVERLOADS!>actual fun foo()<!> {}
@@ -0,0 +1,10 @@
// MODULE: common
// TARGET_PLATFORM: Common
expect fun foo()
// MODULE: intermediate()()(common)
// TARGET_PLATFORM: Common
actual fun foo() {}
// MODULE: main()()(common, intermediate)
actual fun foo() {}
@@ -12,4 +12,4 @@
import java.util.AbstractMap
public actual abstract class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>AbstractMutableMap<!><K, V>() : MutableMap<K, V>, AbstractMap<K, V>()
public actual abstract class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>AbstractMutableMap<!><K, V>() : MutableMap<K, V>, AbstractMap<K, V>()
@@ -12,4 +12,4 @@ public expect abstract class AbstractMutableMap<K, V> : MutableMap<K, V> {
import java.util.AbstractMap
public actual abstract class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>AbstractMutableMap<!><K, V>() : MutableMap<K, V>, AbstractMap<K, V>()
public actual abstract class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>AbstractMutableMap<!><K, V>() : MutableMap<K, V>, AbstractMap<K, V>()
@@ -5,6 +5,8 @@ interface I {
}
expect class Foo : I {
// AMBIGUOUS_ACTUALS in K1, green code in K2.
// Reason: expect-actual matcher doesn't match fields in K2 KT-63667
override val foo: Int
}
@@ -5,6 +5,8 @@ interface I {
}
expect class Foo : I {
// AMBIGUOUS_ACTUALS in K1, green code in K2.
// Reason: expect-actual matcher doesn't match fields in K2 KT-63667
override val <!AMBIGUOUS_ACTUALS{JVM}!>foo<!>: Int
}
@@ -1,6 +1,8 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
// AMBIGUOUS_ACTUALS in K1, green code in K2.
// Reason: expect-actual matcher doesn't match fields in K2 KT-63667
val foo: Int
}
@@ -1,6 +1,8 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
// AMBIGUOUS_ACTUALS in K1, green code in K2.
// Reason: expect-actual matcher doesn't match fields in K2 KT-63667
val <!AMBIGUOUS_ACTUALS{JVM}!>foo<!>: Int
}
@@ -1,6 +1,8 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
// AMBIGUOUS_ACTUALS in K1, green code in K2.
// Reason: expect-actual matcher doesn't match fields in K2 KT-63667
var foo: Int
}
@@ -1,6 +1,8 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
// AMBIGUOUS_ACTUALS in K1, green code in K2.
// Reason: expect-actual matcher doesn't match fields in K2 KT-63667
var <!AMBIGUOUS_ACTUALS{JVM}!>foo<!>: Int
}
@@ -1,7 +1,7 @@
// MODULE: m1-common
// FILE: common.kt
expect fun foo()
<!AMBIGUOUS_ACTUALS{JVM}, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
@@ -0,0 +1,16 @@
// MODULE: m1-common
// FILE: common.kt
expect fun foo()
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
<!CONFLICTING_OVERLOADS!>actual fun foo()<!> {}
<!CONFLICTING_OVERLOADS!>actual fun foo()<!> {}
// MODULE: m3-js()()(m1-common)
// FILE: js.kt
<!CONFLICTING_OVERLOADS!>actual fun foo()<!> {}
<!CONFLICTING_OVERLOADS!>actual fun foo()<!> {}
@@ -26103,6 +26103,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/hmpp"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true);
}
@Test
@TestMetadata("ambiguousActuals.kt")
public void testAmbiguousActuals() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/ambiguousActuals.kt");
}
@Test
@TestMetadata("intermediateActualHasAdditionalSupertypes.kt")
public void testIntermediateActualHasAdditionalSupertypes() throws Exception {