[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
@@ -108,6 +108,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
configuration.put(CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH, arguments.normalizeAbsolutePath)
configuration.put(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME, arguments.renderInternalDiagnosticNames)
configuration.put(CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS, arguments.enableSignatureClashChecks)
return environment
}
@@ -1,5 +1,6 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
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.SerializerOutput
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.CompilerConfiguration
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.backend.ConstValueProviderImpl
import org.jetbrains.kotlin.fir.backend.extractFirDeclarations
@@ -65,8 +70,10 @@ internal fun PhaseContext.firSerializerBase(
}
val actualizedFirDeclarations = fir2IrInput?.irActualizedResult?.actualizedExpectDeclarations?.extractFirDeclarations()
return serializeNativeModule(
val diagnosticReporter = DiagnosticReporterFactory.createPendingReporter()
val serializerOutput = serializeNativeModule(
configuration = configuration,
diagnosticReporter = diagnosticReporter,
messageLogger = configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None,
sourceFiles,
usedResolvedLibraries?.map { it.library as KonanLibrary },
@@ -95,6 +102,12 @@ internal fun PhaseContext.firSerializerBase(
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(
@@ -109,6 +122,7 @@ class KotlinFileSerializedData(
internal fun PhaseContext.serializeNativeModule(
configuration: CompilerConfiguration,
diagnosticReporter: DiagnosticReporter,
messageLogger: IrMessageLogger,
files: List<KtSourceFile>,
dependencies: List<KonanLibrary>?,
@@ -125,9 +139,11 @@ internal fun PhaseContext.serializeNativeModule(
val sourceBaseDirs = configuration[CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES] ?: emptyList()
val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false
val signatureClashChecks = configuration[CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS] ?: true
val serializedIr = moduleFragment?.let {
KonanIrModuleSerializer(
KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, configuration.languageVersionSettings),
messageLogger,
moduleFragment.irBuiltins,
CompatibilityMode.CURRENT,
@@ -135,7 +151,8 @@ internal fun PhaseContext.serializeNativeModule(
sourceBaseDirs = sourceBaseDirs,
languageVersionSettings = configuration.languageVersionSettings,
bodiesOnlyForInlines = bodiesOnlyForInlines,
skipPrivateApi = skipPrivateApi
skipPrivateApi = skipPrivateApi,
shouldCheckSignaturesOnUniqueness = signatureClashChecks,
).serializedIrModule(moduleFragment)
}
@@ -5,6 +5,7 @@
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.serialization.CompatibilityMode
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.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.library.SerializedIrModule
@@ -44,7 +46,12 @@ internal val SerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, Seri
val serializedIr = input.psiToIrOutput?.let {
val ir = it.irModule
KonanIrModuleSerializer(
messageLogger, ir.irBuiltins,
KtDiagnosticReporterWithImplicitIrBasedContext(
DiagnosticReporterFactory.createPendingReporter(),
config.languageVersionSettings
),
messageLogger,
ir.irBuiltins,
compatibilityMode = CompatibilityMode.CURRENT,
normalizeAbsolutePaths = normalizeAbsolutePaths,
sourceBaseDirs = relativePathBase,