From 9f01ccc304420560fde10cec43df9fd62a6b2a81 Mon Sep 17 00:00:00 2001 From: "Evgeniy.Zhelenskiy" Date: Fri, 14 Oct 2022 07:17:03 +0200 Subject: [PATCH] [IR] Support user-defined equals for MFVC Signed-off-by: Evgeniy.Zhelenskiy #KT-1179 --- .../diagnostics/KtFirDataClassConverters.kt | 14 +- .../api/fir/diagnostics/KtFirDiagnostics.kt | 12 +- .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 13 +- ...CompilerTestFE10TestdataTestGenerated.java | 24 ++- .../inlineClasses/inlineClassMembers.fir.txt | 30 ++- .../inlineClasses/inlineClassMembers.kt | 14 +- ...irOldFrontendDiagnosticsTestGenerated.java | 24 ++- ...DiagnosticsWithLightTreeTestGenerated.java | 24 ++- .../diagnostics/FirDiagnosticsList.kt | 5 +- .../fir/analysis/diagnostics/FirErrors.kt | 5 +- .../FirExpectActualDeclarationChecker.kt | 8 +- .../FirInlineClassDeclarationChecker.kt | 44 +++-- .../declaration/FirOperatorModifierChecker.kt | 4 +- .../checkers/declaration/declarationUtils.kt | 8 +- .../diagnostics/FirErrorsDefaultMessages.kt | 10 +- .../FirBlackBoxCodegenTestGenerated.java | 54 ++++- .../fir/declarations/declarationUtils.kt | 2 +- .../jetbrains/kotlin/diagnostics/Errors.java | 3 +- .../rendering/DefaultErrorMessages.java | 7 +- .../kotlin/resolve/OperatorModifierChecker.kt | 6 +- .../checkers/InlineClassDeclarationChecker.kt | 22 ++- .../js/dce/JsUsefulDeclarationProcessor.kt | 1 - .../lower/JvmMultiFieldValueClassLowering.kt | 16 +- .../jetbrains/kotlin/backend/jvm/MfvcNode.kt | 5 +- .../kotlin/backend/jvm/MfvcNodeFactory.kt | 57 ++++-- .../ir/util/DataClassMembersGenerator.kt | 10 +- .../inlineClassBothEqualsOverride.kt | 7 +- .../inlineClassBothEqualsOverride.txt | 55 ++++++ .../inlineClassEqualsConsistency.kt | 7 +- .../inlineClassEqualsConsistency.txt | 44 +++++ ...ineClassEqualsOverriddenForCollections.kt} | 7 +- ...ineClassEqualsOverriddenForCollections.txt | 25 +++ .../inlineClassEqualsOverride.kt | 6 +- .../inlineClassHashCodeOverride.kt | 7 +- .../inlineClassHashCodeOverride.txt | 31 +++ .../inlineClassTypedEqualsGenerics.kt | 13 +- .../inlineClassTypedEqualsGenerics.txt | 99 ++++++++++ .../inlineClassUntypedEqualsOverriden.kt | 7 +- .../inlineClassUntypedEqualsOverriden.txt | 24 +++ .../inlineOverInlineWithCustomEquals.kt | 2 +- .../codegen/box/inlineClasses/kt54536.kt | 2 +- .../codegen/box/inlineClasses/kt54603.kt | 2 +- .../valueClasses/mfvcBothEqualsOverride.kt | 53 +++++ .../valueClasses/mfvcBothEqualsOverride.txt | 29 +++ .../box/valueClasses/mfvcEqualsConsistency.kt | 65 ++++++ .../valueClasses/mfvcEqualsConsistency.txt | 45 +++++ .../mfvcEqualsOverriddenForCollections.kt | 25 +++ .../mfvcEqualsOverriddenForCollections.txt | 25 +++ .../box/valueClasses/mfvcEqualsOverride.kt | 41 ++++ .../box/valueClasses/mfvcEqualsOverride.txt | 32 +++ .../box/valueClasses/mfvcHashCodeOverride.kt | 18 ++ .../box/valueClasses/mfvcHashCodeOverride.txt | 32 +++ .../valueClasses/mfvcTypedEqualsGenerics.kt | 56 ++++++ .../valueClasses/mfvcTypedEqualsGenerics.txt | 99 ++++++++++ .../mfvcUntypedEqualsOverriden.kt | 26 +++ .../mfvcUntypedEqualsOverriden.txt | 25 +++ ...nefficientEqualsOverridingInInlineClass.kt | 10 +- ...edMembersAndConstructsInsideInlineClass.kt | 42 +++- ...dMembersAndConstructsInsideInlineClass.txt | 47 ++++- .../typedEqualsOperatorDeclarationCheck.kt | 8 +- ...ypedEqualsOperatorModifierInInlineClass.kt | 23 +++ .../inefficientEqualsOverridingInMfvc.kt | 28 +++ .../inefficientEqualsOverridingInMfvc.txt | 29 +++ .../reservedMembersAndConstructsInsideMfvc.kt | 24 ++- ...reservedMembersAndConstructsInsideMfvc.txt | 186 ++++++++++++++++++ ...embersAndConstructsInsideValueClass.fir.kt | 0 ...vedMembersAndConstructsInsideValueClass.kt | 57 ------ ...edMembersAndConstructsInsideValueClass.txt | 76 ------- .../typedEqualsOperatorModifierInMfvc.kt | 22 +++ .../typedEqualsOperatorModifierInMfvc.txt | 22 +++ .../test/runners/DiagnosticTestGenerated.java | 24 ++- .../IrBlackBoxCodegenTestGenerated.java | 54 ++++- .../kotlin/config/LanguageVersionSettings.kt | 2 +- .../kotlin/descriptors/descriptorUtil.kt | 10 +- .../jetbrains/kotlin/util/modifierChecks.kt | 6 +- 75 files changed, 1686 insertions(+), 315 deletions(-) create mode 100644 compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.txt create mode 100644 compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.txt rename compiler/testData/codegen/box/inlineClasses/{inlineClassEqualsOverridenForCollections.kt => inlineClassEqualsOverriddenForCollections.kt} (68%) create mode 100644 compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.txt create mode 100644 compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.txt create mode 100644 compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.txt create mode 100644 compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.txt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.kt create mode 100644 compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.txt create mode 100644 compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt create mode 100644 compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt create mode 100644 compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.txt create mode 100644 compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.txt delete mode 100644 compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt delete mode 100644 compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.txt create mode 100644 compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt create mode 100644 compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.txt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index a94a4e9f644..ce915811fcb 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1369,6 +1369,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS) { firDiagnostic -> + SecondaryConstructorWithBodyInsideValueClassImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirErrors.RESERVED_MEMBER_INSIDE_VALUE_CLASS) { firDiagnostic -> ReservedMemberInsideValueClassImpl( firDiagnostic.a, @@ -1376,8 +1382,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } - add(FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS) { firDiagnostic -> - SecondaryConstructorWithBodyInsideValueClassImpl( + add(FirErrors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS) { firDiagnostic -> + TypeArgumentOnTypedValueClassEqualsImpl( firDiagnostic as KtPsiDiagnostic, token, ) @@ -3907,8 +3913,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } - add(FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS) { firDiagnostic -> - InefficientEqualsOverridingInInlineClassImpl( + add(FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS) { firDiagnostic -> + InefficientEqualsOverridingInValueClassImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firDiagnostic as KtPsiDiagnostic, token, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 07328327fa9..1a8a7350e4c 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -987,13 +987,17 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ValueClassCannotBeRecursive::class } + abstract class SecondaryConstructorWithBodyInsideValueClass : KtFirDiagnostic() { + override val diagnosticClass get() = SecondaryConstructorWithBodyInsideValueClass::class + } + abstract class ReservedMemberInsideValueClass : KtFirDiagnostic() { override val diagnosticClass get() = ReservedMemberInsideValueClass::class abstract val name: String } - abstract class SecondaryConstructorWithBodyInsideValueClass : KtFirDiagnostic() { - override val diagnosticClass get() = SecondaryConstructorWithBodyInsideValueClass::class + abstract class TypeArgumentOnTypedValueClassEquals : KtFirDiagnostic() { + override val diagnosticClass get() = TypeArgumentOnTypedValueClassEquals::class } abstract class InnerClassInsideValueClass : KtFirDiagnostic() { @@ -2725,8 +2729,8 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = RedundantInlineSuspendFunctionType::class } - abstract class InefficientEqualsOverridingInInlineClass : KtFirDiagnostic() { - override val diagnosticClass get() = InefficientEqualsOverridingInInlineClass::class + abstract class InefficientEqualsOverridingInValueClass : KtFirDiagnostic() { + override val diagnosticClass get() = InefficientEqualsOverridingInValueClass::class abstract val type: KtType } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 44ba3108c4a..2f7409008eb 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1183,16 +1183,21 @@ internal class ValueClassCannotBeRecursiveImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.ValueClassCannotBeRecursive(), KtAbstractFirDiagnostic +internal class SecondaryConstructorWithBodyInsideValueClassImpl( + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.SecondaryConstructorWithBodyInsideValueClass(), KtAbstractFirDiagnostic + internal class ReservedMemberInsideValueClassImpl( override val name: String, override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, ) : KtFirDiagnostic.ReservedMemberInsideValueClass(), KtAbstractFirDiagnostic -internal class SecondaryConstructorWithBodyInsideValueClassImpl( +internal class TypeArgumentOnTypedValueClassEqualsImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, -) : KtFirDiagnostic.SecondaryConstructorWithBodyInsideValueClass(), KtAbstractFirDiagnostic +) : KtFirDiagnostic.TypeArgumentOnTypedValueClassEquals(), KtAbstractFirDiagnostic internal class InnerClassInsideValueClassImpl( override val firDiagnostic: KtPsiDiagnostic, @@ -3287,11 +3292,11 @@ internal class RedundantInlineSuspendFunctionTypeImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.RedundantInlineSuspendFunctionType(), KtAbstractFirDiagnostic -internal class InefficientEqualsOverridingInInlineClassImpl( +internal class InefficientEqualsOverridingInValueClassImpl( override val type: KtType, override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, -) : KtFirDiagnostic.InefficientEqualsOverridingInInlineClass(), KtAbstractFirDiagnostic +) : KtFirDiagnostic.InefficientEqualsOverridingInValueClass(), KtAbstractFirDiagnostic internal class CannotAllUnderImportFromSingletonImpl( override val objectName: Name, diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index ca647afc442..a6018d77a4b 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -18110,6 +18110,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt"); } + @Test + @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt") + public void testTypedEqualsOperatorModifierInInlineClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt"); + } + @Test @TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt") public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception { @@ -33648,6 +33654,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/valueClasses/identityComparisonWithValueClasses.kt"); } + @Test + @TestMetadata("inefficientEqualsOverridingInMfvc.kt") + public void testInefficientEqualsOverridingInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt"); + } + @Test @TestMetadata("jvmInlineApplicability.kt") public void testJvmInlineApplicability() throws Exception { @@ -33691,9 +33703,15 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag } @Test - @TestMetadata("reservedMembersAndConstructsInsideValueClass.kt") - public void testReservedMembersAndConstructsInsideValueClass() throws Exception { - runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt"); + @TestMetadata("reservedMembersAndConstructsInsideMfvc.kt") + public void testReservedMembersAndConstructsInsideMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt"); + } + + @Test + @TestMetadata("typedEqualsOperatorModifierInMfvc.kt") + public void testTypedEqualsOperatorModifierInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt"); } @Test diff --git a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.fir.txt index 7e0ebef626e..2e9274ffa6f 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.fir.txt @@ -42,8 +42,8 @@ FILE: inlineClassMembers.kt } } - public final inline class ReversedMembers : R|kotlin/Any| { - public constructor(x: R|kotlin/Int|): R|ReversedMembers| { + public final inline class ReservedMembers : R|kotlin/Any| { + public constructor(x: R|kotlin/Int|): R|ReservedMembers| { super() } @@ -64,6 +64,32 @@ FILE: inlineClassMembers.kt ^hashCode Int(1) } + } + public final inline class ReservedMembersMfvc : R|kotlin/Any| { + public constructor(x: R|kotlin/Int|, y: R|kotlin/Int|): R|ReservedMembersMfvc| { + super() + } + + public final val x: R|kotlin/Int| = R|/x| + public get(): R|kotlin/Int| + + public final val y: R|kotlin/Int| = R|/y| + public get(): R|kotlin/Int| + + public final fun box(): R|kotlin/Unit| { + } + + public final fun unbox(): R|kotlin/Unit| { + } + + public final override operator fun equals(other: R|kotlin/Any?|): R|kotlin/Boolean| { + ^equals Boolean(true) + } + + public final override fun hashCode(): R|kotlin/Int| { + ^hashCode Int(1) + } + } public final inline class SecondaryConstructors : R|kotlin/Any| { public constructor(x: R|kotlin/Int|): R|SecondaryConstructors| { diff --git a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.kt b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.kt index 919842cee90..ef68f1ccd37 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.kt +++ b/compiler/fir/analysis-tests/testData/resolve/inlineClasses/inlineClassMembers.kt @@ -1,4 +1,4 @@ -// !LANGUAGE: +CustomEqualsInInlineClasses +// !LANGUAGE: +CustomEqualsInValueClasses, +ValueClasses value class BackingFields(val x: Int) { val y = 0 @@ -14,11 +14,19 @@ inline class DelegatedProp(val x: Int) { val testVal by Val() } -inline class ReversedMembers(val x: Int) { +inline class ReservedMembers(val x: Int) { fun box() {} fun unbox() {} - override fun equals(other: Any?) = true + override fun equals(other: Any?) = true + override fun hashCode() = 1 +} + +inline class ReservedMembersMfvc(val x: Int, val y: Int) { + fun box() {} + fun unbox() {} + + override fun equals(other: Any?) = true override fun hashCode() = 1 } diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index adf186bf1dd..fd390f6f32c 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -18116,6 +18116,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt"); } + @Test + @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt") + public void testTypedEqualsOperatorModifierInInlineClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt"); + } + @Test @TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt") public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception { @@ -33744,6 +33750,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/valueClasses/identityComparisonWithValueClasses.kt"); } + @Test + @TestMetadata("inefficientEqualsOverridingInMfvc.kt") + public void testInefficientEqualsOverridingInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt"); + } + @Test @TestMetadata("jvmInlineApplicability.kt") public void testJvmInlineApplicability() throws Exception { @@ -33787,9 +33799,15 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti } @Test - @TestMetadata("reservedMembersAndConstructsInsideValueClass.kt") - public void testReservedMembersAndConstructsInsideValueClass() throws Exception { - runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt"); + @TestMetadata("reservedMembersAndConstructsInsideMfvc.kt") + public void testReservedMembersAndConstructsInsideMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt"); + } + + @Test + @TestMetadata("typedEqualsOperatorModifierInMfvc.kt") + public void testTypedEqualsOperatorModifierInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt"); } @Test diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 4822fb9e1d4..cd0269ddfc0 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -18110,6 +18110,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt"); } + @Test + @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt") + public void testTypedEqualsOperatorModifierInInlineClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt"); + } + @Test @TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt") public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception { @@ -33648,6 +33654,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/valueClasses/identityComparisonWithValueClasses.kt"); } + @Test + @TestMetadata("inefficientEqualsOverridingInMfvc.kt") + public void testInefficientEqualsOverridingInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt"); + } + @Test @TestMetadata("jvmInlineApplicability.kt") public void testJvmInlineApplicability() throws Exception { @@ -33691,9 +33703,15 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac } @Test - @TestMetadata("reservedMembersAndConstructsInsideValueClass.kt") - public void testReservedMembersAndConstructsInsideValueClass() throws Exception { - runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt"); + @TestMetadata("reservedMembersAndConstructsInsideMfvc.kt") + public void testReservedMembersAndConstructsInsideMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt"); + } + + @Test + @TestMetadata("typedEqualsOperatorModifierInMfvc.kt") + public void testTypedEqualsOperatorModifierInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt"); } @Test diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 56ba9175a58..bab542ab568 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -426,10 +426,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION by error() val VALUE_CLASS_CANNOT_EXTEND_CLASSES by error() val VALUE_CLASS_CANNOT_BE_RECURSIVE by error() + val SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS by error() val RESERVED_MEMBER_INSIDE_VALUE_CLASS by error(PositioningStrategy.DECLARATION_NAME) { parameter("name") } - val SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS by error() + val TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS by error() val INNER_CLASS_INSIDE_VALUE_CLASS by error(PositioningStrategy.INNER_MODIFIER) val VALUE_CLASS_CANNOT_BE_CLONEABLE by error(PositioningStrategy.INLINE_OR_VALUE_MODIFIER) } @@ -1407,7 +1408,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE by warning(PositioningStrategy.SUSPEND_MODIFIER) - val INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS by warning(PositioningStrategy.DECLARATION_NAME) { + val INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS by warning(PositioningStrategy.DECLARATION_NAME) { parameter("type") } } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index ea7c57c97af..f73b989dd92 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -321,8 +321,9 @@ object FirErrors { val VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION by error0() val VALUE_CLASS_CANNOT_EXTEND_CLASSES by error0() val VALUE_CLASS_CANNOT_BE_RECURSIVE by error0() - val RESERVED_MEMBER_INSIDE_VALUE_CLASS by error1(SourceElementPositioningStrategies.DECLARATION_NAME) val SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS by error0() + val RESERVED_MEMBER_INSIDE_VALUE_CLASS by error1(SourceElementPositioningStrategies.DECLARATION_NAME) + val TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS by error0() val INNER_CLASS_INSIDE_VALUE_CLASS by error0(SourceElementPositioningStrategies.INNER_MODIFIER) val VALUE_CLASS_CANNOT_BE_CLONEABLE by error0(SourceElementPositioningStrategies.INLINE_OR_VALUE_MODIFIER) @@ -728,7 +729,7 @@ object FirErrors { val ILLEGAL_INLINE_PARAMETER_MODIFIER by error0(SourceElementPositioningStrategies.INLINE_PARAMETER_MODIFIER) val INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED by error0() val REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE by warning0(SourceElementPositioningStrategies.SUSPEND_MODIFIER) - val INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS by warning1(SourceElementPositioningStrategies.DECLARATION_NAME) + val INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS by warning1(SourceElementPositioningStrategies.DECLARATION_NAME) // Imports val CANNOT_ALL_UNDER_IMPORT_FROM_SINGLETON by error1(SourceElementPositioningStrategies.IMPORT_LAST_NAME) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt index 35533b03889..ceadb83623c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt @@ -8,11 +8,11 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isActual import org.jetbrains.kotlin.fir.languageVersionSettings @@ -188,11 +188,11 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { // we don't require `actual` modifier on // - annotation constructors, because annotation classes can only have one constructor - // - inline class primary constructors, because inline class must have primary constructor + // - value class primary constructors, because value class must have primary constructor // - value parameter inside primary constructor of inline class, because inline class must have one value parameter private fun requireActualModifier(declaration: FirBasedSymbol<*>, session: FirSession): Boolean { return !declaration.isAnnotationConstructor(session) && - !declaration.isPrimaryConstructorOfInlineClass(session) && + !declaration.isPrimaryConstructorOfInlineOrValueClass(session) && !isUnderlyingPropertyOfInlineClass(declaration) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt index 9d2bc198c0f..c23efe86e6e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInlineClassDeclarationChecker.kt @@ -100,7 +100,7 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() { if (functionName in boxAndUnboxNames || (functionName in equalsAndHashCodeNames - && !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) + && !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses)) ) { reporter.reportOn( innerDeclaration.source, FirErrors.RESERVED_MEMBER_INSIDE_VALUE_CLASS, functionName, context @@ -206,29 +206,37 @@ object FirInlineClassDeclarationChecker : FirRegularClassChecker() { } } - if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) { - var equalsFromAnyOverriding: FirSimpleFunction? = null - var typedEquals: FirSimpleFunction? = null - declaration.declarations.forEach { - if (it !is FirSimpleFunction) { - return@forEach + if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses)) { + val (equalsFromAnyOverriding, typedEquals) = run { + var equalsFromAnyOverriding: FirSimpleFunction? = null + var typedEquals: FirSimpleFunction? = null + declaration.declarations.forEach { + if (it !is FirSimpleFunction) { + return@forEach + } + if (it.isEquals()) equalsFromAnyOverriding = it + if (it.isTypedEqualsInValueClass(context.session)) typedEquals = it } - if (it.isEquals()) equalsFromAnyOverriding = it - if (it.isTypedEqualsInInlineClass(context.session)) typedEquals = it + equalsFromAnyOverriding to typedEquals } - - if (typedEquals?.typeParameters?.isNotEmpty() == true) { - reporter.reportOn( - typedEquals!!.source, - FirErrors.TYPE_PARAMETERS_NOT_ALLOWED, - context - ) + if (typedEquals != null) { + if (typedEquals.typeParameters.isNotEmpty()) { + reporter.reportOn( + typedEquals.source, + FirErrors.TYPE_PARAMETERS_NOT_ALLOWED, + context + ) + } + val singleParameterReturnTypeRef = typedEquals.valueParameters.single().returnTypeRef + if (singleParameterReturnTypeRef.coneType.typeArguments.any { !it.isStarProjection }) { + reporter.reportOn(singleParameterReturnTypeRef.source, FirErrors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS, context) + } } if (equalsFromAnyOverriding != null && typedEquals == null) { reporter.reportOn( - equalsFromAnyOverriding!!.source, - FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS, + equalsFromAnyOverriding.source, + FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS, declaration.defaultType().replaceArgumentsWithStarProjections(), context ) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt index f75919393c0..9bbbdff8a7a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirOperatorModifierChecker.kt @@ -186,11 +186,11 @@ private object OperatorFunctionChecks { object : Check { override fun check(context: CheckerContext, function: FirSimpleFunction): String? { val containingClassSymbol = function.containingClassLookupTag()?.toFirRegularClassSymbol(context.session) ?: return null - val customEqualsSupported = context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses) + val customEqualsSupported = context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses) if (function.overriddenFunctions(containingClassSymbol, context) .any { it.containingClassLookupTag()?.classId == StandardClassIds.Any } - || (customEqualsSupported && function.isTypedEqualsInInlineClass(context.session)) + || (customEqualsSupported && function.isTypedEqualsInValueClass(context.session)) ) { return null } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt index dfda9405292..bd1a7b1e3f5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt @@ -119,15 +119,15 @@ fun FirClassSymbol<*>.primaryConstructorSymbol(): FirConstructorSymbol? { return null } -fun FirSimpleFunction.isTypedEqualsInInlineClass(session: FirSession): Boolean = +fun FirSimpleFunction.isTypedEqualsInValueClass(session: FirSession): Boolean = containingClassLookupTag()?.toFirRegularClassSymbol(session)?.run { - val inlineClassStarProjection = this@run.defaultType().replaceArgumentsWithStarProjections() - with(this@isTypedEqualsInInlineClass) { + val valueClassStarProjection = this@run.defaultType().replaceArgumentsWithStarProjections() + with(this@isTypedEqualsInValueClass) { contextReceivers.isEmpty() && receiverParameter == null && name == OperatorNameConventions.EQUALS && this@run.isInline && valueParameters.size == 1 && (returnTypeRef.isBoolean || returnTypeRef.isNothing) - && valueParameters[0].returnTypeRef.coneType == inlineClassStarProjection + && valueParameters[0].returnTypeRef.coneType.let { it is ConeClassLikeType && it.replaceArgumentsWithStarProjections() == valueClassStarProjection } } } ?: false diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 7a1b1e5e8ff..c692b590088 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -259,7 +259,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_CHARACT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_LEFT_COMPONENT_OF_INTERSECTION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCORRECT_RIGHT_COMPONENT_OF_INTERSECTION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INC_DEC_SHOULD_NOT_RETURN_UNIT -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_UNSUCCESSFUL_FORK import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION @@ -501,6 +501,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPEALIAS_SHOULD_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_REDUNDANT_IN_SUPER_QUALIFIER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_CANT_BE_USED_FOR_CONST_VAL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_INFERENCE_ONLY_INPUT_TYPES_ERROR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_MISMATCH @@ -1276,11 +1277,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { FIR, DECLARATION_NAME ) - map.put(RESERVED_MEMBER_INSIDE_VALUE_CLASS, "Member with the name ''{0}'' is reserved for future releases", TO_STRING) map.put( SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS, "Secondary constructors with bodies are reserved for for future releases" ) + map.put(RESERVED_MEMBER_INSIDE_VALUE_CLASS, "Member with the name ''{0}'' is reserved for future releases", TO_STRING) + map.put(TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS, "Type arguments for typed value class equals must be only star projections") map.put(INNER_CLASS_INSIDE_VALUE_CLASS, "Value class cannot have inner classes") map.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable") @@ -2023,8 +2025,8 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() { ) map.put( - INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS, - "Overriding ''equals'' from ''Any'' in inline class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison", + INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS, + "Overriding ''equals'' from ''Any'' in value class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison", RENDER_TYPE ) diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 218089a6b0a..a98a2c05b28 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -21486,15 +21486,15 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT } @Test - @TestMetadata("inlineClassEqualsOverride.kt") - public void testInlineClassEqualsOverride() throws Exception { - runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + @TestMetadata("inlineClassEqualsOverriddenForCollections.kt") + public void testInlineClassEqualsOverriddenForCollections() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } @Test - @TestMetadata("inlineClassEqualsOverridenForCollections.kt") - public void testInlineClassEqualsOverridenForCollections() throws Exception { - runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + @TestMetadata("inlineClassEqualsOverride.kt") + public void testInlineClassEqualsOverride() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } @Test @@ -50465,12 +50465,54 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/valueClasses/functionReferences.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("mfvcBothEqualsOverride.kt") + public void testMfvcBothEqualsOverride() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcEqualsConsistency.kt") + public void testMfvcEqualsConsistency() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcEqualsOverriddenForCollections.kt") + public void testMfvcEqualsOverriddenForCollections() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcEqualsOverride.kt") + public void testMfvcEqualsOverride() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("mfvcFieldInitializationOrder.kt") public void testMfvcFieldInitializationOrder() throws Exception { runTest("compiler/testData/codegen/box/valueClasses/mfvcFieldInitializationOrder.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("mfvcHashCodeOverride.kt") + public void testMfvcHashCodeOverride() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcTypedEqualsGenerics.kt") + public void testMfvcTypedEqualsGenerics() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcUntypedEqualsOverriden.kt") + public void testMfvcUntypedEqualsOverriden() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("overrides.kt") public void testOverrides() throws Exception { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt index e6283d3ab66..d7eab2d035b 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt @@ -85,7 +85,7 @@ fun FirBasedSymbol<*>.isEnumConstructor(session: FirSession): Boolean { return getConstructedClass(session)?.classKind == ClassKind.ENUM_CLASS } -fun FirBasedSymbol<*>.isPrimaryConstructorOfInlineClass(session: FirSession): Boolean { +fun FirBasedSymbol<*>.isPrimaryConstructorOfInlineOrValueClass(session: FirSession): Boolean { if (this !is FirConstructorSymbol) return false return getConstructedClass(session)?.isInlineOrValueClass() == true && this.isPrimary } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 2a7cb40bba0..ff0eb1d5b2f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -420,8 +420,9 @@ public interface Errors { DiagnosticFactory0 VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 VALUE_CLASS_CANNOT_EXTEND_CLASSES = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 VALUE_CLASS_CANNOT_BE_RECURSIVE = DiagnosticFactory0.create(ERROR); - DiagnosticFactory1 RESERVED_MEMBER_INSIDE_VALUE_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory1 RESERVED_MEMBER_INSIDE_VALUE_CLASS = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 INNER_CLASS_INSIDE_VALUE_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 VALUE_CLASS_CANNOT_BE_CLONEABLE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 INLINE_CLASS_DEPRECATED = DiagnosticFactory0.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 11d79f2b8c9..3d02a6bd8ce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -795,14 +795,15 @@ public class DefaultErrorMessages { MAP.put(VALUE_CLASS_CANNOT_IMPLEMENT_INTERFACE_BY_DELEGATION, "Value class cannot implement an interface by delegation if expression is not a parameter"); MAP.put(VALUE_CLASS_CANNOT_EXTEND_CLASSES, "Value class cannot extend classes"); MAP.put(VALUE_CLASS_CANNOT_BE_RECURSIVE, "Value class cannot be recursive"); - MAP.put(RESERVED_MEMBER_INSIDE_VALUE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING); MAP.put(SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_VALUE_CLASS, "Secondary constructors with bodies are reserved for for future releases"); + MAP.put(RESERVED_MEMBER_INSIDE_VALUE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING); + MAP.put(TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS, "Type arguments for typed value class equals must be only star projections"); MAP.put(INNER_CLASS_INSIDE_VALUE_CLASS, "Value class cannot have inner classes"); MAP.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable"); MAP.put(INLINE_CLASS_DEPRECATED, "'inline' modifier is deprecated. Use 'value' instead"); MAP.put(VALUE_CLASS_CANNOT_HAVE_CONTEXT_RECEIVERS, "Value classes cannot have context receivers"); - MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS, - "Overriding ''equals'' from ''Any'' in inline class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison", + MAP.put(INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS, + "Overriding ''equals'' from ''Any'' in value class without operator ''equals(other: {0}): Boolean'' leads to boxing on every equality comparison", RENDER_TYPE); MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt index 09949d1a411..dc5d490b94c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.isTypedEqualsInInlineClass +import org.jetbrains.kotlin.descriptors.isTypedEqualsInValueClass import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens @@ -52,8 +52,8 @@ object OperatorModifierChecker { functionDescriptor.name == OperatorNameConventions.PROVIDE_DELEGATE -> checkSupportsFeature(LanguageFeature.OperatorProvideDelegate, languageVersionSettings, diagnosticHolder, modifier) - functionDescriptor.isTypedEqualsInInlineClass() -> - checkSupportsFeature(LanguageFeature.CustomEqualsInInlineClasses, languageVersionSettings, diagnosticHolder, modifier) + functionDescriptor.isTypedEqualsInValueClass() -> + checkSupportsFeature(LanguageFeature.CustomEqualsInValueClasses, languageVersionSettings, diagnosticHolder, modifier) } if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values && diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt index 9b15c18c48c..2d98866489d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections private val javaLangCloneable = FqNameUnsafe("java.lang.Cloneable") -object InlineClassDeclarationChecker : DeclarationChecker { +object ValueClassDeclarationChecker : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (declaration !is KtClass) return if (descriptor !is ClassDescriptor || !descriptor.isInline && !descriptor.isValue) return @@ -166,20 +166,16 @@ object InlineClassDeclarationChecker : DeclarationChecker { context.trace.bindingContext.get(BindingContext.FUNCTION, declaration) fun isUntypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.overridesEqualsFromAny() ?: false - fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInInlineClass() ?: false + fun isTypedEquals(declaration: KtNamedFunction): Boolean = getFunctionDescriptor(declaration)?.isTypedEqualsInValueClass() ?: false fun KtClass.namedFunctions() = declarations.filterIsInstance() - if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses)) { val typedEquals = declaration.namedFunctions().firstOrNull { isTypedEquals(it) } - if (typedEquals?.typeParameters?.isNotEmpty() == true) { - trace.report(Errors.TYPE_PARAMETERS_NOT_ALLOWED.on(typedEquals)) - } - declaration.namedFunctions().singleOrNull { isUntypedEquals(it) }?.apply { if (typedEquals == null) { trace.report( - Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_INLINE_CLASS.on( + Errors.INEFFICIENT_EQUALS_OVERRIDING_IN_VALUE_CLASS.on( this@apply, descriptor.defaultType.replaceArgumentsWithStarProjections() ) @@ -255,10 +251,18 @@ class ReservedMembersAndConstructsForInlineClass : DeclarationChecker { val functionName = descriptor.name.asString() if (functionName in boxAndUnboxNames || (functionName in equalsAndHashCodeNames - && !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInInlineClasses)) + && !context.languageVersionSettings.supportsFeature(LanguageFeature.CustomEqualsInValueClasses)) ) { val nameIdentifier = ktFunction.nameIdentifier ?: return context.trace.report(Errors.RESERVED_MEMBER_INSIDE_VALUE_CLASS.on(nameIdentifier, functionName)) + } else if (descriptor.isTypedEqualsInValueClass()) { + if (descriptor.typeParameters.isNotEmpty()) { + context.trace.report(Errors.TYPE_PARAMETERS_NOT_ALLOWED.on(declaration)) + } + val parameterType = descriptor.valueParameters.first()?.type + if (parameterType != null && parameterType.arguments.any { !it.isStarProjection }) { + context.trace.report(Errors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS.on(declaration.valueParameters[0].typeReference!!)) + } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt index 8dc774074e0..92860e2f49a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.ir.backend.js.dce -import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.collectOverridenSymbols import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.export.isExported diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index a2dfb5bcbb7..2f8e1fde229 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -5,8 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isEquals -import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isToString +import org.jetbrains.kotlin.backend.common.ScopeWithIr import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irCatch import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase @@ -379,7 +378,10 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV val rootNode = replacements.getRootMfvcNode(declaration) rootNode.replaceFields() declaration.declarations.removeIf { it is IrSimpleFunction && it.isMultiFieldValueClassFieldGetter && it.overriddenSymbols.isEmpty() } - declaration.declarations += rootNode.run { allUnboxMethods + listOf(boxMethod, specializedEqualsMethod) } + declaration.declarations += rootNode.allUnboxMethods + listOfNotNull( + // `takeIf` is a workaround for double addition problem: user-defined typed equals is already defined in the class + rootNode.boxMethod, rootNode.specializedEqualsMethod.takeIf { rootNode.createdNewSpecializedEqualsMethod } + ) rootNode.replacePrimaryMultiFieldValueClassConstructor() } @@ -786,7 +788,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV }.unwrapBlock() } if (expression.isSpecializedMFVCEqEq) { - val backendContext = context return context.createIrBuilder(getCurrentScopeSymbol()).irBlock { val leftArgument = expression.getValueArgument(0)!! val rightArgument = expression.getValueArgument(1)!! @@ -801,14 +802,9 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV leftNode.specializedEqualsMethod } else { // left one is unboxed, right is not - leftClass.functions.single { it.isEquals(backendContext) } + leftClass.functions.single { it.isEquals() } } +irCall(newEquals).apply { - if (rightNode != null) { - for ((index, typeArgument) in (rightArgument.type as IrSimpleType).arguments.withIndex()) { - putTypeArgument(index, typeArgument.typeOrNull) - } - } dispatchReceiver = leftArgument putValueArgument(0, rightArgument) }.transform(this@JvmMultiFieldValueClassLowering, null) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNode.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNode.kt index ed6651d025d..1b1c8d3d9bc 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNode.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNode.kt @@ -381,6 +381,7 @@ class RootMfvcNode internal constructor( val primaryConstructorImpl: IrSimpleFunction, override val boxMethod: IrSimpleFunction, val specializedEqualsMethod: IrSimpleFunction, + val createdNewSpecializedEqualsMethod: Boolean, ) : MfvcNodeWithSubnodes { override val subnodesImpl: MfvcNodeWithSubnodesImpl = MfvcNodeWithSubnodesImpl(subnodes, null) override val type: IrSimpleType = mfvc.defaultType @@ -420,8 +421,8 @@ class RootMfvcNode internal constructor( boxMethod.typeParameters.size, primaryConstructorImpl.typeParameters.size, ) - require(specializedEqualsMethod.typeParameters.size == mfvc.typeParameters.size) { - "Specialized equals method must contain the same number of type parameters as the corresponding MFVC ${mfvc.typeParameters.map { it.defaultType.render() }} but has ${specializedEqualsMethod.typeParameters.map { it.defaultType.render() }}" + require(specializedEqualsMethod.typeParameters.isEmpty()) { + "Specialized equals method must not contain type parameters but has ${specializedEqualsMethod.typeParameters.map { it.defaultType.render() }}" } requireSameSizes(oldPrimaryConstructor.valueParameters.size, subnodes.size) requireSameSizes( diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt index dde9e136569..53ee3cd71d5 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.parents import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound +import org.jetbrains.kotlin.backend.jvm.ir.upperBound import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality @@ -30,6 +31,7 @@ import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions fun createLeafMfvcNode( parent: IrDeclarationContainer, @@ -270,42 +272,59 @@ fun getRootNode(context: JvmBackendContext, mfvc: IrClass): RootMfvcNode { val newPrimaryConstructor = makeMfvcPrimaryConstructor(context, oldPrimaryConstructor, mfvc, leaves, fields) val primaryConstructorImpl = makePrimaryConstructorImpl(context, oldPrimaryConstructor, mfvc, leaves) val boxMethod = makeBoxMethod(context, mfvc, leaves, newPrimaryConstructor) - val specializedEqualsMethod = makeSpecializedEqualsMethod(context, mfvc, oldFields) + + val customEqualsAny = mfvc.functions.singleOrNull { + it.isEquals() && it.origin != IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER + } + + val customEqualsMfvc = mfvc.functions.singleOrNull { + it.name == OperatorNameConventions.EQUALS && + it.contextReceiverParametersCount == 0 && + it.extensionReceiverParameter == null && + it.valueParameters.singleOrNull()?.type?.erasedUpperBound == mfvc + } + + val specializedEqualsMethod = makeSpecializedEqualsMethod(context, mfvc, oldFields, customEqualsAny, customEqualsMfvc) return RootMfvcNode( - mfvc, subnodes, oldPrimaryConstructor, newPrimaryConstructor, primaryConstructorImpl, boxMethod, specializedEqualsMethod + mfvc, subnodes, oldPrimaryConstructor, newPrimaryConstructor, primaryConstructorImpl, boxMethod, + specializedEqualsMethod, customEqualsMfvc == null ) } private fun makeSpecializedEqualsMethod( - context: JvmBackendContext, mfvc: IrClass, oldFields: List -) = context.irFactory.buildFun { - name = Name.identifier("equals") - // TODO: Revisit this once we allow user defined equals methods in value classes. + context: JvmBackendContext, mfvc: IrClass, oldFields: List, + customEqualsAny: IrSimpleFunction?, customEqualsMfvc: IrSimpleFunction? +) = customEqualsMfvc ?: context.irFactory.buildFun { + name = OperatorNameConventions.EQUALS origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER returnType = context.irBuiltIns.booleanType }.apply { parent = mfvc - copyTypeParametersFrom(mfvc) createDispatchReceiverParameter() - val functionTypeParameters = typeParameters - val other = addValueParameter { name = Name.identifier("other") - type = mfvc.defaultType.substitute(mfvc.typeParameters, functionTypeParameters.map { it.defaultType }) + type = mfvc.defaultType.upperBound } body = with(context.createJvmIrBuilder(this.symbol)) { - val leftArgs = oldFields.map { irGetField(irGet(dispatchReceiverParameter!!), it) } - val rightArgs = oldFields.map { irGetField(irGet(other), it) } - val conjunctions = leftArgs.zip(rightArgs) { l, r -> irEquals(l, r) } - irExprBody(conjunctions.reduce { acc, current -> - irCall(context.irBuiltIns.andandSymbol).apply { - putValueArgument(0, acc) - putValueArgument(1, current) - } - }) + if (customEqualsAny != null) { + irExprBody(irCall(customEqualsAny).apply { + dispatchReceiver = irGet(dispatchReceiverParameter!!) + putValueArgument(0, irGet(other)) + }) + } else { + val leftArgs = oldFields.map { irGetField(irGet(dispatchReceiverParameter!!), it) } + val rightArgs = oldFields.map { irGetField(irGet(other), it) } + val conjunctions = leftArgs.zip(rightArgs) { l, r -> irEquals(l, r) } + irExprBody(conjunctions.reduce { acc, current -> + irCall(context.irBuiltIns.andandSymbol).apply { + putValueArgument(0, acc) + putValueArgument(1, current) + } + }) + } } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt index 31fb2c3fc34..3ad9bff6912 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.isNullable import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions /** * A platform-, frontend-independent logic for generating synthetic members of data class: equals, hashCode, toString, componentN, and copy. @@ -125,11 +126,16 @@ abstract class DataClassMembersGenerator( ) } + private fun IrSimpleFunction.isTypedEqualsInValueClass() = name == OperatorNameConventions.EQUALS && + returnType == context.irBuiltIns.booleanType && irClass.isValue + && valueParameters.size == 1 && valueParameters[0].type.classifierOrNull == irClass.symbol + && contextReceiverParametersCount == 0 && extensionReceiverParameter == null + fun generateEqualsMethodBody(properties: List) { val irType = irClass.defaultType - val typedEqualsFunction = irClass.functions.singleOrNull { it.descriptor.isTypedEqualsInInlineClass() } - if (irClass.isSingleFieldValueClass && typedEqualsFunction != null) { + val typedEqualsFunction = irClass.functions.singleOrNull { it.isTypedEqualsInValueClass() } + if (irClass.isValue && typedEqualsFunction != null) { +irIfThenReturnFalse(irNotIs(irOther(), irType)) val otherCasted = irImplicitCast(irOther(), irType) +irReturn(irCall(typedEqualsFunction).apply { diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.kt index b1f04142e48..1edea82b7c0 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.kt @@ -1,7 +1,10 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING interface I { @@ -52,4 +55,4 @@ value class IC2(val y: Int) : I { } } -fun box(): String = if (setOf(IC1(10), IC2(1)).size == 1) "OK" else "Fail" \ No newline at end of file +fun box(): String = if (setOf(IC1(10), IC2(1)).size == 1) "OK" else "Fail" diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.txt b/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.txt new file mode 100644 index 00000000000..58d4b71c815 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassBothEqualsOverride.txt @@ -0,0 +1,55 @@ +@kotlin.Metadata +public interface I { + // source: 'inlineClassBothEqualsOverride.kt' + public abstract method getVal(): int +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC1 { + // source: 'inlineClassBothEqualsOverride.kt' + private final field x: int + private synthetic method (p0: int): void + public synthetic final static method box-impl(p0: int): IC1 + public static method constructor-impl(p0: int): int + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final static method equals-P1kVloU(p0: int, p1: int): boolean + public static method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals-impl0(p0: int, p1: int): boolean + public method getVal(): int + public static method getVal-impl(p0: int): int + public final method getX(): int + public method hashCode(): int + public static method hashCode-impl(p0: int): int + public method toString(): java.lang.String + public static method toString-impl(p0: int): java.lang.String + public synthetic final method unbox-impl(): int +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC2 { + // source: 'inlineClassBothEqualsOverride.kt' + private final field y: int + private synthetic method (p0: int): void + public synthetic final static method box-impl(p0: int): IC2 + public static method constructor-impl(p0: int): int + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final static method equals-ACFGG4I(p0: int, p1: int): boolean + public static method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals-impl0(p0: int, p1: int): boolean + public method getVal(): int + public static method getVal-impl(p0: int): int + public final method getY(): int + public method hashCode(): int + public static method hashCode-impl(p0: int): int + public method toString(): java.lang.String + public static method toString-impl(p0: int): java.lang.String + public synthetic final method unbox-impl(): int +} + +@kotlin.Metadata +public final class InlineClassBothEqualsOverrideKt { + // source: 'inlineClassBothEqualsOverride.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.kt index 45860edab99..f781b7b379c 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.kt @@ -1,7 +1,10 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING import java.lang.AssertionError import kotlin.math.abs @@ -44,4 +47,4 @@ fun box(): String { if ((a2Typed == c2Typed) != (a2Untyped == c2Untyped)) return "Fail 4" return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.txt b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.txt new file mode 100644 index 00000000000..f6513e8e20d --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsConsistency.txt @@ -0,0 +1,44 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC1 { + // source: 'inlineClassEqualsConsistency.kt' + private final field x: double + private synthetic method (p0: double): void + public synthetic final static method box-impl(p0: double): IC1 + public static method constructor-impl(p0: double): double + public method equals(p0: java.lang.Object): boolean + public final static method equals-P1kVloU(p0: double, p1: double): boolean + public static method equals-impl(p0: double, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: double, p1: double): boolean + public final method getX(): double + public method hashCode(): int + public static method hashCode-impl(p0: double): int + public method toString(): java.lang.String + public static method toString-impl(p0: double): java.lang.String + public synthetic final method unbox-impl(): double +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC2 { + // source: 'inlineClassEqualsConsistency.kt' + private final field x: int + private synthetic method (p0: int): void + public synthetic final static method box-impl(p0: int): IC2 + public static method constructor-impl(p0: int): int + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals-impl0(p0: int, p1: int): boolean + public final method getX(): int + public method hashCode(): int + public static method hashCode-impl(p0: int): int + public method toString(): java.lang.String + public static method toString-impl(p0: int): java.lang.String + public synthetic final method unbox-impl(): int +} + +@kotlin.Metadata +public final class InlineClassEqualsConsistencyKt { + // source: 'inlineClassEqualsConsistency.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.kt similarity index 68% rename from compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt rename to compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.kt index abe5b4d2a20..e7a0523eb30 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.kt @@ -1,7 +1,10 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING import kotlin.math.abs @@ -19,4 +22,4 @@ value class IC(val x: Double) { fun box(): String { val set = setOf(IC(1.0), IC(1.5), IC(1.501)) return if (set.size == 2) "OK" else "Fail" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.txt b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.txt new file mode 100644 index 00000000000..5829eca78e1 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.txt @@ -0,0 +1,25 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC { + // source: 'inlineClassEqualsOverriddenForCollections.kt' + private final field x: double + private synthetic method (p0: double): void + public synthetic final static method box-impl(p0: double): IC + public static method constructor-impl(p0: double): double + public method equals(p0: java.lang.Object): boolean + public final static method equals-K5cTq2M(p0: double, p1: double): boolean + public static method equals-impl(p0: double, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: double, p1: double): boolean + public final method getX(): double + public method hashCode(): int + public static method hashCode-impl(p0: double): int + public method toString(): java.lang.String + public static method toString-impl(p0: double): java.lang.String + public synthetic final method unbox-impl(): double +} + +@kotlin.Metadata +public final class InlineClassEqualsOverriddenForCollectionsKt { + // source: 'inlineClassEqualsOverriddenForCollections.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt index 6dd099b3b6d..986fa1b8556 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt @@ -1,6 +1,8 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR // CHECK_BYTECODE_LISTING @@ -93,4 +95,4 @@ fun box() = when { else -> "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt index 01e5313bf3d..e7c68faa081 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.kt @@ -1,6 +1,9 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// CHECK_BYTECODE_LISTING OPTIONAL_JVM_INLINE_ANNOTATION value class A(val value: MyClass) { @@ -11,4 +14,4 @@ class MyClass() { override fun hashCode() = -1 } -fun box(): String = if (A(MyClass()).hashCode() == 42) "OK" else "Fail" \ No newline at end of file +fun box(): String = if (A(MyClass()).hashCode() == 42) "OK" else "Fail" diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.txt b/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.txt new file mode 100644 index 00000000000..699291d4499 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassHashCodeOverride.txt @@ -0,0 +1,31 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class A { + // source: 'inlineClassHashCodeOverride.kt' + private final @org.jetbrains.annotations.NotNull field value: MyClass + private synthetic method (p0: MyClass): void + public synthetic final static method box-impl(p0: MyClass): A + public static @org.jetbrains.annotations.NotNull method constructor-impl(@org.jetbrains.annotations.NotNull p0: MyClass): MyClass + public method equals(p0: java.lang.Object): boolean + public static method equals-impl(p0: MyClass, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: MyClass, p1: MyClass): boolean + public final @org.jetbrains.annotations.NotNull method getValue(): MyClass + public method hashCode(): int + public static method hashCode-impl(p0: MyClass): int + public method toString(): java.lang.String + public static method toString-impl(p0: MyClass): java.lang.String + public synthetic final method unbox-impl(): MyClass +} + +@kotlin.Metadata +public final class InlineClassHashCodeOverrideKt { + // source: 'inlineClassHashCodeOverride.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public final class MyClass { + // source: 'inlineClassHashCodeOverride.kt' + public method (): void + public method hashCode(): int +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.kt index 7ba5253ac1d..4672d028691 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.kt @@ -1,12 +1,13 @@ // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING OPTIONAL_JVM_INLINE_ANNOTATION value class IC1(val x: T) { fun equals(other: Int) = false - operator fun equals(other: IC1<*>) = true + fun equals(other: IC1<*>) = true } class Generic(val x: T, val y: R) @@ -14,19 +15,19 @@ class Generic(val x: T, val y: R) OPTIONAL_JVM_INLINE_ANNOTATION value class IC2(val value: Generic) { fun equals(other: IC1) = false - operator fun equals(other: IC2<*, *>) = true + fun equals(other: IC2<*, *>) = true } OPTIONAL_JVM_INLINE_ANNOTATION value class IC3(val value: T) { fun equals(other: Int) = false - operator fun equals(other: IC3<*>) = true + fun equals(other: IC3<*>) = true } OPTIONAL_JVM_INLINE_ANNOTATION value class IC4(val value: T) { fun equals(other: String) = false - operator fun equals(other: IC4<*>) = true + fun equals(other: IC4<*>) = true } @@ -52,4 +53,4 @@ fun box() = when { (IC4("aba") as Any) != (IC4("caba") as Any) -> "Fail 4.4" else -> "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.txt b/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.txt new file mode 100644 index 00000000000..4d5400b1fd8 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassTypedEqualsGenerics.txt @@ -0,0 +1,99 @@ +@kotlin.Metadata +public final class Generic { + // source: 'inlineClassTypedEqualsGenerics.kt' + private final field x: java.lang.Object + private final field y: java.lang.Object + public method (p0: java.lang.Object, p1: java.lang.Object): void + public final method getX(): java.lang.Object + public final method getY(): java.lang.Object +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC1 { + // source: 'inlineClassTypedEqualsGenerics.kt' + private final @org.jetbrains.annotations.NotNull field x: java.lang.Number + private synthetic method (p0: java.lang.Number): void + public synthetic final static method box-impl(p0: java.lang.Number): IC1 + public static @org.jetbrains.annotations.NotNull method constructor-impl(@org.jetbrains.annotations.NotNull p0: java.lang.Number): java.lang.Number + public method equals(p0: java.lang.Object): boolean + public final static method equals-P1kVloU(p0: java.lang.Number, @org.jetbrains.annotations.NotNull p1: java.lang.Number): boolean + public final static method equals-impl(p0: java.lang.Number, p1: int): boolean + public static method equals-impl(p0: java.lang.Number, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: java.lang.Number, p1: java.lang.Number): boolean + public final @org.jetbrains.annotations.NotNull method getX(): java.lang.Number + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.Number): int + public method toString(): java.lang.String + public static method toString-impl(p0: java.lang.Number): java.lang.String + public synthetic final method unbox-impl(): java.lang.Number +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC2 { + // source: 'inlineClassTypedEqualsGenerics.kt' + private final @org.jetbrains.annotations.NotNull field value: Generic + private synthetic method (p0: Generic): void + public synthetic final static method box-impl(p0: Generic): IC2 + public static @org.jetbrains.annotations.NotNull method constructor-impl(@org.jetbrains.annotations.NotNull p0: Generic): Generic + public method equals(p0: java.lang.Object): boolean + public final static method equals-ACFGG4I(p0: Generic, @org.jetbrains.annotations.NotNull p1: Generic): boolean + public final static method equals-P1kVloU(p0: Generic, @org.jetbrains.annotations.NotNull p1: java.lang.Number): boolean + public static method equals-impl(p0: Generic, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: Generic, p1: Generic): boolean + public final @org.jetbrains.annotations.NotNull method getValue(): Generic + public method hashCode(): int + public static method hashCode-impl(p0: Generic): int + public method toString(): java.lang.String + public static method toString-impl(p0: Generic): java.lang.String + public synthetic final method unbox-impl(): Generic +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC3 { + // source: 'inlineClassTypedEqualsGenerics.kt' + private final field value: java.lang.Object + private synthetic method (p0: java.lang.Object): void + public synthetic final static method box-impl(p0: java.lang.Object): IC3 + public static @org.jetbrains.annotations.NotNull method constructor-impl(p0: java.lang.Object): java.lang.Object + public method equals(p0: java.lang.Object): boolean + public final static method equals-L6TIGmY(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.lang.Object): boolean + public final static method equals-impl(p0: java.lang.Object, p1: int): boolean + public static method equals-impl(p0: java.lang.Object, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: java.lang.Object, p1: java.lang.Object): boolean + public final method getValue(): java.lang.Object + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.Object): int + public method toString(): java.lang.String + public static method toString-impl(p0: java.lang.Object): java.lang.String + public synthetic final method unbox-impl(): java.lang.Object +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC4 { + // source: 'inlineClassTypedEqualsGenerics.kt' + private final field value: java.lang.Object + private synthetic method (p0: java.lang.Object): void + public synthetic final static method box-impl(p0: java.lang.Object): IC4 + public static @org.jetbrains.annotations.NotNull method constructor-impl(p0: java.lang.Object): java.lang.Object + public method equals(p0: java.lang.Object): boolean + public final static method equals-hxZMMoI(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.lang.Object): boolean + public final static method equals-impl(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: java.lang.String): boolean + public static method equals-impl(p0: java.lang.Object, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: java.lang.Object, p1: java.lang.Object): boolean + public final method getValue(): java.lang.Object + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.Object): int + public method toString(): java.lang.String + public static method toString-impl(p0: java.lang.Object): java.lang.String + public synthetic final method unbox-impl(): java.lang.Object +} + +@kotlin.Metadata +public final class InlineClassTypedEqualsGenericsKt { + // source: 'inlineClassTypedEqualsGenerics.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.kt b/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.kt index 270a5e8b35f..ceb036e2421 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.kt @@ -1,7 +1,10 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING import kotlin.math.abs @@ -24,4 +27,4 @@ fun box(): String { if (IC(1) != IC(2)) return "Fail 3" if (IC(1) == IC(5)) return "Fail 4" return "OK" -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.txt b/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.txt new file mode 100644 index 00000000000..72033edfee0 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineClassUntypedEqualsOverriden.txt @@ -0,0 +1,24 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class IC { + // source: 'inlineClassUntypedEqualsOverriden.kt' + private final field x: int + private synthetic method (p0: int): void + public synthetic final static method box-impl(p0: int): IC + public static method constructor-impl(p0: int): int + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): boolean + public final static method equals-impl0(p0: int, p1: int): boolean + public final method getX(): int + public method hashCode(): int + public static method hashCode-impl(p0: int): int + public method toString(): java.lang.String + public static method toString-impl(p0: int): java.lang.String + public synthetic final method unbox-impl(): int +} + +@kotlin.Metadata +public final class InlineClassUntypedEqualsOverridenKt { + // source: 'inlineClassUntypedEqualsOverriden.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt b/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt index 5c59db7de3f..45aad661d7e 100644 --- a/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt +++ b/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt @@ -1,6 +1,6 @@ // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR diff --git a/compiler/testData/codegen/box/inlineClasses/kt54536.kt b/compiler/testData/codegen/box/inlineClasses/kt54536.kt index 1eaec18f024..befce671397 100644 --- a/compiler/testData/codegen/box/inlineClasses/kt54536.kt +++ b/compiler/testData/codegen/box/inlineClasses/kt54536.kt @@ -1,6 +1,6 @@ // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR OPTIONAL_JVM_INLINE_ANNOTATION diff --git a/compiler/testData/codegen/box/inlineClasses/kt54603.kt b/compiler/testData/codegen/box/inlineClasses/kt54603.kt index 89c62cf11e4..7460173184e 100644 --- a/compiler/testData/codegen/box/inlineClasses/kt54603.kt +++ b/compiler/testData/codegen/box/inlineClasses/kt54603.kt @@ -1,6 +1,6 @@ // WITH_STDLIB // WORKS_WHEN_VALUE_CLASS -// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses // TARGET_BACKEND: JVM_IR class A(x: Int) diff --git a/compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.kt b/compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.kt new file mode 100644 index 00000000000..8149a106ad6 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.kt @@ -0,0 +1,53 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +var counter = 0 + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC(val x: Int, val y: Int) { + + fun equals(other: MFVC): Boolean { + counter++ + return x == other.x && this.y == other.y + } + + override fun equals(other: Any?): Boolean { + counter++ + if (other !is MFVC) { + return false + } + return equals(other) + } + + override fun hashCode(): Int { + counter++ + return x + 13 * y + } +} + +fun box(): String { + val mfvc1 = MFVC(1, 2) + val mfvc2 = MFVC(3, 4) + + require(mfvc1.equals(mfvc1)) + require(!mfvc1.equals(mfvc2)) + require(!mfvc2.equals(mfvc1)) + require(mfvc2.equals(mfvc2)) + + require(mfvc1.equals(mfvc1 as Any?)) + require(!mfvc1.equals(mfvc2 as Any?)) + require(!mfvc2.equals(mfvc1 as Any?)) + require(mfvc2.equals(mfvc2 as Any?)) + + require(mfvc1.hashCode() == 27) + require(mfvc2.hashCode() == 55) + + require(counter == 4 + 2 * 4 + 2) + + return "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.txt b/compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.txt new file mode 100644 index 00000000000..2261c424999 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.txt @@ -0,0 +1,29 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC { + // source: 'mfvcBothEqualsOverride.kt' + private final field field-0: int + private final field field-1: int + private synthetic method (p0: int, p1: int): void + public synthetic final static method box-impl(p0: int, p1: int): MFVC + public final static method constructor-impl(p0: int, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, p1: int, @org.jetbrains.annotations.Nullable p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: int, p1: int, p2: int, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: int, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: int, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): int + public synthetic final method unbox-impl-1(): int +} + +@kotlin.Metadata +public final class MfvcBothEqualsOverrideKt { + // source: 'mfvcBothEqualsOverride.kt' + private static field counter: int + static method (): void + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method getCounter(): int + public final static method setCounter(p0: int): void +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.kt b/compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.kt new file mode 100644 index 00000000000..a545372d902 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.kt @@ -0,0 +1,65 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +import java.lang.AssertionError +import kotlin.math.abs + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC1(val x: Int, val y: Int) { + fun equals(other: MFVC1): Boolean { + return abs(x - other.x) < 2 && abs(y - other.y) < 2 + } +} + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC2(val x: Int, val y: Int) { + override fun equals(other: Any?): Boolean { + if (other !is MFVC2) { + return false + } + return abs(x - other.x) < 2 && abs(y - other.y) < 2 + } +} + +fun box(): String { + val a1Typed: MFVC1 = MFVC1(1, 2) + val b1Typed: MFVC1 = MFVC1(2, 3) + val c1Typed: MFVC1 = MFVC1(3, 4) + val a1Untyped: Any = a1Typed + val b1Untyped: Any = b1Typed + val c1Untyped: Any = c1Typed + + val a2Typed: MFVC2 = MFVC2(1, 2) + val b2Typed: MFVC2 = MFVC2(2, 3) + val c2Typed: MFVC2 = MFVC2(3, 4) + val a2Untyped: Any = a2Typed + val b2Untyped: Any = b2Typed + val c2Untyped: Any = c2Typed + + require(a1Typed == a1Typed && a1Untyped == a1Untyped) + require(a1Typed == b1Typed && a1Untyped == b1Untyped) + require(a1Typed != c1Typed && a1Untyped != c1Untyped) + require(b1Typed == a1Typed && b1Untyped == a1Untyped) + require(b1Typed == b1Typed && b1Untyped == b1Untyped) + require(b1Typed == c1Typed && b1Untyped == c1Untyped) + require(c1Typed != a1Typed && c1Untyped != a1Untyped) + require(c1Typed == b1Typed && c1Untyped == b1Untyped) + require(c1Typed == c1Typed && c1Untyped == c1Untyped) + + require(a2Typed == a2Typed && a2Untyped == a2Untyped) + require(a2Typed == b2Typed && a2Untyped == b2Untyped) + require(a2Typed != c2Typed && a2Untyped != c2Untyped) + require(b2Typed == a2Typed && b2Untyped == a2Untyped) + require(b2Typed == b2Typed && b2Untyped == b2Untyped) + require(b2Typed == c2Typed && b2Untyped == c2Untyped) + require(c2Typed != a2Typed && c2Untyped != a2Untyped) + require(c2Typed == b2Typed && c2Untyped == b2Untyped) + require(c2Typed == c2Typed && c2Untyped == c2Untyped) + + return "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.txt b/compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.txt new file mode 100644 index 00000000000..0cc4a1c0c16 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.txt @@ -0,0 +1,45 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC1 { + // source: 'mfvcEqualsConsistency.kt' + private final field field-0: int + private final field field-1: int + private synthetic method (p0: int, p1: int): void + public synthetic final static method box-impl(p0: int, p1: int): MFVC1 + public final static method constructor-impl(p0: int, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, p1: int, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: int, p1: int, p2: int, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: int, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: int, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): int + public synthetic final method unbox-impl-1(): int +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC2 { + // source: 'mfvcEqualsConsistency.kt' + private final field field-0: int + private final field field-1: int + private synthetic method (p0: int, p1: int): void + public synthetic final static method box-impl(p0: int, p1: int): MFVC2 + public final static method constructor-impl(p0: int, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, p1: int, @org.jetbrains.annotations.Nullable p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: int, p1: int, p2: int, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: int, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: int, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): int + public synthetic final method unbox-impl-1(): int +} + +@kotlin.Metadata +public final class MfvcEqualsConsistencyKt { + // source: 'mfvcEqualsConsistency.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.kt b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.kt new file mode 100644 index 00000000000..270806dc061 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.kt @@ -0,0 +1,25 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +import kotlin.math.abs + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC(val x: Double, val y: Int) { + fun equals(other: MFVC): Boolean { + return abs(x - other.x) < 0.1 + } + + override fun hashCode(): Int { + return 0 + } +} + +fun box(): String { + val set = setOf(MFVC(1.0, 100), MFVC(1.5, 200), MFVC(1.501, 300)) + return if (set.size == 2) "OK" else "Fail" +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.txt b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.txt new file mode 100644 index 00000000000..18d2716175a --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.txt @@ -0,0 +1,25 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC { + // source: 'mfvcEqualsOverriddenForCollections.kt' + private final field field-0: double + private final field field-1: int + private synthetic method (p0: double, p1: int): void + public synthetic final static method box-impl(p0: double, p1: int): MFVC + public final static method constructor-impl(p0: double, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: double, p1: int, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: double, p1: int, p2: double, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: double, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: double, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): double + public synthetic final method unbox-impl-1(): int +} + +@kotlin.Metadata +public final class MfvcEqualsOverriddenForCollectionsKt { + // source: 'mfvcEqualsOverriddenForCollections.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.kt b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.kt new file mode 100644 index 00000000000..7d2f2fd9158 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.kt @@ -0,0 +1,41 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +import kotlin.math.abs + +interface I { + fun equals(param: MFVC): Boolean +} + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC(val value: Int, val y: Int) : I { + override fun equals(param: MFVC): Boolean { + return abs(value - param.value) < 2 + } +} + +fun box(): String { + val a1Typed: MFVC = MFVC(1, 2) + val b1Typed: MFVC = MFVC(2, 3) + val c1Typed: MFVC = MFVC(3, 4) + val a1Untyped: I = a1Typed + val b1Untyped: I = b1Typed + val c1Untyped: I = c1Typed + + require(a1Typed == a1Typed && a1Untyped == a1Untyped) + require(a1Typed == b1Typed && a1Untyped == b1Untyped) + require(a1Typed != c1Typed && a1Untyped != c1Untyped) + require(b1Typed == a1Typed && b1Untyped == a1Untyped) + require(b1Typed == b1Typed && b1Untyped == b1Untyped) + require(b1Typed == c1Typed && b1Untyped == c1Untyped) + require(c1Typed != a1Typed && c1Untyped != a1Untyped) + require(c1Typed == b1Typed && c1Untyped == b1Untyped) + require(c1Typed == c1Typed && c1Untyped == c1Untyped) + + return "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.txt b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.txt new file mode 100644 index 00000000000..064ddfe595c --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.txt @@ -0,0 +1,32 @@ +@kotlin.Metadata +public interface I { + // source: 'mfvcEqualsOverride.kt' + public abstract method equals-sUp7gFk(p0: int, p1: int): boolean +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC { + // source: 'mfvcEqualsOverride.kt' + private final field field-0: int + private final field field-1: int + private synthetic method (p0: int, p1: int): void + public synthetic final static method box-impl(p0: int, p1: int): MFVC + public final static method constructor-impl(p0: int, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, p1: int, p2: java.lang.Object): boolean + public method equals-sUp7gFk(p0: int, p1: int): boolean + public static method equals-sUp7gFk(p0: int, p1: int, p2: int, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: int, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: int, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): int + public synthetic final method unbox-impl-1(): int +} + +@kotlin.Metadata +public final class MfvcEqualsOverrideKt { + // source: 'mfvcEqualsOverride.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.kt b/compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.kt new file mode 100644 index 00000000000..95d561dd87a --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.kt @@ -0,0 +1,18 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +OPTIONAL_JVM_INLINE_ANNOTATION +value class A(val value1: MyClass, val value2: MyClass) { + override fun hashCode() = 42 +} + +class MyClass() { + override fun hashCode() = -1 +} + +fun box(): String = if (A(MyClass(), MyClass()).hashCode() == 42) "OK" else "Fail" diff --git a/compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.txt b/compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.txt new file mode 100644 index 00000000000..10cdd92fbcf --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.txt @@ -0,0 +1,32 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class A { + // source: 'mfvcHashCodeOverride.kt' + private final @org.jetbrains.annotations.NotNull field field-0: MyClass + private final @org.jetbrains.annotations.NotNull field field-1: MyClass + private synthetic method (p0: MyClass, p1: MyClass): void + public synthetic final static method box-impl(p0: MyClass, p1: MyClass): A + public final static method constructor-impl(@org.jetbrains.annotations.NotNull p0: MyClass, @org.jetbrains.annotations.NotNull p1: MyClass): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: MyClass, p1: MyClass, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: MyClass, p1: MyClass, p2: MyClass, p3: MyClass): boolean + public method hashCode(): int + public static method hashCode-impl(@org.jetbrains.annotations.NotNull p0: MyClass, @org.jetbrains.annotations.NotNull p1: MyClass): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: MyClass, p1: MyClass): java.lang.String + public synthetic final method unbox-impl-0(): MyClass + public synthetic final method unbox-impl-1(): MyClass +} + +@kotlin.Metadata +public final class MfvcHashCodeOverrideKt { + // source: 'mfvcHashCodeOverride.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} + +@kotlin.Metadata +public final class MyClass { + // source: 'mfvcHashCodeOverride.kt' + public method (): void + public method hashCode(): int +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.kt b/compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.kt new file mode 100644 index 00000000000..428deb9e263 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.kt @@ -0,0 +1,56 @@ +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC1(val x: T, val other: Int) { + fun equals(x: Int, other: Int) = false + fun equals(other: MFVC1<*>) = true +} + +class Generic(val x: T, val y: R) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC2(val value: Generic, val other: Int) { + fun equals(value: MFVC1, other: Int) = false + fun equals(other: MFVC2<*, *>) = true +} + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC3(val value: T, val other: Int) { + fun equals(value: Int, other: Int) = false + fun equals(other: MFVC3<*>) = true +} + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC4(val value: T, val other: Int) { + fun equals(value: Any, other: Int) = false + fun equals(other: MFVC4<*>) = true +} + + +fun box() = when { + MFVC1(5.0, 100) != MFVC1(3, 100) -> "Fail 1.1" + (MFVC1(5.0, 100) as Any) != MFVC1(3, 100) -> "Fail 1.2" + MFVC1(5.0, 100) != (MFVC1(3, 100) as Any) -> "Fail 1.3" + (MFVC1(5.0, 100) as Any) != (MFVC1(3, 100) as Any) -> "Fail 1.4" + + MFVC2(Generic("aba", 5.0), 100) != MFVC2(Generic(3, 8), 100) -> "Fail 2.1" + (MFVC2(Generic("aba", 5.0), 100) as Any) != MFVC2(Generic(3, 8), 100) -> "Fail 2.2" + MFVC2(Generic("aba", 5.0), 100) != (MFVC2(Generic(3, 8), 100) as Any) -> "Fail 2.3" + (MFVC2(Generic("aba", 5.0), 100) as Any) != (MFVC2(Generic(3, 8), 100) as Any) -> "Fail 2.4" + + MFVC3("x", 100) != MFVC3("y", 100) -> "Fail 3.1" + (MFVC3("x", 100) as Any) != MFVC3("y", 100) -> "Fail 3.2" + MFVC3("x", 100) != (MFVC3("y", 100) as Any) -> "Fail 3.3" + (MFVC3("x", 100) as Any) != (MFVC3("y", 100) as Any) -> "Fail 3.4" + + MFVC4("aba", 100) != MFVC4("caba", 100) -> "Fail 4.1" + (MFVC4("aba", 100) as Any) != MFVC4("caba", 100) -> "Fail 4.2" + MFVC4("aba", 100) != (MFVC4("caba", 100) as Any) -> "Fail 4.3" + (MFVC4("aba", 100) as Any) != (MFVC4("caba", 100) as Any) -> "Fail 4.4" + + else -> "OK" +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.txt b/compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.txt new file mode 100644 index 00000000000..df7819a8de1 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.txt @@ -0,0 +1,99 @@ +@kotlin.Metadata +public final class Generic { + // source: 'mfvcTypedEqualsGenerics.kt' + private final field x: java.lang.Object + private final field y: java.lang.Object + public method (p0: java.lang.Object, p1: java.lang.Object): void + public final method getX(): java.lang.Object + public final method getY(): java.lang.Object +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC1 { + // source: 'mfvcTypedEqualsGenerics.kt' + private final @org.jetbrains.annotations.NotNull field field-0: java.lang.Number + private final field field-1: int + private synthetic method (p0: java.lang.Number, p1: int): void + public synthetic final static method box-impl(p0: java.lang.Number, p1: int): MFVC1 + public final static method constructor-impl(@org.jetbrains.annotations.NotNull p0: java.lang.Number, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final static method equals-impl(@org.jetbrains.annotations.NotNull p0: java.lang.Number, p1: int, p2: int, p3: int): boolean + public static method equals-impl(p0: java.lang.Number, p1: int, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(@org.jetbrains.annotations.NotNull p0: java.lang.Number, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.Number, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.Number, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: java.lang.Number, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): java.lang.Number + public synthetic final method unbox-impl-1(): int +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC2 { + // source: 'mfvcTypedEqualsGenerics.kt' + private final @org.jetbrains.annotations.NotNull field field-0: Generic + private final field field-1: int + private synthetic method (p0: Generic, p1: int): void + public synthetic final static method box-impl(p0: Generic, p1: int): MFVC2 + public final static method constructor-impl(@org.jetbrains.annotations.NotNull p0: Generic, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final static method equals-GPBa7dw(@org.jetbrains.annotations.NotNull p0: Generic, p1: int, p2: double, p3: int, p4: int): boolean + public static method equals-impl(p0: Generic, p1: int, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(@org.jetbrains.annotations.NotNull p0: Generic, p1: int, @org.jetbrains.annotations.NotNull p2: Generic, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: Generic, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: Generic, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): Generic + public synthetic final method unbox-impl-1(): int +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC3 { + // source: 'mfvcTypedEqualsGenerics.kt' + private final field field-0: java.lang.Object + private final field field-1: int + private synthetic method (p0: java.lang.Object, p1: int): void + public synthetic final static method box-impl(p0: java.lang.Object, p1: int): MFVC3 + public final static method constructor-impl(p0: java.lang.Object, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final static method equals-impl(p0: java.lang.Object, p1: int, p2: int, p3: int): boolean + public static method equals-impl(p0: java.lang.Object, p1: int, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: java.lang.Object, p1: int, p2: java.lang.Object, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.Object, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: java.lang.Object, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): java.lang.Object + public synthetic final method unbox-impl-1(): int +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC4 { + // source: 'mfvcTypedEqualsGenerics.kt' + private final field field-0: java.lang.Object + private final field field-1: int + private synthetic method (p0: java.lang.Object, p1: int): void + public synthetic final static method box-impl(p0: java.lang.Object, p1: int): MFVC4 + public final static method constructor-impl(p0: java.lang.Object, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final static method equals-impl(p0: java.lang.Object, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.Object, p3: int): boolean + public static method equals-impl(p0: java.lang.Object, p1: int, p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: java.lang.Object, p1: int, p2: java.lang.Object, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.Object, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: java.lang.Object, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): java.lang.Object + public synthetic final method unbox-impl-1(): int +} + +@kotlin.Metadata +public final class MfvcTypedEqualsGenericsKt { + // source: 'mfvcTypedEqualsGenerics.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.kt b/compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.kt new file mode 100644 index 00000000000..bee35b93c28 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.kt @@ -0,0 +1,26 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInValueClasses +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING + +import kotlin.math.abs + +OPTIONAL_JVM_INLINE_ANNOTATION +value class MFVC(val x: Int, val y: Int) { + override fun equals(other: Any?): Boolean { + if (other !is MFVC) { + return false + } + return abs(x - other.x) < 2 && abs(y - other.y) < 2 + } + + override fun hashCode() = 0 +} + +fun box(): String { + val set = setOf(MFVC(1, 2), MFVC(2, 3), MFVC(5, 6)) + return if (set.size == 2) "OK" else "Fail" +} diff --git a/compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.txt b/compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.txt new file mode 100644 index 00000000000..7d3a79de2b3 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.txt @@ -0,0 +1,25 @@ +@kotlin.jvm.JvmInline +@kotlin.Metadata +public final class MFVC { + // source: 'mfvcUntypedEqualsOverriden.kt' + private final field field-0: int + private final field field-1: int + private synthetic method (p0: int, p1: int): void + public synthetic final static method box-impl(p0: int, p1: int): MFVC + public final static method constructor-impl(p0: int, p1: int): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public static method equals-impl(p0: int, p1: int, @org.jetbrains.annotations.Nullable p2: java.lang.Object): boolean + public final static method equals-sUp7gFk(p0: int, p1: int, p2: int, p3: int): boolean + public method hashCode(): int + public static method hashCode-impl(p0: int, p1: int): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String + public static method toString-impl(p0: int, p1: int): java.lang.String + public synthetic final method unbox-impl-0(): int + public synthetic final method unbox-impl-1(): int +} + +@kotlin.Metadata +public final class MfvcUntypedEqualsOverridenKt { + // source: 'mfvcUntypedEqualsOverriden.kt' + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/diagnostics/tests/inlineClasses/inefficientEqualsOverridingInInlineClass.kt b/compiler/testData/diagnostics/tests/inlineClasses/inefficientEqualsOverridingInInlineClass.kt index 39942aa2fdd..2d5c22272be 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/inefficientEqualsOverridingInInlineClass.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/inefficientEqualsOverridingInInlineClass.kt @@ -1,11 +1,11 @@ // FIR_IDENTICAL // WITH_STDLIB // !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST -// LANGUAGE: +CustomEqualsInInlineClasses +// LANGUAGE: +CustomEqualsInValueClasses @JvmInline value class IC1(val x: Int) { - override fun equals(other: Any?): Boolean { + override fun equals(other: Any?): Boolean { if (other !is IC1) { return false } @@ -29,9 +29,9 @@ value class IC3(val x: Int) { @JvmInline value class IC4(val x: Int) { - override fun equals(other: Any?) = true + override fun equals(other: Any?) = true - fun equals(other: IC4) = true + fun equals(other: IC4) = true override fun hashCode() = 0 -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.kt b/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.kt index 9bb82074076..1ecbe48d1f3 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.kt @@ -1,18 +1,21 @@ -// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses, +CustomEqualsInInlineClasses +// WITH_STDLIB +// !LANGUAGE: +CustomEqualsInValueClasses // !DIAGNOSTICS: -UNUSED_PARAMETER -inline class IC1(val x: Any) { +@JvmInline +value class IC1(val x: Any) { fun box() {} fun box(x: Any) {} fun unbox() {} fun unbox(x: Any) {} - override fun equals(other: Any?): Boolean = true + override fun equals(other: Any?): Boolean = true override fun hashCode(): Int = 0 } -inline class IC2(val x: Any) { +@JvmInline +value class IC2(val x: Any) { fun box(x: Any) {} fun box(): Any = TODO() @@ -23,7 +26,8 @@ inline class IC2(val x: Any) { fun hashCode(a: Any): Int = 0 } -inline class IC3(val x: Any) { +@JvmInline +value class IC3(val x: Any) { fun box(x: Any): Any = TODO() fun unbox(x: Any): Any = TODO() @@ -34,12 +38,34 @@ interface WithBox { fun box(): String } -inline class IC4(val s: String) : WithBox { +@JvmInline +value class IC4(val s: String) : WithBox { override fun box(): String = "" } -inline class IC5(val a: String) { +@JvmInline +value class IC5(val a: String) { constructor(i: Int) : this(i.toString()) { TODO("something") } -} \ No newline at end of file +} + +@JvmInline +value class IC6(val a: String) { + fun equals(other: IC6): Boolean = true +} + +@JvmInline +value class IC7(val a: String) { + fun equals(other: IC7<*>): Boolean = true +} + +@JvmInline +value class IC8(val a: String) { + fun equals(other: IC8): Boolean = true +} + +@JvmInline +value class IC9(val a: String) { + fun equals(other: IC9): Boolean = true +} diff --git a/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.txt b/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.txt index 42e8dcd55e8..dcc0e1af246 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.txt +++ b/compiler/testData/diagnostics/tests/inlineClasses/reservedMembersAndConstructsInsideInlineClass.txt @@ -1,6 +1,6 @@ package -public final inline class IC1 { +@kotlin.jvm.JvmInline public final value class IC1 { public constructor IC1(/*0*/ x: kotlin.Any) public final val x: kotlin.Any public final fun box(): kotlin.Unit @@ -12,7 +12,7 @@ public final inline class IC1 { public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit } -public final inline class IC2 { +@kotlin.jvm.JvmInline public final value class IC2 { public constructor IC2(/*0*/ x: kotlin.Any) public final val x: kotlin.Any public final fun box(): kotlin.Any @@ -26,7 +26,7 @@ public final inline class IC2 { public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit } -public final inline class IC3 { +@kotlin.jvm.JvmInline public final value class IC3 { public constructor IC3(/*0*/ x: kotlin.Any) public final val x: kotlin.Any public final fun box(/*0*/ x: kotlin.Any): kotlin.Any @@ -37,7 +37,7 @@ public final inline class IC3 { public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Any } -public final inline class IC4 : WithBox { +@kotlin.jvm.JvmInline public final value class IC4 : WithBox { public constructor IC4(/*0*/ s: kotlin.String) public final val s: kotlin.String public open override /*1*/ fun box(): kotlin.String @@ -46,7 +46,7 @@ public final inline class IC4 : WithBox { public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String } -public final inline class IC5 { +@kotlin.jvm.JvmInline public final value class IC5 { public constructor IC5(/*0*/ i: kotlin.Int) public constructor IC5(/*0*/ a: kotlin.String) public final val a: kotlin.String @@ -55,9 +55,46 @@ public final inline class IC5 { public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String } +@kotlin.jvm.JvmInline public final value class IC6 { + public constructor IC6(/*0*/ a: kotlin.String) + public final val a: kotlin.String + public final fun equals(/*0*/ other: IC6): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmInline public final value class IC7 { + public constructor IC7(/*0*/ a: kotlin.String) + public final val a: kotlin.String + public final fun equals(/*0*/ other: IC7<*>): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmInline public final value class IC8 { + public constructor IC8(/*0*/ a: kotlin.String) + public final val a: kotlin.String + public final fun equals(/*0*/ other: IC8): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmInline public final value class IC9 { + public constructor IC9(/*0*/ a: kotlin.String) + public final val a: kotlin.String + public final fun equals(/*0*/ other: IC9): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + public interface WithBox { public abstract fun box(): kotlin.String 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 } + diff --git a/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt index c9ccf07bab6..d48a07c4355 100644 --- a/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt +++ b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt @@ -1,7 +1,7 @@ // FIR_IDENTICAL // WITH_STDLIB // !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST -// LANGUAGE: +CustomEqualsInInlineClasses +// LANGUAGE: +CustomEqualsInValueClasses @JvmInline @@ -23,12 +23,12 @@ value class IC2(val x: Int) { @JvmInline value class IC3(val x: T) { - operator fun equals(other: IC3) = true + operator fun equals(other: IC3) = true } @JvmInline value class IC4(val x: T) { - operator fun equals(other: IC4) = true + operator fun equals(other: IC4) = true } @JvmInline @@ -49,4 +49,4 @@ value class IC7(val x: T) { @JvmInline value class IC8(val x: T) { operator fun equals(other: IC8<*, *>): Nothing = TODO() -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt new file mode 100644 index 00000000000..27b1a22bc32 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt @@ -0,0 +1,23 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST +// LANGUAGE: +CustomEqualsInValueClasses +// SKIP_TXT + + +@JvmInline +value class IC1(val x: Int) { + override fun equals(other: Any?) = true + + operator fun equals(other: IC1) = true + + override fun hashCode() = 0 +} + +@JvmInline +value class IC2(val x: Int) { + operator fun equals(other: IC1) = true + + operator fun equals(other: IC2) { + } +} diff --git a/compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt b/compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt new file mode 100644 index 00000000000..36769c1e7ae --- /dev/null +++ b/compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt @@ -0,0 +1,28 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST +// LANGUAGE: +CustomEqualsInValueClasses +ValueClasses + +@JvmInline +value class MFVC1(val x: Int, val y: Int) { + override fun equals(other: Any?): Boolean { + if (other !is MFVC1) { + return false + } + return x == other.x + } +} + +@JvmInline +value class MFVC2(val x: Int, val y: Int) { + override fun hashCode() = 0 +} + +@JvmInline +value class MFVC3(val x: Int, val y: Int) { + override fun equals(other: Any?) = true + + fun equals(other: MFVC3) = true + + override fun hashCode() = 0 +} diff --git a/compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.txt b/compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.txt new file mode 100644 index 00000000000..578b313dc92 --- /dev/null +++ b/compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.txt @@ -0,0 +1,29 @@ +package + +@kotlin.jvm.JvmInline public final value class MFVC1 { + public constructor MFVC1(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + public final val y: kotlin.Int + public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmInline public final value class MFVC2 { + public constructor MFVC2(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + public final val y: kotlin.Int + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmInline public final value class MFVC3 { + public constructor MFVC3(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + public final val y: kotlin.Int + public final fun equals(/*0*/ other: MFVC3): kotlin.Boolean + public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt index e4d2a098df9..0830456cd3b 100644 --- a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt +++ b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt @@ -1,5 +1,5 @@ // !SKIP_JAVAC -// !LANGUAGE: +CustomEqualsInInlineClasses, +ValueClasses +// !LANGUAGE: +CustomEqualsInValueClasses, +ValueClasses // ALLOW_KOTLIN_PACKAGE // !DIAGNOSTICS: -UNUSED_PARAMETER @@ -102,4 +102,24 @@ value class MFVC5(val a: String, val b: String) { constructor(i: Int) : this(i.toString(), "6") { TODO("something") } -} \ No newline at end of file +} + +@JvmInline +value class MFVC6(val a: String, val b: String) { + fun equals(other: MFVC6): Boolean = true +} + +@JvmInline +value class MFVC7(val a: String, val b: String) { + fun equals(other: MFVC7<*>): Boolean = true +} + +@JvmInline +value class MFVC8(val a: String, val b: String) { + fun equals(other: MFVC8): Boolean = true +} + +@JvmInline +value class MFVC9(val a: String, val b: String) { + fun equals(other: MFVC9): Boolean = true +} diff --git a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.txt b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.txt new file mode 100644 index 00000000000..3663e89680e --- /dev/null +++ b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.txt @@ -0,0 +1,186 @@ +package + +package kotlin { + + package kotlin.jvm { + + @kotlin.jvm.JvmInline public final value class IC1 { + public constructor IC1(/*0*/ x: kotlin.Any) + public final val x: kotlin.Any + public final fun box(): kotlin.Unit + public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit + public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public final fun unbox(): kotlin.Unit + public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit + } + + @kotlin.jvm.JvmInline public final value class IC2 { + public constructor IC2(/*0*/ x: kotlin.Any) + public final val x: kotlin.Any + public final fun box(): kotlin.Any + public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit + public final fun equals(/*0*/ my: kotlin.Any, /*1*/ other: kotlin.Any): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public final fun hashCode(/*0*/ a: kotlin.Any): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public final fun unbox(): kotlin.Any + public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit + } + + @kotlin.jvm.JvmInline public final value class IC3 { + public constructor IC3(/*0*/ x: kotlin.Any) + public final val x: kotlin.Any + public final fun box(/*0*/ x: kotlin.Any): kotlin.Any + public final fun equals(): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Any + } + + @kotlin.jvm.JvmInline public final value class IC4 : kotlin.jvm.WithBox { + public constructor IC4(/*0*/ s: kotlin.String) + public final val s: kotlin.String + public open override /*1*/ fun box(): kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class IC5 { + public constructor IC5(/*0*/ i: kotlin.Int) + public constructor IC5(/*0*/ a: kotlin.String) + public final val a: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class IC6 { + public constructor IC6(/*0*/ a: kotlin.String) + public final val a: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun equals(/*0*/ other: kotlin.jvm.IC6): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final annotation class JvmInline : kotlin.Annotation { + public constructor JvmInline() + 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 + } + + @kotlin.jvm.JvmInline public final value class MFVC1 { + public constructor MFVC1(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any) + public final val x: kotlin.Any + public final val y: kotlin.Any + public final fun box(): kotlin.Unit + public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit + public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public final fun unbox(): kotlin.Unit + public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit + } + + @kotlin.jvm.JvmInline public final value class MFVC2 { + public constructor MFVC2(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any) + public final val x: kotlin.Any + public final val y: kotlin.Any + public final fun box(): kotlin.Any + public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit + public final fun equals(/*0*/ my: kotlin.Any, /*1*/ other: kotlin.Any): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public final fun hashCode(/*0*/ a: kotlin.Any): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public final fun unbox(): kotlin.Any + public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit + } + + @kotlin.jvm.JvmInline public final value class MFVC3 { + public constructor MFVC3(/*0*/ x: kotlin.Any, /*1*/ y: kotlin.Any) + public final val x: kotlin.Any + public final val y: kotlin.Any + public final fun box(/*0*/ x: kotlin.Any): kotlin.Any + public final fun equals(): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Any + } + + @kotlin.jvm.JvmInline public final value class MFVC4 : kotlin.jvm.WithBox { + public constructor MFVC4(/*0*/ s: kotlin.String, /*1*/ t: kotlin.String) + public final val s: kotlin.String + public final val t: kotlin.String + public open override /*1*/ fun box(): kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class MFVC5 { + public constructor MFVC5(/*0*/ i: kotlin.Int) + public constructor MFVC5(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String) + public final val a: kotlin.String + public final val b: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class MFVC6 { + public constructor MFVC6(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String) + public final val a: kotlin.String + public final val b: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun equals(/*0*/ other: kotlin.jvm.MFVC6): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class MFVC7 { + public constructor MFVC7(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String) + public final val a: kotlin.String + public final val b: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun equals(/*0*/ other: kotlin.jvm.MFVC7<*>): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class MFVC8 { + public constructor MFVC8(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String) + public final val a: kotlin.String + public final val b: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun equals(/*0*/ other: kotlin.jvm.MFVC8): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class MFVC9 { + public constructor MFVC9(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String) + public final val a: kotlin.String + public final val b: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final fun equals(/*0*/ other: kotlin.jvm.MFVC9): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public interface WithBox { + public abstract fun box(): kotlin.String + 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 + } + } +} + diff --git a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.fir.kt b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.fir.kt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt deleted file mode 100644 index 79985c3e777..00000000000 --- a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt +++ /dev/null @@ -1,57 +0,0 @@ -// FIR_IDENTICAL -// !SKIP_JAVAC -// !LANGUAGE: +InlineClasses, +CustomEqualsInInlineClasses -// ALLOW_KOTLIN_PACKAGE -// !DIAGNOSTICS: -UNUSED_PARAMETER - -package kotlin.jvm - -annotation class JvmInline - -@JvmInline -value class IC1(val x: Any) { - fun box() {} - fun box(x: Any) {} - - fun unbox() {} - fun unbox(x: Any) {} - - override fun equals(other: Any?): Boolean = true - override fun hashCode(): Int = 0 -} - -@JvmInline -value class IC2(val x: Any) { - fun box(x: Any) {} - fun box(): Any = TODO() - - fun unbox(x: Any) {} - fun unbox(): Any = TODO() - - fun equals(my: Any, other: Any): Boolean = true - fun hashCode(a: Any): Int = 0 -} - -@JvmInline -value class IC3(val x: Any) { - fun box(x: Any): Any = TODO() - fun unbox(x: Any): Any = TODO() - - fun equals(): Boolean = true -} - -interface WithBox { - fun box(): String -} - -@JvmInline -value class IC4(val s: String) : WithBox { - override fun box(): String = "" -} - -@JvmInline -value class IC5(val a: String) { - constructor(i: Int) : this(i.toString()) { - TODO("something") - } -} diff --git a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.txt b/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.txt deleted file mode 100644 index d56ed1ad11a..00000000000 --- a/compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.txt +++ /dev/null @@ -1,76 +0,0 @@ -package - -package kotlin { - - package kotlin.jvm { - - @kotlin.jvm.JvmInline public final value class IC1 { - public constructor IC1(/*0*/ x: kotlin.Any) - public final val x: kotlin.Any - public final fun box(): kotlin.Unit - public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit - public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String - public final fun unbox(): kotlin.Unit - public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit - } - - @kotlin.jvm.JvmInline public final value class IC2 { - public constructor IC2(/*0*/ x: kotlin.Any) - public final val x: kotlin.Any - public final fun box(): kotlin.Any - public final fun box(/*0*/ x: kotlin.Any): kotlin.Unit - public final fun equals(/*0*/ my: kotlin.Any, /*1*/ other: kotlin.Any): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public final fun hashCode(/*0*/ a: kotlin.Any): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String - public final fun unbox(): kotlin.Any - public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Unit - } - - @kotlin.jvm.JvmInline public final value class IC3 { - public constructor IC3(/*0*/ x: kotlin.Any) - public final val x: kotlin.Any - public final fun box(/*0*/ x: kotlin.Any): kotlin.Any - public final fun equals(): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String - public final fun unbox(/*0*/ x: kotlin.Any): kotlin.Any - } - - @kotlin.jvm.JvmInline public final value class IC4 : kotlin.jvm.WithBox { - public constructor IC4(/*0*/ s: kotlin.String) - public final val s: kotlin.String - public open override /*1*/ fun box(): kotlin.String - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String - } - - @kotlin.jvm.JvmInline public final value class IC5 { - public constructor IC5(/*0*/ i: kotlin.Int) - public constructor IC5(/*0*/ a: kotlin.String) - public final val a: kotlin.String - public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean - public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int - public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String - } - - public final annotation class JvmInline : kotlin.Annotation { - public constructor JvmInline() - 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 - } - - public interface WithBox { - public abstract fun box(): kotlin.String - 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 - } - } -} diff --git a/compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt b/compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt new file mode 100644 index 00000000000..3b689cc975d --- /dev/null +++ b/compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt @@ -0,0 +1,22 @@ +// FIR_IDENTICAL +// WITH_STDLIB +// !DIAGNOSTICS: -DEBUG_INFO_SMARTCAST +// LANGUAGE: +CustomEqualsInValueClasses, +ValueClasses + + +@JvmInline +value class MFVC1(val x: Int, val y: Int) { + override fun equals(other: Any?) = true + + operator fun equals(other: MFVC1) = true + + override fun hashCode() = 0 +} + +@JvmInline +value class MFVC2(val x: Int, val y: Int) { + operator fun equals(other: MFVC1) = true + + operator fun equals(other: MFVC2) { + } +} diff --git a/compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.txt b/compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.txt new file mode 100644 index 00000000000..ee87305a60f --- /dev/null +++ b/compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.txt @@ -0,0 +1,22 @@ +package + +@kotlin.jvm.JvmInline public final value class MFVC1 { + public constructor MFVC1(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + public final val y: kotlin.Int + public final operator fun equals(/*0*/ other: MFVC1): kotlin.Boolean + public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmInline public final value class MFVC2 { + public constructor MFVC2(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + public final val x: kotlin.Int + public final val y: kotlin.Int + public final operator fun equals(/*0*/ other: MFVC1): kotlin.Boolean + public final operator fun equals(/*0*/ other: MFVC2): kotlin.Unit + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} 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 007b82ad89c..127bfed8c3d 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 @@ -18116,6 +18116,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorDeclarationCheck.kt"); } + @Test + @TestMetadata("typedEqualsOperatorModifierInInlineClass.kt") + public void testTypedEqualsOperatorModifierInInlineClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/inlineClasses/typedEqualsOperatorModifierInInlineClass.kt"); + } + @Test @TestMetadata("unsignedLiteralsWithoutArtifactOnClasspath.kt") public void testUnsignedLiteralsWithoutArtifactOnClasspath() throws Exception { @@ -33744,6 +33750,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/valueClasses/identityComparisonWithValueClasses.kt"); } + @Test + @TestMetadata("inefficientEqualsOverridingInMfvc.kt") + public void testInefficientEqualsOverridingInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/inefficientEqualsOverridingInMfvc.kt"); + } + @Test @TestMetadata("jvmInlineApplicability.kt") public void testJvmInlineApplicability() throws Exception { @@ -33787,9 +33799,15 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { } @Test - @TestMetadata("reservedMembersAndConstructsInsideValueClass.kt") - public void testReservedMembersAndConstructsInsideValueClass() throws Exception { - runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideValueClass.kt"); + @TestMetadata("reservedMembersAndConstructsInsideMfvc.kt") + public void testReservedMembersAndConstructsInsideMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/reservedMembersAndConstructsInsideMfvc.kt"); + } + + @Test + @TestMetadata("typedEqualsOperatorModifierInMfvc.kt") + public void testTypedEqualsOperatorModifierInMfvc() throws Exception { + runTest("compiler/testData/diagnostics/tests/valueClasses/typedEqualsOperatorModifierInMfvc.kt"); } @Test diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 915297764db..6a511179142 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -21486,15 +21486,15 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } @Test - @TestMetadata("inlineClassEqualsOverride.kt") - public void testInlineClassEqualsOverride() throws Exception { - runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + @TestMetadata("inlineClassEqualsOverriddenForCollections.kt") + public void testInlineClassEqualsOverriddenForCollections() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverriddenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } @Test - @TestMetadata("inlineClassEqualsOverridenForCollections.kt") - public void testInlineClassEqualsOverridenForCollections() throws Exception { - runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverridenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + @TestMetadata("inlineClassEqualsOverride.kt") + public void testInlineClassEqualsOverride() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/inlineClassEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } @Test @@ -50465,12 +50465,54 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/valueClasses/functionReferences.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("mfvcBothEqualsOverride.kt") + public void testMfvcBothEqualsOverride() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcBothEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcEqualsConsistency.kt") + public void testMfvcEqualsConsistency() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcEqualsConsistency.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcEqualsOverriddenForCollections.kt") + public void testMfvcEqualsOverriddenForCollections() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcEqualsOverriddenForCollections.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcEqualsOverride.kt") + public void testMfvcEqualsOverride() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcEqualsOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("mfvcFieldInitializationOrder.kt") public void testMfvcFieldInitializationOrder() throws Exception { runTest("compiler/testData/codegen/box/valueClasses/mfvcFieldInitializationOrder.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("mfvcHashCodeOverride.kt") + public void testMfvcHashCodeOverride() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcHashCodeOverride.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcTypedEqualsGenerics.kt") + public void testMfvcTypedEqualsGenerics() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcTypedEqualsGenerics.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + + @Test + @TestMetadata("mfvcUntypedEqualsOverriden.kt") + public void testMfvcUntypedEqualsOverriden() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/mfvcUntypedEqualsOverriden.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("overrides.kt") public void testOverrides() throws Exception { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 82bcd8377f5..4f3c5cd899e 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -277,7 +277,7 @@ enum class LanguageFeature( ForbidSuperDelegationToAbstractFakeOverride(KOTLIN_1_9, kind = BUG_FIX), // KT-49017 DataObjects(KOTLIN_1_9), // KT-4107 ProhibitAccessToEnumCompanionMembersInEnumConstructorCall(KOTLIN_1_9, kind = BUG_FIX), // KT-49110 - CustomEqualsInInlineClasses(KOTLIN_1_9), // KT-24874 + CustomEqualsInValueClasses(KOTLIN_1_9), // KT-24874 EnhanceNullabilityOfPrimitiveArrays(KOTLIN_1_9, kind = BUG_FIX), // KT-54521 RefineTypeCheckingOnAssignmentsToJavaFields(KOTLIN_1_9, kind = BUG_FIX), // KT-46727 InlineLateinit(KOTLIN_1_9, kind = OTHER), // KT-23814 diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/descriptorUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/descriptorUtil.kt index b19eaf6ecba..14aec1cca42 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/descriptorUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/descriptorUtil.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.resolve.isInlineClass +import org.jetbrains.kotlin.resolve.isValueClass import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinTypeFactory import org.jetbrains.kotlin.types.typeUtil.* @@ -91,14 +91,14 @@ fun DeclarationDescriptor.containingPackage(): FqName? { object DeserializedDeclarationsFromSupertypeConflictDataKey : CallableDescriptor.UserDataKey -fun FunctionDescriptor.isTypedEqualsInInlineClass(): Boolean { - val inlineClassStarProjection = - (containingDeclaration as? ClassDescriptor)?.takeIf { it.isInlineClass() }?.defaultType?.replaceArgumentsWithStarProjections() +fun FunctionDescriptor.isTypedEqualsInValueClass(): Boolean { + val valueClassStarProjection = + (containingDeclaration as? ClassDescriptor)?.takeIf { it.isValueClass() }?.defaultType?.replaceArgumentsWithStarProjections() ?: return false val returnType = returnType ?: return false return name == OperatorNameConventions.EQUALS && (returnType.isBoolean() || returnType.isNothing()) - && valueParameters.size == 1 && valueParameters[0].type == inlineClassStarProjection + && valueParameters.size == 1 && valueParameters[0].type.replaceArgumentsWithStarProjections() == valueClassStarProjection && contextReceiverParameters.isEmpty() && extensionReceiverParameter == null } diff --git a/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt b/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt index e141435294b..92fd67bf652 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt @@ -25,7 +25,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.resolve.isInlineClass +import org.jetbrains.kotlin.resolve.isValueClass import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf @@ -200,10 +200,10 @@ object OperatorChecks : AbstractModifierChecks() { Checks(RANGE_UNTIL, MemberOrExtension, SingleValueParameter, NoDefaultAndVarargsCheck), Checks(EQUALS, Member) { fun DeclarationDescriptor.isAny() = this is ClassDescriptor && KotlinBuiltIns.isAny(this) - ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() } || isTypedEqualsInInlineClass()) { + ensure(containingDeclaration.isAny() || overriddenDescriptors.any { it.containingDeclaration.isAny() } || isTypedEqualsInValueClass()) { buildString { append("must override ''equals()'' in Any") - if (containingDeclaration.isInlineClass()) { + if (containingDeclaration.isValueClass()) { val expectedParameterTypeRendered = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType( (containingDeclaration as ClassDescriptor).defaultType.replaceArgumentsWithStarProjections() )