From 30aad31ece22f9fae62e36b1bfa9ff7c6e4f5587 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 19 Dec 2023 15:32:38 +0100 Subject: [PATCH] [FIR, IR] Prohibit actualization of expect Kotlin property to Java field The problem from KT-63624 was that during matching phase we must choose only one candidate, but in Java we can have two successfully matched properties: 1) from field and 2) from method, which overrides Kotlin property. See test `propertyAgainstJavaPrivateFieldAndPublicMethod.kt`. As a result, we choose field candidate, throw away method candidate, and then fail during visibility check. Instead of inventing special rule of prioritizing field over method it was decided to prohibit actualization to Java field at all because: 1. It doesn't seem that Java fields actualization was implemented in K1 on purpose 2. People usually don't use public Java fields, and use instead private field + getter, especially when compatibility is important, so it shouldn't be a breaking change Besides that, such solution simplifies code and is consistent with the current logic of matcher, which doesn't expect that two members can be matched successfully. Also, it fixes KT-63624 and KT-63667. ^KT-63624 Fixed ^KT-63667 Fixed --- .../mpp/FirExpectActualMatchingContextImpl.kt | 4 ++++ .../IrExpectActualMatchingContext.kt | 3 +++ .../calls/mpp/AbstractExpectActualMatcher.kt | 4 ++++ .../calls/mpp/ExpectActualMatchingContext.kt | 2 ++ ...instJavaPrivateFieldAndPublicMethod.fir.kt | 2 +- ...nstJavaPublicFieldAndPublicGetter.fir.fail | 1 - ...ainstJavaPublicFieldAndPublicGetter.fir.kt | 23 +++++++++++++++++++ ...tyAgainstJavaPublicFieldAndPublicGetter.kt | 1 - .../java/varPropertyAgainstJavaField.fir.fail | 1 - .../java/varPropertyAgainstJavaField.fir.kt | 14 +++++++++++ .../java/varPropertyAgainstJavaField.kt | 1 - .../java/varPropertyAgainstJavaField.ll.kt | 14 +++++++++++ ...AgainstJavaGetterAndNonFinalField.fir.fail | 1 - ...tyAgainstJavaGetterAndNonFinalField.fir.kt | 23 +++++++++++++++++++ ...opertyAgainstJavaGetterAndNonFinalField.kt | 1 - .../ExpectActualCompatibility.kt | 1 + 16 files changed, 89 insertions(+), 7 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.fail create mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.fail create mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.ll.kt delete mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.fail create mode 100644 compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.kt 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 8c11eeaf989..0a13253e913 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isActual import org.jetbrains.kotlin.fir.declarations.utils.isExpect +import org.jetbrains.kotlin.fir.declarations.utils.isJava import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.providers.getRegularClassSymbolByClassId @@ -435,6 +436,9 @@ class FirExpectActualMatchingContextImpl private constructor( override val CallableSymbolMarker.hasStableParameterNames: Boolean get() = asSymbol().rawStatus.hasStableParameterNames + override val CallableSymbolMarker.isJavaField: Boolean + get() = this is FirFieldSymbol && fir.isJava + override val DeclarationSymbolMarker.annotations: List get() = asSymbol().resolvedAnnotationsWithArguments.map(::AnnotationCallInfoImpl) diff --git a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt index ff6b6c06a79..507853c5153 100644 --- a/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt +++ b/compiler/ir/ir.actualization/src/main/kotlin/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt @@ -492,6 +492,9 @@ internal abstract class IrExpectActualMatchingContext( } } + override val CallableSymbolMarker.isJavaField: Boolean + get() = this is IrFieldSymbol && owner.isFromJava() + override fun onMatchedMembers( expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker, diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt index 8503bbabfa5..da24c58f312 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualMatcher.kt @@ -144,6 +144,10 @@ object AbstractExpectActualMatcher { return ExpectActualMatchingCompatibility.CallableKind } + if (actualDeclaration.isJavaField) { + return ExpectActualMatchingCompatibility.ActualJavaField + } + val expectedReceiverType = expectDeclaration.extensionReceiverType val actualReceiverType = actualDeclaration.extensionReceiverType if ((expectedReceiverType != null) != (actualReceiverType != null)) { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt index b953916bbbe..eca3901e811 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt @@ -163,6 +163,8 @@ interface ExpectActualMatchingContext : TypeSystemC val CallableSymbolMarker.hasStableParameterNames: Boolean + val CallableSymbolMarker.isJavaField: Boolean + fun onMatchedMembers( expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker, diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPrivateFieldAndPublicMethod.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPrivateFieldAndPublicMethod.fir.kt index 6b47b89be2f..6f23493c40c 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPrivateFieldAndPublicMethod.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPrivateFieldAndPublicMethod.fir.kt @@ -10,7 +10,7 @@ expect class Foo : I { // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt -actual typealias Foo = JavaFoo +actual typealias Foo = JavaFoo // FILE: JavaFoo.java public class JavaFoo implements I { diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.fail b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.fail deleted file mode 100644 index 35dc1cfbea0..00000000000 --- a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.fail +++ /dev/null @@ -1 +0,0 @@ -Fails in K2 due to KT-63667 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.kt new file mode 100644 index 00000000000..a15fdc9398f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.fir.kt @@ -0,0 +1,23 @@ +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + val foo: Int +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual typealias Foo = JavaFoo + +interface I { + val foo: Int +} + +// FILE: JavaFoo.java +public class JavaFoo implements I { + public final int foo = 1; + + @Override + public int getFoo() { + return 0; + } +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.kt b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.kt index 7849c8b7159..2cfd2d1d3ce 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/propertyAgainstJavaPublicFieldAndPublicGetter.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // MODULE: m1-common // FILE: common.kt expect class Foo { diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.fail b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.fail deleted file mode 100644 index 35dc1cfbea0..00000000000 --- a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.fail +++ /dev/null @@ -1 +0,0 @@ -Fails in K2 due to KT-63667 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.kt new file mode 100644 index 00000000000..a918045a423 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.fir.kt @@ -0,0 +1,14 @@ +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + var foo: Int +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual typealias Foo = JavaFoo + +// FILE: JavaFoo.java +public class JavaFoo { + public int foo = 0; +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.kt b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.kt index 5c005d793cb..95304ee09d5 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // MODULE: m1-common // FILE: common.kt expect class Foo { diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.ll.kt new file mode 100644 index 00000000000..d0abac9531d --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaField.ll.kt @@ -0,0 +1,14 @@ +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + var foo: Int +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +actual typealias Foo = JavaFoo + +// FILE: JavaFoo.java +public class JavaFoo { + public int foo = 0; +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.fail b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.fail deleted file mode 100644 index 35dc1cfbea0..00000000000 --- a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.fail +++ /dev/null @@ -1 +0,0 @@ -Fails in K2 due to KT-63667 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.kt new file mode 100644 index 00000000000..c8548f9ccb0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.fir.kt @@ -0,0 +1,23 @@ +// MODULE: m1-common +// FILE: common.kt +expect class Foo { + var foo: Int +} + +// MODULE: m2-jvm()()(m1-common) +// FILE: jvm.kt +interface I { + val foo: Int +} + +actual typealias Foo = JavaFoo + +// FILE: JavaFoo.java +public class JavaFoo implements I { + public int foo; + + @Override + public int getFoo() { + return 0; + } +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.kt b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.kt index a4d2fc6475b..3ac46537cdb 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/java/varPropertyAgainstJavaGetterAndNonFinalField.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // MODULE: m1-common // FILE: common.kt expect class Foo { diff --git a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt index 24d03362251..911df9592fb 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt @@ -34,6 +34,7 @@ sealed class ExpectActualMatchingCompatibility : ExpectActualCompatibility object CallableKind : Mismatch("callable kinds are different (function vs property)") + object ActualJavaField : Mismatch("actualization to Java field is prohibited") object ParameterShape : Mismatch("parameter shapes are different (extension vs non-extension)") object ParameterCount : Mismatch("number of value parameters is different") object FunctionTypeParameterCount : Mismatch(TYPE_PARAMETER_COUNT)