K2: report DUPLICATE_CLASS_NAMES error in JVM backend

It was already reported in the K2+PSI mode, but not LT because
BuilderFactoryForDuplicateClassNameDiagnostics relied on PSI, and did
not do anything if PSI was missing.

No tests were added because it fixes the already existing test
`compiler/testData/cli/jvm/fileClassClashMultipleFiles` after the
project is migrated to 2.0.

 #KT-59586
This commit is contained in:
Alexander Udalov
2023-07-25 17:41:01 +02:00
committed by Space Team
parent 84bf411cc3
commit 2823fff63d
6 changed files with 43 additions and 16 deletions
@@ -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<String, JvmDeclarationOrigin>()
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)
}
}
}
}
@@ -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
@@ -39,6 +39,8 @@ object JvmBackendErrors {
val NOT_ALL_MULTIFILE_CLASS_PARTS_ARE_JVM_SYNTHETIC by error0<PsiElement>()
val DUPLICATE_CLASS_NAMES by error2<PsiElement, String, String>()
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)
}
}
@@ -25,6 +25,11 @@ open class JvmDeclarationOrigin(
val descriptor: DeclarationDescriptor?,
val parametersForJvmOverload: List<KtParameter?>? = 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"
@@ -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 =
@@ -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() {