diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 23bb2e07731..543094cba25 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -364,6 +364,7 @@ public interface Errors { DiagnosticFactory1 RESERVED_MEMBER_INSIDE_INLINE_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 INNER_CLASS_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 VALUE_CLASS_CANNOT_BE_CLONEABLE = DiagnosticFactory0.create(ERROR); // Result class 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 997874e2a62..119c9b782c9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -719,6 +719,7 @@ public class DefaultErrorMessages { MAP.put(RESERVED_MEMBER_INSIDE_INLINE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING); MAP.put(SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, "Secondary constructors with bodies are reserved for for future releases"); MAP.put(INNER_CLASS_INSIDE_INLINE_CLASS, "Inline class cannot have inner classes"); + MAP.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable"); MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type"); MAP.put(RESULT_CLASS_WITH_NULLABLE_OPERATOR, "Expression of type 'kotlin.Result' cannot be used as a left operand of ''{0}''", STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt index e17df9f33b1..b122fefe91b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt @@ -6,18 +6,26 @@ package org.jetbrains.kotlin.resolve.checkers import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.modalityModifier import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.utils.addToStdlib.safeAs +private val javaLangCloneable = FqNameUnsafe("java.lang.Cloneable") + object InlineClassDeclarationChecker : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { if (declaration !is KtClass) return @@ -87,6 +95,13 @@ object InlineClassDeclarationChecker : DeclarationChecker { } } } + + if (descriptor.getAllSuperClassifiers().any { + it.fqNameUnsafe == StandardNames.FqNames.cloneable || it.fqNameUnsafe == javaLangCloneable } + ) { + trace.report(Errors.VALUE_CLASS_CANNOT_BE_CLONEABLE.on(inlineOrValueKeyword)) + return + } } private fun KotlinType.isInapplicableParameterType() = diff --git a/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt new file mode 100644 index 00000000000..fc93e2f2698 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt @@ -0,0 +1,31 @@ +// !LANGUAGE: +InlineClasses +// !DIAGNOSTICS: -UNUSED_PARAMETER, -PLATFORM_CLASS_MAPPED_TO_KOTLIN +// WITH_RUNTIME + +package kotlin.jvm + +annotation class JvmInline + +inline class IC0(val a: Any): Cloneable + +@JvmInline +value class VC0(val a: Any): Cloneable + +inline class IC1(val a: Any): java.lang.Cloneable + +@JvmInline +value class VC1(val a: Any): java.lang.Cloneable + +interface MyCloneable1: Cloneable + +inline class IC2(val a: Any): MyCloneable1 + +@JvmInline +value class VC2(val a: Any): MyCloneable1 + +interface MyCloneable2: java.lang.Cloneable + +inline class IC3(val a: Any): MyCloneable2 + +@JvmInline +value class VC3(val a: Any): MyCloneable2 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.txt b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.txt new file mode 100644 index 00000000000..e854a208261 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.txt @@ -0,0 +1,95 @@ +package + +package kotlin { + + package kotlin.jvm { + + public final inline class IC0 : kotlin.Cloneable { + public constructor IC0(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final inline class IC1 : java.lang.Cloneable { + public constructor IC1(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final inline class IC2 : kotlin.jvm.MyCloneable1 { + public constructor IC2(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final inline class IC3 : kotlin.jvm.MyCloneable2 { + public constructor IC3(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final annotation class JvmInline : kotlin.Annotation { + public constructor JvmInline() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface MyCloneable1 : kotlin.Cloneable { + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface MyCloneable2 : java.lang.Cloneable { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC0 : kotlin.Cloneable { + public constructor VC0(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC1 : java.lang.Cloneable { + public constructor VC1(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC2 : kotlin.jvm.MyCloneable1 { + public constructor VC2(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC3 : kotlin.jvm.MyCloneable2 { + public constructor VC3(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + 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/testsWithJvmBackend/valueClasses/cloneable.kt b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.kt new file mode 100644 index 00000000000..fc93e2f2698 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.kt @@ -0,0 +1,31 @@ +// !LANGUAGE: +InlineClasses +// !DIAGNOSTICS: -UNUSED_PARAMETER, -PLATFORM_CLASS_MAPPED_TO_KOTLIN +// WITH_RUNTIME + +package kotlin.jvm + +annotation class JvmInline + +inline class IC0(val a: Any): Cloneable + +@JvmInline +value class VC0(val a: Any): Cloneable + +inline class IC1(val a: Any): java.lang.Cloneable + +@JvmInline +value class VC1(val a: Any): java.lang.Cloneable + +interface MyCloneable1: Cloneable + +inline class IC2(val a: Any): MyCloneable1 + +@JvmInline +value class VC2(val a: Any): MyCloneable1 + +interface MyCloneable2: java.lang.Cloneable + +inline class IC3(val a: Any): MyCloneable2 + +@JvmInline +value class VC3(val a: Any): MyCloneable2 \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.txt b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.txt new file mode 100644 index 00000000000..e854a208261 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.txt @@ -0,0 +1,95 @@ +package + +package kotlin { + + package kotlin.jvm { + + public final inline class IC0 : kotlin.Cloneable { + public constructor IC0(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final inline class IC1 : java.lang.Cloneable { + public constructor IC1(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final inline class IC2 : kotlin.jvm.MyCloneable1 { + public constructor IC2(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final inline class IC3 : kotlin.jvm.MyCloneable2 { + public constructor IC3(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + public final annotation class JvmInline : kotlin.Annotation { + public constructor JvmInline() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface MyCloneable1 : kotlin.Cloneable { + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public interface MyCloneable2 : java.lang.Cloneable { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC0 : kotlin.Cloneable { + public constructor VC0(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC1 : java.lang.Cloneable { + public constructor VC1(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC2 : kotlin.jvm.MyCloneable1 { + public constructor VC2(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + + @kotlin.jvm.JvmInline public final value class VC3 : kotlin.jvm.MyCloneable2 { + public constructor VC3(/*0*/ a: kotlin.Any) + public final val a: kotlin.Any + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String + } + } +} diff --git a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJvmIrBackendGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJvmIrBackendGenerated.java index a2f5b30b304..4a14b006062 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJvmIrBackendGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJvmIrBackendGenerated.java @@ -583,4 +583,27 @@ public class DiagnosticsTestWithJvmIrBackendGenerated extends AbstractDiagnostic } } } + + @TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValueClasses extends AbstractDiagnosticsTestWithJvmIrBackend { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInValueClasses() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("cloneable.kt") + public void testCloneable() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.kt"); + } + + @TestMetadata("cloneable.fir.kt") + public void testCloneable_fir() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt"); + } + } } diff --git a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithOldJvmBackendGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithOldJvmBackendGenerated.java index 6b90997e0e1..1e3f91bf185 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithOldJvmBackendGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/checkers/DiagnosticsTestWithOldJvmBackendGenerated.java @@ -573,4 +573,27 @@ public class DiagnosticsTestWithOldJvmBackendGenerated extends AbstractDiagnosti } } } + + @TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValueClasses extends AbstractDiagnosticsTestWithOldJvmBackend { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_OLD, testDataFilePath); + } + + public void testAllFilesPresentInValueClasses() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_OLD, true); + } + + @TestMetadata("cloneable.kt") + public void testCloneable() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.kt"); + } + + @TestMetadata("cloneable.fir.kt") + public void testCloneable_fir() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt"); + } + } }