KT-4107 Data objects
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 5c95698..43a879a 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
@@ -2438,6 +2438,12 @@
token,
)
}
+ add(FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE) { firDiagnostic ->
+ DataObjectCustomEqualsOrHashCodeImpl(
+ firDiagnostic as KtPsiDiagnostic,
+ token,
+ )
+ }
add(FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE) { firDiagnostic ->
FunInterfaceConstructorReferenceImpl(
firDiagnostic as KtPsiDiagnostic,
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 ce18175..012ba58 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
@@ -1729,6 +1729,10 @@
override val diagnosticClass get() = TailRecursionInTryIsNotSupported::class
}
+ abstract class DataObjectCustomEqualsOrHashCode : KtFirDiagnostic<KtNamedFunction>() {
+ override val diagnosticClass get() = DataObjectCustomEqualsOrHashCode::class
+ }
+
abstract class FunInterfaceConstructorReference : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = FunInterfaceConstructorReference::class
}
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 8a3e8c1..788c975 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
@@ -2073,6 +2073,11 @@
override val token: KtLifetimeToken,
) : KtFirDiagnostic.TailRecursionInTryIsNotSupported(), KtAbstractFirDiagnostic<PsiElement>
+internal class DataObjectCustomEqualsOrHashCodeImpl(
+ override val firDiagnostic: KtPsiDiagnostic,
+ override val token: KtLifetimeToken,
+) : KtFirDiagnostic.DataObjectCustomEqualsOrHashCode(), KtAbstractFirDiagnostic<KtNamedFunction>
+
internal class FunInterfaceConstructorReferenceImpl(
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
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 49a09de..d2f5011 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
@@ -6957,6 +6957,12 @@
}
@Test
+ @TestMetadata("companionDataObject.kt")
+ public void testCompanionDataObject() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
+ }
+
+ @Test
@TestMetadata("componentNamedComponent1.kt")
public void testComponentNamedComponent1() throws Exception {
runTest("compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt");
@@ -7029,9 +7035,21 @@
}
@Test
- @TestMetadata("dataObject.kt")
- public void testDataObject() throws Exception {
- runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
+ @TestMetadata("dataObjectDisabled.kt")
+ public void testDataObjectDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectEnabled.kt")
+ public void testDataObjectEnabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectLiteral.kt")
+ public void testDataObjectLiteral() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
}
@Test
@@ -7656,6 +7674,22 @@
}
@Nested
+ @TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
+ }
+
+ @Test
+ @TestMetadata("overrideEqualsAndHashCode.kt")
+ public void testOverrideEqualsAndHashCode() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
@TestDataPath("$PROJECT_ROOT")
public class DeclarationChecks {
diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/DataClassMethodGenerator.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/DataClassMethodGenerator.kt
index 1eec756..a22905c 100644
--- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/DataClassMethodGenerator.kt
+++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/DataClassMethodGenerator.kt
@@ -19,6 +19,7 @@
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.BindingContext
@@ -33,8 +34,10 @@
) : FunctionsFromAnyGenerator(declaration, bindingContext) {
override fun generate() {
- generateComponentFunctionsForDataClasses()
- generateCopyFunctionForDataClasses(primaryConstructorParameters)
+ if (declaration !is KtObjectDeclaration) {
+ generateComponentFunctionsForDataClasses()
+ generateCopyFunctionForDataClasses(primaryConstructorParameters)
+ }
super.generate()
}
diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/FunctionsFromAnyGenerator.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/FunctionsFromAnyGenerator.kt
index 577314e..67f0498 100644
--- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/FunctionsFromAnyGenerator.kt
+++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/FunctionsFromAnyGenerator.kt
@@ -21,11 +21,9 @@
open fun generate() {
val properties = primaryConstructorProperties
- if (properties.isNotEmpty()) {
- generateToStringIfNeeded(properties)
- generateHashCodeIfNeeded(properties)
- generateEqualsIfNeeded(properties)
- }
+ generateToStringIfNeeded(properties)
+ generateHashCodeIfNeeded(properties)
+ generateEqualsIfNeeded(properties)
}
protected abstract fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>)
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java
index dd86b2f..a8a9d45 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java
@@ -17,8 +17,10 @@
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
+import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.resolve.BindingContext;
+import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
@@ -103,42 +105,46 @@
mv.visitCode();
- StringConcatGenerator generator = StringConcatGenerator.Companion.create(generationState, iv);
- generator.genStringBuilderConstructorIfNeded();
- boolean first = true;
+ if (properties.isEmpty()) {
+ iv.aconst(classDescriptor.getName().asString());
+ } else {
+ StringConcatGenerator generator = StringConcatGenerator.Companion.create(generationState, iv);
+ generator.genStringBuilderConstructorIfNeded();
+ boolean first = true;
- for (PropertyDescriptor propertyDescriptor : properties) {
- if (first) {
- generator.addStringConstant(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "=");
- first = false;
- }
- else {
- generator.addStringConstant(", " + propertyDescriptor.getName().asString() + "=");
- }
-
- JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
- Type asmType = type.getType();
- KotlinType kotlinType = type.getKotlinType();
-
- if (asmType.getSort() == Type.ARRAY) {
- Type elementType = correctElementType(asmType);
- if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
- iv.invokestatic("java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
- asmType = JAVA_STRING_TYPE;
- kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
+ for (PropertyDescriptor propertyDescriptor : properties) {
+ if (first) {
+ generator.addStringConstant(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "=");
+ first = false;
}
- else if (elementType.getSort() != Type.CHAR) {
- iv.invokestatic("java/util/Arrays", "toString", "(" + asmType.getDescriptor() + ")Ljava/lang/String;", false);
- asmType = JAVA_STRING_TYPE;
- kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
+ else {
+ generator.addStringConstant(", " + propertyDescriptor.getName().asString() + "=");
}
+
+ JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
+ Type asmType = type.getType();
+ KotlinType kotlinType = type.getKotlinType();
+
+ if (asmType.getSort() == Type.ARRAY) {
+ Type elementType = correctElementType(asmType);
+ if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
+ iv.invokestatic("java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
+ asmType = JAVA_STRING_TYPE;
+ kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
+ }
+ else if (elementType.getSort() != Type.CHAR) {
+ iv.invokestatic("java/util/Arrays", "toString", "(" + asmType.getDescriptor() + ")Ljava/lang/String;", false);
+ asmType = JAVA_STRING_TYPE;
+ kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
+ }
+ }
+ genInvokeAppendMethod(generator, asmType, kotlinType, typeMapper, StackValue.onStack(asmType));
}
- genInvokeAppendMethod(generator, asmType, kotlinType, typeMapper, StackValue.onStack(asmType));
+
+ generator.addStringConstant(")");
+
+ generator.genToString();
}
-
- generator.addStringConstant(")");
-
- generator.genToString();
iv.areturn(JAVA_STRING_TYPE);
FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration());
@@ -175,41 +181,45 @@
InstructionAdapter iv = new InstructionAdapter(mv);
mv.visitCode();
- boolean first = true;
- for (PropertyDescriptor propertyDescriptor : properties) {
- if (!first) {
- iv.iconst(31);
- iv.mul(Type.INT_TYPE);
- }
+ if (properties.isEmpty()) {
+ iv.iconst(DescriptorUtils.getFqNameSafe(classDescriptor).asString().hashCode());
+ } else {
+ boolean first = true;
+ for (PropertyDescriptor propertyDescriptor : properties) {
+ if (!first) {
+ iv.iconst(31);
+ iv.mul(Type.INT_TYPE);
+ }
- JvmKotlinType propertyType = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
- KotlinType kotlinType = propertyDescriptor.getReturnType();
- Type asmType = typeMapper.mapType(kotlinType);
- StackValue.coerce(propertyType.getType(), propertyType.getKotlinType(), asmType, kotlinType, iv);
+ JvmKotlinType propertyType = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
+ KotlinType kotlinType = propertyDescriptor.getReturnType();
+ Type asmType = typeMapper.mapType(kotlinType);
+ StackValue.coerce(propertyType.getType(), propertyType.getKotlinType(), asmType, kotlinType, iv);
- Label ifNull = null;
- if (!isPrimitive(asmType)) {
- ifNull = new Label();
- iv.dup();
- iv.ifnull(ifNull);
- }
+ Label ifNull = null;
+ if (!isPrimitive(asmType)) {
+ ifNull = new Label();
+ iv.dup();
+ iv.ifnull(ifNull);
+ }
- genHashCode(mv, iv, asmType, generationState.getTarget());
+ genHashCode(mv, iv, asmType, generationState.getTarget());
- if (ifNull != null) {
- Label end = new Label();
- iv.goTo(end);
- iv.mark(ifNull);
- iv.pop();
- iv.iconst(0);
- iv.mark(end);
- }
+ if (ifNull != null) {
+ Label end = new Label();
+ iv.goTo(end);
+ iv.mark(ifNull);
+ iv.pop();
+ iv.iconst(0);
+ iv.mark(end);
+ }
- if (first) {
- first = false;
- }
- else {
- iv.add(Type.INT_TYPE);
+ if (first) {
+ first = false;
+ }
+ else {
+ iv.add(Type.INT_TYPE);
+ }
}
}
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 1afd739..f90e2d1 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
@@ -6957,6 +6957,12 @@
}
@Test
+ @TestMetadata("companionDataObject.kt")
+ public void testCompanionDataObject() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
+ }
+
+ @Test
@TestMetadata("componentNamedComponent1.kt")
public void testComponentNamedComponent1() throws Exception {
runTest("compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt");
@@ -7029,9 +7035,21 @@
}
@Test
- @TestMetadata("dataObject.kt")
- public void testDataObject() throws Exception {
- runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
+ @TestMetadata("dataObjectDisabled.kt")
+ public void testDataObjectDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectEnabled.kt")
+ public void testDataObjectEnabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectLiteral.kt")
+ public void testDataObjectLiteral() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
}
@Test
@@ -7656,6 +7674,22 @@
}
@Nested
+ @TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
+ }
+
+ @Test
+ @TestMetadata("overrideEqualsAndHashCode.kt")
+ public void testOverrideEqualsAndHashCode() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
@TestDataPath("$PROJECT_ROOT")
public class DeclarationChecks {
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 600cb95..2839427 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
@@ -6957,6 +6957,12 @@
}
@Test
+ @TestMetadata("companionDataObject.kt")
+ public void testCompanionDataObject() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
+ }
+
+ @Test
@TestMetadata("componentNamedComponent1.kt")
public void testComponentNamedComponent1() throws Exception {
runTest("compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt");
@@ -7029,9 +7035,21 @@
}
@Test
- @TestMetadata("dataObject.kt")
- public void testDataObject() throws Exception {
- runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
+ @TestMetadata("dataObjectDisabled.kt")
+ public void testDataObjectDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectEnabled.kt")
+ public void testDataObjectEnabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectLiteral.kt")
+ public void testDataObjectLiteral() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
}
@Test
@@ -7656,6 +7674,22 @@
}
@Nested
+ @TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
+ }
+
+ @Test
+ @TestMetadata("overrideEqualsAndHashCode.kt")
+ public void testOverrideEqualsAndHashCode() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
@TestDataPath("$PROJECT_ROOT")
public class DeclarationChecks {
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 a442590..59bd1a5 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
@@ -16,6 +16,7 @@
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.diagnostics.deprecationError2
+import org.jetbrains.kotlin.diagnostics.error0
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
@@ -901,6 +902,7 @@
val TAILREC_ON_VIRTUAL_MEMBER_ERROR by error<KtNamedFunction>(PositioningStrategy.TAILREC_MODIFIER)
val NON_TAIL_RECURSIVE_CALL by warning<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
val TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED by warning<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
+ val DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE by error<KtNamedFunction>(PositioningStrategy.OVERRIDE_MODIFIER)
}
val FUN_INTERFACES by object : DiagnosticGroup("Fun interfaces") {
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 a221de1..db0c832 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
@@ -483,6 +483,7 @@
val TAILREC_ON_VIRTUAL_MEMBER_ERROR by error0<KtNamedFunction>(SourceElementPositioningStrategies.TAILREC_MODIFIER)
val NON_TAIL_RECURSIVE_CALL by warning0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED by warning0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
+ val DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE by error0<KtNamedFunction>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
// Fun interfaces
val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error0<KtExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt
index 561e5f3..5344e26 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt
@@ -54,6 +54,7 @@
FirTailrecFunctionChecker,
FirTopLevelFunctionsChecker,
FirMemberFunctionsChecker,
+ FirDataObjectContentChecker,
)
override val propertyCheckers: Set<FirPropertyChecker>
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDataObjectContentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDataObjectContentChecker.kt
new file mode 100644
index 0000000..13e23c2
--- /dev/null
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDataObjectContentChecker.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.fir.analysis.checkers.declaration
+
+import org.jetbrains.kotlin.KtFakeSourceElementKind
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
+import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
+import org.jetbrains.kotlin.fir.declarations.FirClass
+import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
+import org.jetbrains.kotlin.fir.types.isNullableAny
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.util.OperatorNameConventions
+
+object FirDataObjectContentChecker : FirSimpleFunctionChecker() {
+ override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
+ if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
+ val source = declaration.source
+ if (source == null || source.kind is KtFakeSourceElementKind) return
+
+ val containingClass = context.containingDeclarations.lastOrNull() as? FirClass ?: return
+ if (containingClass.classKind != ClassKind.OBJECT || !containingClass.hasModifier(KtTokens.DATA_KEYWORD)) return
+
+ if (
+ (declaration.name == OperatorNameConventions.HASH_CODE && declaration.valueParameters.isEmpty()) ||
+ (declaration.name == OperatorNameConventions.EQUALS &&
+ declaration.valueParameters.singleOrNull()?.returnTypeRef?.isNullableAny == true)
+ ) {
+ reporter.reportOn(source, FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE, context)
+ }
+ }
+}
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt
index 6336f08..e0b4850 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirModifierChecker.kt
@@ -7,6 +7,7 @@
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
+import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.Companion.classActualTargets
import org.jetbrains.kotlin.fir.analysis.checkers.FirModifier
@@ -27,6 +28,7 @@
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.lexer.KtTokens.DATA_KEYWORD
import org.jetbrains.kotlin.resolve.*
object FirModifierChecker : FirBasicDeclarationChecker() {
@@ -182,7 +184,11 @@
}
val set = map[modifierToken] ?: emptySet()
val checkResult = if (factory == FirErrors.WRONG_MODIFIER_TARGET) {
- actualTargets.none { it in set }
+ actualTargets.none { it in set } ||
+ // TODO: Implement some generic feature-checking mechanism
+ (modifierToken == DATA_KEYWORD
+ && actualTargets.contains(KotlinTarget.STANDALONE_OBJECT)
+ && !context.languageVersionSettings.supportsFeature(LanguageFeature.DataObjects))
} else {
actualTargets.any { it in set }
}
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 5d63b7c..c07319a 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
@@ -136,6 +136,7 @@
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_OVERRIDE_CONFLICT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_WITHOUT_PARAMETERS
+import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DECLARATION_CANT_BE_INLINED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_INSIDE_VALUE_CLASS
@@ -579,7 +580,6 @@
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_PARAMETER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_RETURN_TYPE
-import org.jetbrains.kotlin.psi.KtReferenceExpression
@Suppress("unused")
object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
@@ -1456,6 +1456,7 @@
map.put(TAILREC_ON_VIRTUAL_MEMBER_ERROR, "Tailrec is not allowed on open members")
map.put(NON_TAIL_RECURSIVE_CALL, "Recursive call is not a tail call")
map.put(TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, "Tail recursion optimization inside try/catch/finally is not supported")
+ map.put(DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE, "Data object cannot have a custom implementation of 'equals' or 'hashCode'")
// Fun interfaces
map.put(FUN_INTERFACE_CONSTRUCTOR_REFERENCE, "Functional/SAM interface constructor references are prohibited")
diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt
index 2c36821..bdd019c 100644
--- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt
+++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/DataClassMembersGenerator.kt
@@ -87,6 +87,7 @@
IrGeneratorContextBase(components.irBuiltIns),
components.symbolTable,
irClass,
+ irClass.kotlinFqName,
origin
) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
@@ -183,9 +184,6 @@
fun generate(klass: FirClass): List<FirDeclaration> {
val propertyParametersCount = irClass.primaryConstructor?.explicitParameters?.size ?: 0
val properties = irClass.properties.filter { it.backingField != null }.take(propertyParametersCount).toList()
- if (properties.isEmpty()) {
- return emptyList()
- }
val result = mutableListOf<FirDeclaration>()
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 e4f5d57..c4738d93 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
@@ -13649,6 +13649,46 @@
}
@Nested
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
+ }
+
+ @Test
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @Test
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @Test
+ @TestMetadata("multipleInstances.kt")
+ public void testMultipleInstances() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/multipleInstances.kt");
+ }
+
+ @Test
+ @TestMetadata("serialization.kt")
+ public void testSerialization() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/serialization.kt");
+ }
+
+ @Test
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
public class DeadCodeElimination {
diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt
index ce0846b..bf2e6bc 100644
--- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt
+++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt
@@ -8,6 +8,7 @@
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.KtNodeTypes.*
+import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.*
@@ -38,6 +39,7 @@
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.parsing.*
+import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -1078,8 +1080,10 @@
private val createParameterTypeRefWithSourceKind: (FirProperty, KtFakeSourceElementKind) -> FirTypeRef,
) {
fun generate() {
- generateComponentFunctions()
- generateCopyFunction()
+ if (classBuilder.classKind != ClassKind.OBJECT) {
+ generateComponentFunctions()
+ generateCopyFunction()
+ }
// Refer to (IR utils or FIR backend) DataClassMembersGenerator for generating equals, hashCode, and toString
}
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java
index 50499fd..8700376 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java
+++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java
@@ -737,6 +737,7 @@
DiagnosticFactory0<PsiElement> DATA_CLASS_WITHOUT_PARAMETERS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> DATA_CLASS_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> DATA_CLASS_NOT_PROPERTY_PARAMETER = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0<PsiElement> DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> CATCH_PARAMETER_WITH_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR);
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 42472a0..364ee63 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java
+++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java
@@ -1058,6 +1058,7 @@
MAP.put(DATA_CLASS_WITHOUT_PARAMETERS, "Data class must have at least one primary constructor parameter");
MAP.put(DATA_CLASS_VARARG_PARAMETER, "Primary constructor vararg parameters are forbidden for data classes");
MAP.put(DATA_CLASS_NOT_PROPERTY_PARAMETER, "Data class primary constructor must only have property (val / var) parameters");
+ MAP.put(DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE, "Data object cannot have a custom implementation of 'equals' or 'hashCode'");
MAP.put(CATCH_PARAMETER_WITH_DEFAULT_VALUE, "Catch clause parameter may not have a default value");
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt
index ace2226..c947604 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt
@@ -52,6 +52,7 @@
ValueParameterUsageInDefaultArgumentChecker,
CyclicAnnotationsChecker,
UnsupportedUntilRangeDeclarationChecker,
+ DataObjectContentChecker,
)
private val DEFAULT_CALL_CHECKERS = listOf(
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataClassDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataClassDeclarationChecker.kt
index 7821464..fe66ec8 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataClassDeclarationChecker.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataClassDeclarationChecker.kt
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -27,7 +28,7 @@
if (descriptor !is ClassDescriptor) return
if (declaration !is KtClassOrObject) return
- if (descriptor.isData) {
+ if (descriptor.isData && descriptor.kind == ClassKind.CLASS) {
if (descriptor.unsubstitutedPrimaryConstructor == null && descriptor.constructors.isNotEmpty()) {
declaration.nameIdentifier?.let { context.trace.report(Errors.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS.on(it)) }
}
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataObjectContentChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataObjectContentChecker.kt
new file mode 100644
index 0000000..66270fb
--- /dev/null
+++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/DataObjectContentChecker.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.resolve.checkers
+
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.diagnostics.Errors
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.util.OperatorNameConventions
+
+object DataObjectContentChecker : DeclarationChecker {
+ override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
+ if (descriptor !is FunctionDescriptor) return
+ if (descriptor.name != OperatorNameConventions.EQUALS && descriptor.name != OperatorNameConventions.HASH_CODE) return
+
+ val container = descriptor.containingDeclaration
+ if (container !is ClassDescriptor) return
+ if (!container.isData || container.kind != ClassKind.OBJECT) return
+
+ if (DescriptorUtils.getAllOverriddenDescriptors(descriptor).any(::isDeclaredInAny)) {
+ val target = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration
+ context.trace.report(Errors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE.on(target))
+ }
+ }
+
+ private fun isDeclaredInAny(descriptor: FunctionDescriptor): Boolean {
+ val container = descriptor.containingDeclaration
+ return container is ClassDescriptor && KotlinBuiltIns.isAny(container)
+ }
+}
diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt
index 1062505..e015771 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt
@@ -482,7 +482,7 @@
}
private fun addDataClassMethods(result: MutableCollection<DeclarationDescriptor>, location: LookupLocation) {
- if (!thisDescriptor.isData) return
+ if (!thisDescriptor.isData || thisDescriptor.kind != ClassKind.CLASS) return
if (getPrimaryConstructor() == null) return
diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt
index 839ed89..a783975 100644
--- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt
+++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/MethodsFromAnyGeneratorForLowerings.kt
@@ -14,17 +14,13 @@
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.*
-import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isArray
-import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
-import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
-import org.jetbrains.kotlin.ir.util.functions
-import org.jetbrains.kotlin.ir.util.isPrimitiveArray
+import org.jetbrains.kotlin.ir.util.*
class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irClass: IrClass, val origin: IrDeclarationOrigin) {
private fun IrClass.addSyntheticFunction(name: String, returnType: IrType) =
@@ -74,6 +70,7 @@
IrGeneratorContextBase(backendContext.irBuiltIns),
backendContext.ir.symbols.externalSymbolTable,
irClass,
+ irClass.kotlinFqName,
origin,
forbidDirectFieldAccess
) {
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt
index 23cb79c..8356fed 100644
--- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt
+++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt
@@ -353,6 +353,7 @@
enumClassPhase,
objectClassPhase,
+ readResolveForDataObjectsPhase,
staticInitializersPhase,
initializersPhase,
initializersCleanupPhase,
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReadResolveForDataObjectsLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReadResolveForDataObjectsLowering.kt
new file mode 100644
index 0000000..8827f87
--- /dev/null
+++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ReadResolveForDataObjectsLowering.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.backend.jvm.lower
+
+import org.jetbrains.kotlin.backend.common.ClassLoweringPass
+import org.jetbrains.kotlin.backend.common.ir.addDispatchReceiver
+import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
+import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
+import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
+import org.jetbrains.kotlin.config.LanguageFeature
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
+import org.jetbrains.kotlin.descriptors.Modality
+import org.jetbrains.kotlin.ir.builders.declarations.buildFun
+import org.jetbrains.kotlin.ir.builders.irExprBody
+import org.jetbrains.kotlin.ir.builders.irGetField
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
+import org.jetbrains.kotlin.ir.util.defaultType
+import org.jetbrains.kotlin.ir.util.fields
+import org.jetbrains.kotlin.ir.util.getAllSuperclasses
+import org.jetbrains.kotlin.ir.util.hasEqualFqName
+import org.jetbrains.kotlin.load.java.JvmAbi
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+
+internal val readResolveForDataObjectsPhase = makeIrFilePhase(
+ ::ReadResolveForDataObjectsLowering,
+ name = "ReadResolveForDataObjectsLowering",
+ description = "Generate readResolve for serializable data objects"
+)
+
+private class ReadResolveForDataObjectsLowering(val context: JvmBackendContext) : ClassLoweringPass {
+ override fun lower(irClass: IrClass) {
+ if (!context.state.languageVersionSettings.supportsFeature(LanguageFeature.DataObjects)) return
+
+ if (!irClass.isData || irClass.kind != ClassKind.OBJECT || !irClass.isSerializable()) return
+
+ context.irFactory.buildFun {
+ name = Name.identifier("readResolve")
+ modality = Modality.FINAL
+ origin = IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER
+ returnType = context.irBuiltIns.anyType
+ visibility = DescriptorVisibilities.PRIVATE
+ }.apply {
+ addDispatchReceiver { type = irClass.defaultType }
+ parent = irClass
+ body = context.createJvmIrBuilder(symbol).run {
+ val instanceField = irClass.fields.single { it.name.asString() == JvmAbi.INSTANCE_FIELD }
+ irExprBody(irGetField(null, instanceField))
+ }
+ irClass.declarations.add(this)
+ }
+ }
+}
+
+private val SERIALIZABLE_FQ_NAME = FqName("java.io.Serializable")
+
+private fun IrClass.isSerializable(): Boolean =
+ getAllSuperclasses().any { it.hasEqualFqName(SERIALIZABLE_FQ_NAME) }
diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt
index facbe96..813c516 100644
--- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt
+++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DataClassMembersGenerator.kt
@@ -74,8 +74,9 @@
val origin: IrDeclarationOrigin
) : DataClassMethodGenerator(ktClassOrObject, declarationGenerator.context.bindingContext) {
- private val irDataClassMembersGenerator =
- object : DataClassMembersGenerator(context, context.symbolTable, irClass, origin, generateBodies = generateBodies) {
+ private val irDataClassMembersGenerator = object : DataClassMembersGenerator(
+ context, context.symbolTable, irClass, ktClassOrObject.fqName, origin, generateBodies = generateBodies
+ ) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction =
declareSimpleFunction(startOffset, endOffset, origin, functionDescriptor)
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 8f07c29..c071eb2 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
@@ -19,6 +19,7 @@
import org.jetbrains.kotlin.ir.types.IrType
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
/**
@@ -32,6 +33,7 @@
val context: IrGeneratorContext,
val symbolTable: ReferenceSymbolTable,
val irClass: IrClass,
+ val fqName: FqName?,
val origin: IrDeclarationOrigin,
val forbidDirectFieldAccess: Boolean = false,
val generateBodies: Boolean = false
@@ -139,9 +141,9 @@
+irReturnTrue()
}
- fun generateHashCodeMethodBody(properties: List<IrProperty>) {
+ fun generateHashCodeMethodBody(properties: List<IrProperty>, constHashCode: Int) {
if (properties.isEmpty()) {
- +irReturn(irInt(0))
+ +irReturn(irInt(constHashCode))
return
} else if (properties.size == 1) {
+irReturn(getHashCodeOfProperty(properties[0]))
@@ -186,6 +188,10 @@
}
fun generateToStringMethodBody(properties: List<IrProperty>) {
+ if (properties.isEmpty() && irClass.kind == ClassKind.OBJECT) {
+ +irReturn(irString(irClass.name.asString()))
+ return
+ }
val irConcat = irConcat()
irConcat.addArgument(irString(irClass.classNameForToString() + "("))
var first = true
@@ -259,6 +265,7 @@
) {
MemberFunctionBuilder(startOffset, endOffset, declareSimpleFunction(startOffset, endOffset, function)).addToClass { irFunction ->
irFunction.buildWithScope {
+ irFunction.parent = irClass
generateSyntheticFunctionParameterDeclarations(irFunction)
body(irFunction)
}
@@ -344,14 +351,20 @@
// Entry for psi2ir
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
- generateHashCodeMethodBody(properties.map { getIrProperty(it) })
+ generateHashCodeMethodBody(
+ properties.map { getIrProperty(it) },
+ if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
+ )
}
}
// Entry for fir2ir
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
- generateHashCodeMethodBody(properties)
+ generateHashCodeMethodBody(
+ properties,
+ if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
+ )
}
}
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClass.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClass.kt
index 9c88e1c..f26366f 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClass.kt
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClass.kt
@@ -29,7 +29,6 @@
_stub?.isInterface() ?: (findChildByType<PsiElement>(KtTokens.INTERFACE_KEYWORD) != null)
fun isEnum(): Boolean = hasModifier(KtTokens.ENUM_KEYWORD)
- fun isData(): Boolean = hasModifier(KtTokens.DATA_KEYWORD)
fun isSealed(): Boolean = hasModifier(KtTokens.SEALED_KEYWORD)
fun isInner(): Boolean = hasModifier(KtTokens.INNER_KEYWORD)
fun isInline(): Boolean = hasModifier(KtTokens.INLINE_KEYWORD)
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt
index bbabbac..3cac8da 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtClassOrObject.kt
@@ -100,6 +100,8 @@
override fun isLocal(): Boolean = stub?.isLocal() ?: KtPsiUtil.isLocal(this)
+ fun isData(): Boolean = hasModifier(KtTokens.DATA_KEYWORD)
+
override fun getDeclarations(): List<KtDeclaration> = getBody()?.declarations.orEmpty()
override fun getPresentation(): ItemPresentation? = ItemPresentationProviders.getItemPresentation(this)
diff --git a/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt b/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt
index 9215c55..13ef31f 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt
+++ b/compiler/psi/src/org/jetbrains/kotlin/resolve/ModifierCheckerHelpers.kt
@@ -132,7 +132,8 @@
EXPECT_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
ACTUAL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables),
- FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion)
+ FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion),
+ DATA_KEYWORD to listOf(LanguageFeature.DataObjects)
)
val featureDependenciesTargets = mapOf(
@@ -141,7 +142,8 @@
LanguageFeature.LateinitTopLevelProperties to setOf(KotlinTarget.TOP_LEVEL_PROPERTY),
LanguageFeature.InlineClasses to setOf(KotlinTarget.CLASS_ONLY),
LanguageFeature.JvmInlineValueClasses to setOf(KotlinTarget.CLASS_ONLY),
- LanguageFeature.FunctionalInterfaceConversion to setOf(KotlinTarget.INTERFACE)
+ LanguageFeature.FunctionalInterfaceConversion to setOf(KotlinTarget.INTERFACE),
+ LanguageFeature.DataObjects to setOf(KotlinTarget.STANDALONE_OBJECT)
)
val defaultVisibilityTargets: EnumSet<KotlinTarget> = EnumSet.of(
@@ -204,7 +206,7 @@
KotlinTarget.LOCAL_VARIABLE,
KotlinTarget.BACKING_FIELD
),
- DATA_KEYWORD to EnumSet.of(KotlinTarget.CLASS_ONLY, KotlinTarget.LOCAL_CLASS),
+ DATA_KEYWORD to EnumSet.of(KotlinTarget.CLASS_ONLY, KotlinTarget.LOCAL_CLASS, KotlinTarget.STANDALONE_OBJECT),
INLINE_KEYWORD to EnumSet.of(
KotlinTarget.FUNCTION,
KotlinTarget.PROPERTY,
diff --git a/compiler/testData/codegen/box/dataObjects/equals.kt b/compiler/testData/codegen/box/dataObjects/equals.kt
new file mode 100644
index 0000000..90aa2ab
--- /dev/null
+++ b/compiler/testData/codegen/box/dataObjects/equals.kt
@@ -0,0 +1,15 @@
+// LANGUAGE: +DataObjects
+// WITH_STDLIB
+
+package com.example
+
+import kotlin.test.*
+
+data object DataObject
+
+fun box(): String {
+ assertEquals(DataObject, DataObject)
+ assertFalse(DataObject == null)
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/compiler/testData/codegen/box/dataObjects/hashCode.kt b/compiler/testData/codegen/box/dataObjects/hashCode.kt
new file mode 100644
index 0000000..ad2826e
--- /dev/null
+++ b/compiler/testData/codegen/box/dataObjects/hashCode.kt
@@ -0,0 +1,19 @@
+// LANGUAGE: +DataObjects
+// WITH_STDLIB
+
+package com.example
+
+import kotlin.test.*
+
+data object DataObject {
+ data object Inner
+}
+
+fun box(): String {
+ assertEquals(DataObject.hashCode(), DataObject.hashCode())
+ assertNotEquals(DataObject.hashCode(), DataObject.Inner.hashCode())
+ assertNotEquals(0, DataObject.hashCode())
+ assertNotEquals(0, DataObject.Inner.hashCode())
+
+ return "OK"
+}
diff --git a/compiler/testData/codegen/box/dataObjects/multipleInstances.kt b/compiler/testData/codegen/box/dataObjects/multipleInstances.kt
new file mode 100644
index 0000000..7b4c49e
--- /dev/null
+++ b/compiler/testData/codegen/box/dataObjects/multipleInstances.kt
@@ -0,0 +1,18 @@
+// LANGUAGE: +DataObjects
+// WITH_STDLIB
+// TARGET_BACKEND: JVM_IR
+
+import kotlin.test.*
+
+data object DataObject
+
+val doppelganger = DataObject::class.java.declaredConstructors[0].apply { isAccessible = true }.newInstance()
+
+fun box(): String {
+ assertFalse(DataObject === doppelganger)
+ assertEquals(DataObject, doppelganger)
+ assertEquals(DataObject.hashCode(), DataObject::class.java.cast(doppelganger).hashCode())
+
+ return "OK"
+}
+
diff --git a/compiler/testData/codegen/box/dataObjects/serialization.kt b/compiler/testData/codegen/box/dataObjects/serialization.kt
new file mode 100644
index 0000000..62eb0d0
--- /dev/null
+++ b/compiler/testData/codegen/box/dataObjects/serialization.kt
@@ -0,0 +1,19 @@
+// LANGUAGE: +DataObjects
+// WITH_STDLIB
+// TARGET_BACKEND: JVM_IR
+// FULL_JDK
+// CHECK_BYTECODE_LISTING
+
+import java.io.*
+
+data object NonSerializableDataObject
+
+data object SerializableDataObject: Serializable
+
+fun box(): String {
+ ByteArrayOutputStream().use { baos ->
+ ObjectOutputStream(baos).use { oos -> oos.writeObject(SerializableDataObject) }
+ ByteArrayInputStream(baos.toByteArray()).use { bais -> assert(java.io.ObjectInputStream(bais).readObject() === SerializableDataObject) }
+ }
+ return "OK"
+}
\ No newline at end of file
diff --git a/compiler/testData/codegen/box/dataObjects/serialization.txt b/compiler/testData/codegen/box/dataObjects/serialization.txt
new file mode 100644
index 0000000..429be3e
--- /dev/null
+++ b/compiler/testData/codegen/box/dataObjects/serialization.txt
@@ -0,0 +1,28 @@
+@kotlin.Metadata
+public final class NonSerializableDataObject {
+ // source: 'serialization.kt'
+ public final static @org.jetbrains.annotations.NotNull field INSTANCE: NonSerializableDataObject
+ static method <clinit>(): void
+ private method <init>(): void
+ public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
+ public method hashCode(): int
+ public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
+}
+
+@kotlin.Metadata
+public final class SerializableDataObject {
+ // source: 'serialization.kt'
+ public final static @org.jetbrains.annotations.NotNull field INSTANCE: SerializableDataObject
+ static method <clinit>(): void
+ private method <init>(): void
+ public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
+ public method hashCode(): int
+ private final method readResolve(): java.lang.Object
+ public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
+}
+
+@kotlin.Metadata
+public final class SerializationKt {
+ // source: 'serialization.kt'
+ public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
+}
diff --git a/compiler/testData/codegen/box/dataObjects/toString.kt b/compiler/testData/codegen/box/dataObjects/toString.kt
new file mode 100644
index 0000000..7819b09
--- /dev/null
+++ b/compiler/testData/codegen/box/dataObjects/toString.kt
@@ -0,0 +1,52 @@
+// LANGUAGE: +DataObjects
+// WITH_STDLIB
+
+package com.example
+
+import kotlin.test.*
+
+data object DataObject {
+ data object Nested
+}
+
+class Foo {
+ data object Inner
+}
+
+data object Declared {
+ override fun toString() = "Overriden"
+}
+
+open class WithFinalToString {
+ final override fun toString() = "FinalToString"
+}
+
+data object InheritedFromClassWithFinalToString: WithFinalToString()
+
+open class WithOpenToString {
+ override fun toString() = "OpenToString"
+}
+
+data object InheritedFromClassWithOpenToString: WithOpenToString()
+
+abstract class WithAbstractToString {
+ abstract override fun toString(): String
+}
+
+data object InheritedFromClassWithAbstractToString : WithAbstractToString()
+
+class C {
+ companion object CC
+}
+
+fun box(): String {
+ assertEquals("DataObject", DataObject.toString())
+ assertEquals("Nested", DataObject.Nested.toString())
+ assertEquals("Inner", Foo.Inner.toString())
+ assertEquals("Overriden", Declared.toString())
+ assertEquals("FinalToString", InheritedFromClassWithFinalToString.toString())
+ assertEquals("InheritedFromClassWithOpenToString", InheritedFromClassWithOpenToString.toString())
+ assertEquals("InheritedFromClassWithAbstractToString", InheritedFromClassWithAbstractToString.toString())
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt b/compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt
new file mode 100644
index 0000000..8d7726b
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt
@@ -0,0 +1,6 @@
+// FIR_IDENTICAL
+// LANGUAGE: +DataObjects
+
+class C {
+ companion <!WRONG_MODIFIER_TARGET!>data<!> object Object
+}
diff --git a/compiler/testData/diagnostics/tests/dataClasses/companionDataObject.txt b/compiler/testData/diagnostics/tests/dataClasses/companionDataObject.txt
new file mode 100644
index 0000000..cf0fb1a
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/companionDataObject.txt
@@ -0,0 +1,16 @@
+package
+
+public final class C {
+ public constructor C()
+ 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 data companion object Object {
+ private constructor Object()
+ 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/testData/diagnostics/tests/dataClasses/dataObject.kt b/compiler/testData/diagnostics/tests/dataClasses/dataObject.kt
deleted file mode 100644
index 2c99760..0000000
--- a/compiler/testData/diagnostics/tests/dataClasses/dataObject.kt
+++ /dev/null
@@ -1,2 +0,0 @@
-// FIR_IDENTICAL
-<!WRONG_MODIFIER_TARGET!>data<!> object Object<!CONSTRUCTOR_IN_OBJECT!>(val x: Int, val y: Int)<!>
diff --git a/compiler/testData/diagnostics/tests/dataClasses/dataObject.txt b/compiler/testData/diagnostics/tests/dataClasses/dataObject.txt
deleted file mode 100644
index b270a54..0000000
--- a/compiler/testData/diagnostics/tests/dataClasses/dataObject.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-package
-
-public data object Object {
- private constructor Object(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int)
- public final val x: kotlin.Int
- public final val y: kotlin.Int
- public final operator /*synthesized*/ fun component1(): kotlin.Int
- public final operator /*synthesized*/ fun component2(): kotlin.Int
- public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ..., /*1*/ y: kotlin.Int = ...): Object
- 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/testData/diagnostics/tests/dataClasses/dataObjectDisabled.fir.kt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.fir.kt
new file mode 100644
index 0000000..dfa99ea
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.fir.kt
@@ -0,0 +1,2 @@
+// LANGUAGE: -DataObjects
+<!WRONG_MODIFIER_TARGET!>data<!> object Object
diff --git a/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt
new file mode 100644
index 0000000..782e17f
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt
@@ -0,0 +1,2 @@
+// LANGUAGE: -DataObjects
+<!UNSUPPORTED_FEATURE!>data<!> object Object
diff --git a/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.txt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.txt
new file mode 100644
index 0000000..71427a2
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.txt
@@ -0,0 +1,9 @@
+package
+
+public data object Object {
+ private constructor Object()
+ 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/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt
new file mode 100644
index 0000000..4046975
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt
@@ -0,0 +1,3 @@
+// LANGUAGE: +DataObjects
+// FIR_IDENTICAL
+data object Object
diff --git a/compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.txt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.txt
new file mode 100644
index 0000000..71427a2
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.txt
@@ -0,0 +1,9 @@
+package
+
+public data object Object {
+ private constructor Object()
+ 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/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt
new file mode 100644
index 0000000..c80e42f
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt
@@ -0,0 +1,10 @@
+// FIR_IDENTICAL
+// LANGUAGE: +DataObjects
+
+interface I {
+ fun foo()
+}
+
+val o = <!UNRESOLVED_REFERENCE!>data<!><!SYNTAX!><!> object<!SYNTAX!><!>: I {
+ override fun foo() {}
+}
diff --git a/compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.txt b/compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.txt
new file mode 100644
index 0000000..f624cf4
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.txt
@@ -0,0 +1,18 @@
+package
+
+public val o: [Error type: Not found recorded type for data]
+
+public object `<no name provided>` : I {
+ private constructor `<no name provided>`()
+ public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ fun foo(): kotlin.Unit
+ public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
+}
+
+public interface I {
+ public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public abstract fun foo(): kotlin.Unit
+ public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
+}
diff --git a/compiler/testData/diagnostics/tests/dataClasses/strange.kt b/compiler/testData/diagnostics/tests/dataClasses/strange.kt
index a181e42..0998adc 100644
--- a/compiler/testData/diagnostics/tests/dataClasses/strange.kt
+++ b/compiler/testData/diagnostics/tests/dataClasses/strange.kt
@@ -3,8 +3,8 @@
B(2)
}
-<!WRONG_MODIFIER_TARGET!>data<!> object <!DATA_CLASS_WITHOUT_PARAMETERS!>Second<!>
+<!UNSUPPORTED_FEATURE!>data<!> object Second
-<!WRONG_MODIFIER_TARGET!>data<!> interface <!DATA_CLASS_WITHOUT_PARAMETERS!>Third<!>
+<!WRONG_MODIFIER_TARGET!>data<!> interface Third
-<!WRONG_MODIFIER_TARGET!>data<!> annotation class Fourth(val x: Int)
\ No newline at end of file
+<!WRONG_MODIFIER_TARGET!>data<!> annotation class Fourth(val x: Int)
diff --git a/compiler/testData/diagnostics/tests/dataClasses/strange.txt b/compiler/testData/diagnostics/tests/dataClasses/strange.txt
index 7531d4d..57fd6e3 100644
--- a/compiler/testData/diagnostics/tests/dataClasses/strange.txt
+++ b/compiler/testData/diagnostics/tests/dataClasses/strange.txt
@@ -11,8 +11,6 @@
public final val x: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: First): kotlin.Int
- public final operator /*synthesized*/ fun component1(): kotlin.Int
- public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ...): First
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<First!>!
@@ -27,8 +25,6 @@
public final data annotation class Fourth : kotlin.Annotation {
public constructor Fourth(/*0*/ x: kotlin.Int)
public final val x: kotlin.Int
- public final operator /*synthesized*/ fun component1(): kotlin.Int
- public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ...): Fourth
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
@@ -36,7 +32,6 @@
public data object Second {
private constructor Second()
- public final /*synthesized*/ fun copy(): Second
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
@@ -47,3 +42,4 @@
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/dataObjects/overrideEqualsAndHashCode.kt b/compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt
new file mode 100644
index 0000000..a3a780b
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt
@@ -0,0 +1,44 @@
+// FIR_IDENTICAL
+// LANGUAGE: +DataObjects
+
+data object Override {
+ <!DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE!>override<!> fun equals(other: Any?): Boolean {
+ return true
+ }
+
+ <!DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE!>override<!> fun hashCode(): Int {
+ return 1
+ }
+}
+
+open class Base {
+ open fun hashCode(x: Int) = x
+}
+
+data object NoOverride: Base() {
+ fun equals(other: Any?, tag: Int): Boolean {
+ return true
+ }
+
+ fun hashCode(param: String): Int {
+ return 1
+ }
+
+ override fun hashCode(x: Int) = x + 1
+}
+
+open class Super {
+ override fun equals(other: Any?): Boolean {
+ return super.equals(other)
+ }
+
+ override fun hashCode(): Int {
+ return 1
+ }
+}
+
+data object OverridenInSuper: Super() {
+ <!DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE!>override<!> fun equals(other: Any?): Boolean {
+ return super.equals(other)
+ }
+}
diff --git a/compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.txt b/compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.txt
new file mode 100644
index 0000000..49dfff5
--- /dev/null
+++ b/compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.txt
@@ -0,0 +1,40 @@
+package
+
+public open class Base {
+ public constructor Base()
+ public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
+ public open fun hashCode(/*0*/ x: kotlin.Int): kotlin.Int
+ public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
+}
+
+public data object NoOverride : Base {
+ private constructor NoOverride()
+ public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public final fun equals(/*0*/ other: kotlin.Any?, /*1*/ tag: kotlin.Int): kotlin.Boolean
+ public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ fun hashCode(/*0*/ x: kotlin.Int): kotlin.Int
+ public final fun hashCode(/*0*/ param: kotlin.String): kotlin.Int
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
+public data object Override {
+ private constructor Override()
+ 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 data object OverridenInSuper : Super {
+ private constructor OverridenInSuper()
+ 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
+}
+
+public open class Super {
+ public constructor Super()
+ public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ fun hashCode(): kotlin.Int
+ public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
+}
diff --git a/compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.kt b/compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.kt
new file mode 100644
index 0000000..a4d5363
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.kt
@@ -0,0 +1,16 @@
+// !LANGUAGE: +DataObjects
+// TARGET_BACKEND: JVM_IR
+
+import java.io.Serializable
+
+data <!CONFLICTING_JVM_DECLARATIONS!>object A<!> : Serializable {
+ <!CONFLICTING_JVM_DECLARATIONS!>private fun readResolve(): Any<!> = this
+}
+
+data object B : Serializable {
+ private fun readResolve(): B = this
+}
+
+data object C {
+ private fun readResolve(): Any = this
+}
diff --git a/compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.txt b/compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.txt
new file mode 100644
index 0000000..ec615f4
--- /dev/null
+++ b/compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.txt
@@ -0,0 +1,26 @@
+package
+
+public data object A : java.io.Serializable {
+ private constructor A()
+ public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
+ private final fun readResolve(): kotlin.Any
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
+public data object B : java.io.Serializable {
+ private constructor B()
+ public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
+ private final fun readResolve(): B
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
+public data object C {
+ private constructor C()
+ public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
+ public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
+ private final fun readResolve(): kotlin.Any
+ public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
+}
+
diff --git a/compiler/testData/ir/irText/declarations/parameters/dataClassMembers.fir.kt.txt b/compiler/testData/ir/irText/declarations/parameters/dataClassMembers.fir.kt.txt
index 99ac9bd..6d3ce06 100644
--- a/compiler/testData/ir/irText/declarations/parameters/dataClassMembers.fir.kt.txt
+++ b/compiler/testData/ir/irText/declarations/parameters/dataClassMembers.fir.kt.txt
@@ -56,3 +56,4 @@
}
}
+
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 7337e3d..0117bd2 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
@@ -6963,6 +6963,12 @@
}
@Test
+ @TestMetadata("companionDataObject.kt")
+ public void testCompanionDataObject() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
+ }
+
+ @Test
@TestMetadata("componentNamedComponent1.kt")
public void testComponentNamedComponent1() throws Exception {
runTest("compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt");
@@ -7035,9 +7041,21 @@
}
@Test
- @TestMetadata("dataObject.kt")
- public void testDataObject() throws Exception {
- runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
+ @TestMetadata("dataObjectDisabled.kt")
+ public void testDataObjectDisabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectEnabled.kt")
+ public void testDataObjectEnabled() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
+ }
+
+ @Test
+ @TestMetadata("dataObjectLiteral.kt")
+ public void testDataObjectLiteral() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
}
@Test
@@ -7662,6 +7680,22 @@
}
@Nested
+ @TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
+ }
+
+ @Test
+ @TestMetadata("overrideEqualsAndHashCode.kt")
+ public void testOverrideEqualsAndHashCode() throws Exception {
+ runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
@TestDataPath("$PROJECT_ROOT")
public class DeclarationChecks {
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJvmIrBackendGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJvmIrBackendGenerated.java
index 4d18425..c1de6f2 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJvmIrBackendGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithJvmIrBackendGenerated.java
@@ -62,6 +62,22 @@
}
@Nested
+ @TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects"), Pattern.compile("^(.+)\\.kts?$"), null, TargetBackend.JVM_IR, true);
+ }
+
+ @Test
+ @TestMetadata("customReadResolve.kt")
+ public void testCustomReadResolve() throws Exception {
+ runTest("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/duplicateJvmSignature")
@TestDataPath("$PROJECT_ROOT")
public class DuplicateJvmSignature {
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithOldJvmBackendGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithOldJvmBackendGenerated.java
index 038a0d5..573d9c4 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithOldJvmBackendGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsTestWithOldJvmBackendGenerated.java
@@ -62,6 +62,16 @@
}
@Nested
+ @TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_OLD, true);
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/duplicateJvmSignature")
@TestDataPath("$PROJECT_ROOT")
public class DuplicateJvmSignature {
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java
index 310bcc0..bf84b14 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java
@@ -13529,6 +13529,34 @@
}
@Nested
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
+ }
+
+ @Test
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @Test
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @Test
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
public class DeadCodeElimination {
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 b3c8351..7cbcafd 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
@@ -13649,6 +13649,46 @@
}
@Nested
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
+ }
+
+ @Test
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @Test
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @Test
+ @TestMetadata("multipleInstances.kt")
+ public void testMultipleInstances() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/multipleInstances.kt");
+ }
+
+ @Test
+ @TestMetadata("serialization.kt")
+ public void testSerialization() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/serialization.kt");
+ }
+
+ @Test
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
public class DeadCodeElimination {
diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
index fed2f732..aaa8874 100644
--- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
+++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
@@ -10981,6 +10981,34 @@
}
}
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class DataObjects extends AbstractLightAnalysisModeTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
+ }
+
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
index be45645..c96fe94 100644
--- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
+++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt
@@ -263,6 +263,7 @@
RefineTypeCheckingOnAssignmentsToJavaFields(KOTLIN_1_8, kind = BUG_FIX),
RangeUntilOperator(KOTLIN_1_8), // KT-15613
GenericInlineClassParameter(sinceVersion = KOTLIN_1_8, kind = UNSTABLE_FEATURE), // KT-32162
+ DataObjects(KOTLIN_1_8), // KT-4107
// 1.9
diff --git a/core/compiler.common/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt b/core/compiler.common/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt
index b9525cb1..61bf33b 100644
--- a/core/compiler.common/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt
+++ b/core/compiler.common/src/org/jetbrains/kotlin/descriptors/annotations/KotlinTarget.kt
@@ -34,8 +34,9 @@
// includes only top level classes and nested/inner classes (but not enums, objects, interfaces and local classes)
CLASS_ONLY("class", false),
- // does not include OBJECT_LITERAL but DOES include COMPANION_OBJECT
+ // does not include OBJECT_LITERAL but DOES include both STANDALONE_OBJECT and COMPANION_OBJECT
OBJECT("object", false),
+ STANDALONE_OBJECT("standalone object", false),
COMPANION_OBJECT("companion object", false),
INTERFACE("interface", false),
ENUM_CLASS("enum class", false),
@@ -84,7 +85,7 @@
val LOCAL_CLASS_LIST = listOf(LOCAL_CLASS, CLASS)
val CLASS_LIST = listOf(CLASS_ONLY, CLASS)
val COMPANION_OBJECT_LIST = listOf(COMPANION_OBJECT, OBJECT, CLASS)
- val OBJECT_LIST = listOf(OBJECT, CLASS)
+ val OBJECT_LIST = listOf(STANDALONE_OBJECT, OBJECT, CLASS)
val INTERFACE_LIST = listOf(INTERFACE, CLASS)
val ENUM_LIST = listOf(ENUM_CLASS, CLASS)
val ENUM_ENTRY_LIST = listOf(ENUM_ENTRY, PROPERTY, FIELD)
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java
index c61fa00..4808038 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java
@@ -10117,6 +10117,34 @@
}
@Nested
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
+ }
+
+ @Test
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @Test
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @Test
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
public class DeadCodeElimination {
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java
index 7018d4d..8ee5a65 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java
@@ -10159,6 +10159,34 @@
}
@Nested
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
+ }
+
+ @Test
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @Test
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @Test
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
public class DeadCodeElimination {
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
index 623c73f..108acbd 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
@@ -8996,6 +8996,34 @@
}
}
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class DataObjects extends AbstractIrCodegenBoxWasmTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
+ }
+
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsEqualsHashcodeToStringGenerator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsEqualsHashcodeToStringGenerator.java
index 22ada7d..f71b755 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsEqualsHashcodeToStringGenerator.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/JsEqualsHashcodeToStringGenerator.java
@@ -16,7 +16,9 @@
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
+import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtClassOrObject;
+import org.jetbrains.kotlin.psi.KtObjectDeclaration;
import org.jetbrains.kotlin.psi.KtParameter;
import org.jetbrains.kotlin.resolve.source.PsiSourceElementKt;
@@ -35,6 +37,11 @@
@Override
public void generateToStringMethod(@NotNull FunctionDescriptor function, @NotNull List<? extends PropertyDescriptor> classProperties) {
+ if (getDeclaration() instanceof KtObjectDeclaration) {
+ generateJsMethod(function).getBody().getStatements().add(new JsReturn(new JsStringLiteral(getDeclaration().getName())));
+ return;
+ }
+
// TODO: relax this limitation, with the data generation logic fixed.
assert !classProperties.isEmpty();
JsFunction functionObj = generateJsMethod(function);
@@ -65,6 +72,14 @@
@Override
public void generateHashCodeMethod(@NotNull FunctionDescriptor function, @NotNull List<? extends PropertyDescriptor> classProperties) {
JsFunction functionObj = generateJsMethod(function);
+ if (classProperties.isEmpty()) {
+ FqName fqName = getDeclaration().getFqName();
+ JsExpression returnExpression = new JsIntLiteral(fqName != null ? fqName.hashCode() : 0);
+ JsReturn returnStatement = new JsReturn(returnExpression);
+ returnStatement.setSource(getDeclaration());
+ functionObj.getBody().getStatements().add(returnStatement);
+ return;
+ }
List<JsStatement> statements = functionObj.getBody().getStatements();
@@ -92,8 +107,8 @@
@Override
public void generateEqualsMethod(@NotNull FunctionDescriptor function, @NotNull List<? extends PropertyDescriptor> classProperties) {
- assert !classProperties.isEmpty();
JsFunction functionObj = generateJsMethod(function);
+
JsFunctionScope funScope = functionObj.getScope();
JsName paramName = funScope.declareName("other");
@@ -120,9 +135,8 @@
fieldChain = and(fieldChain, next);
}
}
- assert fieldChain != null;
- JsExpression returnExpression = or(referenceEqual, and(isNotNull, and(otherIsObject, and(prototypeEqual, fieldChain))));
+ JsExpression returnExpression = or(referenceEqual, and(isNotNull, and(otherIsObject, fieldChain != null ? and(prototypeEqual, fieldChain) : prototypeEqual)));
JsReturn returnStatement = new JsReturn(returnExpression);
returnStatement.setSource(getDeclaration());
functionObj.getBody().getStatements().add(returnStatement);
diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java
index 9dd93e7..dfa5f1b 100644
--- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java
+++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java
@@ -11103,6 +11103,36 @@
}
@Nested
+ @TestMetadata("compiler/testData/codegen/box/dataObjects")
+ @TestDataPath("$PROJECT_ROOT")
+ @Tag("codegen")
+ @UseExtTestCaseGroupProvider()
+ public class DataObjects {
+ @Test
+ public void testAllFilesPresentInDataObjects() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
+ }
+
+ @Test
+ @TestMetadata("equals.kt")
+ public void testEquals() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
+ }
+
+ @Test
+ @TestMetadata("hashCode.kt")
+ public void testHashCode() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
+ }
+
+ @Test
+ @TestMetadata("toString.kt")
+ public void testToString() throws Exception {
+ runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
+ }
+ }
+
+ @Nested
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
@TestDataPath("$PROJECT_ROOT")
@Tag("codegen")
diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.kt b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.kt
index 47748c4..3c90a9a 100644
--- a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.kt
+++ b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.kt
@@ -32,7 +32,7 @@
annotation class Type
@Type
- data class Group(s: String)
+ data class Group(var s: String)
}
class Foo {
diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.txt b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.txt
index d59a60b..b954ee0 100644
--- a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.txt
+++ b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2.txt
@@ -100,6 +100,8 @@
@kotlin.Metadata()
@Experiment.Type()
public static final class Group {
+ @org.jetbrains.annotations.NotNull()
+ private java.lang.String s;
@org.jetbrains.annotations.NotNull()
public final Experiment.Group copy(@org.jetbrains.annotations.NotNull()
@@ -107,10 +109,41 @@
return null;
}
+ @java.lang.Override()
+ public boolean equals(@org.jetbrains.annotations.Nullable()
+ java.lang.Object other) {
+ return false;
+ }
+
+ @java.lang.Override()
+ public int hashCode() {
+ return 0;
+ }
+
+ @org.jetbrains.annotations.NotNull()
+ @java.lang.Override()
+ public java.lang.String toString() {
+ return null;
+ }
+
public Group(@org.jetbrains.annotations.NotNull()
java.lang.String s) {
super();
}
+
+ @org.jetbrains.annotations.NotNull()
+ public final java.lang.String component1() {
+ return null;
+ }
+
+ @org.jetbrains.annotations.NotNull()
+ public final java.lang.String getS() {
+ return null;
+ }
+
+ public final void setS(@org.jetbrains.annotations.NotNull()
+ java.lang.String p0) {
+ }
}
}
diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2_ir.txt b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2_ir.txt
index 3805239..cf445ac 100644
--- a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2_ir.txt
+++ b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClasses2_ir.txt
@@ -100,6 +100,8 @@
@kotlin.Metadata()
@Experiment.Type()
public static final class Group {
+ @org.jetbrains.annotations.NotNull()
+ private java.lang.String s;
public Group(@org.jetbrains.annotations.NotNull()
java.lang.String s) {
@@ -107,6 +109,20 @@
}
@org.jetbrains.annotations.NotNull()
+ public final java.lang.String getS() {
+ return null;
+ }
+
+ public final void setS(@org.jetbrains.annotations.NotNull()
+ java.lang.String p0) {
+ }
+
+ @org.jetbrains.annotations.NotNull()
+ public final java.lang.String component1() {
+ return null;
+ }
+
+ @org.jetbrains.annotations.NotNull()
public final Experiment.Group copy(@org.jetbrains.annotations.NotNull()
java.lang.String s) {
return null;
diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.kt b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.kt
index 22d71e1..e9223f4 100644
--- a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.kt
+++ b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.kt
@@ -38,7 +38,7 @@
annotation class Type
@Type
- data class Group(s: String)
+ data class Group(val s: String)
}
class Foo {
diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.txt b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.txt
index d720558..e743d18 100644
--- a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.txt
+++ b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage.txt
@@ -103,6 +103,8 @@
@kotlin.Metadata()
@test.Experiment.Type()
public static final class Group {
+ @org.jetbrains.annotations.NotNull()
+ private final java.lang.String s = null;
@org.jetbrains.annotations.NotNull()
public final test.Experiment.Group copy(@org.jetbrains.annotations.NotNull()
@@ -110,10 +112,37 @@
return null;
}
+ @java.lang.Override()
+ public boolean equals(@org.jetbrains.annotations.Nullable()
+ java.lang.Object other) {
+ return false;
+ }
+
+ @java.lang.Override()
+ public int hashCode() {
+ return 0;
+ }
+
+ @org.jetbrains.annotations.NotNull()
+ @java.lang.Override()
+ public java.lang.String toString() {
+ return null;
+ }
+
public Group(@org.jetbrains.annotations.NotNull()
java.lang.String s) {
super();
}
+
+ @org.jetbrains.annotations.NotNull()
+ public final java.lang.String component1() {
+ return null;
+ }
+
+ @org.jetbrains.annotations.NotNull()
+ public final java.lang.String getS() {
+ return null;
+ }
}
}
diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage_ir.txt b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage_ir.txt
index 2150b45..2878030 100644
--- a/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage_ir.txt
+++ b/plugins/kapt3/kapt3-compiler/testData/converter/nestedClassesNonRootPackage_ir.txt
@@ -103,6 +103,8 @@
@kotlin.Metadata()
@test.Experiment.Type()
public static final class Group {
+ @org.jetbrains.annotations.NotNull()
+ private final java.lang.String s = null;
public Group(@org.jetbrains.annotations.NotNull()
java.lang.String s) {
@@ -110,6 +112,16 @@
}
@org.jetbrains.annotations.NotNull()
+ public final java.lang.String getS() {
+ return null;
+ }
+
+ @org.jetbrains.annotations.NotNull()
+ public final java.lang.String component1() {
+ return null;
+ }
+
+ @org.jetbrains.annotations.NotNull()
public final test.Experiment.Group copy(@org.jetbrains.annotations.NotNull()
java.lang.String s) {
return null;