diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt index 1826f4f06b1..6fd48fbca16 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/BuilderFactoryForDuplicateClassNameDiagnostics.kt @@ -18,34 +18,33 @@ package org.jetbrains.kotlin.codegen.state import org.jetbrains.kotlin.codegen.ClassBuilderFactory import org.jetbrains.kotlin.codegen.ClassNameCollectionClassBuilderFactory -import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import java.util.concurrent.ConcurrentHashMap - class BuilderFactoryForDuplicateClassNameDiagnostics( - builderFactory: ClassBuilderFactory, - private val diagnostics: DiagnosticSink + builderFactory: ClassBuilderFactory, + private val state: GenerationState, ) : ClassNameCollectionClassBuilderFactory(builderFactory) { private val className = ConcurrentHashMap() override fun handleClashingNames(internalName: String, origin: JvmDeclarationOrigin) { - val another = className.getOrPut(internalName, { origin }) - //workaround for inlined anonymous objects - if (origin.element != another.element) { + val another = className.getOrPut(internalName) { origin } + // Allow clashing classes if they are originated from the same source element. For example, this happens during inlining anonymous + // objects. In JVM IR, this also happens for anonymous classes in default arguments of tailrec functions, because default arguments + // are deep-copied (see JvmTailrecLowering). + if (origin.originalSourceElement != another.originalSourceElement) { reportError(internalName, origin, another) } } private fun reportError(internalName: String, vararg another: JvmDeclarationOrigin) { - val fromString = another.mapNotNull { it.descriptor }. - joinToString { DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(it) } + val duplicateClasses = + another.mapNotNull { it.descriptor }.joinToString { DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.render(it) } - another.mapNotNull { it.element }.forEach { - diagnostics.report(ErrorsJvm.DUPLICATE_CLASS_NAMES.on(it, internalName, fromString)) + for (origin in another) { + state.reportDuplicateClassNameError(origin, internalName, duplicateClasses) } } -} \ No newline at end of file +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 831e11923d4..0a371f24766 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.diagnostics.OnDemandSuppressCache import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.JvmCompilerDeserializationConfiguration +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind.* import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration @@ -291,6 +292,12 @@ class GenerationState private constructor( var multiFieldValueClassUnboxInfo: (ClassDescriptor) -> MultiFieldValueClassUnboxInfo? = { null } + var reportDuplicateClassNameError: (JvmDeclarationOrigin, String, String) -> Unit = { origin, internalName, duplicateClasses -> + origin.element?.let { + diagnostics.report(ErrorsJvm.DUPLICATE_CLASS_NAMES.on(it, internalName, duplicateClasses)) + } + } + val typeApproximator: TypeApproximator? = if (languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) TypeApproximator(module.builtIns, languageVersionSettings) @@ -308,7 +315,7 @@ class GenerationState private constructor( }, { // In IR backend, we have more precise information about classes and methods we are going to generate, - // and report signature conflict errors in JvmSignatureClashTracker. + // and report signature conflict errors in JvmSignatureClashDetector. if (isIrBackend) it else @@ -318,7 +325,7 @@ class GenerationState private constructor( shouldGenerate = { origin -> !shouldOnlyCollectSignatures(origin) }, ).apply { duplicateSignatureFactory = this } }, - { BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) }, + { BuilderFactoryForDuplicateClassNameDiagnostics(it, this) }, { configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH) ?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt index 6d14296c022..865639a7e8d 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmBackendErrors.kt @@ -39,6 +39,8 @@ object JvmBackendErrors { val NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC by error0() + val DUPLICATE_CLASS_NAMES by error2() + init { RootDiagnosticRendererFactory.registerFactory(KtDefaultJvmErrorMessages) } @@ -78,5 +80,7 @@ object KtDefaultJvmErrorMessages : BaseDiagnosticRendererFactory() { JvmBackendErrors.NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC, "All of multi-file class parts should be annotated with @JvmSynthetic if at least one of them is" ) + + map.put(JvmBackendErrors.DUPLICATE_CLASS_NAMES, "Duplicate JVM class name ''{0}'' generated from: {1}", STRING, STRING) } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt index d92d5020b09..2c0cdce713b 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/JvmDeclarationOrigin.kt @@ -25,6 +25,11 @@ open class JvmDeclarationOrigin( val descriptor: DeclarationDescriptor?, val parametersForJvmOverload: List? = null ) { + // This property is used to get the original element in the sources, from which this declaration was generated. + // In the old JVM backend, it is just the PSI element. In JVM IR, it is the original IR element (before any deep copy). + open val originalSourceElement: Any? + get() = element + override fun toString(): String = if (this == NO_ORIGIN) "NO_ORIGIN" else "origin=$originKind element=${element?.javaClass?.simpleName} descriptor=$descriptor" diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 596a2d88a0d..83138e7cdf8 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.RegularMapping import org.jetbrains.kotlin.backend.jvm.caches.BridgeLoweringCache import org.jetbrains.kotlin.backend.jvm.caches.CollectionStubComputer +import org.jetbrains.kotlin.backend.jvm.extensions.JvmIrDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.mapping.IrTypeMapper import org.jetbrains.kotlin.backend.jvm.mapping.MethodSignatureMapper import org.jetbrains.kotlin.codegen.inline.SMAP @@ -40,6 +41,7 @@ import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors import org.jetbrains.org.objectweb.asm.Type import java.util.concurrent.ConcurrentHashMap @@ -205,6 +207,13 @@ class JvmBackendContext( node.leaves.map { Triple(defaultTypeMapper.mapType(it.type), it.fullMethodName.asString(), it.fullFieldName.asString()) } GenerationState.MultiFieldValueClassUnboxInfo(leavesInfo) } + + state.reportDuplicateClassNameError = { origin, internalName, duplicateClasses -> + val declaration = (origin as JvmIrDeclarationOrigin).declaration + if (declaration != null) { + ktDiagnosticReporter.at(declaration).report(JvmBackendErrors.DUPLICATE_CLASS_NAMES, internalName, duplicateClasses) + } + } } fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/extensions/JvmIrDeclarationOrigin.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/extensions/JvmIrDeclarationOrigin.kt index 319ea027193..e1dd19a0bf6 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/extensions/JvmIrDeclarationOrigin.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/extensions/JvmIrDeclarationOrigin.kt @@ -20,7 +20,10 @@ class JvmIrDeclarationOrigin( originKind: JvmDeclarationOriginKind, element: PsiElement?, val declaration: IrDeclaration?, -) : JvmDeclarationOrigin(originKind, element, declaration?.toIrBasedDescriptor(), null) +) : JvmDeclarationOrigin(originKind, element, declaration?.toIrBasedDescriptor(), null) { + override val originalSourceElement: Any? + get() = (declaration as? IrAttributeContainer)?.attributeOwnerId +} val IrDeclaration.descriptorOrigin: JvmIrDeclarationOrigin get() {