[FIR] Fix serialization of empty varargs in annotations in KLIBs

#KT-59249 Fixed
This commit is contained in:
Kirill Rakhman
2023-07-26 13:51:06 +02:00
committed by Space Team
parent 487c3e80bb
commit 38a54549ff
10 changed files with 189 additions and 37 deletions
@@ -147,7 +147,7 @@ abstract class AbstractLoadedMetadataDumpHandler<A : ResultingArtifact.Binary<A>
}
val emptyModule = TestModule(
name = "empty", module.targetPlatform, module.targetBackend, FrontendKinds.FIR,
name = "dump-${module.name}", module.targetPlatform, module.targetBackend, FrontendKinds.FIR,
BackendKinds.IrBackend, module.binaryKind, files = emptyList(),
allDependencies = listOf(DependencyDescription(module.name, dependencyKind, DependencyRelation.RegularDependency)),
RegisteredDirectives.Empty, languageVersionSettings
@@ -6,15 +6,14 @@
package org.jetbrains.kotlin.fir.serialization.constant
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.constant.AnnotationValue
import org.jetbrains.kotlin.constant.ConstantValue
import org.jetbrains.kotlin.constant.ErrorValue
import org.jetbrains.kotlin.constant.KClassValue
import org.jetbrains.kotlin.constant.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.itOrExpectHasDefaultParameterValue
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -22,24 +21,34 @@ import org.jetbrains.kotlin.utils.addToStdlib.runIf
internal fun Map<Name, FirExpression>.convertToConstantValues(
session: FirSession,
constValueProvider: ConstValueProvider?
): Map<Name, ConstantValue<*>> {
return this.map { (name, firExpression) ->
val constantValue = constValueProvider?.findConstantValueFor(firExpression)
): MutableMap<Name, ConstantValue<*>> {
return this.mapValuesTo(mutableMapOf()) { (_, firExpression) ->
constValueProvider?.findConstantValueFor(firExpression)
?: firExpression.toConstantValue(session, constValueProvider)
?: runIf(session.languageVersionSettings.getFlag(AnalysisFlags.metadataCompilation)) {
ErrorValue.ErrorValueWithMessage("Constant conversion can be ignored in metadata compilation mode")
}
?: error("Cannot convert expression ${firExpression.render()} to constant")
name to constantValue
}.toMap()
}
}
internal fun MutableMap<Name, ConstantValue<Any?>>.addEmptyVarargValuesFor(
symbol: FirFunctionSymbol<*>?,
): MutableMap<Name, ConstantValue<Any?>> = apply {
if (symbol == null) return@apply
for ((i, parameter) in symbol.valueParameterSymbols.withIndex()) {
if (parameter.name !in this && parameter.isVararg && !symbol.fir.itOrExpectHasDefaultParameterValue(i)) {
this[parameter.name] = ArrayValue(emptyList())
}
}
}
internal fun LinkedHashMap<FirExpression, FirValueParameter>.convertToConstantValues(
session: FirSession,
constValueProvider: ConstValueProvider?,
): Map<Name, ConstantValue<*>> {
return this.map { (firExpression, firValueParameter) -> firValueParameter.name to firExpression }
.toMap().convertToConstantValues(session, constValueProvider)
): MutableMap<Name, ConstantValue<*>> {
return this.entries.associate { (firExpression, firValueParameter) -> firValueParameter.name to firExpression }
.convertToConstantValues(session, constValueProvider)
}
inline fun <reified T : ConeKotlinType> AnnotationValue.coneTypeSafe(): T? {
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.declarations.FirField
import org.jetbrains.kotlin.fir.declarations.utils.isConst
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.toResolvedFunctionSymbol
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirArrayOfCallTransformer
@@ -101,9 +102,12 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<
override fun visitAnnotation(
annotation: FirAnnotation,
data: FirToConstantValueTransformerData
data: FirToConstantValueTransformerData,
): ConstantValue<*> {
val mapping = annotation.argumentMapping.mapping.convertToConstantValues(data.session, data.constValueProvider)
val mapping = annotation.argumentMapping.mapping.convertToConstantValues(
data.session,
data.constValueProvider
).addEmptyVarargValuesFor(annotation.toReference()?.toResolvedFunctionSymbol())
return AnnotationValue.create(annotation.annotationTypeRef.coneType, mapping)
}
@@ -149,7 +153,10 @@ internal object FirToConstantValueTransformer : FirDefaultVisitor<ConstantValue<
if (constructedClassSymbol.classKind != ClassKind.ANNOTATION_CLASS) return null
val mapping = constructorCall.resolvedArgumentMapping
?.convertToConstantValues(data.session, data.constValueProvider)
?.convertToConstantValues(
data.session,
data.constValueProvider
)?.addEmptyVarargValuesFor(symbol)
?: return null
return AnnotationValue.create(qualifiedAccessExpression.typeRef.coneType, mapping)
}
@@ -48,6 +48,10 @@ fun FirReference.toResolvedValueParameterSymbol(discardErrorReference: Boolean =
return this.toResolvedSymbol<FirValueParameterSymbol>(discardErrorReference)
}
fun FirReference.toResolvedFunctionSymbol(discardErrorReference: Boolean = false): FirFunctionSymbol<*>? {
return this.toResolvedSymbol<FirFunctionSymbol<*>>(discardErrorReference)
}
fun FirReference.toResolvedNamedFunctionSymbol(discardErrorReference: Boolean = false): FirNamedFunctionSymbol? {
return this.toResolvedSymbol<FirNamedFunctionSymbol>(discardErrorReference)
}
@@ -1,7 +1,9 @@
Module: common
Module: platform
field:@FIELD:R|test/Anno|(t = <implicitArrayOf>(String(prosper))) public final val bar: R|kotlin/Int|
public get(): R|kotlin/Int|
@R|test/Anno|(t = <implicitArrayOf>()) public final fun baz(): R|kotlin/Unit|
@R|test/Anno|(t = <implicitArrayOf>()) @R|test/Anno2|() @R|test/Anno3|() @R|test/Anno4|(t = <implicitArrayOf>()) @R|test/Anno5|(t = <implicitArrayOf>()) @R|test/Anno6|(t = <implicitArrayOf>()) public final fun baz(): R|kotlin/Unit|
@R|test/Anno|(t = <implicitArrayOf>(String(live), String(long))) public final fun foo(): R|kotlin/Unit|
@@ -13,3 +15,42 @@ public final annotation class Anno : R|kotlin/Annotation| {
}
public final annotation class Anno2 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>()
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno2|
}
public final annotation class Anno3 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>(String(a))
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno3|
}
public final annotation class Anno4 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>|): R|test/Anno4|
}
public final annotation class Anno5 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>()
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno5|
}
public final annotation class Anno6 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>(String(a))
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno6|
}
@@ -1,7 +1,7 @@
field:@FIELD:R|test/Anno|(t = <implicitArrayOf>(String(prosper))) public final val bar: R|kotlin/Int|
public get(): R|kotlin/Int|
@R|test/Anno|(t = <implicitArrayOf>()) public final fun baz(): R|kotlin/Unit|
@R|test/Anno|(t = <implicitArrayOf>()) @R|test/Anno2|() @R|test/Anno3|() @R|test/Anno4|(t = <implicitArrayOf>()) @R|test/Anno5|() @R|test/Anno6|() public final fun baz(): R|kotlin/Unit|
@R|test/Anno|(t = <implicitArrayOf>(String(live), String(long))) public final fun foo(): R|kotlin/Unit|
@@ -13,3 +13,42 @@ public final annotation class Anno : R|kotlin/Annotation| {
}
public final annotation class Anno2 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>()
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno2|
}
public final annotation class Anno3 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>(String(a))
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno3|
}
public final annotation class Anno4 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>|): R|test/Anno4|
}
public final annotation class Anno5 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>()
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno5|
}
public final annotation class Anno6 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>| = <implicitArrayOf>(String(a))
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno6|
}
@@ -1,7 +1,7 @@
public final val bar: R|kotlin/Int|
public get(): R|kotlin/Int|
@R|test/Anno|() public final fun baz(): R|kotlin/Unit|
@R|test/Anno|(t = <implicitArrayOf>()) @R|test/Anno2|() @R|test/Anno3|() @R|test/Anno4|(t = <implicitArrayOf>()) @R|test/Anno5|() @R|test/Anno6|() public final fun baz(): R|kotlin/Unit|
@R|test/Anno|(t = <implicitArrayOf>(String(live), String(long))) public final fun foo(): R|kotlin/Unit|
@@ -12,3 +12,43 @@ public final annotation class Anno : R|kotlin/Annotation| {
public constructor(vararg t: R|kotlin/Array<out kotlin/String>|): R|test/Anno|
}
public final annotation class Anno2 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno2|
}
public final annotation class Anno3 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno3|
}
public final annotation class Anno4 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>|): R|test/Anno4|
}
public final annotation class Anno5 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno5|
}
public final annotation class Anno6 : R|kotlin/Annotation| {
public final val t: R|kotlin/Array<out kotlin/String>|
public get(): R|kotlin/Array<out kotlin/String>|
public constructor(vararg t: R|kotlin/Array<out kotlin/String>| = STUB): R|test/Anno6|
}
@@ -1,11 +1,33 @@
// PLATFORM_DEPENDANT_METADATA
//ALLOW_AST_ACCESS
// ALLOW_AST_ACCESS
// NO_CHECK_SOURCE_VS_BINARY
// IGNORE_BACKEND_K1: JVM_IR, ANY
// LANGUAGE: +MultiPlatformProjects
// IGNORE K1
// ^mute KotlinpCompilerTestDataTest
// K1/K2 difference: KT-60820
// MODULE: common
// FILE: common.kt
package test
expect annotation class Anno4(vararg val t: String)
expect annotation class Anno5(vararg val t: String = [])
expect annotation class Anno6(vararg val t: String = ["a"])
// MODULE: platform()()(common)
// FILE: test.kt
package test
annotation class Anno(vararg val t: String)
annotation class Anno2(vararg val t: String = [])
annotation class Anno3(vararg val t: String = ["a"])
actual annotation class Anno4(vararg val t: String)
actual annotation class Anno5(vararg val t: String)
actual annotation class Anno6(vararg val t: String)
@Anno("live", "long") fun foo() {}
@field:Anno("prosper") val bar = { 42 }()
@Anno() fun baz() {}
@Anno() @Anno2() @Anno3() @Anno4() @Anno5() @Anno6() fun baz() {}
@@ -1,12 +0,0 @@
package test
@field:test.Anno(t = {"prosper"}) public val bar: kotlin.Int
public fun `<get-bar>`(): kotlin.Int
@test.Anno(t = {}) public fun baz(): kotlin.Unit
@test.Anno(t = {"live", "long"}) public fun foo(): kotlin.Unit
public final annotation class Anno : kotlin.Annotation {
/*primary*/ public constructor Anno(/*0*/ vararg t: kotlin.String /*kotlin.Array<out kotlin.String>*/)
public final val t: kotlin.Array<out kotlin.String>
public final fun `<get-t>`(): kotlin.Array<out kotlin.String>
}
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.File
import kotlin.test.fail
private const val IGNORE_K1_DIRECTIVE = "// IGNORE K1"
private const val IGNORE_K2_DIRECTIVE = "// IGNORE K2"
fun compareAllFiles(
@@ -37,15 +38,16 @@ fun compareAllFiles(
readWriteAndCompare: Boolean,
useK2: Boolean,
) {
val isMutedForK2 = useK2 && InTextDirectivesUtils.findStringWithPrefixes(file.readText(), IGNORE_K2_DIRECTIVE) != null
val directive = if (useK2) IGNORE_K2_DIRECTIVE else IGNORE_K1_DIRECTIVE
val isMuted = InTextDirectivesUtils.findStringWithPrefixes(file.readText(), directive) != null
try {
compileAndPrintAllFiles(file, disposable, tmpdir, compareWithTxt, readWriteAndCompare, useK2)
} catch (e: Throwable) {
if (isMutedForK2) return
if (isMuted) return
throw e
}
if (isMutedForK2) {
throw AssertionError("Looks like this test can be unmuted. Remove the \"$IGNORE_K2_DIRECTIVE\" directive.")
if (isMuted) {
throw AssertionError("Looks like this test can be unmuted. Remove the \"$directive\" directive.")
}
}