[klib] Implement diagnostics for clashing KLIB signatures

Now, we detect clashing signatures during serialization to KLIB and
report a compiler error if two or more declarations have the same
`IdSignature`

For example, for the following code:
```kotlin
@Deprecated("", level = DeprecationLevel.HIDDEN)
fun foo(): String = ""

fun foo(): Int = 0
```

the compiler will produce this diagnostic:
```
e: main.kt:1:1 Platform declaration clash: The following declarations
       have the same KLIB signature (/foo|foo(){}[0]):
    fun foo(): String defined in root package
    fun foo(): Int defined in root package
e: main.kt:4:1 Platform declaration clash: The following declarations
       have the same KLIB signature (/foo|foo(){}[0]):
    fun foo(): String defined in root package
    fun foo(): Int defined in root package
```

Note that we report this diagnostic during serialization and not earlier
(e.g., in fir2ir) for more robustness, so ensure that we check
exactly the signatures that will be written to a KLIB.
If we later introduce some annotation for customizing a declaration's
signature (e.g., for preserving binary compatibility), this
diagnostic will continue to work as expected.

^KT-63670 Fixed
This commit is contained in:
Sergej Jaskiewicz
2023-12-15 20:05:02 +01:00
committed by Space Team
parent e0cb145c6b
commit eda30ff704
27 changed files with 366 additions and 98 deletions
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.joinToWithBuffer
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
@@ -63,4 +64,27 @@ object CommonRenderers {
} }
} }
} }
@JvmStatic
fun <Declaration, Data> renderConflictingSignatureData(
signatureKind: String,
sortUsing: Comparator<Declaration>,
declarationRenderer: DiagnosticParameterRenderer<Declaration>,
renderSignature: StringBuilder.(Data) -> Unit,
declarations: (Data) -> Collection<Declaration>,
) = Renderer<Data> { data ->
val sortedDeclarations = declarations(data).sortedWith(sortUsing)
val renderingContext = RenderingContext.Impl(sortedDeclarations)
buildString {
append("The following declarations have the same ")
append(signatureKind)
append(" signature (")
renderSignature(data)
appendLine("):")
sortedDeclarations.joinToWithBuffer(this, separator = "\n") { descriptor ->
append(" ")
append(declarationRenderer.render(descriptor, renderingContext))
}
}
}
} }
@@ -11,12 +11,14 @@ import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies.DECLA
import org.jetbrains.kotlin.diagnostics.error0 import org.jetbrains.kotlin.diagnostics.error0
import org.jetbrains.kotlin.diagnostics.error1 import org.jetbrains.kotlin.diagnostics.error1
import org.jetbrains.kotlin.diagnostics.error2 import org.jetbrains.kotlin.diagnostics.error2
import org.jetbrains.kotlin.diagnostics.rendering.* import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.NAME import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.NAME
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.MemberComparator import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.utils.join
object JvmBackendErrors { object JvmBackendErrors {
val CONFLICTING_JVM_DECLARATIONS by error1<PsiElement, ConflictingJvmDeclarationsData>(DECLARATION_SIGNATURE_OR_DEFAULT) val CONFLICTING_JVM_DECLARATIONS by error1<PsiElement, ConflictingJvmDeclarationsData>(DECLARATION_SIGNATURE_OR_DEFAULT)
@@ -48,17 +50,16 @@ object JvmBackendErrors {
object KtDefaultJvmErrorMessages : BaseDiagnosticRendererFactory() { object KtDefaultJvmErrorMessages : BaseDiagnosticRendererFactory() {
@JvmField @JvmField
val CONFLICTING_JVM_DECLARATIONS_DATA = Renderer<ConflictingJvmDeclarationsData> { val CONFLICTING_JVM_DECLARATIONS_DATA = CommonRenderers.renderConflictingSignatureData(
val renderedDescriptors = it.signatureDescriptors.sortedWith(MemberComparator.INSTANCE) signatureKind = "JVM",
val renderingContext = RenderingContext.Impl(renderedDescriptors) sortUsing = MemberComparator.INSTANCE,
""" declarationRenderer = Renderers.WITHOUT_MODIFIERS,
The following declarations have the same JVM signature (${it.signature.name}${it.signature.desc}): renderSignature = {
append(it.signature.name)
""".trimIndent() + append(it.signature.desc)
join(renderedDescriptors.map { descriptor -> },
" " + Renderers.WITHOUT_MODIFIERS.render(descriptor, renderingContext) declarations = ConflictingJvmDeclarationsData::signatureDescriptors,
}, "\n") )
}
override val MAP = KtDiagnosticFactoryToRendererMap("KT").also { map -> override val MAP = KtDiagnosticFactoryToRendererMap("KT").also { map ->
map.put(JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS, "Platform declaration clash: {0}", CONFLICTING_JVM_DECLARATIONS_DATA) map.put(JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS, "Platform declaration clash: {0}", CONFLICTING_JVM_DECLARATIONS_DATA)
@@ -31,13 +31,14 @@ import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newTe
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.isCommon import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.DEBUG_TEXT import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.DEBUG_TEXT
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.PropertyAccessorRenderingPolicy import org.jetbrains.kotlin.renderer.PropertyAccessorRenderingPolicy
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.calls.inference.* import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
@@ -52,6 +53,7 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.IDEAPlatforms import org.jetbrains.kotlin.utils.IDEAPlatforms
import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.joinToWithBuffer
object Renderers { object Renderers {
@@ -706,9 +708,7 @@ object Renderers {
@JvmField @JvmField
val COMPACT_WITHOUT_SUPERTYPES = DescriptorRenderer.COMPACT_WITHOUT_SUPERTYPES.asRenderer() val COMPACT_WITHOUT_SUPERTYPES = DescriptorRenderer.COMPACT_WITHOUT_SUPERTYPES.asRenderer()
@JvmField @JvmField
val WITHOUT_MODIFIERS = DescriptorRenderer.withOptions { val WITHOUT_MODIFIERS = DescriptorRenderer.WITHOUT_MODIFIERS.asRenderer()
modifiers = emptySet()
}.asRenderer()
@JvmField @JvmField
val SHORT_NAMES_IN_TYPES = DescriptorRenderer.SHORT_NAMES_IN_TYPES.asRenderer() val SHORT_NAMES_IN_TYPES = DescriptorRenderer.SHORT_NAMES_IN_TYPES.asRenderer()
@JvmField @JvmField
@@ -10,6 +10,7 @@ dependencies {
api(project(":kotlin-util-klib-metadata")) api(project(":kotlin-util-klib-metadata"))
api(project(":compiler:util")) api(project(":compiler:util"))
implementation(project(":compiler:psi")) implementation(project(":compiler:psi"))
implementation(project(":compiler:frontend.common-psi"))
compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false } compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
compileOnly(commonDependency("org.jetbrains.intellij.deps.fastutil:intellij-deps-fastutil")) compileOnly(commonDependency("org.jetbrains.intellij.deps.fastutil:intellij-deps-fastutil"))
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2023 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.common.diagnostics
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.util.IdSignature
internal data class ConflictingKlibSignaturesData(val signature: IdSignature, val declarations: Collection<IrDeclaration>)
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2023 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.common.diagnostics
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.linkage.SignatureClashDetector
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentClassOrNull
class IdSignatureClashDetector : SignatureClashDetector<IdSignature, IrDeclaration>() {
override fun reportSignatureConflict(
signature: IdSignature,
declarations: Collection<IrDeclaration>,
diagnosticReporter: IrDiagnosticReporter
) {
reportSignatureClashTo(
diagnosticReporter,
SerializationErrors.CONFLICTING_KLIB_SIGNATURES_ERROR,
declarations,
ConflictingKlibSignaturesData(signature, declarations),
reportOnIfSynthetic = { it.parentClassOrNull ?: it.file },
)
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2023 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.common.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.diagnostics.SerializationDiagnosticRenderers.CONFLICTING_KLIB_SIGNATURES_DATA
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.error1
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.MemberComparator
internal object SerializationErrors {
val CONFLICTING_KLIB_SIGNATURES_ERROR by error1<PsiElement, ConflictingKlibSignaturesData>()
init {
RootDiagnosticRendererFactory.registerFactory(KtDefaultSerializationErrorMessages)
}
}
internal object KtDefaultSerializationErrorMessages : BaseDiagnosticRendererFactory() {
override val MAP = KtDiagnosticFactoryToRendererMap("KT").also { map ->
map.put(
SerializationErrors.CONFLICTING_KLIB_SIGNATURES_ERROR,
"Platform declaration clash: {0}",
CONFLICTING_KLIB_SIGNATURES_DATA,
)
}
}
internal object SerializationDiagnosticRenderers {
val CONFLICTING_KLIB_SIGNATURES_DATA =
CommonRenderers.renderConflictingSignatureData<DeclarationDescriptor, ConflictingKlibSignaturesData>(
signatureKind = "KLIB",
sortUsing = MemberComparator.INSTANCE,
declarationRenderer = Renderer {
DescriptorRenderer.WITHOUT_MODIFIERS.render(it)
},
renderSignature = { append(it.signature.render()) },
declarations = { it.declarations.map(IrDeclaration::toIrBasedDescriptor) },
)
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.common.serialization package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.backend.common.diagnostics.IdSignatureClashDetector
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureFactory import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureFactory
import org.jetbrains.kotlin.backend.common.serialization.signature.PublicIdSignatureComputer import org.jetbrains.kotlin.backend.common.serialization.signature.PublicIdSignatureComputer
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
@@ -15,37 +16,24 @@ import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.KotlinMangler import org.jetbrains.kotlin.ir.util.KotlinMangler
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
abstract class GlobalDeclarationTable(private val mangler: KotlinMangler.IrMangler) {
interface IdSignatureClashTracker {
fun commit(declaration: IrDeclaration, signature: IdSignature)
companion object {
val DEFAULT_TRACKER = object : IdSignatureClashTracker {
override fun commit(declaration: IrDeclaration, signature: IdSignature) {}
}
}
}
abstract class GlobalDeclarationTable(
private val mangler: KotlinMangler.IrMangler,
private val clashTracker: IdSignatureClashTracker
) {
val publicIdSignatureComputer = PublicIdSignatureComputer(mangler) val publicIdSignatureComputer = PublicIdSignatureComputer(mangler)
internal val clashDetector = IdSignatureClashDetector()
protected val table = hashMapOf<IrDeclaration, IdSignature>() protected val table = hashMapOf<IrDeclaration, IdSignature>()
constructor(mangler: KotlinMangler.IrMangler) : this(mangler, IdSignatureClashTracker.DEFAULT_TRACKER)
protected fun loadKnownBuiltins(builtIns: IrBuiltIns) { protected fun loadKnownBuiltins(builtIns: IrBuiltIns) {
builtIns.knownBuiltins.forEach { builtIns.knownBuiltins.forEach {
val symbol = (it as IrSymbolOwner).symbol val symbol = (it as IrSymbolOwner).symbol
table[it] = symbol.signature!!.also { id -> clashTracker.commit(it, id) } table[it] = symbol.signature!!.also { id -> clashDetector.trackDeclaration(it, id) }
} }
} }
open fun computeSignatureByDeclaration(declaration: IrDeclaration, compatibleMode: Boolean): IdSignature { open fun computeSignatureByDeclaration(declaration: IrDeclaration, compatibleMode: Boolean): IdSignature {
return table.getOrPut(declaration) { return table.getOrPut(declaration) {
publicIdSignatureComputer.composePublicIdSignature(declaration, compatibleMode).also { clashTracker.commit(declaration, it) } publicIdSignatureComputer.composePublicIdSignature(declaration, compatibleMode).also {
clashDetector.trackDeclaration(declaration, it)
}
} }
} }
@@ -6,14 +6,23 @@
package org.jetbrains.kotlin.backend.common.serialization package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment import org.jetbrains.kotlin.builtins.FunctionInterfacePackageFragment
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.library.SerializedIrFile import org.jetbrains.kotlin.library.SerializedIrFile
import org.jetbrains.kotlin.library.SerializedIrModule import org.jetbrains.kotlin.library.SerializedIrModule
abstract class IrModuleSerializer<F : IrFileSerializer>(protected val messageLogger: IrMessageLogger, protected val compatibilityMode: CompatibilityMode, protected val normalizeAbsolutePaths: Boolean, protected val sourceBaseDirs: Collection<String>) { abstract class IrModuleSerializer<Serializer : IrFileSerializer>(
abstract fun createSerializerForFile(file: IrFile): F protected val diagnosticReporter: IrDiagnosticReporter,
protected val messageLogger: IrMessageLogger,
protected val compatibilityMode: CompatibilityMode,
protected val normalizeAbsolutePaths: Boolean,
protected val sourceBaseDirs: Collection<String>,
private val shouldCheckSignaturesOnUniqueness: Boolean,
) {
abstract fun createSerializerForFile(file: IrFile): Serializer
/** /**
* Allows to skip [file] during serialization. * Allows to skip [file] during serialization.
@@ -23,6 +32,8 @@ abstract class IrModuleSerializer<F : IrFileSerializer>(protected val messageLog
protected open fun backendSpecificFileFilter(file: IrFile): Boolean = protected open fun backendSpecificFileFilter(file: IrFile): Boolean =
true true
protected abstract val globalDeclarationTable: GlobalDeclarationTable
private fun serializeIrFile(file: IrFile): SerializedIrFile { private fun serializeIrFile(file: IrFile): SerializedIrFile {
val fileSerializer = createSerializerForFile(file) val fileSerializer = createSerializerForFile(file)
return fileSerializer.serializeIrFile(file) return fileSerializer.serializeIrFile(file)
@@ -33,6 +44,9 @@ abstract class IrModuleSerializer<F : IrFileSerializer>(protected val messageLog
.filter { it.packageFragmentDescriptor !is FunctionInterfacePackageFragment } .filter { it.packageFragmentDescriptor !is FunctionInterfacePackageFragment }
.filter(this::backendSpecificFileFilter) .filter(this::backendSpecificFileFilter)
.map(this::serializeIrFile) .map(this::serializeIrFile)
if (shouldCheckSignaturesOnUniqueness) {
globalDeclarationTable.clashDetector.reportErrorsTo(diagnosticReporter)
}
return SerializedIrModule(serializedFiles) return SerializedIrModule(serializedFiles)
} }
} }
@@ -631,7 +631,7 @@ fun serializeModuleIntoKlib(
val compatibilityMode = CompatibilityMode(abiVersion) val compatibilityMode = CompatibilityMode(abiVersion)
val sourceBaseDirs = configuration[CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES] ?: emptyList() val sourceBaseDirs = configuration[CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES] ?: emptyList()
val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false
val signatureClashChecks = configuration[CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS] ?: false val signatureClashChecks = configuration[CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS] ?: true
val moduleExportedNames = moduleFragment.collectExportedNames() val moduleExportedNames = moduleFragment.collectExportedNames()
@@ -643,6 +643,7 @@ fun serializeModuleIntoKlib(
val serializedIr = val serializedIr =
JsIrModuleSerializer( JsIrModuleSerializer(
irDiagnosticReporter,
messageLogger, messageLogger,
moduleFragment.irBuiltins, moduleFragment.irBuiltins,
compatibilityMode, compatibilityMode,
@@ -650,8 +651,7 @@ fun serializeModuleIntoKlib(
sourceBaseDirs = sourceBaseDirs, sourceBaseDirs = sourceBaseDirs,
configuration.languageVersionSettings, configuration.languageVersionSettings,
signatureClashChecks, signatureClashChecks,
jsIrFileMetadataFactory = { JsIrFileMetadata(moduleExportedNames[it]?.values?.toSmartList() ?: emptyList()) } ) { JsIrFileMetadata(moduleExportedNames[it]?.values?.toSmartList() ?: emptyList()) }.serializedIrModule(moduleFragment)
).serializedIrModule(moduleFragment)
val moduleDescriptor = moduleFragment.descriptor val moduleDescriptor = moduleFragment.descriptor
@@ -6,36 +6,9 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
import org.jetbrains.kotlin.backend.common.serialization.GlobalDeclarationTable import org.jetbrains.kotlin.backend.common.serialization.GlobalDeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.IdSignatureClashTracker
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.render
class JsUniqIdClashTracker() : IdSignatureClashTracker { class JsGlobalDeclarationTable(builtIns: IrBuiltIns) : GlobalDeclarationTable(JsManglerIr) {
private val committedIdSignatures = mutableMapOf<IdSignature, IrDeclaration>()
override fun commit(declaration: IrDeclaration, signature: IdSignature) {
if (!signature.isPubliclyVisible) return // don't track local ids
if (signature in committedIdSignatures) {
val clashedDeclaration = committedIdSignatures[signature]!!
val parent = declaration.parent
val clashedParent = clashedDeclaration.parent
if (declaration !is IrTypeParameter || parent !is IrSimpleFunction || clashedParent !is IrSimpleFunction || parent.correspondingPropertySymbol !== clashedParent.correspondingPropertySymbol) {
// TODO: handle clashes properly
error("IdSignature clash: $signature; Existed declaration ${clashedDeclaration.render()} clashed with new ${declaration.render()}")
}
}
committedIdSignatures[signature] = declaration
}
}
class JsGlobalDeclarationTable(builtIns: IrBuiltIns, tracker: IdSignatureClashTracker = JsUniqIdClashTracker()) :
GlobalDeclarationTable(JsManglerIr, tracker) {
init { init {
loadKnownBuiltins(builtIns) loadKnownBuiltins(builtIns)
} }
@@ -7,14 +7,16 @@ package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.IdSignatureClashTracker
import org.jetbrains.kotlin.backend.common.serialization.IrModuleSerializer import org.jetbrains.kotlin.backend.common.serialization.IrModuleSerializer
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
class JsIrModuleSerializer( class JsIrModuleSerializer(
diagnosticReporter: IrDiagnosticReporter,
messageLogger: IrMessageLogger, messageLogger: IrMessageLogger,
irBuiltIns: IrBuiltIns, irBuiltIns: IrBuiltIns,
compatibilityMode: CompatibilityMode, compatibilityMode: CompatibilityMode,
@@ -22,13 +24,17 @@ class JsIrModuleSerializer(
sourceBaseDirs: Collection<String>, sourceBaseDirs: Collection<String>,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
shouldCheckSignaturesOnUniqueness: Boolean = true, shouldCheckSignaturesOnUniqueness: Boolean = true,
private val jsIrFileMetadataFactory: JsIrFileMetadataFactory = JsIrFileEmptyMetadataFactory private val jsIrFileMetadataFactory: JsIrFileMetadataFactory = JsIrFileEmptyMetadataFactory,
) : IrModuleSerializer<JsIrFileSerializer>(messageLogger, compatibilityMode, normalizeAbsolutePaths, sourceBaseDirs) { ) : IrModuleSerializer<JsIrFileSerializer>(
diagnosticReporter,
messageLogger,
compatibilityMode,
normalizeAbsolutePaths,
sourceBaseDirs,
shouldCheckSignaturesOnUniqueness,
) {
private val globalDeclarationTable = JsGlobalDeclarationTable( override val globalDeclarationTable = JsGlobalDeclarationTable(irBuiltIns)
irBuiltIns,
if (shouldCheckSignaturesOnUniqueness) JsUniqIdClashTracker() else IdSignatureClashTracker.DEFAULT_TRACKER
)
override fun createSerializerForFile(file: IrFile): JsIrFileSerializer = override fun createSerializerForFile(file: IrFile): JsIrFileSerializer =
JsIrFileSerializer( JsIrFileSerializer(
@@ -8,12 +8,15 @@ package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.IrModuleSerializer import org.jetbrains.kotlin.backend.common.serialization.IrModuleSerializer
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrDiagnosticReporter
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.name.NativeStandardInteropNames import org.jetbrains.kotlin.name.NativeStandardInteropNames
class KonanIrModuleSerializer( class KonanIrModuleSerializer(
diagnosticReporter: IrDiagnosticReporter,
messageLogger: IrMessageLogger, messageLogger: IrMessageLogger,
irBuiltIns: IrBuiltIns, irBuiltIns: IrBuiltIns,
compatibilityMode: CompatibilityMode, compatibilityMode: CompatibilityMode,
@@ -22,9 +25,17 @@ class KonanIrModuleSerializer(
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val bodiesOnlyForInlines: Boolean = false, private val bodiesOnlyForInlines: Boolean = false,
private val skipPrivateApi: Boolean = false, private val skipPrivateApi: Boolean = false,
) : IrModuleSerializer<KonanIrFileSerializer>(messageLogger, compatibilityMode, normalizeAbsolutePaths, sourceBaseDirs) { shouldCheckSignaturesOnUniqueness: Boolean = true,
) : IrModuleSerializer<KonanIrFileSerializer>(
diagnosticReporter,
messageLogger,
compatibilityMode,
normalizeAbsolutePaths,
sourceBaseDirs,
shouldCheckSignaturesOnUniqueness,
) {
private val globalDeclarationTable = KonanGlobalDeclarationTable(irBuiltIns) override val globalDeclarationTable = KonanGlobalDeclarationTable(irBuiltIns)
// We skip files with IR for C structs and enums because they should be // We skip files with IR for C structs and enums because they should be
// generated anew. // generated anew.
@@ -0,0 +1,14 @@
/foo.kt:7:1: error: Platform declaration clash: The following declarations have the same KLIB signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
fun foo(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics
/main.kt:13:1: error: Platform declaration clash: The following declarations have the same KLIB signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
fun foo(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics
/main.kt:16:1: error: Platform declaration clash: The following declarations have the same KLIB signature (com.example.klib.serialization.diagnostics/foo|foo(){}[0]):
fun foo(): kotlin.Long defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics
@@ -0,0 +1,20 @@
// FIR_IDENTICAL
// RENDER_ALL_DIAGNOSTICS_FULL_TEXT
// FILE: foo.kt
package com.example.klib.serialization.diagnostics
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>@Deprecated("", level = DeprecationLevel.HIDDEN)
fun foo(): Long = 0L<!>
// FILE: main.kt
package com.example.klib.serialization.diagnostics
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>@Deprecated("", level = DeprecationLevel.HIDDEN)
fun foo(): String = ""<!>
<!CONFLICTING_KLIB_SIGNATURES_ERROR!>fun foo(): Int = 0<!>
fun main() {
foo()
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.klib
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.CommonKLibResolver import org.jetbrains.kotlin.backend.common.CommonKLibResolver
import org.jetbrains.kotlin.ir.util.isExpect import org.jetbrains.kotlin.ir.util.isExpect
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
@@ -24,6 +25,8 @@ import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.AbstractIrGeneratorTestCase import org.jetbrains.kotlin.ir.AbstractIrGeneratorTestCase
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -139,16 +142,25 @@ abstract class AbstractKlibIrTextTestCase : CodegenTestCase() {
return serializePackageFragment(moduleDescriptor, memberScope, ktFile.packageFqName) return serializePackageFragment(moduleDescriptor, memberScope, ktFile.packageFqName)
} }
protected fun serializeModule(irModuleFragment: IrModuleFragment, bindingContext: BindingContext, stdlib: KotlinLibrary, containsErrorCode: Boolean): String { private fun serializeModule(
irModuleFragment: IrModuleFragment,
bindingContext: BindingContext,
stdlib: KotlinLibrary,
containsErrorCode: Boolean,
): String {
val ktFiles = myFiles.psiFiles val ktFiles = myFiles.psiFiles
val serializedIr = JsIrModuleSerializer( val serializedIr = JsIrModuleSerializer(
IrMessageLogger.None, KtDiagnosticReporterWithImplicitIrBasedContext(
irModuleFragment.irBuiltins, DiagnosticReporterFactory.createPendingReporter(),
CompatibilityMode.CURRENT,
false,
emptyList(),
myEnvironment.configuration.languageVersionSettings, myEnvironment.configuration.languageVersionSettings,
).serializedIrModule(irModuleFragment) ),
IrMessageLogger.None,
irModuleFragment.irBuiltins,
CompatibilityMode.CURRENT,
false,
emptyList(),
myEnvironment.configuration.languageVersionSettings,
).serializedIrModule(irModuleFragment)
val moduleDescriptor = irModuleFragment.descriptor val moduleDescriptor = irModuleFragment.descriptor
val metadataSerializer = klibMetadataIncrementalSerializer(myEnvironment.configuration, myEnvironment.project, containsErrorCode) val metadataSerializer = klibMetadataIncrementalSerializer(myEnvironment.configuration, myEnvironment.project, containsErrorCode)
@@ -103,6 +103,11 @@ abstract class DescriptorRenderer {
return DescriptorRendererImpl(options) return DescriptorRendererImpl(options)
} }
@JvmField
val WITHOUT_MODIFIERS: DescriptorRenderer = withOptions {
modifiers = emptySet()
}
@JvmField @JvmField
val COMPACT_WITH_MODIFIERS: DescriptorRenderer = withOptions { val COMPACT_WITH_MODIFIERS: DescriptorRenderer = withOptions {
withDefinedIn = false withDefinedIn = false
@@ -11,6 +11,7 @@ import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.KtPsiSourceFile import org.jetbrains.kotlin.KtPsiSourceFile
import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols import org.jetbrains.kotlin.backend.common.linkage.issues.checkNoUnboundSymbols
@@ -37,7 +38,6 @@ import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.collectExportedNames
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -529,7 +529,11 @@ class GenerateIrRuntime {
private fun doSerializeIrModule(module: IrModuleFragment): SerializedIrModule { private fun doSerializeIrModule(module: IrModuleFragment): SerializedIrModule {
val serializedIr = JsIrModuleSerializer( return JsIrModuleSerializer(
KtDiagnosticReporterWithImplicitIrBasedContext(
DiagnosticReporterFactory.createPendingReporter(),
configuration.languageVersionSettings,
),
IrMessageLogger.None, IrMessageLogger.None,
module.irBuiltins, module.irBuiltins,
CompatibilityMode.CURRENT, CompatibilityMode.CURRENT,
@@ -537,7 +541,6 @@ class GenerateIrRuntime {
emptyList(), emptyList(),
configuration.languageVersionSettings, configuration.languageVersionSettings,
).serializedIrModule(module) ).serializedIrModule(module)
return serializedIr
} }
private fun doWriteIrModuleToStorage(serializedIrModule: SerializedIrModule, writer: KotlinLibraryOnlyIrWriter) { private fun doWriteIrModuleToStorage(serializedIrModule: SerializedIrModule, writer: KotlinLibraryOnlyIrWriter) {
@@ -211,6 +211,15 @@ fun main(args: Array<String>) {
model("debug/localVariables") model("debug/localVariables")
} }
testClass<AbstractFirPsiJsDiagnosticWithBackendTest>(suiteTestClassName = "FirPsiJsKlibDiagnosticsTestGenerated") {
model(
relativeRootPath = "diagnostics/klibSerializationTests",
pattern = "^([^_](.+))\\.kt$",
excludedPattern = excludedFirTestdataPattern,
targetBackend = TargetBackend.JS_IR
)
}
testClass<AbstractFirPsiJsDiagnosticTest>(suiteTestClassName = "FirPsiJsOldFrontendDiagnosticsTestGenerated") { testClass<AbstractFirPsiJsDiagnosticTest>(suiteTestClassName = "FirPsiJsOldFrontendDiagnosticsTestGenerated") {
model( model(
relativeRootPath = "diagnostics/testsWithJsStdLib", relativeRootPath = "diagnostics/testsWithJsStdLib",
@@ -164,5 +164,7 @@ fun <
irHandlersStep() irHandlersStep()
facadeStep(backendFacade) facadeStep(backendFacade)
klibArtifactsHandlersStep() klibArtifactsHandlersStep {
useHandlers(::JsBackendDiagnosticsHandler)
}
} }
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2024 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.js.test.fir;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateJsTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/diagnostics/klibSerializationTests")
@TestDataPath("$PROJECT_ROOT")
public class FirPsiJsKlibDiagnosticsTestGenerated extends AbstractFirPsiJsDiagnosticWithBackendTest {
@Test
public void testAllFilesPresentInKlibSerializationTests() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/klibSerializationTests"), Pattern.compile("^([^_](.+))\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("signatureClash.kt")
public void testSignatureClash() throws Exception {
runTest("compiler/testData/diagnostics/klibSerializationTests/signatureClash.kt");
}
}
@@ -108,6 +108,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
configuration.put(CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH, arguments.normalizeAbsolutePath) configuration.put(CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH, arguments.normalizeAbsolutePath)
configuration.put(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME, arguments.renderInternalDiagnosticNames) configuration.put(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME, arguments.renderInternalDiagnosticNames)
configuration.put(CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS, arguments.enableSignatureClashChecks)
return environment return environment
} }
@@ -1,5 +1,6 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.KtSourceFile import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.metadata.makeSerializedKlibMetadata import org.jetbrains.kotlin.backend.common.serialization.metadata.makeSerializedKlibMetadata
@@ -10,9 +11,13 @@ import org.jetbrains.kotlin.backend.konan.driver.phases.FirOutput
import org.jetbrains.kotlin.backend.konan.driver.phases.FirSerializerInput import org.jetbrains.kotlin.backend.konan.driver.phases.FirSerializerInput
import org.jetbrains.kotlin.backend.konan.driver.phases.SerializerOutput import org.jetbrains.kotlin.backend.konan.driver.phases.SerializerOutput
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.fir.reportToMessageCollector
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.backend.ConstValueProviderImpl import org.jetbrains.kotlin.fir.backend.ConstValueProviderImpl
import org.jetbrains.kotlin.fir.backend.extractFirDeclarations import org.jetbrains.kotlin.fir.backend.extractFirDeclarations
@@ -65,8 +70,10 @@ internal fun PhaseContext.firSerializerBase(
} }
val actualizedFirDeclarations = fir2IrInput?.irActualizedResult?.actualizedExpectDeclarations?.extractFirDeclarations() val actualizedFirDeclarations = fir2IrInput?.irActualizedResult?.actualizedExpectDeclarations?.extractFirDeclarations()
return serializeNativeModule( val diagnosticReporter = DiagnosticReporterFactory.createPendingReporter()
val serializerOutput = serializeNativeModule(
configuration = configuration, configuration = configuration,
diagnosticReporter = diagnosticReporter,
messageLogger = configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None, messageLogger = configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None,
sourceFiles, sourceFiles,
usedResolvedLibraries?.map { it.library as KonanLibrary }, usedResolvedLibraries?.map { it.library as KonanLibrary },
@@ -95,6 +102,12 @@ internal fun PhaseContext.firSerializerBase(
produceHeaderKlib, produceHeaderKlib,
) )
} }
val renderDiagnosticNames = configuration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME)
diagnosticReporter.reportToMessageCollector(messageCollector, renderDiagnosticNames)
if (diagnosticReporter.hasErrors) {
throw KonanCompilationException("Compilation failed: there were errors during module serialization")
}
return serializerOutput
} }
class KotlinFileSerializedData( class KotlinFileSerializedData(
@@ -109,6 +122,7 @@ class KotlinFileSerializedData(
internal fun PhaseContext.serializeNativeModule( internal fun PhaseContext.serializeNativeModule(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
diagnosticReporter: DiagnosticReporter,
messageLogger: IrMessageLogger, messageLogger: IrMessageLogger,
files: List<KtSourceFile>, files: List<KtSourceFile>,
dependencies: List<KonanLibrary>?, dependencies: List<KonanLibrary>?,
@@ -125,9 +139,11 @@ internal fun PhaseContext.serializeNativeModule(
val sourceBaseDirs = configuration[CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES] ?: emptyList() val sourceBaseDirs = configuration[CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES] ?: emptyList()
val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false
val signatureClashChecks = configuration[CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS] ?: true
val serializedIr = moduleFragment?.let { val serializedIr = moduleFragment?.let {
KonanIrModuleSerializer( KonanIrModuleSerializer(
KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, configuration.languageVersionSettings),
messageLogger, messageLogger,
moduleFragment.irBuiltins, moduleFragment.irBuiltins,
CompatibilityMode.CURRENT, CompatibilityMode.CURRENT,
@@ -135,7 +151,8 @@ internal fun PhaseContext.serializeNativeModule(
sourceBaseDirs = sourceBaseDirs, sourceBaseDirs = sourceBaseDirs,
languageVersionSettings = configuration.languageVersionSettings, languageVersionSettings = configuration.languageVersionSettings,
bodiesOnlyForInlines = bodiesOnlyForInlines, bodiesOnlyForInlines = bodiesOnlyForInlines,
skipPrivateApi = skipPrivateApi skipPrivateApi = skipPrivateApi,
shouldCheckSignaturesOnUniqueness = signatureClashChecks,
).serializedIrModule(moduleFragment) ).serializedIrModule(moduleFragment)
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.konan.driver.phases package org.jetbrains.kotlin.backend.konan.driver.phases
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.phaser.createSimpleNamedCompilerPhase import org.jetbrains.kotlin.backend.common.phaser.createSimpleNamedCompilerPhase
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
@@ -14,6 +15,7 @@ import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.library.SerializedIrModule import org.jetbrains.kotlin.library.SerializedIrModule
@@ -44,7 +46,12 @@ internal val SerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, Seri
val serializedIr = input.psiToIrOutput?.let { val serializedIr = input.psiToIrOutput?.let {
val ir = it.irModule val ir = it.irModule
KonanIrModuleSerializer( KonanIrModuleSerializer(
messageLogger, ir.irBuiltins, KtDiagnosticReporterWithImplicitIrBasedContext(
DiagnosticReporterFactory.createPendingReporter(),
config.languageVersionSettings
),
messageLogger,
ir.irBuiltins,
compatibilityMode = CompatibilityMode.CURRENT, compatibilityMode = CompatibilityMode.CURRENT,
normalizeAbsolutePaths = normalizeAbsolutePaths, normalizeAbsolutePaths = normalizeAbsolutePaths,
sourceBaseDirs = relativePathBase, sourceBaseDirs = relativePathBase,
@@ -0,0 +1,12 @@
package com.example.klib.serialization.diagnostics
class A {
@Deprecated("", level = DeprecationLevel.HIDDEN)
fun foo(): String = ""
fun foo(): Int = 0
}
fun main() {
println(A().foo())
}
@@ -0,0 +1,11 @@
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:4:5: error: platform declaration clash: The following declarations have the same KLIB signature (com.example.klib.serialization.diagnostics/A.foo|foo(){}[0]):
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics.A
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics.A
@Deprecated("", level = DeprecationLevel.HIDDEN)
^
native/native.tests/testData/compilerOutput/SignatureClashDiagnostics/main.kt:7:5: error: platform declaration clash: The following declarations have the same KLIB signature (com.example.klib.serialization.diagnostics/A.foo|foo(){}[0]):
fun foo(): kotlin.String defined in com.example.klib.serialization.diagnostics.A
fun foo(): kotlin.Int defined in com.example.klib.serialization.diagnostics.A
fun foo(): Int = 0
^
COMPILATION_ERROR
@@ -109,7 +109,7 @@ abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() {
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput()) KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
} }
private fun compileLibrary( internal fun compileLibrary(
settings: Settings, settings: Settings,
source: File, source: File,
freeCompilerArgs: List<String> = emptyList(), freeCompilerArgs: List<String> = emptyList(),
@@ -126,7 +126,7 @@ abstract class CompilerOutputTestBase : AbstractNativeSimpleTest() {
return compilation.result return compilation.result
} }
private fun TestCompilationResult<*>.toOutput(): String { internal fun TestCompilationResult<*>.toOutput(): String {
check(this is TestCompilationResult.ImmediateResult<*>) { this } check(this is TestCompilationResult.ImmediateResult<*>) { this }
val loggedData = this.loggedData val loggedData = this.loggedData
check(loggedData is LoggedData.CompilationToolCall) { loggedData::class } check(loggedData is LoggedData.CompilationToolCall) { loggedData::class }
@@ -152,4 +152,16 @@ class ClassicCompilerOutputTest : CompilerOutputTestBase()
@Tag("frontend-fir") @Tag("frontend-fir")
@TestDataPath("\$PROJECT_ROOT") @TestDataPath("\$PROJECT_ROOT")
@EnforcedProperty(ClassLevelProperty.COMPILER_OUTPUT_INTERCEPTOR, "NONE") @EnforcedProperty(ClassLevelProperty.COMPILER_OUTPUT_INTERCEPTOR, "NONE")
class FirCompilerOutputTest : CompilerOutputTestBase() class FirCompilerOutputTest : CompilerOutputTestBase() {
@Test
fun testSignatureClashDiagnostics() {
// TODO: use the Compiler Core test infrastructure for testing these diagnostics (KT-64393)
val rootDir = File("native/native.tests/testData/compilerOutput/SignatureClashDiagnostics")
val settings = testRunSettings
val compilationResult = compileLibrary(settings, rootDir.resolve("main.kt"))
val goldenData = rootDir.resolve("output.txt")
KotlinTestUtils.assertEqualsToFile(goldenData, compilationResult.toOutput())
}
}