From bef50c0342d54d126c29d8e5627e44e29be8e869 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Wed, 25 Nov 2020 18:42:51 +0300 Subject: [PATCH] Correct descriptor shape for @JvmRecord annotated classes This commit adds relevant functions: hashCode, toString, equals (if the class is not a data class) And supertype j.l.Record It only affects descriptor contents, i.e. works for FE ^KT-43677 In Progress --- .../jvm/JvmAdditionalClassPartsProvider.kt | 49 +++++++++++++++++++ .../jvm/platform/JvmPlatformConfigurator.kt | 1 + .../kotlin/resolve/DescriptorResolver.java | 6 ++- .../kotlin/resolve/FunctionsFromAny.kt | 29 +++++++++-- .../kotlin/resolve/lazy/LazyClassContext.kt | 1 + .../kotlin/resolve/lazy/ResolveSession.java | 13 +++++ .../lazy/descriptors/LazyClassMemberScope.kt | 25 ++-------- .../expressions/LocalClassifierAnalyzer.kt | 11 +++-- .../jvmRecord/jvmRecordDescriptorStructure.kt | 11 +++++ .../jvmRecordDescriptorStructure.txt | 27 ++++++++++ .../DiagnosticsWithJdk15TestGenerated.java | 5 ++ .../resolve/AdditionalClassPartsProvider.kt | 44 +++++++++++++++++ 12 files changed, 193 insertions(+), 29 deletions(-) create mode 100644 compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAdditionalClassPartsProvider.kt create mode 100644 compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt create mode 100644 compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.txt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/resolve/AdditionalClassPartsProvider.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAdditionalClassPartsProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAdditionalClassPartsProvider.kt new file mode 100644 index 00000000000..a3021869654 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/JvmAdditionalClassPartsProvider.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2020 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.jvm + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.AdditionalClassPartsProvider +import org.jetbrains.kotlin.resolve.FunctionsFromAny +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass +import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord +import org.jetbrains.kotlin.types.KotlinType + +class JvmAdditionalClassPartsProvider : AdditionalClassPartsProvider { + override fun generateAdditionalMethods( + thisDescriptor: ClassDescriptor, + result: MutableCollection, + name: Name, + location: LookupLocation, + fromSupertypes: Collection + ) { + if (thisDescriptor.isJvmRecord()) { + FunctionsFromAny.addFunctionFromAnyIfNeeded(thisDescriptor, result, name, fromSupertypes) + } + } + + override fun getAdditionalSupertypes( + thisDescriptor: ClassDescriptor, + existingSupertypes: List + ): List { + if (thisDescriptor.isJvmRecord() && existingSupertypes.none(::isJavaLangRecordType)) { + thisDescriptor.module.resolveTopLevelClass(JAVA_LANG_RECORD_FQ_NAME, NoLookupLocation.FOR_DEFAULT_IMPORTS)?.defaultType?.let { + return listOf(it) + } + } + + return emptyList() + } +} + +private fun isJavaLangRecordType(it: KotlinType) = + KotlinBuiltIns.isConstructedFromGivenClass(it, JAVA_LANG_RECORD_FQ_NAME) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 1154a8cfe6c..efb12405a6a 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -110,6 +110,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( container.useImpl() container.useImpl() container.useImpl() + container.useImpl() container.useInstance(FunctionWithBigAritySupport.LanguageVersionDependent) container.useInstance(GenericArrayClassLiteralSupport.Enabled) container.useInstance(JavaActualAnnotationArgumentExtractor()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index c684d9fb48e..2714eeef1ff 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -92,6 +92,7 @@ public class DescriptorResolver { private final DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer; private final DataFlowValueFactory dataFlowValueFactory; private final Iterable anonymousTypeTransformers; + private final AdditionalClassPartsProvider additionalClassPartsProvider; public DescriptorResolver( @NotNull AnnotationResolver annotationResolver, @@ -111,7 +112,8 @@ public class DescriptorResolver { @NotNull TypeApproximator approximator, @NotNull DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer, @NotNull DataFlowValueFactory dataFlowValueFactory, - @NotNull Iterable anonymousTypeTransformers + @NotNull Iterable anonymousTypeTransformers, + @NotNull AdditionalClassPartsProvider additionalClassPartsProvider ) { this.annotationResolver = annotationResolver; this.builtIns = builtIns; @@ -131,6 +133,7 @@ public class DescriptorResolver { this.declarationReturnTypeSanitizer = declarationReturnTypeSanitizer; this.dataFlowValueFactory = dataFlowValueFactory; this.anonymousTypeTransformers = anonymousTypeTransformers; + this.additionalClassPartsProvider = additionalClassPartsProvider; } public List resolveSupertypes( @@ -156,6 +159,7 @@ public class DescriptorResolver { } syntheticResolveExtension.addSyntheticSupertypes(classDescriptor, supertypes); + supertypes.addAll(additionalClassPartsProvider.getAdditionalSupertypes(classDescriptor, supertypes)); if (supertypes.isEmpty()) { addValidSupertype(supertypes, getDefaultSupertype(classDescriptor)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt index 1ccaaeadf23..ff2c838fb45 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionsFromAny.kt @@ -19,10 +19,29 @@ object FunctionsFromAny { val HASH_CODE_METHOD_NAME = Name.identifier("hashCode") val TO_STRING_METHOD_NAME = Name.identifier("toString") + fun addFunctionFromAnyIfNeeded( + thisDescriptor: ClassDescriptor, + result: MutableCollection, + name: Name, + fromSupertypes: Collection, + ) { + if (shouldAddEquals(name, result, fromSupertypes)) { + result.add(createEqualsFunctionDescriptor(thisDescriptor)) + } + + if (shouldAddHashCode(name, result, fromSupertypes)) { + result.add(createHashCodeFunctionDescriptor(thisDescriptor)) + } + + if (shouldAddToString(name, result, fromSupertypes)) { + result.add(createToStringFunctionDescriptor(thisDescriptor)) + } + } + fun shouldAddEquals( name: Name, declaredFunctions: Collection, - fromSupertypes: List + fromSupertypes: Collection ): Boolean { return name == EQUALS_METHOD_NAME && shouldAddFunctionFromAny( declaredFunctions, @@ -36,7 +55,7 @@ object FunctionsFromAny { fun shouldAddHashCode( name: Name, declaredFunctions: Collection, - fromSupertypes: List + fromSupertypes: Collection ): Boolean { return name == HASH_CODE_METHOD_NAME && shouldAddFunctionFromAny( declaredFunctions, @@ -49,7 +68,7 @@ object FunctionsFromAny { fun shouldAddToString( name: Name, declaredFunctions: Collection, - fromSupertypes: List + fromSupertypes: Collection ): Boolean { return name == TO_STRING_METHOD_NAME && shouldAddFunctionFromAny( declaredFunctions, @@ -90,8 +109,8 @@ object FunctionsFromAny { } private fun shouldAddFunctionFromAny( - declaredFunctions: Collection , - fromSupertypes: List, + declaredFunctions: Collection, + fromSupertypes: Collection, checkParameters: (FunctionDescriptor) -> Boolean ): Boolean { // Add 'equals', 'hashCode', 'toString' iff there is no such declared member AND there is no such final member in supertypes diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/LazyClassContext.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/LazyClassContext.kt index d5f5ed1b061..c0f217ee5b6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/LazyClassContext.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/LazyClassContext.kt @@ -47,4 +47,5 @@ interface LazyClassContext { val wrappedTypeFactory: WrappedTypeFactory val kotlinTypeChecker: NewKotlinTypeChecker val samConversionResolver: SamConversionResolver + val additionalClassPartsProvider: AdditionalClassPartsProvider } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ResolveSession.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ResolveSession.java index a4ea5c5269f..cd86cb6e2a9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ResolveSession.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ResolveSession.java @@ -86,6 +86,8 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext { private PlatformDiagnosticSuppressor platformDiagnosticSuppressor; private SamConversionResolver samConversionResolver; + private AdditionalClassPartsProvider additionalClassPartsProvider; + private final SyntheticResolveExtension syntheticResolveExtension; private final NewKotlinTypeChecker kotlinTypeChecker; @@ -157,6 +159,11 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext { this.samConversionResolver = samConversionResolver; } + @Inject + public void setAdditionalClassPartsProvider(@NotNull AdditionalClassPartsProvider additionalClassPartsProvider) { + this.additionalClassPartsProvider = additionalClassPartsProvider; + } + // Only calls from injectors expected @Deprecated public ResolveSession( @@ -500,4 +507,10 @@ public class ResolveSession implements KotlinCodeAnalyzer, LazyClassContext { public SamConversionResolver getSamConversionResolver() { return samConversionResolver; } + + @NotNull + @Override + public AdditionalClassPartsProvider getAdditionalClassPartsProvider() { + return additionalClassPartsProvider; + } } 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 089a712515c..1b96443b57e 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 @@ -250,6 +250,9 @@ open class LazyClassMemberScope( generateDataClassMethods(result, name, location, fromSupertypes) generateFunctionsFromAnyForInlineClass(result, name, fromSupertypes) c.syntheticResolveExtension.generateSyntheticMethods(thisDescriptor, name, trace.bindingContext, fromSupertypes, result) + + c.additionalClassPartsProvider.generateAdditionalMethods(thisDescriptor, result, name, location, fromSupertypes) + generateFakeOverrides(name, fromSupertypes, result, SimpleFunctionDescriptor::class.java) } @@ -259,7 +262,7 @@ open class LazyClassMemberScope( fromSupertypes: List ) { if (!thisDescriptor.isInlineClass()) return - addFunctionFromAnyIfNeeded(result, name, fromSupertypes) + FunctionsFromAny.addFunctionFromAnyIfNeeded(thisDescriptor, result, name, fromSupertypes) } private fun generateDataClassMethods( @@ -309,25 +312,7 @@ open class LazyClassMemberScope( } if (c.languageVersionSettings.supportsFeature(LanguageFeature.DataClassInheritance)) { - addFunctionFromAnyIfNeeded(result, name, fromSupertypes) - } - } - - private fun addFunctionFromAnyIfNeeded( - result: MutableCollection, - name: Name, - fromSupertypes: List - ) { - if (FunctionsFromAny.shouldAddEquals(name, result, fromSupertypes)) { - result.add(FunctionsFromAny.createEqualsFunctionDescriptor(thisDescriptor)) - } - - if (FunctionsFromAny.shouldAddHashCode(name, result, fromSupertypes)) { - result.add(FunctionsFromAny.createHashCodeFunctionDescriptor(thisDescriptor)) - } - - if (FunctionsFromAny.shouldAddToString(name, result, fromSupertypes)) { - result.add(FunctionsFromAny.createToStringFunctionDescriptor(thisDescriptor)) + FunctionsFromAny.addFunctionFromAnyIfNeeded(thisDescriptor, result, name, fromSupertypes) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LocalClassifierAnalyzer.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LocalClassifierAnalyzer.kt index 839ae065ee9..c92f40eb10c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LocalClassifierAnalyzer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/LocalClassifierAnalyzer.kt @@ -69,7 +69,8 @@ class LocalClassifierAnalyzer( private val delegationFilter: DelegationFilter, private val wrappedTypeFactory: WrappedTypeFactory, private val kotlinTypeChecker: NewKotlinTypeChecker, - private val samConversionResolver: SamConversionResolver + private val samConversionResolver: SamConversionResolver, + private val additionalClassPartsProvider: AdditionalClassPartsProvider, ) { fun processClassOrObject( scope: LexicalWritableScope?, @@ -104,7 +105,8 @@ class LocalClassifierAnalyzer( delegationFilter, wrappedTypeFactory, kotlinTypeChecker, - samConversionResolver + samConversionResolver, + additionalClassPartsProvider, ), analyzerServices ) @@ -134,7 +136,8 @@ class LocalClassDescriptorHolder( val delegationFilter: DelegationFilter, val wrappedTypeFactory: WrappedTypeFactory, val kotlinTypeChecker: NewKotlinTypeChecker, - val samConversionResolver: SamConversionResolver + val samConversionResolver: SamConversionResolver, + val additionalClassPartsProvider: AdditionalClassPartsProvider, ) { // We do not need to synchronize here, because this code is used strictly from one thread private var classDescriptor: ClassDescriptor? = null @@ -176,6 +179,8 @@ class LocalClassDescriptorHolder( override val wrappedTypeFactory: WrappedTypeFactory = this@LocalClassDescriptorHolder.wrappedTypeFactory override val kotlinTypeChecker: NewKotlinTypeChecker = this@LocalClassDescriptorHolder.kotlinTypeChecker override val samConversionResolver: SamConversionResolver = this@LocalClassDescriptorHolder.samConversionResolver + override val additionalClassPartsProvider: AdditionalClassPartsProvider = + this@LocalClassDescriptorHolder.additionalClassPartsProvider }, containingDeclaration, classOrObject.nameAsSafeName, diff --git a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt new file mode 100644 index 00000000000..e2f2df9e5db --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt @@ -0,0 +1,11 @@ +// !LANGUAGE: +JvmRecordSupport + +@JvmRecord +class BasicRecord(val x: String) + +@JvmRecord +data class BasicDataRecord(val x: String) + +@JvmRecord +class BasicRecordWithSuperClass(val x: String) : Record() + diff --git a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.txt b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.txt new file mode 100644 index 00000000000..5c4b0ada9f6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.txt @@ -0,0 +1,27 @@ +package + +@kotlin.jvm.JvmRecord public final data class BasicDataRecord : java.lang.Record { + public constructor BasicDataRecord(/*0*/ x: kotlin.String) + public final val x: kotlin.String + public final operator /*synthesized*/ fun component1(): kotlin.String + public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): BasicDataRecord + 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.JvmRecord public final class BasicRecord : java.lang.Record { + public constructor BasicRecord(/*0*/ x: kotlin.String) + public final val x: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} + +@kotlin.jvm.JvmRecord public final class BasicRecordWithSuperClass : java.lang.Record { + public constructor BasicRecordWithSuperClass(/*0*/ x: kotlin.String) + public final val x: kotlin.String + public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java index 453580a3942..80854b70acc 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java @@ -50,6 +50,11 @@ public class DiagnosticsWithJdk15TestGenerated extends AbstractDiagnosticsWithJd runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/disabledFeature.kt"); } + @TestMetadata("jvmRecordDescriptorStructure.kt") + public void testJvmRecordDescriptorStructure() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt"); + } + @TestMetadata("simpleRecords.kt") public void testSimpleRecords() throws Exception { runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/simpleRecords.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/AdditionalClassPartsProvider.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/AdditionalClassPartsProvider.kt new file mode 100644 index 00000000000..6237006ea77 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/AdditionalClassPartsProvider.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2020 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 + +import org.jetbrains.kotlin.container.DefaultImplementation +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType + +@DefaultImplementation(impl = AdditionalClassPartsProvider.Default::class) +interface AdditionalClassPartsProvider { + fun generateAdditionalMethods( + thisDescriptor: ClassDescriptor, + result: MutableCollection, + name: Name, + location: LookupLocation, + fromSupertypes: Collection + ) + + fun getAdditionalSupertypes( + thisDescriptor: ClassDescriptor, + existingSupertypes: List + ): List + + object Default : AdditionalClassPartsProvider { + override fun generateAdditionalMethods( + thisDescriptor: ClassDescriptor, + result: MutableCollection, + name: Name, + location: LookupLocation, + fromSupertypes: Collection + ) {} + + override fun getAdditionalSupertypes( + thisDescriptor: ClassDescriptor, + existingSupertypes: List + ): List = emptyList() + } +}